Merge pull request 'Sprint-4/MS-36-FE-Technical' (#92) from Sprint-4/MS-36-FE-Technical into master
Reviewed-on: #92
This commit is contained in:
commit
f55ac7e65c
|
|
@ -21,6 +21,43 @@ import { useForm } from '@mantine/form'
|
|||
import { Xdelete } from '@/rtk/helpers/CRUD'
|
||||
import moment from 'moment'
|
||||
|
||||
function OrganizationSettings() {
|
||||
const [activeTab, setActiveTab] = useState<string | null>('technical')
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
<h3>
|
||||
<Text>Admin/</Text>
|
||||
Organization Settings
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<Box w="100%" display={'flex'} mt={15} ml={10}>
|
||||
<Tabs w="100%" value={activeTab} onChange={setActiveTab}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="technical">Technical Setting</Tabs.Tab>
|
||||
<Tabs.Tab value="second">Setting 2</Tabs.Tab>
|
||||
<Tabs.Tab value="third">Setting 3</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="technical" pt="xs">
|
||||
<TechnicalSettingTab />
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="second" pt="xs">
|
||||
Setting 2
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="third" pt="xs">
|
||||
Setting 3
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface DataTechnical {
|
||||
id: number
|
||||
name: string
|
||||
|
|
@ -29,15 +66,13 @@ interface DataTechnical {
|
|||
updated_at: string
|
||||
}
|
||||
|
||||
function OrganizationSettings() {
|
||||
const [activeTab, setActiveTab] = useState<string | null>('technical')
|
||||
const TechnicalSettingTab = () => {
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [addTechnicalOpen, setAddTechnicalOpen] = useState(false)
|
||||
const [deleteTechnicalOpen, setDeleteTechnicalOpen] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState(0)
|
||||
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name: '',
|
||||
|
|
@ -169,23 +204,7 @@ function OrganizationSettings() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
<h3>
|
||||
<Text>Admin/</Text>
|
||||
Staff Evaluation
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<Box w="100%" display={'flex'} mt={15} ml={10}>
|
||||
<Tabs w="100%" value={activeTab} onChange={setActiveTab}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="technical">Technical setting</Tabs.Tab>
|
||||
<Tabs.Tab value="second">Setting 2</Tabs.Tab>
|
||||
<Tabs.Tab value="third">Setting 3</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<Tabs.Panel value="technical" pt="xs">
|
||||
<>
|
||||
<Box mt={'md'} p={16}>
|
||||
<Text fw={500}>Note: </Text>
|
||||
|
||||
|
|
@ -223,24 +242,11 @@ function OrganizationSettings() {
|
|||
size=""
|
||||
searchInput
|
||||
infoTotal={
|
||||
<Button onClick={() => setAddTechnicalOpen(true)}>
|
||||
+ Add
|
||||
</Button>
|
||||
<Button onClick={() => setAddTechnicalOpen(true)}>+ Add</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="second" pt="xs">
|
||||
Setting 2
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="third" pt="xs">
|
||||
Setting 3
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
<Modal
|
||||
opened={addTechnicalOpen}
|
||||
|
|
@ -320,7 +326,7 @@ function OrganizationSettings() {
|
|||
</Group>
|
||||
</Box>
|
||||
</Dialog>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from '@mantine/core'
|
||||
import { notifications } from '@mantine/notifications'
|
||||
import {
|
||||
IconExchange,
|
||||
IconPasswordUser,
|
||||
IconUserCode,
|
||||
IconUserCog,
|
||||
|
|
@ -39,19 +40,6 @@ import moment from 'moment'
|
|||
|
||||
const isCompactMenu = false
|
||||
|
||||
interface DataTechnical {
|
||||
id: number
|
||||
name: string
|
||||
level: number
|
||||
point: number
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
interface UpdateDataTechnical {
|
||||
technical_id: number | string
|
||||
point: number | string
|
||||
}
|
||||
|
||||
const Profile = () => {
|
||||
const user = useAppSelector((state) => state.authentication.user)
|
||||
const userData = getUser()
|
||||
|
|
@ -70,14 +58,6 @@ const Profile = () => {
|
|||
const [dataProfile, setDataProfile] = useState<any>([])
|
||||
const [countSpam, setCountSpam] = useState(0)
|
||||
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
const [loadingTechnical, setLoadingTechnical] = useState(false)
|
||||
const [isUpdateTechnical, setIsUpdateTechnical] = useState(false)
|
||||
|
||||
const [updatedDataTechnical, setUpdatedDataTechnical] = useState<
|
||||
UpdateDataTechnical[]
|
||||
>([])
|
||||
|
||||
const [selectedAvatar, setSelectedAvatar] = useState<string | null>(null)
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useAppDispatch()
|
||||
|
|
@ -221,144 +201,6 @@ const Profile = () => {
|
|||
dispatch(logout(navigate))
|
||||
}, [dispatch, navigate])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
await getListUserTechnical()
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'level',
|
||||
size: '5%',
|
||||
header: 'Level',
|
||||
render: (row: any) => {
|
||||
return (
|
||||
<Box
|
||||
style={
|
||||
row?.level
|
||||
? row?.level === 1
|
||||
? { backgroundColor: '#d9d2e9' }
|
||||
: row?.level === 2
|
||||
? { backgroundColor: '#ffd966' }
|
||||
: { backgroundColor: '#cfe2f3' }
|
||||
: { backgroundColor: '' }
|
||||
}
|
||||
fw={500}
|
||||
ta="center"
|
||||
p={4}
|
||||
>
|
||||
{row?.level ? row.level : ''}
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
size: '50%',
|
||||
header: 'Name',
|
||||
render: (row: any) => {
|
||||
return <Text ta="start">{row?.name}</Text>
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
size: '5%',
|
||||
header: 'Point',
|
||||
render: (row: any) => {
|
||||
return (
|
||||
<TextInput
|
||||
mb={'md'}
|
||||
value={
|
||||
updatedDataTechnical.find(
|
||||
(technicalItem: UpdateDataTechnical) =>
|
||||
technicalItem.technical_id === row?.id,
|
||||
)?.point
|
||||
}
|
||||
onChange={(e) => {
|
||||
setUpdatedDataTechnical((prev: any) =>
|
||||
prev.map((technicalItem: UpdateDataTechnical) => {
|
||||
if (technicalItem.technical_id === row.id) {
|
||||
return {
|
||||
...technicalItem,
|
||||
point: e.target.value,
|
||||
}
|
||||
}
|
||||
|
||||
return technicalItem
|
||||
}),
|
||||
)
|
||||
setIsUpdateTechnical(true)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
size: '40%',
|
||||
header: 'Last update',
|
||||
render: (row: any) => {
|
||||
if (row?.updated_at) {
|
||||
return (
|
||||
<Text ta="start">
|
||||
{moment(row?.updated_at).format('HH:mm:ss DD/MM/YYYY')}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const getListUserTechnical = async () => {
|
||||
try {
|
||||
setLoadingTechnical(true)
|
||||
const params = {}
|
||||
const res = await get(listUserTechnical, params)
|
||||
if (res.status) {
|
||||
setDataTechnical(res.data)
|
||||
setUpdatedDataTechnical(
|
||||
res.data?.map((technicalItem: DataTechnical) => {
|
||||
return {
|
||||
technical_id: technicalItem.id,
|
||||
point: technicalItem.point,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
} finally {
|
||||
setLoadingTechnical(false)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
const handleUpdateTechnical = async () => {
|
||||
try {
|
||||
const res = await post(updateUserTechnical, {
|
||||
technicals: updatedDataTechnical,
|
||||
})
|
||||
if (res.status === true) {
|
||||
await getListUserTechnical()
|
||||
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: res.message,
|
||||
color: 'green',
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
|
|
@ -491,79 +333,7 @@ const Profile = () => {
|
|||
</Box>
|
||||
|
||||
{swapTap ? (
|
||||
<Box className={classes.projectInvolvement}>
|
||||
<Flex justify="space-between" wrap="wrap">
|
||||
<Box px={16} ta="start">
|
||||
<Text fw={500}>Level: </Text>
|
||||
|
||||
<Flex gap={8}>
|
||||
<Box p={8} bg="#d9d2e9">
|
||||
<span style={{ fontWeight: 500 }}>1:</span> 3-12 Month
|
||||
</Box>
|
||||
<Box p={8} bg="#ffd966">
|
||||
<span style={{ fontWeight: 500 }}>2:</span> 3-5 Year
|
||||
</Box>
|
||||
<Box p={8} bg="#cfe2f3">
|
||||
<span style={{ fontWeight: 500 }}>3:</span> 5 -8 Year
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
<Box px={16} ta="start">
|
||||
<Text fw={500}>Point: </Text>
|
||||
|
||||
<Flex gap={8}>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>0:</span> Unknown
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>1:</span> Basic
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>2:</span> Advanced
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>3:</span> Master
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex justify="space-between" mt={'lg'}>
|
||||
<div></div>
|
||||
<Title order={3}>Technicals</Title>
|
||||
|
||||
<Button
|
||||
disabled={loadingTechnical || !isUpdateTechnical}
|
||||
onClick={handleUpdateTechnical}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{loadingTechnical ? (
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: '10%',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
|
||||
<Text fw={600} c={'gray'} mt={8}>
|
||||
Loading Technical...
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<DataTableAll
|
||||
data={dataTechnical}
|
||||
columns={columns}
|
||||
size=""
|
||||
searchInput
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<UserTechnical />
|
||||
) : (
|
||||
<Box className={classes.projectInvolvement}>
|
||||
<Title order={3}>Project Involved</Title>
|
||||
|
|
@ -672,4 +442,259 @@ const Profile = () => {
|
|||
)
|
||||
}
|
||||
|
||||
// User Technical
|
||||
interface DataTechnical {
|
||||
id: number
|
||||
name: string
|
||||
level: number
|
||||
point: number
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
interface UpdateDataTechnical {
|
||||
technical_id: number | string
|
||||
point: number | string
|
||||
}
|
||||
|
||||
const UserTechnical = () => {
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
const [loadingTechnical, setLoadingTechnical] = useState(false)
|
||||
const [isUpdateTechnical, setIsUpdateTechnical] = useState<
|
||||
string[] | number[]
|
||||
>([])
|
||||
const [updatedDataTechnical, setUpdatedDataTechnical] = useState<
|
||||
UpdateDataTechnical[]
|
||||
>([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
await getListUserTechnical()
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'level',
|
||||
size: '5%',
|
||||
header: 'Level',
|
||||
render: (row: any) => {
|
||||
return (
|
||||
<Box
|
||||
style={
|
||||
row?.level
|
||||
? row?.level === 1
|
||||
? { backgroundColor: '#d9d2e9' }
|
||||
: row?.level === 2
|
||||
? { backgroundColor: '#ffd966' }
|
||||
: { backgroundColor: '#cfe2f3' }
|
||||
: { backgroundColor: '' }
|
||||
}
|
||||
fw={500}
|
||||
ta="center"
|
||||
p={4}
|
||||
>
|
||||
{row?.level ? row.level : ''}
|
||||
</Box>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
size: '45%',
|
||||
header: 'Name',
|
||||
render: (row: any) => {
|
||||
return <Text ta="start">{row?.name}</Text>
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
size: '10%',
|
||||
header: 'Point',
|
||||
render: (row: any) => {
|
||||
return (
|
||||
<TextInput
|
||||
mb={'md'}
|
||||
value={
|
||||
updatedDataTechnical.find(
|
||||
(technicalItem: UpdateDataTechnical) =>
|
||||
technicalItem.technical_id === row?.id,
|
||||
)?.point
|
||||
}
|
||||
rightSection={
|
||||
isUpdateTechnical.some(
|
||||
(item: string | number) => item === row.id,
|
||||
) ? (
|
||||
<IconExchange size={16} color="#1c7ed6" />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value
|
||||
|
||||
// Ensure the input is between 0 and 3
|
||||
if (value === '' || (Number(value) >= 0 && Number(value) <= 3)) {
|
||||
setUpdatedDataTechnical((prev: any) =>
|
||||
prev.map((technicalItem: UpdateDataTechnical) => {
|
||||
if (technicalItem.technical_id === row.id) {
|
||||
return {
|
||||
...technicalItem,
|
||||
point: value,
|
||||
}
|
||||
}
|
||||
return technicalItem
|
||||
}),
|
||||
)
|
||||
setIsUpdateTechnical((prev) => [...prev, row?.id])
|
||||
}
|
||||
}}
|
||||
onFocus={(e) => e.target.select()}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
size: '40%',
|
||||
header: 'Last update',
|
||||
render: (row: any) => {
|
||||
if (row?.updated_at) {
|
||||
return (
|
||||
<Text ta="start">
|
||||
{moment(row?.updated_at).format('HH:mm:ss DD/MM/YYYY')}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const getListUserTechnical = async () => {
|
||||
try {
|
||||
setLoadingTechnical(true)
|
||||
const params = {}
|
||||
const res = await get(listUserTechnical, params)
|
||||
if (res.status) {
|
||||
setDataTechnical(res.data)
|
||||
setUpdatedDataTechnical(
|
||||
res.data?.map((technicalItem: DataTechnical) => {
|
||||
return {
|
||||
technical_id: technicalItem.id,
|
||||
point: technicalItem.point,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
} finally {
|
||||
setLoadingTechnical(false)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
const handleUpdateUserTechnical = async () => {
|
||||
try {
|
||||
const res = await post(updateUserTechnical, {
|
||||
technicals: updatedDataTechnical,
|
||||
})
|
||||
if (res.status === true) {
|
||||
await getListUserTechnical()
|
||||
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: res.message,
|
||||
color: 'green',
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box className={classes.projectInvolvement}>
|
||||
<Flex justify="space-between" wrap="wrap">
|
||||
{/* Level */}
|
||||
<Box px={16} ta="start">
|
||||
<Text fw={500}>Level: </Text>
|
||||
|
||||
<Flex gap={8} wrap="wrap">
|
||||
<Box p={8} bg="#d9d2e9">
|
||||
<span style={{ fontWeight: 500 }}>1:</span> 3-12 Month
|
||||
</Box>
|
||||
<Box p={8} bg="#ffd966">
|
||||
<span style={{ fontWeight: 500 }}>2:</span> 3-5 Year
|
||||
</Box>
|
||||
<Box p={8} bg="#cfe2f3">
|
||||
<span style={{ fontWeight: 500 }}>3:</span> 5 -8 Year
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* Point */}
|
||||
<Box px={16} ta="start" mt={{ base: 8, lg: 0 }}>
|
||||
<Text fw={500}>Point: </Text>
|
||||
|
||||
<Flex gap={8} wrap="wrap">
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>0:</span> Unknown
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>1:</span> Basic
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>2:</span> Advanced
|
||||
</Box>
|
||||
<Box p={8} bg="#FFEA00">
|
||||
<span style={{ fontWeight: 500 }}>3:</span> Master
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Flex justify="space-between" mt={'lg'}>
|
||||
<div></div>
|
||||
<Title order={3}>Technicals</Title>
|
||||
|
||||
<Button
|
||||
disabled={loadingTechnical || isUpdateTechnical.length === 0}
|
||||
onClick={handleUpdateUserTechnical}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{loadingTechnical ? (
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
marginTop: '10%',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
|
||||
<Text fw={600} c={'gray'} mt={8}>
|
||||
Loading Technical...
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<DataTableAll
|
||||
data={dataTechnical}
|
||||
columns={columns}
|
||||
size=""
|
||||
searchInput
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Profile
|
||||
|
|
|
|||
Loading…
Reference in New Issue