Technical: add Profile page, Organization Settings page
This commit is contained in:
parent
5bc84a10b8
commit
bb40261a0d
|
|
@ -76,9 +76,17 @@ export const getProfilesData = API_URL + 'v1/admin/criterias/profiles-data'
|
||||||
export const updateProfilesData =
|
export const updateProfilesData =
|
||||||
API_URL + 'v1/admin/criterias/profiles-data/update'
|
API_URL + 'v1/admin/criterias/profiles-data/update'
|
||||||
|
|
||||||
|
export const listUserTechnical = API_URL + 'v1/admin/technical/get-tech-of-user'
|
||||||
|
export const updateUserTechnical = API_URL + 'v1/admin/technical/technicals-user/update'
|
||||||
|
|
||||||
export const getAllUser = API_URL + 'v1/admin/technical/get-all-user'
|
export const getAllUser = API_URL + 'v1/admin/technical/get-all-user'
|
||||||
export const getAllTechByUserId =
|
export const getAllTechByUserId =
|
||||||
API_URL + 'v1/admin/technical/get-tech-by-user-id'
|
API_URL + 'v1/admin/technical/get-tech-by-user-id'
|
||||||
|
|
||||||
export const evaluation = API_URL + 'v1/admin/evaluation/report'
|
export const evaluation = API_URL + 'v1/admin/evaluation/report'
|
||||||
export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
|
export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
|
||||||
|
|
||||||
|
//Technical
|
||||||
|
export const listTechnical = API_URL + 'v1/admin/technical/get-all'
|
||||||
|
export const createTechnical = API_URL + 'v1/admin/technical/create'
|
||||||
|
export const deleteTechnical = API_URL + 'v1/admin/technical/delete'
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,11 @@ import {
|
||||||
IconQrcode,
|
IconQrcode,
|
||||||
IconReport,
|
IconReport,
|
||||||
IconScan,
|
IconScan,
|
||||||
|
IconSettings,
|
||||||
IconSun,
|
IconSun,
|
||||||
IconTicket,
|
IconTicket,
|
||||||
IconUsersGroup,
|
IconUsersGroup,
|
||||||
IconZoomExclamation
|
IconZoomExclamation,
|
||||||
} from '@tabler/icons-react'
|
} from '@tabler/icons-react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
@ -114,6 +115,12 @@ const data = [
|
||||||
icon: IconChartDots2,
|
icon: IconChartDots2,
|
||||||
group: 'admin',
|
group: 'admin',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
link: '/organization-settings',
|
||||||
|
label: 'Organization Settings',
|
||||||
|
icon: IconSettings,
|
||||||
|
group: 'admin',
|
||||||
|
},
|
||||||
// { link: '/jira', label: 'Jira', icon: IconSubtask },
|
// { link: '/jira', label: 'Jira', icon: IconSubtask },
|
||||||
// { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
|
// { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
|
||||||
// { link: '/general-setting', label: 'General Setting', icon: IconSettings },
|
// { link: '/general-setting', label: 'General Setting', icon: IconSettings },
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
.title {
|
||||||
|
background-color: light-dark(var(white), var(--mantine-color-dark-7));
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 var(--mantine-spacing-sm) var(--mantine-spacing-lg)
|
||||||
|
var(--mantine-spacing-sm);
|
||||||
|
border-bottom: solid rgba(201, 201, 201, 0.377) 1px;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
Flex,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
Modal,
|
||||||
|
Tabs,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
} from '@mantine/core'
|
||||||
|
import classes from './OrganizationSettings.module.css'
|
||||||
|
import DataTableAll from '@/components/DataTable/DataTable'
|
||||||
|
import { get, post } from '@/rtk/helpers/apiService'
|
||||||
|
import { notifications } from '@mantine/notifications'
|
||||||
|
import { createTechnical, deleteTechnical, listTechnical } from '@/api/Admin'
|
||||||
|
import { useForm } from '@mantine/form'
|
||||||
|
import { Xdelete } from '@/rtk/helpers/CRUD'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
interface DataTechnical {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
level: number
|
||||||
|
point: number
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrganizationSettings() {
|
||||||
|
const [activeTab, setActiveTab] = useState<string | null>('technical')
|
||||||
|
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: '',
|
||||||
|
level: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
await getListTechnical()
|
||||||
|
}
|
||||||
|
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: '40%',
|
||||||
|
header: 'Name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'created_at',
|
||||||
|
size: '40%',
|
||||||
|
header: 'Created at',
|
||||||
|
render: (row: any) => {
|
||||||
|
return (
|
||||||
|
<Box>{moment(row?.created_at).format('HH:mm:ss DD/MM/YYYY')}</Box>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'action',
|
||||||
|
size: '15%',
|
||||||
|
header: 'Action',
|
||||||
|
render: (row: any) => {
|
||||||
|
return (
|
||||||
|
<Flex justify="center">
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteTechnicalOpen(true)
|
||||||
|
setSelectedId(row?.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const getListTechnical = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
const params = {}
|
||||||
|
const res = await get(listTechnical, params)
|
||||||
|
if (res.status) {
|
||||||
|
setDataTechnical(res.data)
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
notifications.show({
|
||||||
|
title: 'Error',
|
||||||
|
message: error.message ?? error,
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreate = async (values: any) => {
|
||||||
|
try {
|
||||||
|
const { id, ...data } = values
|
||||||
|
const res = await post(createTechnical, data)
|
||||||
|
if (res.status === true) {
|
||||||
|
setAddTechnicalOpen(false)
|
||||||
|
form.reset()
|
||||||
|
await getListTechnical()
|
||||||
|
|
||||||
|
notifications.show({
|
||||||
|
title: 'Success',
|
||||||
|
message: res.message,
|
||||||
|
color: 'green',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
try {
|
||||||
|
await Xdelete(deleteTechnical, { id: id }, getListTechnical)
|
||||||
|
setSelectedId(0)
|
||||||
|
setDeleteTechnicalOpen(false)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
<Flex gap={8}>
|
||||||
|
<Box p={8} bg="#d9d2e9">
|
||||||
|
<span style={{ fontWeight: 500 }}>Level 1:</span> 3-12 Month
|
||||||
|
</Box>
|
||||||
|
<Box p={8} bg="#ffd966">
|
||||||
|
<span style={{ fontWeight: 500 }}>Level 2:</span> 3-5 Year
|
||||||
|
</Box>
|
||||||
|
<Box p={8} bg="#cfe2f3">
|
||||||
|
<span style={{ fontWeight: 500 }}>Level 3:</span> 5 -8 Year
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box mt={'md'}>
|
||||||
|
{isLoading ? (
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
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
|
||||||
|
infoTotal={
|
||||||
|
<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}
|
||||||
|
onClose={() => {
|
||||||
|
setAddTechnicalOpen(false)
|
||||||
|
form.reset()
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<Text pl={'sm'} fw={700} fz={'lg'}>
|
||||||
|
Add Technical
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
onSubmit={form.onSubmit(async (values) => {
|
||||||
|
await handleCreate(values)
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Box pl={'md'} pr={'md'}>
|
||||||
|
<TextInput
|
||||||
|
label="Name"
|
||||||
|
mb={'md'}
|
||||||
|
value={form.values.name}
|
||||||
|
error={form.errors.name}
|
||||||
|
onChange={(e) => form.setFieldValue('name', e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label="Level"
|
||||||
|
mb={'md'}
|
||||||
|
value={form.values.level}
|
||||||
|
error={form.errors.level}
|
||||||
|
onChange={(e) => form.setFieldValue('level', e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box ta={'center'}>
|
||||||
|
<Button mt={'lg'} bg={'green'} type="submit">
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
opened={deleteTechnicalOpen}
|
||||||
|
className={classes.dialog}
|
||||||
|
withCloseButton
|
||||||
|
onClose={() => setDeleteTechnicalOpen(false)}
|
||||||
|
size="lg"
|
||||||
|
radius="md"
|
||||||
|
position={{ top: 30, right: 10 }}
|
||||||
|
>
|
||||||
|
<Box className={classes.dialogText} size="sm" mb="xs" fw={500}>
|
||||||
|
Do you want to delete this technical?
|
||||||
|
<Group justify="center" m={10}>
|
||||||
|
<Button
|
||||||
|
fw={700}
|
||||||
|
size="xs"
|
||||||
|
variant="filled"
|
||||||
|
color="red"
|
||||||
|
onClick={async () => handleDelete(selectedId)}
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
fw={700}
|
||||||
|
size="xs"
|
||||||
|
variant="filled"
|
||||||
|
color="gray"
|
||||||
|
onClick={() => setDeleteTechnicalOpen(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OrganizationSettings
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
import { getProfilesData, updateProfilesData } from '@/api/Admin'
|
import {
|
||||||
|
getProfilesData,
|
||||||
|
listUserTechnical,
|
||||||
|
updateProfilesData,
|
||||||
|
updateUserTechnical,
|
||||||
|
} from '@/api/Admin'
|
||||||
import { changePassword } from '@/api/Auth'
|
import { changePassword } from '@/api/Auth'
|
||||||
import PasswordRequirementInput from '@/components/PasswordRequirementInput/PasswordRequirementInput'
|
import PasswordRequirementInput from '@/components/PasswordRequirementInput/PasswordRequirementInput'
|
||||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
|
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
|
||||||
|
|
@ -12,6 +17,8 @@ import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Flex,
|
||||||
|
Loader,
|
||||||
Modal,
|
Modal,
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -19,12 +26,32 @@ import {
|
||||||
Title,
|
Title,
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import { notifications } from '@mantine/notifications'
|
import { notifications } from '@mantine/notifications'
|
||||||
import { IconPasswordUser } from '@tabler/icons-react'
|
import {
|
||||||
|
IconPasswordUser,
|
||||||
|
IconUserCode,
|
||||||
|
IconUserCog,
|
||||||
|
} from '@tabler/icons-react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import classes from './Profile.module.css'
|
import classes from './Profile.module.css'
|
||||||
|
import DataTableAll from '@/components/DataTable/DataTable'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
const isCompactMenu = false
|
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 Profile = () => {
|
||||||
const user = useAppSelector((state) => state.authentication.user)
|
const user = useAppSelector((state) => state.authentication.user)
|
||||||
const userData = getUser()
|
const userData = getUser()
|
||||||
|
|
@ -35,11 +62,22 @@ const Profile = () => {
|
||||||
new_password: '',
|
new_password: '',
|
||||||
confirm_password: '',
|
confirm_password: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [swapTap, setSwapTab] = useState(false)
|
||||||
|
|
||||||
const [avatar, setAvatar] = useState(user.user.avatar)
|
const [avatar, setAvatar] = useState(user.user.avatar)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [dataProfile, setDataProfile] = useState<any>([])
|
const [dataProfile, setDataProfile] = useState<any>([])
|
||||||
const [countSpam, setCountSpam] = useState(0)
|
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 [selectedAvatar, setSelectedAvatar] = useState<string | null>(null)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
@ -183,6 +221,142 @@ const Profile = () => {
|
||||||
dispatch(logout(navigate))
|
dispatch(logout(navigate))
|
||||||
}, [dispatch, 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) => {
|
||||||
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.title}>
|
<div className={classes.title}>
|
||||||
|
|
@ -281,28 +455,119 @@ const Profile = () => {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
}}
|
}}
|
||||||
mt={10}
|
mt={10}
|
||||||
>
|
>
|
||||||
<a
|
<Button
|
||||||
href="#"
|
style={{ width: '50%' }}
|
||||||
className={classes.link}
|
|
||||||
onClick={() => setOpened(true)}
|
onClick={() => setOpened(true)}
|
||||||
|
color="green"
|
||||||
>
|
>
|
||||||
<IconPasswordUser className={classes.linkIcon} stroke={1.5} />
|
<IconPasswordUser stroke={1.5} />
|
||||||
<span
|
Change password
|
||||||
className={`${classes.itemLabel} ${classes.labelCompactMenu}`}
|
</Button>
|
||||||
|
|
||||||
|
{swapTap ? (
|
||||||
|
<Button
|
||||||
|
style={{ width: '50%' }}
|
||||||
|
onClick={() => setSwapTab(!swapTap)}
|
||||||
>
|
>
|
||||||
Change password
|
<IconUserCode stroke={1.5} />
|
||||||
</span>
|
Project Involved
|
||||||
</a>
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
style={{ width: '50%' }}
|
||||||
|
onClick={() => setSwapTab(!swapTap)}
|
||||||
|
>
|
||||||
|
<IconUserCog stroke={1.5} />
|
||||||
|
Your Technical
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box className={classes.projectInvolvement}>
|
{swapTap ? (
|
||||||
<Title order={3}>Project Involved</Title>
|
<Box className={classes.projectInvolvement}>
|
||||||
<ProjectInvolvement dataProfile={dataProfile} page="profile" />
|
<Flex justify="space-between" wrap="wrap">
|
||||||
</Box>
|
<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>
|
||||||
|
) : (
|
||||||
|
<Box className={classes.projectInvolvement}>
|
||||||
|
<Title order={3}>Project Involved</Title>
|
||||||
|
<ProjectInvolvement dataProfile={dataProfile} page="profile" />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import Allocation from '@/pages/Allocation/Allocation'
|
||||||
import PageLogin from '@/pages/Auth/Login/Login'
|
import PageLogin from '@/pages/Auth/Login/Login'
|
||||||
import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
|
import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
|
||||||
import PageNotFound from '@/pages/NotFound/NotFound'
|
import PageNotFound from '@/pages/NotFound/NotFound'
|
||||||
|
import OrganizationSettings from '@/pages/OrganizationSettings/OrganizationSettings'
|
||||||
import Profile from '@/pages/Profile/Profile'
|
import Profile from '@/pages/Profile/Profile'
|
||||||
import SprintReview from '@/pages/SprintReview/SprintReview'
|
import SprintReview from '@/pages/SprintReview/SprintReview'
|
||||||
import StaffEvaluation from '@/pages/StaffEvaluation/StaffEvaluation'
|
import StaffEvaluation from '@/pages/StaffEvaluation/StaffEvaluation'
|
||||||
|
|
@ -219,6 +220,20 @@ const mainRoutes = [
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/organization-settings',
|
||||||
|
element: (
|
||||||
|
<ProtectedRoute mode="route" permission="admin">
|
||||||
|
<BasePage
|
||||||
|
main={
|
||||||
|
<>
|
||||||
|
<OrganizationSettings />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
></BasePage>
|
||||||
|
</ProtectedRoute>
|
||||||
|
),
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// path: '/packages',
|
// path: '/packages',
|
||||||
// element: (
|
// element: (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue