Intergate api chức năng Staff evaluation
This commit is contained in:
parent
a247d08d45
commit
bd4a481aab
|
|
@ -86,6 +86,7 @@ class TechnicalController extends Controller
|
|||
{
|
||||
$technicals = TechnicalUser::with('technical')
|
||||
->where('user_id', $userId)
|
||||
->orderBy('point', 'desc')
|
||||
->get();
|
||||
|
||||
if ($technicals->isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -73,4 +73,9 @@ export const createTestCase = API_URL + 'v1/admin/criterias/test-cases'
|
|||
|
||||
//Profile
|
||||
export const getProfilesData = API_URL + 'v1/admin/criterias/profiles-data'
|
||||
export const updateProfilesData = API_URL + 'v1/admin/criterias/profiles-data/update'
|
||||
export const updateProfilesData =
|
||||
API_URL + 'v1/admin/criterias/profiles-data/update'
|
||||
|
||||
export const getAllUser = API_URL + 'v1/admin/technical/get-all-user'
|
||||
export const getAllTechByUserId =
|
||||
API_URL + 'v1/admin/technical/get-tech-by-user-id'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,15 @@
|
|||
justify-items: center;
|
||||
}
|
||||
|
||||
.totalBox {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: end;
|
||||
justify-content: right;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.headers {
|
||||
height: 6vh;
|
||||
z-index: 100;
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ export const DataTableAll = ({
|
|||
)}
|
||||
</Box>
|
||||
<Box
|
||||
className={classes.paginationBox}
|
||||
className={classes.totalBox}
|
||||
display={infoTotal ? 'flex' : 'none'}
|
||||
>
|
||||
<Text fz={'sm'} ta={'right'}>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Box, Table, Text } from '@mantine/core'
|
||||
import { IconCornerDownRight } from '@tabler/icons-react'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import classes from './ProjectInvolvement.module.css'
|
||||
|
||||
interface Project {
|
||||
|
|
@ -81,6 +81,23 @@ const ProjectInvolvement = ({ dataProfile, page }: ProjectInvolvementProps) => {
|
|||
const [expandedProjects, setExpandedProjects] = useState<ExpandedProjects>({})
|
||||
const [expandedSprints, setExpandedSprints] = useState<ExpandedSprints>({})
|
||||
|
||||
useEffect(() => {
|
||||
const initialProjectState: ExpandedProjects = {}
|
||||
const initialSprintState: ExpandedSprints = {}
|
||||
|
||||
dataProfile.forEach((project) => {
|
||||
initialProjectState[project.name] = true // Expand all projects by default
|
||||
initialSprintState[project.name] = {}
|
||||
|
||||
project.sprints.forEach((sprint) => {
|
||||
initialSprintState[project.name][sprint.name] = true // Expand all sprints by default
|
||||
})
|
||||
})
|
||||
|
||||
setExpandedProjects(initialProjectState)
|
||||
setExpandedSprints(initialSprintState)
|
||||
}, [dataProfile])
|
||||
|
||||
const handleProjectToggle = (projectName: string) => {
|
||||
setExpandedProjects((prev) => ({
|
||||
...prev,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { getProfilesData } from '@/api/Admin'
|
||||
import { getAllTechByUserId, getAllUser, getProfilesData } from '@/api/Admin'
|
||||
import DataTableAll from '@/components/DataTable/DataTable'
|
||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
|
||||
import { get } from '@/rtk/helpers/apiService'
|
||||
import { Box, Button, Select, Text, Title } from '@mantine/core'
|
||||
import { Box, Button, Loader, Select, Text, Title } from '@mantine/core'
|
||||
import { DateInput } from '@mantine/dates'
|
||||
import { notifications } from '@mantine/notifications'
|
||||
import moment from 'moment'
|
||||
|
|
@ -27,8 +27,19 @@ interface Filter {
|
|||
// other properties of the filter object
|
||||
}
|
||||
|
||||
interface DataTechnical {
|
||||
id: number
|
||||
name: string
|
||||
level: number
|
||||
point: number
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
const StaffEvaluation = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadingTechnical, setLoadingTechnical] = useState(false)
|
||||
const [dataProfile, setDataProfile] = useState<any>([])
|
||||
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
|
||||
const [listUsers, setListUsers] = useState<User[]>([])
|
||||
const [filter, setFilter] = useState<Filter>({
|
||||
userID: '',
|
||||
|
|
@ -38,36 +49,10 @@ const StaffEvaluation = () => {
|
|||
|
||||
console.log(filter, 'filter')
|
||||
|
||||
// const getAllTracking = async () => {
|
||||
// try {
|
||||
// const searchParams = new URLSearchParams(window.location.search)
|
||||
// const params = {}
|
||||
|
||||
// for (const [key, value] of searchParams.entries()) {
|
||||
// if (key === 'page' && value === '') {
|
||||
// Object.assign(params, { [`${key}`]: 1 })
|
||||
// } else {
|
||||
// Object.assign(params, { [`${key}`]: value })
|
||||
// }
|
||||
// }
|
||||
|
||||
// const res = await get(getListTracking, params)
|
||||
// if (res.status) {
|
||||
// setListTracking(res)
|
||||
// }
|
||||
// } catch (error:any) {
|
||||
// notifications.show({
|
||||
// title: 'Error',
|
||||
// message: error.message??error,
|
||||
// color: 'red',
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
const getListProfilesData = async () => {
|
||||
const getListUser = async () => {
|
||||
try {
|
||||
const params = {}
|
||||
const res = await get(getProfilesData, params)
|
||||
const res = await get(getAllUser, params)
|
||||
if (res.status) {
|
||||
return res.data
|
||||
}
|
||||
|
|
@ -83,18 +68,96 @@ const StaffEvaluation = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const result = await getListProfilesData()
|
||||
setDataProfile(result ?? [])
|
||||
const result = await getListUser()
|
||||
setListUsers(result ?? [])
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const getListProfilesData = async (filterSearch: Filter) => {
|
||||
try {
|
||||
const params = {
|
||||
userID: filterSearch.userID ?? '',
|
||||
fromDate: filterSearch.fromDate
|
||||
? moment(filterSearch.fromDate).format('YYYY-MM-DD')
|
||||
: null,
|
||||
toDate: filterSearch.toDate
|
||||
? moment(filterSearch.toDate).format('YYYY-MM-DD')
|
||||
: null,
|
||||
}
|
||||
const res = await get(getProfilesData, params)
|
||||
if (res.status) {
|
||||
return res.data
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const getListTechnicalByUserId = async (id: string) => {
|
||||
try {
|
||||
const params = {}
|
||||
const res = await get(`${getAllTechByUserId}/${id}`, params)
|
||||
if (res.status) {
|
||||
return res.data
|
||||
}
|
||||
} catch (error: any) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: error.message ?? error,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (filter?.userID) {
|
||||
setLoading(true)
|
||||
const fetchData = async () => {
|
||||
const result = await getListProfilesData(filter)
|
||||
setDataProfile(result ?? [])
|
||||
setLoading(false)
|
||||
}
|
||||
fetchData()
|
||||
}
|
||||
}, [filter])
|
||||
|
||||
useEffect(() => {
|
||||
if (filter?.userID) {
|
||||
setLoadingTechnical(true)
|
||||
const fetchData = async () => {
|
||||
const result = await getListTechnicalByUserId(filter?.userID)
|
||||
setDataTechnical(result ?? [])
|
||||
setLoadingTechnical(false)
|
||||
}
|
||||
fetchData()
|
||||
}
|
||||
}, [filter])
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'level',
|
||||
size: '20%',
|
||||
size: '10%',
|
||||
header: 'Level',
|
||||
render: (row: any) => {
|
||||
if (row.level)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{row?.level}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
|
|
@ -106,16 +169,17 @@ const StaffEvaluation = () => {
|
|||
size: '10%',
|
||||
header: 'Point',
|
||||
render: (row: any) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{row?.point}
|
||||
</div>
|
||||
)
|
||||
if (row?.point > 0)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{row?.point}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -130,31 +194,33 @@ const StaffEvaluation = () => {
|
|||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{moment(row?.updated_at).format('HH:mm:ss DD/MM/YYYY')}
|
||||
{moment(row?.updated_at).format('DD/MM/YYYY HH:mm:ss')}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const users = [
|
||||
{
|
||||
id: 1,
|
||||
level: 'Level 1',
|
||||
name: 'React JS',
|
||||
point: 3,
|
||||
created_at: null,
|
||||
updated_at: '2024-09-19T09:08:48.000000Z',
|
||||
},
|
||||
]
|
||||
|
||||
const infoTotal = () => {
|
||||
// Tính tổng point và số lượng point > 0
|
||||
let totalPoint = 0
|
||||
let count = 0
|
||||
|
||||
dataTechnical.forEach((item) => {
|
||||
if (item.point > 0) {
|
||||
totalPoint += item.point
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
||||
const averagePoint = count > 0 ? (totalPoint / count).toFixed(2) : '0.00'
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Text mr={20} fs={'italic'}>
|
||||
Avg:{users.length}
|
||||
Avg: {averagePoint}
|
||||
</Text>
|
||||
<Text fs={'italic'}>Total: {users.length}</Text>
|
||||
<Text fs={'italic'}>Total: {totalPoint}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -166,6 +232,7 @@ const StaffEvaluation = () => {
|
|||
Staff Evaluation
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<Box w="100%" display={'flex'} mt={15} ml={10}>
|
||||
<Box w="50%" display={'flex'}>
|
||||
<Text
|
||||
|
|
@ -177,11 +244,16 @@ const StaffEvaluation = () => {
|
|||
User:
|
||||
</Text>
|
||||
<Select
|
||||
style={{ width: '50%' }}
|
||||
label={''}
|
||||
placeholder="Select user"
|
||||
maxLength={255}
|
||||
required
|
||||
data={listUsers.map((i: User) => i.name)}
|
||||
data={listUsers.map((i: User) => ({
|
||||
value: i.id.toString(),
|
||||
label: i.name,
|
||||
}))}
|
||||
value={filter.userID}
|
||||
onChange={(e) => setFilter({ ...filter, userID: e! })}
|
||||
/>
|
||||
</Box>
|
||||
|
|
@ -251,20 +323,50 @@ const StaffEvaluation = () => {
|
|||
></DateInput>
|
||||
</Box>
|
||||
</Box>
|
||||
<ProjectInvolvement dataProfile={dataProfile} page="admin" />
|
||||
<Box
|
||||
style={{
|
||||
marginTop: '10%',
|
||||
textAlign: 'center',
|
||||
display: loading ? 'block' : 'none',
|
||||
// display: 'none',
|
||||
}}
|
||||
>
|
||||
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
|
||||
<Text fw={600} c={'gray'}>
|
||||
Loading . . .
|
||||
</Text>
|
||||
</Box>
|
||||
{loading ? null : (
|
||||
<ProjectInvolvement dataProfile={dataProfile} page="admin" />
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box className={classes.sidebar}>
|
||||
<Title order={3} className={classes.titleSidebar}>
|
||||
Technicals
|
||||
</Title>
|
||||
<DataTableAll
|
||||
data={users}
|
||||
columns={columns}
|
||||
size=""
|
||||
searchInput
|
||||
infoTotal={infoTotal()}
|
||||
/>
|
||||
{loadingTechnical ? (
|
||||
<Box
|
||||
style={{
|
||||
marginTop: '10%',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
<Loader size={'sm'} color="green" type="bars" m={'0 auto'} />
|
||||
<Text fw={600} c={'gray'}>
|
||||
Loading . . .
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<DataTableAll
|
||||
data={dataTechnical}
|
||||
columns={columns}
|
||||
size=""
|
||||
searchInput
|
||||
infoTotal={infoTotal()}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue