Fakultas Ilmu Komputer UI
Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
ppl-fasilkom-ui
PPL Sosial
tbcare
tbcare-web
Commits
aaec2801
Commit
aaec2801
authored
Jun 06, 2021
by
Nabilah Adani
Browse files
[CHO
parents
72be312f
8facf5b7
Pipeline
#81686
failed with stages
in 1 minute and 12 seconds
Changes
16
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
src/App/components/Routes/components/Layout/index.tsx
View file @
aaec2801
...
...
@@ -25,6 +25,10 @@ const navigators = [
name
:
'
Manajemen Akun
'
,
to
:
'
/account-management
'
,
},
{
name
:
'
Log Aktivitas
'
,
to
:
'
/activity-log
'
,
},
];
const
Layout
=
(
props
:
LayoutProps
)
=>
{
...
...
src/App/components/Routes/index.tsx
View file @
aaec2801
...
...
@@ -6,6 +6,7 @@ import { Home, AccountManagement, Login, CaseRecapitulation, PositiveCaseInput }
import
{
LocalStorage
}
from
'
services
'
;
import
{
DEFAULT_SECRET_KEY
}
from
'
constant
'
;
import
Layout
from
'
./components/Layout
'
;
import
ActivityLog
from
'
scenes/ActivityLog
'
;
export
default
function
Routes
()
{
const
{
user
,
setUser
,
setToken
,
token
,
services
,
alert
}
=
useContext
(
...
...
@@ -78,6 +79,11 @@ export default function Routes() {
path
=
"/positive-case-input"
component
=
{
PositiveCaseInput
}
/>
<
Route
exact
path
=
"/activity-log"
component
=
{
ActivityLog
}
/>
</
Layout
>
</>
)
:
(
...
...
src/components/Table/index.tsx
View file @
aaec2801
...
...
@@ -9,7 +9,6 @@ import Button from 'components/Button';
import
Loading
from
'
components/Loading
'
;
import
Icon
from
'
components/Icon
'
;
type
ValueType
=
string
|
number
;
interface
TableProps
{
...
...
@@ -17,10 +16,14 @@ interface TableProps {
header
:
Array
<
string
>
;
data
?:
Array
<
Array
<
ValueType
>>
;
setData
?:
any
;
onChange
?:
(
value
:
string
,
pageNumber
:
number
)
=>
Promise
<
Array
<
Array
<
ValueType
>>>
;
onChange
?:
(
value
:
string
,
pageNumber
:
number
)
=>
Promise
<
Array
<
Array
<
ValueType
>>>
;
searchPlaceholder
?:
string
;
maximumData
?:
number
;
rowOnClick
?:
(
row
:
Array
<
ValueType
>
)
=>
void
rowOnClick
?:
(
row
:
Array
<
ValueType
>
)
=>
void
;
itemPerPage
?:
number
;
}
const
Click
=
styled
.
div
`
...
...
@@ -68,7 +71,7 @@ const TableHeader = styled.th`
const
LoadingContainer
=
styled
.
tr
`
height: 400px;
`
`
;
const
DEFAULT_THEME
=
{
colors
:
{
...
...
@@ -91,6 +94,7 @@ export default function Table({
onChange
=
async
()
=>
new
Promise
<
Array
<
Array
<
ValueType
>>>
(()
=>
[]),
maximumData
=
-
1
,
rowOnClick
,
itemPerPage
,
}:
TableProps
)
{
const
{
colors
}
=
useContext
(
ThemeContext
)
||
DEFAULT_THEME
;
const
[
previousDataTotal
,
setPreviousDataTotal
]
=
useState
(
0
);
...
...
@@ -116,16 +120,24 @@ export default function Table({
return
;
setIsLoading
(
true
);
const
newData
:
Array
<
Array
<
ValueType
>>
=
await
onChange
(
searchValue
,
newPageNumber
);
setIsLoading
(
false
);
await
onChange
(
searchValue
,
newPageNumber
).
then
((
newData
)
=>
{
setIsLoading
(
false
);
if
(
newData
.
length
===
0
||
newData
[
0
].
length
===
0
)
return
;
setPageNumber
(
newPageNumber
);
setPreviousDataTotal
(
previousDataTotal
+
(
sign
===
1
?
data
.
length
:
-
newData
.
length
));
setData
([...
newData
]);
if
(
newData
.
length
===
0
||
newData
[
0
].
length
===
0
)
return
;
setPageNumber
(
newPageNumber
);
setPreviousDataTotal
(
previousDataTotal
+
(
sign
===
1
?
data
.
length
:
-
newData
.
length
)
);
if
(
action
===
PageAction
.
Prev
&&
previousDataTotal
+
data
.
length
===
maximumData
)
{
setPreviousDataTotal
(
previousDataTotal
-
(
itemPerPage
?
itemPerPage
:
10
)
);
}
setData
([...
newData
]);
});
};
const
[
couldGoBack
,
couldGoNext
]
=
[
...
...
@@ -136,7 +148,7 @@ export default function Table({
useEffect
(()
=>
{
setData
(
data
);
},
[
JSON
.
stringify
(
data
)]);
useEffect
(()
=>
{
const
initializeData
=
async
()
=>
{
setIsLoading
(
true
);
...
...
@@ -149,7 +161,7 @@ export default function Table({
initializeData
();
},
[]);
if
(
data
[
0
]
&&
(
header
.
length
>
data
[
0
].
length
)
)
{
if
(
data
[
0
]
&&
header
.
length
>
data
[
0
].
length
)
{
throw
new
Error
(
'
The shape of Header and data are not consistent
'
);
}
...
...
@@ -171,14 +183,14 @@ export default function Table({
<
Button
data-test-id
=
"search-button"
onClick
=
{
async
()
=>
{
const
newPageNumber
=
1
;
const
newPageNumber
=
pageNumber
;
setIsLoading
(
true
);
const
newData
=
await
onChange
(
searchValue
,
newPageNumber
);
setIsLoading
(
false
);
if
(
newData
.
length
===
0
||
newData
[
0
].
length
===
0
)
return
;
setPageNumber
(
newPageNumber
);
setPreviousDataTotal
(
0
);
setData
([...
newData
]);
}
}
>
...
...
@@ -218,7 +230,7 @@ export default function Table({
})
}
</
tr
>
</
thead
>
{
isLoading
?
(
{
isLoading
?
(
<
tbody
>
<
LoadingContainer
>
<
Loading
isLoading
=
{
isLoading
}
shouldSetBlackTheme
=
{
true
}
/>
...
...
@@ -235,20 +247,22 @@ export default function Table({
index
%
2
===
0
?
'
transparent
'
:
colors
.
almostWhite
}
>
{
row
.
filter
((
_
,
index
)
=>
index
<
header
.
length
).
map
((
value
:
ValueType
,
index
:
number
)
=>
{
return
(
<
td
key
=
{
index
}
>
<
Text
width
=
"100%"
type
=
{
Text
.
StyleType
.
Small
}
align
=
"center"
>
{
value
.
toString
()
}
</
Text
>
</
td
>
);
})
}
{
rowOnClick
?
(
{
row
.
filter
((
_
,
index
)
=>
index
<
header
.
length
)
.
map
((
value
:
ValueType
,
index
:
number
)
=>
{
return
(
<
td
key
=
{
index
}
>
<
Text
width
=
"100%"
type
=
{
Text
.
StyleType
.
Small
}
align
=
"center"
>
{
value
.
toString
()
}
</
Text
>
</
td
>
);
})
}
{
rowOnClick
?
(
<
td
>
<
Box
height
=
"100%"
...
...
@@ -269,34 +283,33 @@ export default function Table({
crossAxis
=
"center"
background
=
{
colors
.
green
}
>
<
Icon
src
=
"/assets/icons/zoom.svg"
height
=
"50%"
cursor
=
"pointer"
/>
<
Icon
src
=
"/assets/icons/zoom.svg"
height
=
"50%"
cursor
=
"pointer"
/>
</
Box
>
</
Click
>
</
Box
>
</
td
>
)
:
<></>
}
)
:
(
<></>
)
}
</
Row
>
);
})
}
</
tbody
>
)
}
</
StyledTable
>
{
!
isLoading
&&
data
.
length
===
0
?
(
<
Box
width
=
"100%"
height
=
"300px"
mainAxis
=
'center'
crossAxis
=
'center'
>
<
Text
type
=
{
Text
.
StyleType
.
Medium
}
color
=
{
colors
.
mediumGray
}
>
Tidak ada data
</
Text
>
</
Box
>
)
:
<></>
}
{
!
isLoading
&&
data
.
length
===
0
?
(
<
Box
width
=
"100%"
height
=
"300px"
mainAxis
=
"center"
crossAxis
=
"center"
>
<
Text
type
=
{
Text
.
StyleType
.
Medium
}
color
=
{
colors
.
mediumGray
}
>
Tidak ada data
</
Text
>
</
Box
>
)
:
(
<></>
)
}
<
Gap
gap
=
{
20
}
axis
=
{
Gap
.
Axis
.
Vertical
}
/>
<
Box
width
=
"100%"
mainAxis
=
"flex-end"
>
<
Box
crossAxis
=
"center"
>
...
...
@@ -310,24 +323,35 @@ export default function Table({
data-test-id
=
"prev-button"
id
=
"prev-button"
cursor
=
"pointer"
onClick
=
{
couldGoBack
?
()
=>
changePage
(
PageAction
.
Prev
)
:
()
=>
{}
}
onClick
=
{
couldGoBack
&&
searchValue
===
''
?
()
=>
changePage
(
PageAction
.
Prev
)
:
()
=>
{}
}
src
=
"/assets/icons/right-paging.svg"
origin
=
"50% 50%"
height
=
"16px"
opacity
=
{
couldGoBack
?
1
:
0.5
}
opacity
=
{
couldGoBack
&&
searchValue
===
''
?
1
:
0.5
}
transform
=
"rotateZ(180deg)"
/>
<
Gap
gap
=
{
12
}
axis
=
{
Gap
.
Axis
.
Horizontal
}
/>
<
Text
id
=
'page-number'
type
=
{
Text
.
StyleType
.
Small
}
>
{
`
${
pageNumber
}
`
}
</
Text
>
<
Text
id
=
"page-number"
type
=
{
Text
.
StyleType
.
Small
}
>
{
`
${
pageNumber
}
`
}
</
Text
>
<
Gap
gap
=
{
12
}
axis
=
{
Gap
.
Axis
.
Horizontal
}
/>
<
Icon
data-test-id
=
"next-button"
id
=
"next-button"
cursor
=
"pointer"
onClick
=
{
couldGoNext
?
()
=>
changePage
(
PageAction
.
Next
)
:
()
=>
{}
}
onClick
=
{
couldGoNext
&&
searchValue
===
''
?
()
=>
changePage
(
PageAction
.
Next
)
:
()
=>
{}
}
src
=
"/assets/icons/right-paging.svg"
height
=
"16px"
opacity
=
{
couldGoNext
?
1
:
0.5
}
opacity
=
{
couldGoNext
&&
searchValue
===
''
?
1
:
0.5
}
/>
</
Box
>
</
Box
>
...
...
src/scenes/
Home
/components/ActivityL
og
/index.test.tsx
→
src/scenes/
ActivityLog
/components/ActivityL
ist
/index.test.tsx
View file @
aaec2801
import
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
renderer
,
{
act
}
from
'
react-test-renderer
'
;
import
ActivityL
og
from
'
.
'
;
import
renderer
from
'
react-test-renderer
'
;
import
ActivityL
ist
from
'
.
'
;
import
{
useMainService
}
from
'
services
'
;
import
{
AppContext
}
from
'
contexts
'
;
import
{
mount
}
from
'
enzyme
'
;
const
dummyLog
=
{
status
:
200
,
data
:
{
username
:
"
Test
"
,
is_admin
:
false
,
previous
:
true
,
next
:
true
,
count
:
1
,
results
:
[
{
action_type
:
"
Delete
"
,
model_name
:
"
Account
"
,
object_id
:
"
12346
"
,
recorded_at
:
"
2020-05-31T23:03:35.854615+07:00
"
,
},
]
}
}
jest
.
mock
(
'
axios
'
);
const
mockedAxios
=
axios
as
jest
.
Mocked
<
typeof
axios
>
;
const
testProps
=
{
...
...
@@ -14,9 +33,9 @@ const testProps = {
},
};
it
(
'
renders correctly
'
,
()
=>
{
it
(
'
ActivityList
renders correctly
'
,
()
=>
{
const
instance
=
renderer
.
create
(
<
ActivityL
og
/>
<
ActivityL
ist
/>
);
expect
(
instance
).
toBeTruthy
();
...
...
@@ -45,13 +64,13 @@ describe('load logs and generate log messages correctly', () => {
}
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -75,13 +94,13 @@ describe('load logs and generate log messages correctly', () => {
}
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -102,13 +121,13 @@ describe('load logs and generate log messages correctly', () => {
],
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -131,13 +150,13 @@ describe('load logs and generate log messages correctly', () => {
]
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -158,13 +177,13 @@ describe('load logs and generate log messages correctly', () => {
]
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -187,13 +206,13 @@ describe('load logs and generate log messages correctly', () => {
]
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -214,13 +233,13 @@ describe('load logs and generate log messages correctly', () => {
]
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
...
...
@@ -243,96 +262,55 @@ describe('load logs and generate log messages correctly', () => {
]
}
});
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
});
});
it
(
'
fetch new logs and change page number when press next or previous button
'
,
()
=>
{
mockedAxios
.
request
.
mockResolvedValue
({
status
:
200
,
data
:
{
username
:
"
Test
"
,
is_admin
:
false
,
previous
:
true
,
next
:
true
,
count
:
1
,
results
:
[
{
action_type
:
"
Delete
"
,
model_name
:
"
Account
"
,
object_id
:
"
1234
"
,
recorded_at
:
"
2020-05-31T23:03:35.854615+07:00
"
,
},
]
}
});
mockedAxios
.
request
.
mockResolvedValue
(
dummyLog
);
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
const
prevButton
=
instance
.
find
(
'
#prev-button
'
);
const
nextButton
=
instance
.
find
(
'
#next-button
'
);
let
pageNumber
=
instance
.
find
(
'
Text
'
).
findWhere
(
elem
=>
elem
.
prop
(
'
id
'
)
===
'
page-number
'
);
expect
(
pageNumber
.
text
()).
toBe
(
'
1
'
);
nextButton
.
at
(
0
).
simulate
(
'
click
'
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
prevButton
.
at
(
0
).
simulate
(
'
click
'
);
pageNumber
=
instance
.
find
(
'
Text
'
).
findWhere
(
elem
=>
elem
.
prop
(
'
id
'
)
===
'
page-number
'
);
expect
(
pageNumber
.
text
()).
toBe
(
'
1
'
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
})
it
(
'
Sort and filter are displayed and clickable
'
,
()
=>
{
mockedAxios
.
request
.
mockResolvedValue
({
status
:
200
,
data
:
{
username
:
"
Test
"
,
is_admin
:
false
,
previous
:
true
,
next
:
true
,
count
:
1
,
results
:
[
{
action_type
:
"
Delete
"
,
model_name
:
"
Account
"
,
object_id
:
"
1234
"
,
recorded_at
:
"
2020-05-31T23:03:35.854615+07:00
"
,
},
]
}
});
it
(
'
filter are displayed and clickable
'
,
()
=>
{
mockedAxios
.
request
.
mockResolvedValue
(
dummyLog
);
const
instance
=
mount
(
<
AppContext
.
Provider
value
=
{
testProps
}
>
<
ActivityL
og
/>
<
ActivityL
ist
/>
</
AppContext
.
Provider
>
);
expect
(
mockedAxios
.
request
).
toBeCalled
();
let
categoryButton
=
instance
.
find
(
'
[data-test-id="button-Tabel"]
'
);
categoryButton
.
at
(
0
).
simulate
(
'
click
'
);
let
sortByDate
=
instance
.
find
(
"
#sortbydate
"
);
let
sortByObj
=
instance
.
find
(
"
#sortbyobj
"
);
let
filter
=
instance
.
find
(
"
#filter
"
);
sortByDate
.
simulate
(
"
click
"
);
sortByObj
.
simulate
(
"
click
"
);
filter
.
simulate
(
"
click
"
);
let
table
=
instance
.
find
(
"
Table
"
);
expect
(
table
).
toBeTruthy
();
let
list
=
instance
.
find
(
"
#list-activity
"
);
expect
(
list
).
toBeTruthy
();
});
\ No newline at end of file
src/scenes/ActivityLog/components/ActivityList/index.tsx
0 → 100644
View file @
aaec2801
import
React
,
{
useContext
,
useEffect
,
useState
}
from
'
react
'
;
import
styled
,
{
ThemeContext
}
from
'
styled-components
'
;
import
{
Box
,
Text
,
Gap
,
Icon
,
Field
,
Button
,
Checkbox
}
from
'
components
'
;
import
{
AppContext
}
from
'
contexts
'
;
import
Loading
from
'
components/Loading
'
;
import
{
DateProps
}
from
'
contexts/AppContext/types
'
;
import
{
Log
}
from
'
scenes/ActivityLog/types/types
'
;
import
{
generateLogDetail
,
generateLogMessage
,
}
from
'
scenes/ActivityLog/utilities/utils
'
;
import
{
DEFAULT_THEME
}
from
'
scenes/ActivityLog
'
;
export
default
function
ActivityList
()
{
const
{
colors
}
=
useContext
(
ThemeContext
)
||
DEFAULT_THEME
;
const
{
services
}
=
useContext
(
AppContext
);
const
[
logList
,
setLogList
]
=
useState
<
Log
[]
>
([]);
const
[
page
,
setPage
]
=
useState
(
1
);
const
[
next
,
setNext
]
=
useState
(
false
);
const
[
prev
,
setPrev
]
=
useState
(
false
);
const
[
totalLog
,
setTotalLog
]
=
useState
(
0
);
const
[
isLoading
,
setIsLoading
]
=
useState
(
false
);
const
verticalLineHeight
=
(
logList
.
length
-
1
)
*
52
;
const
[
roleFilterQuery
,
setRoleFilterQuery
]
=
useState
<
String
>
(
'
Semua Peran
'
);
const
[
ignoreDateFilterQuery
,
setIgnoredateFilterQuery
]
=
useState
<
boolean
>
(
true
);
const
[
dateFilterQuery
,
setDateFilterQuery
]
=
useState
<
DateProps
>
({
start_date
:
new
Date
(),
end_date
:
new
Date
(),
});
const
VerticalLine
=
styled
.
div
`
border-left: 2px solid
${
colors
.
mediumGray
}
;
height:
${
verticalLineHeight
}
px;
position: absolute;
left: 12px;
top: 5px;
`
;
const
Circle
=
styled
.
div
`
left: 12px;
width: 24px;
height: 24px;
border-radius: 12px;
background:
${
colors
.
mediumGray
}
;
margin-right: 20px;