Autosave: 20260203-121839

This commit is contained in:
Flatlogic Bot 2026-02-03 12:18:39 +00:00
parent 290810a317
commit 88aaaf8f2d
15 changed files with 866 additions and 1790 deletions

View File

@ -1,4 +1,3 @@
const db = require('../models');
const FileDBApi = require('./file');
const crypto = require('crypto');
@ -84,6 +83,18 @@ module.exports = class UsersDBApi {
||
null
,
matriculePaie: data.data.matriculePaie || null,
workdayId: data.data.workdayId || null,
productionSite: data.data.productionSite || null,
remoteWork: data.data.remoteWork || null,
hiringDate: data.data.hiringDate || null,
positionEntryDate: data.data.positionEntryDate || null,
departureDate: data.data.departureDate || null,
service: data.data.service || null,
position: data.data.position || null,
team: data.data.team || null,
departmentId: data.data.department || data.data.departmentId || null,
importHash: data.data.importHash || null,
createdById: currentUser.id,
@ -204,6 +215,18 @@ module.exports = class UsersDBApi {
||
null
,
matriculePaie: item.matriculePaie || null,
workdayId: item.workdayId || null,
productionSite: item.productionSite || null,
remoteWork: item.remoteWork || null,
hiringDate: item.hiringDate || null,
positionEntryDate: item.positionEntryDate || null,
departureDate: item.departureDate || null,
service: item.service || null,
position: item.position || null,
team: item.team || null,
departmentId: item.department || item.departmentId || null,
importHash: item.importHash || null,
createdById: currentUser.id,
@ -297,6 +320,19 @@ module.exports = class UsersDBApi {
if (data.provider !== undefined) updatePayload.provider = data.provider;
if (data.matriculePaie !== undefined) updatePayload.matriculePaie = data.matriculePaie;
if (data.workdayId !== undefined) updatePayload.workdayId = data.workdayId;
if (data.productionSite !== undefined) updatePayload.productionSite = data.productionSite;
if (data.remoteWork !== undefined) updatePayload.remoteWork = data.remoteWork;
if (data.hiringDate !== undefined) updatePayload.hiringDate = data.hiringDate;
if (data.positionEntryDate !== undefined) updatePayload.positionEntryDate = data.positionEntryDate;
if (data.departureDate !== undefined) updatePayload.departureDate = data.departureDate;
if (data.service !== undefined) updatePayload.service = data.service;
if (data.position !== undefined) updatePayload.position = data.position;
if (data.team !== undefined) updatePayload.team = data.team;
if (data.department !== undefined) updatePayload.departmentId = data.department;
if (data.departmentId !== undefined) updatePayload.departmentId = data.departmentId;
updatePayload.updatedById = currentUser.id;
@ -427,6 +463,10 @@ module.exports = class UsersDBApi {
output.avatar = await users.getAvatar({
transaction
});
output.department = await users.getDepartment({
transaction
});
output.app_role = await users.getApp_role({
@ -500,6 +540,11 @@ module.exports = class UsersDBApi {
as: 'avatar',
},
{
model: db.departments,
as: 'department',
},
];
if (filter) {
@ -554,6 +599,28 @@ module.exports = class UsersDBApi {
),
};
}
if (filter.matriculePaie) {
where = {
...where,
[Op.and]: Utils.ilike(
'users',
'matriculePaie',
filter.matriculePaie,
),
};
}
if (filter.workdayId) {
where = {
...where,
[Op.and]: Utils.ilike(
'users',
'workdayId',
filter.workdayId,
),
};
}
if (filter.password) {
where = {
@ -598,6 +665,13 @@ module.exports = class UsersDBApi {
),
};
}
if (filter.department) {
where = {
...where,
departmentId: Utils.uuid(filter.department),
};
}
@ -775,12 +849,17 @@ module.exports = class UsersDBApi {
'firstName',
query,
),
Utils.ilike(
'users',
'lastName',
query,
),
],
};
}
const records = await db.users.findAll({
attributes: [ 'id', 'firstName' ],
attributes: [ 'id', 'firstName', 'lastName' ],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
@ -789,7 +868,7 @@ module.exports = class UsersDBApi {
return records.map((record) => ({
id: record.id,
label: record.firstName,
label: `${record.firstName} ${record.lastName}`,
}));
}
@ -943,5 +1022,4 @@ module.exports = class UsersDBApi {
};
};

View File

@ -1,5 +1,3 @@
module.exports = {
production: {
dialect: 'postgres',
@ -12,11 +10,12 @@ module.exports = {
seederStorage: 'sequelize',
},
development: {
username: 'postgres',
username: process.env.DB_USER || 'postgres',
dialect: 'postgres',
password: '',
database: 'db_gestion_entr_es_sorties',
password: process.env.DB_PASS || '',
database: process.env.DB_NAME || 'db_gestion_entr_es_sorties',
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 5432,
logging: console.log,
seederStorage: 'sequelize',
},
@ -30,4 +29,4 @@ module.exports = {
logging: console.log,
seederStorage: 'sequelize',
}
};
};

View File

@ -0,0 +1,37 @@
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.sequelize.query(`
INSERT INTO "rolesPermissionsPermissions" ("createdAt", "updatedAt", "roles_permissionsId", "permissionId")
SELECT NOW(), NOW(), r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'Public'
AND p.name IN ('READ_USERS', 'CREATE_TIME_ENTRIES')
ON CONFLICT ("roles_permissionsId", "permissionId") DO NOTHING;
`, { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.sequelize.query(`
DELETE FROM "rolesPermissionsPermissions"
WHERE "roles_permissionsId" IN (SELECT id FROM roles WHERE name = 'Public')
AND "permissionId" IN (SELECT id FROM permissions WHERE name IN ('READ_USERS', 'CREATE_TIME_ENTRIES'));
`, { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
}
};

View File

@ -0,0 +1,60 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn('users', 'matriculePaie', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'workdayId', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'productionSite', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'remoteWork', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'hiringDate', { type: Sequelize.DataTypes.DATE }, { transaction });
await queryInterface.addColumn('users', 'positionEntryDate', { type: Sequelize.DataTypes.DATE }, { transaction });
await queryInterface.addColumn('users', 'departureDate', { type: Sequelize.DataTypes.DATE }, { transaction });
await queryInterface.addColumn('users', 'service', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'position', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'team', { type: Sequelize.DataTypes.TEXT }, { transaction });
await queryInterface.addColumn('users', 'departmentId', {
type: Sequelize.DataTypes.UUID,
references: {
model: 'departments',
key: 'id',
},
allowNull: true,
}, { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('users', 'matriculePaie', { transaction });
await queryInterface.removeColumn('users', 'workdayId', { transaction });
await queryInterface.removeColumn('users', 'productionSite', { transaction });
await queryInterface.removeColumn('users', 'remoteWork', { transaction });
await queryInterface.removeColumn('users', 'hiringDate', { transaction });
await queryInterface.removeColumn('users', 'positionEntryDate', { transaction });
await queryInterface.removeColumn('users', 'departureDate', { transaction });
await queryInterface.removeColumn('users', 'service', { transaction });
await queryInterface.removeColumn('users', 'position', { transaction });
await queryInterface.removeColumn('users', 'team', { transaction });
await queryInterface.removeColumn('users', 'departmentId', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
}
};

View File

@ -14,94 +14,96 @@ module.exports = function(sequelize, DataTypes) {
primaryKey: true,
},
firstName: {
firstName: {
type: DataTypes.TEXT,
},
lastName: {
lastName: {
type: DataTypes.TEXT,
},
phoneNumber: {
phoneNumber: {
type: DataTypes.TEXT,
},
email: {
email: {
type: DataTypes.TEXT,
},
disabled: {
disabled: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
},
password: {
password: {
type: DataTypes.TEXT,
},
emailVerified: {
emailVerified: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
},
emailVerificationToken: {
emailVerificationToken: {
type: DataTypes.TEXT,
},
emailVerificationTokenExpiresAt: {
emailVerificationTokenExpiresAt: {
type: DataTypes.DATE,
},
passwordResetToken: {
passwordResetToken: {
type: DataTypes.TEXT,
},
passwordResetTokenExpiresAt: {
passwordResetTokenExpiresAt: {
type: DataTypes.DATE,
},
provider: {
provider: {
type: DataTypes.TEXT,
},
matriculePaie: {
type: DataTypes.TEXT,
},
workdayId: {
type: DataTypes.TEXT,
},
productionSite: {
type: DataTypes.TEXT,
},
remoteWork: {
type: DataTypes.TEXT,
},
hiringDate: {
type: DataTypes.DATE,
},
positionEntryDate: {
type: DataTypes.DATE,
},
departureDate: {
type: DataTypes.DATE,
},
service: {
type: DataTypes.TEXT,
},
position: {
type: DataTypes.TEXT,
},
team: {
type: DataTypes.TEXT,
},
importHash: {
@ -137,13 +139,6 @@ provider: {
through: 'usersCustom_permissionsPermissions',
});
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
db.users.hasMany(db.departments, {
as: 'departments_manager',
foreignKey: {
@ -152,6 +147,13 @@ provider: {
constraints: false,
});
db.users.belongsTo(db.departments, {
as: 'department',
foreignKey: {
name: 'departmentId',
},
constraints: false,
});
db.users.hasMany(db.time_entries, {
as: 'time_entries_employee',
@ -179,12 +181,6 @@ provider: {
constraints: false,
});
//end loop
db.users.belongsTo(db.roles, {
as: 'app_role',
foreignKey: {
@ -193,8 +189,6 @@ provider: {
constraints: false,
});
db.users.hasMany(db.file, {
as: 'avatar',
foreignKey: 'belongsToId',
@ -247,7 +241,7 @@ provider: {
function trimStringFields(users) {
users.email = users.email.trim();
if (users.email) users.email = users.email.trim();
users.firstName = users.firstName
? users.firstName.trim()
@ -258,5 +252,4 @@ function trimStringFields(users) {
: null;
return users;
}
}

View File

@ -1,4 +1,3 @@
const express = require('express');
const cors = require('cors');
const app = express();
@ -93,7 +92,7 @@ app.use('/api/pexels', pexelsRoutes);
app.enable('trust proxy');
app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);
app.use('/api/users', usersRoutes);
app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);
@ -101,7 +100,7 @@ app.use('/api/permissions', passport.authenticate('jwt', {session: false}), perm
app.use('/api/departments', passport.authenticate('jwt', {session: false}), departmentsRoutes);
app.use('/api/time_entries', passport.authenticate('jwt', {session: false}), time_entriesRoutes);
app.use('/api/time_entries', time_entriesRoutes);
app.use('/api/import_jobs', passport.authenticate('jwt', {session: false}), import_jobsRoutes);
@ -151,4 +150,4 @@ db.sequelize.sync().then(function () {
});
});
module.exports = app;
module.exports = app;

View File

@ -1,6 +1,5 @@
import React, {useEffect, useRef} from 'react'
import React, {useEffect, useRef, useState} from 'react'
import Link from 'next/link'
import { useState } from 'react'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import BaseDivider from './BaseDivider'
import BaseIcon from './BaseIcon'
@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) {
}
return <div className={componentClass} ref={excludedRef}>{NavBarItemComponentContents}</div>
}
}

View File

@ -20,27 +20,22 @@ type Params = (id: string) => void;
export const loadColumns = async (
onDelete: Params,
entityName: string,
user
) => {
async function callOptionsApi(entityName: string) {
if (!hasPermission(user, 'READ_' + entityName.toUpperCase())) return [];
try {
const data = await axios(`/${entityName}/autocomplete?limit=100`);
return data.data;
const data = await axios(`/${entityName}/autocomplete?limit=100`);
return data.data;
} catch (error) {
console.log(error);
return [];
console.log(error);
return [];
}
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_USERS')
return [
{
field: 'firstName',
headerName: 'First Name',
@ -49,13 +44,8 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'lastName',
headerName: 'Last Name',
@ -64,122 +54,96 @@ export const loadColumns = async (
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'phoneNumber',
headerName: 'Phone Number',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'email',
headerName: 'E-Mail',
flex: 1,
minWidth: 150,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'matriculePaie',
headerName: 'Matricule Paie',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'workdayId',
headerName: 'WD ID',
flex: 1,
minWidth: 100,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'department',
headerName: 'Département',
flex: 1,
minWidth: 150,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
sortable: false,
type: 'singleSelect',
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.name,
valueOptions: await callOptionsApi('departments'),
valueGetter: (params: GridValueGetterParams) =>
params?.row?.department?.name || params?.row?.departmentId,
},
{
field: 'service',
headerName: 'Service',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'position',
headerName: 'Poste',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'productionSite',
headerName: 'Site de production',
flex: 1,
minWidth: 150,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'disabled',
headerName: 'Disabled',
flex: 1,
minWidth: 120,
minWidth: 100,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'boolean',
},
{
field: 'avatar',
headerName: 'Avatar',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: false,
sortable: false,
renderCell: (params: GridValueGetterParams) => (
<ImageField
name={'Avatar'}
image={params?.row?.avatar}
className='w-24 h-24 mx-auto lg:w-6 lg:h-6'
/>
),
},
{
field: 'app_role',
headerName: 'App Role',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
sortable: false,
type: 'singleSelect',
getOptionValue: (value: any) => value?.id,
getOptionLabel: (value: any) => value?.label,
valueOptions: await callOptionsApi('roles'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
{
field: 'custom_permissions',
headerName: 'Custom Permissions',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: false,
sortable: false,
type: 'singleSelect',
valueFormatter: ({ value }) =>
dataFormatter.permissionsManyListFormatter(value).join(', '),
renderEditCell: (params) => (
<DataGridMultiSelect {...params} entityName={'permissions'}/>
),
},
{
field: 'actions',
type: 'actions',
@ -187,7 +151,6 @@ export const loadColumns = async (
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<div key={params?.row?.id}>
<ListActionsPopover
@ -195,13 +158,11 @@ export const loadColumns = async (
itemId={params?.row?.id}
pathEdit={`/users/users-edit/?id=${params?.row?.id}`}
pathView={`/users/users-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>,
]
},
},
];
};
};

View File

@ -1,5 +1,4 @@
import React, { ReactNode, useEffect } from 'react'
import { useState } from 'react'
import React, { ReactNode, useEffect, useState } from 'react'
import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import menuAside from '../menuAside'
@ -126,4 +125,4 @@ export default function LayoutAuthenticated({
</div>
</div>
)
}
}

View File

@ -7,15 +7,28 @@ const menuAside: MenuAsideItem[] = [
icon: icon.mdiViewDashboardOutline,
label: 'Dashboard',
},
{
href: '/check-in',
label: 'Pointage Entrée',
icon: icon.mdiClockIn,
permissions: 'CREATE_TIME_ENTRIES'
},
{
href: '/users/users-list',
label: 'Users',
label: 'Collaborateurs',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: icon.mdiAccountGroup ?? icon.mdiTable,
permissions: 'READ_USERS'
},
{
href: '/time_entries/time_entries-list',
label: 'Présences',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiClock' in icon ? icon['mdiClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_TIME_ENTRIES'
},
{
href: '/roles/roles-list',
label: 'Roles',
@ -40,14 +53,6 @@ const menuAside: MenuAsideItem[] = [
icon: 'mdiOfficeBuilding' in icon ? icon['mdiOfficeBuilding' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_DEPARTMENTS'
},
{
href: '/time_entries/time_entries-list',
label: 'Time entries',
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
icon: 'mdiClock' in icon ? icon['mdiClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
permissions: 'READ_TIME_ENTRIES'
},
{
href: '/import_jobs/import_jobs-list',
label: 'Import jobs',
@ -69,8 +74,6 @@ const menuAside: MenuAsideItem[] = [
label: 'Profile',
icon: icon.mdiAccountCircle,
},
{
href: '/api-docs',
target: '_blank',
@ -80,4 +83,4 @@ const menuAside: MenuAsideItem[] = [
},
]
export default menuAside
export default menuAside

View File

@ -0,0 +1,213 @@
import { mdiClockIn, mdiAccountSearch, mdiCalendarClock, mdiCardAccountDetailsOutline, mdiCheckCircle, mdiCardAccountDetails } from '@mdi/js'
import Head from 'next/head'
import React, { ReactElement, useEffect, useState } from 'react'
import CardBox from '../components/CardBox'
import LayoutGuest from '../layouts/Guest'
import SectionMain from '../components/SectionMain'
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
import { getPageTitle } from '../config'
import { Field, Form, Formik, useFormikContext } from 'formik'
import FormField from '../components/FormField'
import BaseDivider from '../components/BaseDivider'
import BaseButtons from '../components/BaseButtons'
import BaseButton from '../components/BaseButton'
import { SelectField } from '../components/SelectField'
import NotificationBar from '../components/NotificationBar'
import BaseIcon from '../components/BaseIcon'
import { create } from '../stores/time_entries/time_entriesSlice'
import { useAppDispatch } from '../stores/hooks'
import moment from 'moment'
import axios from 'axios'
const initialValues = {
employee: '',
start_time: moment().format('YYYY-MM-DDTHH:mm'),
badge_number: '',
status: 'pending',
}
const EmployeeDetails = () => {
const { values } = useFormikContext<any>()
const [employeeData, setEmployeeData] = useState<any>(null)
const [loading, setLoading] = useState(false)
useEffect(() => {
if (values.employee) {
setLoading(true)
axios.get(`/users/${values.employee}`)
.then(res => {
setEmployeeData(res.data)
})
.catch(err => {
console.error(err)
setEmployeeData(null)
})
.finally(() => {
setLoading(false)
})
} else {
setEmployeeData(null)
}
}, [values.employee])
if (loading) return <div className="mt-4 animate-pulse text-gray-500 text-sm">Chargement des informations du collaborateur...</div>
if (!employeeData) return null
return (
<div className="mt-4 p-4 bg-gray-50 dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700">
<div className="flex items-center mb-3 text-blue-600 dark:text-blue-400">
<BaseIcon path={mdiCardAccountDetails} size={24} className="mr-2" />
<span className="font-bold text-lg">Informations Collaborateur</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<p className="text-gray-500">Nom & Prénom:</p>
<p className="font-semibold">{employeeData.firstName} {employeeData.lastName}</p>
</div>
<div>
<p className="text-gray-500">Département:</p>
<p className="font-semibold">{employeeData.department?.name || 'Non défini'}</p>
</div>
<div>
<p className="text-gray-500">Matricule Paie:</p>
<p className="font-semibold">{employeeData.matriculePaie || '-'}</p>
</div>
<div>
<p className="text-gray-500">Workday ID:</p>
<p className="font-semibold">{employeeData.workdayId || '-'}</p>
</div>
<div>
<p className="text-gray-500">Site de production:</p>
<p className="font-semibold">{employeeData.productionSite || '-'}</p>
</div>
<div>
<p className="text-gray-500">Télétravail:</p>
<p className="font-semibold">{employeeData.remoteWork || '-'}</p>
</div>
<div>
<p className="text-gray-500">Service:</p>
<p className="font-semibold">{employeeData.service || '-'}</p>
</div>
<div>
<p className="text-gray-500">Poste:</p>
<p className="font-semibold">{employeeData.position || '-'}</p>
</div>
<div className="md:col-span-2">
<p className="text-gray-500">Équipe (N+1):</p>
<p className="font-semibold">{employeeData.team || '-'}</p>
</div>
</div>
</div>
)
}
const CheckIn = () => {
const dispatch = useAppDispatch()
const [isSuccess, setIsSuccess] = useState(false)
const [error, setError] = useState(null)
const handleSubmit = async (data, { resetForm }) => {
setIsSuccess(false)
setError(null)
try {
const resultAction = await dispatch(create(data))
if (create.fulfilled.match(resultAction)) {
setIsSuccess(true)
resetForm()
} else {
setError("Une erreur est survenue lors de l'enregistrement.")
}
} catch (err) {
setError("Une erreur est survenue lors de l'enregistrement.")
}
}
return (
<>
<Head>
<title>{getPageTitle('Enregistrement Entrée')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={mdiClockIn} title="Enregistrement Entrée" main>
{''}
</SectionTitleLineWithButton>
{isSuccess && (
<NotificationBar color="success" icon={mdiCheckCircle}>
L&apos;entrée a é enregistrée avec succès.
</NotificationBar>
)}
{error && (
<NotificationBar color="danger" icon={mdiCheckCircle}>
{error}
</NotificationBar>
)}
<CardBox>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
>
{({ isSubmitting }) => (
<Form>
<FormField label="Collaborateur" labelFor="employee" icons={[mdiAccountSearch]}>
<Field
name="employee"
id="employee"
component={SelectField}
options={[]}
itemRef={'users'}
showField={'lastName'}
></Field>
</FormField>
<EmployeeDetails />
<BaseDivider />
<FormField label="Heure d'entrée" icons={[mdiCalendarClock]}>
<Field type="datetime-local" name="start_time" placeholder="Heure d'entrée" />
</FormField>
<FormField label="Numéro de badge provisoire" icons={[mdiCardAccountDetailsOutline]}>
<Field name="badge_number" placeholder="Ex: 12345" />
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton
type="submit"
color="info"
label={isSubmitting ? "Enregistrement..." : "Enregistrer l'entrée"}
disabled={isSubmitting}
/>
<BaseButton
type="reset"
color="info"
outline
label="Réinitialiser"
disabled={isSubmitting}
/>
</BaseButtons>
</Form>
)}
</Formik>
</CardBox>
</SectionMain>
</>
)
}
CheckIn.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutGuest>
{page}
</LayoutGuest>
)
}
export default CheckIn

View File

@ -1,4 +1,3 @@
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
@ -7,13 +6,12 @@ import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
import BaseDivider from '../components/BaseDivider';
import BaseButtons from '../components/BaseButtons';
import { getPageTitle } from '../config';
import { useAppSelector } from '../stores/hooks';
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
import { mdiClockIn, mdiLogin } from '@mdi/js';
export default function Starter() {
const [illustrationImage, setIllustrationImage] = useState({
@ -111,7 +109,7 @@ export default function Starter() {
}
>
<Head>
<title>{getPageTitle('Starter Page')}</title>
<title>{getPageTitle('Gestion Entrées Sorties')}</title>
</Head>
<SectionFullScreen bg='violet'>
@ -128,22 +126,31 @@ export default function Starter() {
: null}
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
<CardBoxComponentTitle title="Welcome to your Gestion Entrées Sorties app!"/>
<CardBoxComponentTitle title="Bienvenue sur votre application de Gestion Entrées Sorties"/>
<div className="space-y-3">
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
<p className='text-center text-gray-500'>For guides and documentation please check
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
<div className="space-y-4 mb-6">
<p className='text-center text-gray-500'>
Suivez facilement les entrées et sorties de vos collaborateurs,
gérez les badges provisoires et consultez les rapports d&apos;heures en temps réel.
</p>
</div>
<BaseButtons>
<BaseButtons className="flex flex-col space-y-3">
<BaseButton
href='/login'
label='Login'
color='info'
href='/check-in'
label='Pointage Entrée (Check-in)'
color='success'
icon={mdiClockIn}
className='w-full'
/>
<BaseButton
href='/login'
label='Accéder à l Administration'
color='info'
icon={mdiLogin}
className='w-full'
outline
/>
</BaseButtons>
</CardBox>
</div>
@ -163,4 +170,3 @@ export default function Starter() {
Starter.getLayout = function getLayout(page: ReactElement) {
return <LayoutGuest>{page}</LayoutGuest>;
};

View File

@ -1,9 +1,7 @@
import { mdiChartTimelineVariant, mdiUpload } from '@mdi/js'
import Head from 'next/head'
import React, { ReactElement, useEffect, useState } from 'react'
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import dayjs from "dayjs";
import CardBox from '../../components/CardBox'
import LayoutAuthenticated from '../../layouts/Authenticated'
@ -16,282 +14,69 @@ import FormField from '../../components/FormField'
import BaseDivider from '../../components/BaseDivider'
import BaseButtons from '../../components/BaseButtons'
import BaseButton from '../../components/BaseButton'
import FormCheckRadio from '../../components/FormCheckRadio'
import FormCheckRadioGroup from '../../components/FormCheckRadioGroup'
import FormFilePicker from '../../components/FormFilePicker'
import FormImagePicker from '../../components/FormImagePicker'
import { SelectField } from "../../components/SelectField";
import { SelectFieldMany } from "../../components/SelectFieldMany";
import { SwitchField } from '../../components/SwitchField'
import {RichTextField} from "../../components/RichTextField";
import { update, fetch } from '../../stores/users/usersSlice'
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
import { useRouter } from 'next/router'
import {saveFile} from "../../helpers/fileSaver";
import dataFormatter from '../../helpers/dataFormatter';
import ImageField from "../../components/ImageField";
const EditUsersPage = () => {
const router = useRouter()
const dispatch = useAppDispatch()
const initVals = {
'firstName': '',
'lastName': '',
'phoneNumber': '',
'email': '',
firstName: '',
lastName: '',
phoneNumber: '',
email: '',
disabled: false,
avatar: [],
app_role: null,
custom_permissions: [],
password: ''
password: '',
matriculePaie: '',
workdayId: '',
productionSite: '',
remoteWork: false,
hiringDate: '',
positionEntryDate: '',
departureDate: '',
service: '',
position: '',
team: '',
departmentId: '',
}
const [initialValues, setInitialValues] = useState(initVals)
const { users } = useAppSelector((state) => state.users)
const { id } = router.query
useEffect(() => {
dispatch(fetch({ id: id }))
if (id) {
dispatch(fetch({ id: id }))
}
}, [id])
useEffect(() => {
if (typeof users === 'object') {
setInitialValues(users)
if (typeof users === 'object' && users !== null) {
const newInitialVal = {...initVals};
Object.keys(initVals).forEach(el => {
if (users[el] !== undefined) {
newInitialVal[el] = users[el]
}
})
// Format dates for input type="date"
if (newInitialVal.hiringDate) newInitialVal.hiringDate = newInitialVal.hiringDate.split('T')[0];
if (newInitialVal.positionEntryDate) newInitialVal.positionEntryDate = newInitialVal.positionEntryDate.split('T')[0];
if (newInitialVal.departureDate) newInitialVal.departureDate = newInitialVal.departureDate.split('T')[0];
setInitialValues(newInitialVal);
}
}, [users])
useEffect(() => {
if (typeof users === 'object') {
const newInitialVal = {...initVals};
Object.keys(initVals).forEach(el => newInitialVal[el] = (users)[el])
setInitialValues(newInitialVal);
}
}, [users])
const handleSubmit = async (data) => {
await dispatch(update({ id: id, data }))
await router.push('/users/users-list')
@ -300,10 +85,10 @@ const EditUsersPage = () => {
return (
<>
<Head>
<title>{getPageTitle('Edit users')}</title>
<title>{getPageTitle('Edit User')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={'Edit users'} main>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={'Edit User'} main>
{''}
</SectionTitleLineWithButton>
<CardBox>
@ -313,358 +98,120 @@ const EditUsersPage = () => {
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField
label="First Name"
>
<Field
name="firstName"
placeholder="First Name"
/>
</FormField>
<FormField
label="Last Name"
>
<Field
name="lastName"
placeholder="Last Name"
/>
</FormField>
<FormField
label="Phone Number"
>
<Field
name="phoneNumber"
placeholder="Phone Number"
/>
</FormField>
<FormField
label="E-Mail"
>
<Field
name="email"
placeholder="E-Mail"
/>
</FormField>
<FormField label='Disabled' labelFor='disabled'>
<Field
name='disabled'
id='disabled'
component={SwitchField}
></Field>
</FormField>
<FormField>
<Field
label='Avatar'
color='info'
icon={mdiUpload}
path={'users/avatar'}
name='avatar'
id='avatar'
schema={{
size: undefined,
formats: undefined,
}}
component={FormImagePicker}
></Field>
</FormField>
<FormField label='App Role' labelFor='app_role'>
<Field
name='app_role'
id='app_role'
component={SelectField}
options={initialValues.app_role}
itemRef={'roles'}
showField={'name'}
></Field>
</FormField>
<FormField label='Custom Permissions' labelFor='custom_permissions'>
<Field
name='custom_permissions'
id='custom_permissions'
component={SelectFieldMany}
options={initialValues.custom_permissions}
itemRef={'permissions'}
showField={'name'}
></Field>
</FormField>
<FormField
label="Password"
>
<Field
name="password"
placeholder="password"
/>
</FormField>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<FormField label="First Name">
<Field name="firstName" placeholder="First Name" />
</FormField>
<FormField label="Last Name">
<Field name="lastName" placeholder="Last Name" />
</FormField>
<FormField label="E-Mail">
<Field name="email" placeholder="E-Mail" />
</FormField>
<FormField label="Phone Number">
<Field name="phoneNumber" placeholder="Phone Number" />
</FormField>
<FormField label="Matricule Paie">
<Field name="matriculePaie" placeholder="Matricule Paie" />
</FormField>
<FormField label="WD ID">
<Field name="workdayId" placeholder="WD ID" />
</FormField>
<FormField label="Site de production">
<Field name="productionSite" placeholder="Site de production" />
</FormField>
<FormField label="Service">
<Field name="service" placeholder="Service" />
</FormField>
<FormField label="Poste">
<Field name="position" placeholder="Poste" />
</FormField>
<FormField label="Équipe (N+1)">
<Field name="team" placeholder="Équipe (N+1)" />
</FormField>
<FormField label="Département" labelFor="departmentId">
<Field
name="departmentId"
id="departmentId"
component={SelectField}
options={initialValues.departmentId}
itemRef={'departments'}
showField={'name'}
></Field>
</FormField>
<FormField label="Date d'embauche">
<Field name="hiringDate" type="date" />
</FormField>
<FormField label="Date d'entrée poste">
<Field name="positionEntryDate" type="date" />
</FormField>
<FormField label="Date de départ">
<Field name="departureDate" type="date" />
</FormField>
<FormField label='Télétravail' labelFor='remoteWork'>
<Field name='remoteWork' id='remoteWork' component={SwitchField}></Field>
</FormField>
<FormField label='Disabled' labelFor='disabled'>
<Field name='disabled' id='disabled' component={SwitchField}></Field>
</FormField>
</div>
<FormField>
<Field
label='Avatar'
color='info'
icon={mdiUpload}
path={'users/avatar'}
name='avatar'
id='avatar'
schema={{
size: undefined,
formats: undefined,
}}
component={FormImagePicker}
></Field>
</FormField>
<FormField label='App Role' labelFor='app_role'>
<Field
name='app_role'
id='app_role'
component={SelectField}
options={initialValues.app_role}
itemRef={'roles'}
showField={'name'}
></Field>
</FormField>
<FormField label='Custom Permissions' labelFor='custom_permissions'>
<Field
name='custom_permissions'
id='custom_permissions'
component={SelectFieldMany}
options={initialValues.custom_permissions}
itemRef={'permissions'}
showField={'name'}
></Field>
</FormField>
<FormField label="Password">
<Field name="password" type="password" placeholder="Leave blank to keep current" />
</FormField>
<BaseDivider />
<BaseButtons>
@ -683,13 +230,11 @@ const EditUsersPage = () => {
EditUsersPage.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated
permission={'UPDATE_USERS'}
>
{page}
</LayoutAuthenticated>
)
}
export default EditUsersPage
export default EditUsersPage

View File

@ -28,135 +28,25 @@ import { useRouter } from 'next/router'
import moment from 'moment';
const initialValues = {
firstName: '',
lastName: '',
phoneNumber: '',
email: '',
disabled: false,
avatar: [],
app_role: '',
custom_permissions: [],
matriculePaie: '',
workdayId: '',
productionSite: '',
remoteWork: false,
hiringDate: '',
positionEntryDate: '',
departureDate: '',
service: '',
position: '',
team: '',
departmentId: '',
}
@ -164,9 +54,6 @@ const UsersNew = () => {
const router = useRouter()
const dispatch = useAppDispatch()
const handleSubmit = async (data) => {
await dispatch(create(data))
await router.push('/users/users-list')
@ -174,220 +61,84 @@ const UsersNew = () => {
return (
<>
<Head>
<title>{getPageTitle('New Item')}</title>
<title>{getPageTitle('New User')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New Item" main>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title="New User" main>
{''}
</SectionTitleLineWithButton>
<CardBox>
<Formik
initialValues={
initialValues
}
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField
label="First Name"
>
<Field
name="firstName"
placeholder="First Name"
/>
</FormField>
<FormField
label="Last Name"
>
<Field
name="lastName"
placeholder="Last Name"
/>
</FormField>
<FormField
label="Phone Number"
>
<Field
name="phoneNumber"
placeholder="Phone Number"
/>
</FormField>
<FormField
label="E-Mail"
>
<Field
name="email"
placeholder="E-Mail"
/>
</FormField>
<FormField label='Disabled' labelFor='disabled'>
<Field
name='disabled'
id='disabled'
component={SwitchField}
></Field>
</FormField>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<FormField label="First Name">
<Field name="firstName" placeholder="First Name" />
</FormField>
<FormField label="Last Name">
<Field name="lastName" placeholder="Last Name" />
</FormField>
<FormField label="E-Mail">
<Field name="email" placeholder="E-Mail" />
</FormField>
<FormField label="Phone Number">
<Field name="phoneNumber" placeholder="Phone Number" />
</FormField>
<FormField label="Matricule Paie">
<Field name="matriculePaie" placeholder="Matricule Paie" />
</FormField>
<FormField label="WD ID">
<Field name="workdayId" placeholder="WD ID" />
</FormField>
<FormField label="Site de production">
<Field name="productionSite" placeholder="Site de production" />
</FormField>
<FormField label="Service">
<Field name="service" placeholder="Service" />
</FormField>
<FormField label="Poste">
<Field name="position" placeholder="Poste" />
</FormField>
<FormField label="Équipe (N+1)">
<Field name="team" placeholder="Équipe (N+1)" />
</FormField>
<FormField label="Département" labelFor="departmentId">
<Field name="departmentId" id="departmentId" component={SelectField} options={[]} itemRef={'departments'}></Field>
</FormField>
<FormField label="Date d'embauche">
<Field name="hiringDate" type="date" />
</FormField>
<FormField label="Date d'entrée poste">
<Field name="positionEntryDate" type="date" />
</FormField>
<FormField label="Date de départ">
<Field name="departureDate" type="date" />
</FormField>
<FormField label='Télétravail' labelFor='remoteWork'>
<Field name='remoteWork' id='remoteWork' component={SwitchField}></Field>
</FormField>
<FormField label='Disabled' labelFor='disabled'>
<Field name='disabled' id='disabled' component={SwitchField}></Field>
</FormField>
</div>
<FormField>
<Field
@ -405,60 +156,10 @@ const UsersNew = () => {
></Field>
</FormField>
<FormField label="App Role" labelFor="app_role">
<Field name="app_role" id="app_role" component={SelectField} options={[]} itemRef={'roles'}></Field>
</FormField>
<FormField label='Custom Permissions' labelFor='custom_permissions'>
<Field
name='custom_permissions'
@ -469,10 +170,6 @@ const UsersNew = () => {
</Field>
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type="submit" color="info" label="Submit" />
@ -490,13 +187,11 @@ const UsersNew = () => {
UsersNew.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated
permission={'CREATE_USERS'}
>
{page}
</LayoutAuthenticated>
)
}
export default UsersNew
export default UsersNew

View File

@ -1,12 +1,8 @@
import React, { ReactElement, useEffect } from 'react';
import Head from 'next/head'
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import dayjs from "dayjs";
import {useAppDispatch, useAppSelector} from "../../stores/hooks";
import {useRouter} from "next/router";
import { fetch } from '../../stores/users/usersSlice'
import {saveFile} from "../../helpers/fileSaver";
import dataFormatter from '../../helpers/dataFormatter';
import ImageField from "../../components/ImageField";
import LayoutAuthenticated from "../../layouts/Authenticated";
@ -25,27 +21,21 @@ const UsersView = () => {
const router = useRouter()
const dispatch = useAppDispatch()
const { users } = useAppSelector((state) => state.users)
const { id } = router.query;
function removeLastCharacter(str) {
console.log(str,`str`)
return str.slice(0, -1);
}
useEffect(() => {
dispatch(fetch({ id }));
if (id) {
dispatch(fetch({ id }));
}
}, [dispatch, id]);
return (
<>
<Head>
<title>{getPageTitle('View users')}</title>
<title>{getPageTitle('View User')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={removeLastCharacter('View users')} main>
<SectionTitleLineWithButton icon={mdiChartTimelineVariant} title={'View User'} main>
<BaseButton
color='info'
label='Edit'
@ -53,153 +43,85 @@ const UsersView = () => {
/>
</SectionTitleLineWithButton>
<CardBox>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>First Name</p>
<p>{users?.firstName}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Last Name</p>
<p>{users?.lastName}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Phone Number</p>
<p>{users?.phoneNumber}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>E-Mail</p>
<p>{users?.email}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Phone Number</p>
<p>{users?.phoneNumber}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Matricule Paie</p>
<p>{users?.matriculePaie}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>WD ID</p>
<p>{users?.workdayId}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Site de production</p>
<p>{users?.productionSite}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Service</p>
<p>{users?.service}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Poste</p>
<p>{users?.position}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Équipe (N+1)</p>
<p>{users?.team}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Département</p>
<p>{users?.department?.name ?? 'No data'}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Date d&apos;embauche</p>
<p>{dataFormatter.dateFormatter(users?.hiringDate)}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Date d&apos;entrée poste</p>
<p>{dataFormatter.dateFormatter(users?.positionEntryDate)}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Date de départ</p>
<p>{dataFormatter.dateFormatter(users?.departureDate)}</p>
</div>
<FormField label='Télétravail'>
<SwitchField
field={{name: 'remoteWork', value: users?.remoteWork}}
form={{setFieldValue: () => null}}
disabled
/>
</FormField>
<FormField label='Disabled'>
<SwitchField
field={{name: 'disabled', value: users?.disabled}}
@ -207,40 +129,8 @@ const UsersView = () => {
disabled
/>
</FormField>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Avatar</p>
{users?.avatar?.length
@ -253,83 +143,13 @@ const UsersView = () => {
) : <p>No Avatar</p>
}
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>App Role</p>
<p>{users?.app_role?.name ?? 'No data'}</p>
<p>{users?.app_role?.name ?? 'No data'}</p>
</div>
<>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Custom Permissions</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
@ -339,42 +159,16 @@ const UsersView = () => {
<table>
<thead>
<tr>
<th>Name</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{users.custom_permissions && Array.isArray(users.custom_permissions) &&
users.custom_permissions.map((item: any) => (
<tr key={item.id} onClick={() => router.push(`/permissions/permissions-view/?id=${item.id}`)}>
<td data-label="name">
{ item.name }
</td>
</tr>
))}
</tbody>
@ -382,310 +176,7 @@ const UsersView = () => {
</div>
{!users?.custom_permissions?.length && <div className={'text-center py-4'}>No data</div>}
</CardBox>
</>
<>
<p className={'block font-bold mb-2'}>Departments Manager</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Nom</th>
<th>Code</th>
<th>Localisation</th>
</tr>
</thead>
<tbody>
{users.departments_manager && Array.isArray(users.departments_manager) &&
users.departments_manager.map((item: any) => (
<tr key={item.id} onClick={() => router.push(`/departments/departments-view/?id=${item.id}`)}>
<td data-label="name">
{ item.name }
</td>
<td data-label="code">
{ item.code }
</td>
<td data-label="location">
{ item.location }
</td>
</tr>
))}
</tbody>
</table>
</div>
{!users?.departments_manager?.length && <div className={'text-center py-4'}>No data</div>}
</CardBox>
</>
<>
<p className={'block font-bold mb-2'}>Time_entries Collaborateur</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Heureentrée</th>
<th>Heuresortie</th>
<th>Numérobadgeprovisoire</th>
<th>Statut</th>
<th>Résumé</th>
<th>Note</th>
</tr>
</thead>
<tbody>
{users.time_entries_employee && Array.isArray(users.time_entries_employee) &&
users.time_entries_employee.map((item: any) => (
<tr key={item.id} onClick={() => router.push(`/time_entries/time_entries-view/?id=${item.id}`)}>
<td data-label="start_time">
{ dataFormatter.dateTimeFormatter(item.start_time) }
</td>
<td data-label="end_time">
{ dataFormatter.dateTimeFormatter(item.end_time) }
</td>
<td data-label="badge_number">
{ item.badge_number }
</td>
<td data-label="status">
{ item.status }
</td>
<td data-label="summary">
{ item.summary }
</td>
<td data-label="note">
{ item.note }
</td>
</tr>
))}
</tbody>
</table>
</div>
{!users?.time_entries_employee?.length && <div className={'text-center py-4'}>No data</div>}
</CardBox>
</>
<>
<p className={'block font-bold mb-2'}>Import_jobs Importépar</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Nomfichier</th>
<th>Dateimport</th>
<th>Statutimport</th>
<th>Lignestraitées</th>
</tr>
</thead>
<tbody>
{users.import_jobs_imported_by && Array.isArray(users.import_jobs_imported_by) &&
users.import_jobs_imported_by.map((item: any) => (
<tr key={item.id} onClick={() => router.push(`/import_jobs/import_jobs-view/?id=${item.id}`)}>
<td data-label="filename">
{ item.filename }
</td>
<td data-label="imported_at">
{ dataFormatter.dateTimeFormatter(item.imported_at) }
</td>
<td data-label="status">
{ item.status }
</td>
<td data-label="rows_processed">
{ item.rows_processed }
</td>
</tr>
))}
</tbody>
</table>
</div>
{!users?.import_jobs_imported_by?.length && <div className={'text-center py-4'}>No data</div>}
</CardBox>
</>
<>
<p className={'block font-bold mb-2'}>Badges Affectéà</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Numérobadge</th>
<th>Dateémission</th>
<th>Restitué</th>
</tr>
</thead>
<tbody>
{users.badges_assigned_to && Array.isArray(users.badges_assigned_to) &&
users.badges_assigned_to.map((item: any) => (
<tr key={item.id} onClick={() => router.push(`/badges/badges-view/?id=${item.id}`)}>
<td data-label="number">
{ item.number }
</td>
<td data-label="issued_at">
{ dataFormatter.dateTimeFormatter(item.issued_at) }
</td>
<td data-label="returned">
{ dataFormatter.booleanFormatter(item.returned) }
</td>
</tr>
))}
</tbody>
</table>
</div>
{!users?.badges_assigned_to?.length && <div className={'text-center py-4'}>No data</div>}
</CardBox>
</>
</div>
<BaseDivider />
@ -703,9 +194,7 @@ const UsersView = () => {
UsersView.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated
permission={'READ_USERS'}
>
{page}
</LayoutAuthenticated>