Intergate api chức năng Staff evaluation

This commit is contained in:
Truong Vo 2024-09-20 16:52:56 +07:00
parent a247d08d45
commit bd4a481aab
6 changed files with 203 additions and 69 deletions

View File

@ -86,6 +86,7 @@ class TechnicalController extends Controller
{ {
$technicals = TechnicalUser::with('technical') $technicals = TechnicalUser::with('technical')
->where('user_id', $userId) ->where('user_id', $userId)
->orderBy('point', 'desc')
->get(); ->get();
if ($technicals->isEmpty()) { if ($technicals->isEmpty()) {

View File

@ -73,4 +73,9 @@ export const createTestCase = API_URL + 'v1/admin/criterias/test-cases'
//Profile //Profile
export const getProfilesData = API_URL + 'v1/admin/criterias/profiles-data' 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'

View File

@ -27,6 +27,15 @@
justify-items: center; justify-items: center;
} }
.totalBox {
width: 60%;
display: flex;
flex-flow: row;
align-items: end;
justify-content: right;
justify-items: center;
}
.headers { .headers {
height: 6vh; height: 6vh;
z-index: 100; z-index: 100;

View File

@ -326,7 +326,7 @@ export const DataTableAll = ({
)} )}
</Box> </Box>
<Box <Box
className={classes.paginationBox} className={classes.totalBox}
display={infoTotal ? 'flex' : 'none'} display={infoTotal ? 'flex' : 'none'}
> >
<Text fz={'sm'} ta={'right'}> <Text fz={'sm'} ta={'right'}>

View File

@ -1,6 +1,6 @@
import { Box, Table, Text } from '@mantine/core' import { Box, Table, Text } from '@mantine/core'
import { IconCornerDownRight } from '@tabler/icons-react' import { IconCornerDownRight } from '@tabler/icons-react'
import { useState } from 'react' import { useEffect, useState } from 'react'
import classes from './ProjectInvolvement.module.css' import classes from './ProjectInvolvement.module.css'
interface Project { interface Project {
@ -81,6 +81,23 @@ const ProjectInvolvement = ({ dataProfile, page }: ProjectInvolvementProps) => {
const [expandedProjects, setExpandedProjects] = useState<ExpandedProjects>({}) const [expandedProjects, setExpandedProjects] = useState<ExpandedProjects>({})
const [expandedSprints, setExpandedSprints] = useState<ExpandedSprints>({}) 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) => { const handleProjectToggle = (projectName: string) => {
setExpandedProjects((prev) => ({ setExpandedProjects((prev) => ({
...prev, ...prev,

View File

@ -1,8 +1,8 @@
import { getProfilesData } from '@/api/Admin' import { getAllTechByUserId, getAllUser, getProfilesData } from '@/api/Admin'
import DataTableAll from '@/components/DataTable/DataTable' import DataTableAll from '@/components/DataTable/DataTable'
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement' import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
import { get } from '@/rtk/helpers/apiService' 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 { DateInput } from '@mantine/dates'
import { notifications } from '@mantine/notifications' import { notifications } from '@mantine/notifications'
import moment from 'moment' import moment from 'moment'
@ -27,8 +27,19 @@ interface Filter {
// other properties of the filter object // other properties of the filter object
} }
interface DataTechnical {
id: number
name: string
level: number
point: number
updated_at: string
}
const StaffEvaluation = () => { const StaffEvaluation = () => {
const [loading, setLoading] = useState(false)
const [loadingTechnical, setLoadingTechnical] = useState(false)
const [dataProfile, setDataProfile] = useState<any>([]) const [dataProfile, setDataProfile] = useState<any>([])
const [dataTechnical, setDataTechnical] = useState<DataTechnical[]>([])
const [listUsers, setListUsers] = useState<User[]>([]) const [listUsers, setListUsers] = useState<User[]>([])
const [filter, setFilter] = useState<Filter>({ const [filter, setFilter] = useState<Filter>({
userID: '', userID: '',
@ -38,36 +49,10 @@ const StaffEvaluation = () => {
console.log(filter, 'filter') console.log(filter, 'filter')
// const getAllTracking = async () => { const getListUser = 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 () => {
try { try {
const params = {} const params = {}
const res = await get(getProfilesData, params) const res = await get(getAllUser, params)
if (res.status) { if (res.status) {
return res.data return res.data
} }
@ -83,18 +68,96 @@ const StaffEvaluation = () => {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
const result = await getListProfilesData() const result = await getListUser()
setDataProfile(result ?? [])
setListUsers(result ?? []) setListUsers(result ?? [])
} }
fetchData() 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 = [ const columns = [
{ {
name: 'level', name: 'level',
size: '20%', size: '10%',
header: 'Level', header: 'Level',
render: (row: any) => {
if (row.level)
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
}}
>
{row?.level}
</div>
)
},
}, },
{ {
name: 'name', name: 'name',
@ -106,16 +169,17 @@ const StaffEvaluation = () => {
size: '10%', size: '10%',
header: 'Point', header: 'Point',
render: (row: any) => { render: (row: any) => {
return ( if (row?.point > 0)
<div return (
style={{ <div
display: 'flex', style={{
justifyContent: 'center', display: 'flex',
}} justifyContent: 'center',
> }}
{row?.point} >
</div> {row?.point}
) </div>
)
}, },
}, },
{ {
@ -130,31 +194,33 @@ const StaffEvaluation = () => {
justifyContent: 'center', 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> </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 = () => { 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 ( return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Text mr={20} fs={'italic'}> <Text mr={20} fs={'italic'}>
Avg:{users.length} Avg: {averagePoint}
</Text> </Text>
<Text fs={'italic'}>Total: {users.length}</Text> <Text fs={'italic'}>Total: {totalPoint}</Text>
</div> </div>
) )
} }
@ -166,6 +232,7 @@ const StaffEvaluation = () => {
Staff Evaluation Staff Evaluation
</h3> </h3>
</div> </div>
<Box w="100%" display={'flex'} mt={15} ml={10}> <Box w="100%" display={'flex'} mt={15} ml={10}>
<Box w="50%" display={'flex'}> <Box w="50%" display={'flex'}>
<Text <Text
@ -177,11 +244,16 @@ const StaffEvaluation = () => {
User: User:
</Text> </Text>
<Select <Select
style={{ width: '50%' }}
label={''} label={''}
placeholder="Select user" placeholder="Select user"
maxLength={255} maxLength={255}
required 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! })} onChange={(e) => setFilter({ ...filter, userID: e! })}
/> />
</Box> </Box>
@ -251,20 +323,50 @@ const StaffEvaluation = () => {
></DateInput> ></DateInput>
</Box> </Box>
</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>
<Box className={classes.sidebar}> <Box className={classes.sidebar}>
<Title order={3} className={classes.titleSidebar}> <Title order={3} className={classes.titleSidebar}>
Technicals Technicals
</Title> </Title>
<DataTableAll {loadingTechnical ? (
data={users} <Box
columns={columns} style={{
size="" marginTop: '10%',
searchInput textAlign: 'center',
infoTotal={infoTotal()} 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>
</Box> </Box>
</div> </div>