Autosave: 20260413-132151
This commit is contained in:
parent
c9732c5db8
commit
da86c68992
BIN
assets/pasted-20260413-130727-e67a0305.png
Normal file
BIN
assets/pasted-20260413-130727-e67a0305.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
assets/pasted-20260413-131715-2c4e92ec.png
Normal file
BIN
assets/pasted-20260413-131715-2c4e92ec.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
@ -240,9 +240,19 @@ module.exports = class Payroll_runsDBApi {
|
||||
|
||||
|
||||
|
||||
output.payroll_line_items_payroll_run = await payroll_runs.getPayroll_line_items_payroll_run({
|
||||
transaction
|
||||
});
|
||||
output.payroll_line_items_payroll_run = await Promise.all(
|
||||
(await payroll_runs.getPayroll_line_items_payroll_run({
|
||||
transaction
|
||||
})).map(async (payrollLineItem) => {
|
||||
const payrollLineItemOutput = payrollLineItem.get({ plain: true });
|
||||
|
||||
payrollLineItemOutput.employee = await payrollLineItem.getEmployee({
|
||||
transaction,
|
||||
});
|
||||
|
||||
return payrollLineItemOutput;
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
@ -5,6 +5,11 @@ const db = require('../db/models');
|
||||
const { wrapAsync } = require('../helpers');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
const getInclusiveDateRange = (startDate, endDate) => ({
|
||||
start: new Date(`${startDate}T00:00:00.000Z`),
|
||||
end: new Date(`${endDate}T23:59:59.999Z`),
|
||||
});
|
||||
|
||||
router.post('/preview', passport.authenticate('jwt', { session: false }), wrapAsync(async (req, res) => {
|
||||
const { startDate, endDate } = req.body;
|
||||
|
||||
@ -12,11 +17,13 @@ router.post('/preview', passport.authenticate('jwt', { session: false }), wrapAs
|
||||
return res.status(400).send('startDate and endDate are required');
|
||||
}
|
||||
|
||||
const { start, end } = getInclusiveDateRange(startDate, endDate);
|
||||
|
||||
// Find job logs in range that are not paid
|
||||
const jobLogs = await db.job_logs.findAll({
|
||||
where: {
|
||||
work_date: {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
[Op.between]: [start, end]
|
||||
},
|
||||
status: {
|
||||
[Op.ne]: 'paid'
|
||||
@ -85,11 +92,13 @@ router.post('/generate', passport.authenticate('jwt', { session: false }), wrapA
|
||||
return res.status(400).send('startDate and endDate are required');
|
||||
}
|
||||
|
||||
const { start, end } = getInclusiveDateRange(startDate, endDate);
|
||||
|
||||
// Find job logs
|
||||
const jobLogs = await db.job_logs.findAll({
|
||||
where: {
|
||||
work_date: {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
[Op.between]: [start, end]
|
||||
},
|
||||
status: {
|
||||
[Op.ne]: 'paid'
|
||||
|
||||
BIN
frontend/public/assets/vm-shot-2026-04-13T13-17-17-604Z.jpg
Normal file
BIN
frontend/public/assets/vm-shot-2026-04-13T13-17-17-604Z.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -218,52 +218,7 @@ export const loadColumns = async (
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'odometer_start',
|
||||
headerName: 'OdometerStart',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'number',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'odometer_end',
|
||||
headerName: 'OdometerEnd',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
type: 'number',
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
field: 'job_address',
|
||||
headerName: 'JobAddress',
|
||||
flex: 1,
|
||||
minWidth: 120,
|
||||
filterable: false,
|
||||
headerClassName: 'datagrid--header',
|
||||
cellClassName: 'datagrid--cell',
|
||||
|
||||
|
||||
editable: hasUpdatePermission,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
field: 'status',
|
||||
|
||||
@ -84,28 +84,12 @@ const menuAside: MenuAsideItem[] = [
|
||||
icon: 'mdiLinkVariant' in icon ? icon['mdiLinkVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'UPDATE_USERS'
|
||||
},
|
||||
{
|
||||
href: '/chemical_products/chemical_products-list',
|
||||
label: 'Chemical products',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFlask' in icon ? icon['mdiFlask' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'UPDATE_USERS'
|
||||
},
|
||||
{
|
||||
href: '/job_logs/job_logs-list',
|
||||
label: 'All Job Logs',
|
||||
icon: 'mdiClipboardTextClock' in icon ? icon['mdiClipboardTextClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'UPDATE_USERS'
|
||||
},
|
||||
{
|
||||
href: '/job_chemical_usages/job_chemical_usages-list',
|
||||
label: 'Job chemical usages',
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
icon: 'mdiFlaskOutline' in icon ? icon['mdiFlaskOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
||||
permissions: 'UPDATE_USERS'
|
||||
},
|
||||
{
|
||||
href: '/payroll_runs/payroll_runs-list',
|
||||
label: 'Payroll runs',
|
||||
|
||||
@ -32,9 +32,7 @@ const Dashboard = () => {
|
||||
const [vehicles, setVehicles] = React.useState(loadingMessage);
|
||||
const [pay_types, setPay_types] = React.useState(loadingMessage);
|
||||
const [employee_pay_types, setEmployee_pay_types] = React.useState(loadingMessage);
|
||||
const [chemical_products, setChemical_products] = React.useState(loadingMessage);
|
||||
const [job_logs, setJob_logs] = React.useState(loadingMessage);
|
||||
const [job_chemical_usages, setJob_chemical_usages] = React.useState(loadingMessage);
|
||||
const [payroll_runs, setPayroll_runs] = React.useState(loadingMessage);
|
||||
const [payroll_line_items, setPayroll_line_items] = React.useState(loadingMessage);
|
||||
|
||||
@ -58,8 +56,8 @@ const Dashboard = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const entities = ['users','roles','permissions','customers','vehicles','pay_types','employee_pay_types','chemical_products','job_logs','job_chemical_usages','payroll_runs','payroll_line_items',];
|
||||
const fns = [setUsers,setRoles,setPermissions,setCustomers,setVehicles,setPay_types,setEmployee_pay_types,setChemical_products,setJob_logs,setJob_chemical_usages,setPayroll_runs,setPayroll_line_items,];
|
||||
const entities = ['users','roles','permissions','customers','vehicles','pay_types','employee_pay_types','job_logs','payroll_runs','payroll_line_items',];
|
||||
const fns = [setUsers,setRoles,setPermissions,setCustomers,setVehicles,setPay_types,setEmployee_pay_types,setJob_logs,setPayroll_runs,setPayroll_line_items,];
|
||||
|
||||
const requests = entities.map((entity, index) => {
|
||||
|
||||
@ -355,34 +353,6 @@ const Dashboard = () => {
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_CHEMICAL_PRODUCTS') && <Link href={'/chemical_products/chemical_products-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Chemical products
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{chemical_products}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiFlask' in icon ? icon['mdiFlask' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_JOB_LOGS') && <Link href={'/job_logs/job_logs-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
@ -411,34 +381,6 @@ const Dashboard = () => {
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_JOB_CHEMICAL_USAGES') && <Link href={'/job_chemical_usages/job_chemical_usages-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
>
|
||||
<div className="flex justify-between align-center">
|
||||
<div>
|
||||
<div className="text-lg leading-tight text-gray-500 dark:text-gray-400">
|
||||
Job chemical usages
|
||||
</div>
|
||||
<div className="text-3xl leading-tight font-semibold">
|
||||
{job_chemical_usages}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<BaseIcon
|
||||
className={`${iconsColor}`}
|
||||
w="w-16"
|
||||
h="h-16"
|
||||
size={48}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
path={'mdiFlaskOutline' in icon ? icon['mdiFlaskOutline' as keyof typeof icon] : icon.mdiTable || icon.mdiTable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>}
|
||||
|
||||
{hasPermission(currentUser, 'READ_PAYROLL_RUNS') && <Link href={'/payroll_runs/payroll_runs-list'}>
|
||||
<div
|
||||
className={`${corners !== 'rounded-full'? corners : 'rounded-3xl'} dark:bg-dark-900 ${cardsStyle} dark:border-dark-700 p-6`}
|
||||
|
||||
@ -270,7 +270,6 @@ const EditJob_logs = () => {
|
||||
|
||||
|
||||
|
||||
odometer_start: '',
|
||||
|
||||
|
||||
|
||||
@ -298,7 +297,6 @@ const EditJob_logs = () => {
|
||||
|
||||
|
||||
|
||||
odometer_end: '',
|
||||
|
||||
|
||||
|
||||
@ -320,7 +318,6 @@ const EditJob_logs = () => {
|
||||
|
||||
|
||||
|
||||
'job_address': '',
|
||||
|
||||
|
||||
|
||||
@ -875,146 +872,6 @@ const EditJob_logs = () => {
|
||||
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerStart"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_start"
|
||||
placeholder="OdometerStart"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerEnd"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_end"
|
||||
placeholder="OdometerEnd"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="JobAddress"
|
||||
>
|
||||
<Field
|
||||
name="job_address"
|
||||
placeholder="JobAddress"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Status" labelFor="status">
|
||||
<Field name="status" id="status" component="select">
|
||||
|
||||
|
||||
@ -271,7 +271,6 @@ const EditJob_logsPage = () => {
|
||||
|
||||
|
||||
|
||||
odometer_start: '',
|
||||
|
||||
|
||||
|
||||
@ -299,7 +298,6 @@ const EditJob_logsPage = () => {
|
||||
|
||||
|
||||
|
||||
odometer_end: '',
|
||||
|
||||
|
||||
|
||||
@ -321,7 +319,6 @@ const EditJob_logsPage = () => {
|
||||
|
||||
|
||||
|
||||
'job_address': '',
|
||||
|
||||
|
||||
|
||||
@ -867,146 +864,6 @@ const EditJob_logsPage = () => {
|
||||
|
||||
></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerStart"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_start"
|
||||
placeholder="OdometerStart"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerEnd"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_end"
|
||||
placeholder="OdometerEnd"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="JobAddress"
|
||||
>
|
||||
<Field
|
||||
name="job_address"
|
||||
placeholder="JobAddress"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Status" labelFor="status">
|
||||
<Field name="status" id="status" component="select">
|
||||
|
||||
|
||||
@ -34,8 +34,7 @@ const Job_logsTablesPage = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
const [filters] = useState([{label: 'JobAddress', title: 'job_address'},{label: 'NotesToAdmin', title: 'notes_to_admin'},
|
||||
{label: 'OdometerStart', title: 'odometer_start', number: 'true'},{label: 'OdometerEnd', title: 'odometer_end', number: 'true'},
|
||||
const [filters] = useState([{label: 'NotesToAdmin', title: 'notes_to_admin'},
|
||||
{label: 'HoursConducted', title: 'hours_conducted', number: 'true'},{label: 'ClientPaid', title: 'client_paid', number: 'true'},
|
||||
{label: 'WorkDate', title: 'work_date', date: 'true'},
|
||||
|
||||
|
||||
@ -163,7 +163,6 @@ const initialValues = {
|
||||
|
||||
|
||||
|
||||
odometer_start: '',
|
||||
|
||||
|
||||
|
||||
@ -179,7 +178,6 @@ const initialValues = {
|
||||
|
||||
|
||||
|
||||
odometer_end: '',
|
||||
|
||||
|
||||
|
||||
@ -192,7 +190,6 @@ const initialValues = {
|
||||
|
||||
|
||||
|
||||
job_address: '',
|
||||
|
||||
|
||||
|
||||
@ -527,136 +524,6 @@ const Job_logsNew = () => {
|
||||
<FormField label="Vehicle" labelFor="vehicle">
|
||||
<Field name="vehicle" id="vehicle" component={SelectField} options={[]} itemRef={'vehicles'}></Field>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerStart"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_start"
|
||||
placeholder="OdometerStart"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="OdometerEnd"
|
||||
>
|
||||
<Field
|
||||
type="number"
|
||||
name="odometer_end"
|
||||
placeholder="OdometerEnd"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField
|
||||
label="JobAddress"
|
||||
>
|
||||
<Field
|
||||
name="job_address"
|
||||
placeholder="JobAddress"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<FormField label="Status" labelFor="status">
|
||||
<Field name="status" id="status" component="select">
|
||||
|
||||
|
||||
@ -34,8 +34,7 @@ const Job_logsTablesPage = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
const [filters] = useState([{label: 'JobAddress', title: 'job_address'},{label: 'NotesToAdmin', title: 'notes_to_admin'},
|
||||
{label: 'OdometerStart', title: 'odometer_start', number: 'true'},{label: 'OdometerEnd', title: 'odometer_end', number: 'true'},
|
||||
const [filters] = useState([{label: 'NotesToAdmin', title: 'notes_to_admin'},
|
||||
{label: 'HoursConducted', title: 'hours_conducted', number: 'true'},{label: 'ClientPaid', title: 'client_paid', number: 'true'},
|
||||
{label: 'WorkDate', title: 'work_date', date: 'true'},
|
||||
|
||||
|
||||
@ -421,127 +421,6 @@ const Job_logsView = () => {
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>OdometerStart</p>
|
||||
<p>{job_logs?.odometer_start || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>OdometerEnd</p>
|
||||
<p>{job_logs?.odometer_end || 'No data'}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>JobAddress</p>
|
||||
<p>{job_logs?.job_address}</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div className={'mb-4'}>
|
||||
<p className={'block font-bold mb-2'}>Status</p>
|
||||
<p>{job_logs?.status ?? 'No data'}</p>
|
||||
@ -600,65 +479,6 @@ const Job_logsView = () => {
|
||||
|
||||
|
||||
|
||||
<>
|
||||
<p className={'block font-bold mb-2'}>Job_chemical_usages JobLog</p>
|
||||
<CardBox
|
||||
className='mb-6 border border-gray-300 rounded overflow-hidden'
|
||||
hasTable
|
||||
>
|
||||
<div className='overflow-x-auto'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>QuantityUsed</th>
|
||||
|
||||
|
||||
|
||||
<th>Notes</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{job_logs.job_chemical_usages_job_log && Array.isArray(job_logs.job_chemical_usages_job_log) &&
|
||||
job_logs.job_chemical_usages_job_log.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/job_chemical_usages/job_chemical_usages-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="quantity_used">
|
||||
{ item.quantity_used }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="notes">
|
||||
{ item.notes }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{!job_logs?.job_chemical_usages_job_log?.length && <div className={'text-center py-4'}>No data</div>}
|
||||
</CardBox>
|
||||
</>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<BaseDivider />
|
||||
|
||||
<BaseButton
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { mdiPencil, mdiPlus, mdiTrashCan } from '@mdi/js';
|
||||
import { mdiPencil } from '@mdi/js';
|
||||
import Head from 'next/head';
|
||||
import React, { ReactElement, useEffect, useState } from 'react';
|
||||
import CardBox from '../components/CardBox';
|
||||
@ -8,7 +8,7 @@ import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton
|
||||
import JobLogPayPreview from '../components/JobLogPayPreview';
|
||||
import { getPageTitle } from '../config';
|
||||
|
||||
import { Field, Form, Formik, FieldArray } from 'formik';
|
||||
import { Field, Form, Formik } from 'formik';
|
||||
import FormField from '../components/FormField';
|
||||
import BaseDivider from '../components/BaseDivider';
|
||||
import BaseButtons from '../components/BaseButtons';
|
||||
@ -27,11 +27,13 @@ const LogWorkPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser?.id) {
|
||||
axios.get(`/employee_pay_types?employee=${currentUser.id}&active=true`)
|
||||
axios
|
||||
.get(`/employee_pay_types?employee=${currentUser.id}&active=true`)
|
||||
.then((res) => {
|
||||
if (res.data && res.data.rows) {
|
||||
// Ensure we are extracting the inner pay_type relation from employee_pay_types
|
||||
const payTypes = res.data.rows.map((row: any) => row.pay_type).filter(Boolean);
|
||||
const payTypes = res.data.rows
|
||||
.map((row: any) => row.pay_type)
|
||||
.filter(Boolean);
|
||||
setAssignedPayTypes(payTypes);
|
||||
}
|
||||
})
|
||||
@ -40,7 +42,7 @@ const LogWorkPage = () => {
|
||||
}, [currentUser]);
|
||||
|
||||
const initialValues = {
|
||||
work_date: new Date().toISOString().slice(0, 16),
|
||||
work_date: new Date().toISOString().slice(0, 10),
|
||||
employee: currentUser?.id || '',
|
||||
customer: '',
|
||||
hours_conducted: '',
|
||||
@ -48,12 +50,8 @@ const LogWorkPage = () => {
|
||||
workersCompClass: '',
|
||||
pay_type: '',
|
||||
vehicle: '',
|
||||
odometer_start: '',
|
||||
odometer_end: '',
|
||||
job_address: '',
|
||||
status: 'submitted',
|
||||
notes_to_admin: '',
|
||||
chemical_usages: [],
|
||||
};
|
||||
|
||||
const handleSubmit = async (data: any) => {
|
||||
@ -72,10 +70,10 @@ const LogWorkPage = () => {
|
||||
</SectionTitleLineWithButton>
|
||||
<CardBox>
|
||||
<Formik initialValues={initialValues} onSubmit={(values) => handleSubmit(values)}>
|
||||
{({ values }) => (
|
||||
{() => (
|
||||
<Form>
|
||||
<FormField label="Work Date">
|
||||
<Field type="datetime-local" name="work_date" />
|
||||
<Field type="date" name="work_date" />
|
||||
</FormField>
|
||||
<FormField label="Customer" labelFor="customer">
|
||||
<Field name="customer" id="customer" placeholder="Enter customer name" />
|
||||
@ -87,7 +85,14 @@ const LogWorkPage = () => {
|
||||
<Field type="number" name="client_paid" placeholder="Amount" />
|
||||
</FormField>
|
||||
<FormField label="Worker's Comp Class" labelFor="workersCompClass">
|
||||
<Field name="workersCompClass" id="workersCompClass" component={SelectField} options={[]} itemRef={"workers_comp_classes"} showField={"name"} />
|
||||
<Field
|
||||
name="workersCompClass"
|
||||
id="workersCompClass"
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'workers_comp_classes'}
|
||||
showField={'name'}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Pay Type" labelFor="pay_type">
|
||||
<Field name="pay_type" id="pay_type" as="select">
|
||||
@ -101,68 +106,18 @@ const LogWorkPage = () => {
|
||||
</FormField>
|
||||
<JobLogPayPreview payTypeOptions={assignedPayTypes} />
|
||||
<FormField label="Vehicle" labelFor="vehicle">
|
||||
<Field name="vehicle" id="vehicle" component={SelectField} options={[]} itemRef={'vehicles'} />
|
||||
</FormField>
|
||||
<FormField label="Odometer Start">
|
||||
<Field type="number" name="odometer_start" placeholder="Start" />
|
||||
</FormField>
|
||||
<FormField label="Odometer End">
|
||||
<Field type="number" name="odometer_end" placeholder="End" />
|
||||
</FormField>
|
||||
<FormField label="Job Address">
|
||||
<Field name="job_address" placeholder="Address" />
|
||||
<Field
|
||||
name="vehicle"
|
||||
id="vehicle"
|
||||
component={SelectField}
|
||||
options={[]}
|
||||
itemRef={'vehicles'}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Notes to Admin" hasTextareaHeight>
|
||||
<Field name="notes_to_admin" as="textarea" placeholder="Notes..." />
|
||||
</FormField>
|
||||
|
||||
<BaseDivider />
|
||||
<h3 className="text-lg font-bold mb-4">Chemical Usage (Optional)</h3>
|
||||
<FieldArray name="chemical_usages">
|
||||
{({ push, remove }) => (
|
||||
<div>
|
||||
{values.chemical_usages.map((usage, index) => (
|
||||
<div key={index} className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4 items-end border p-4 rounded-lg bg-gray-50 dark:bg-slate-800">
|
||||
<FormField label="Chemical Product" labelFor={`chemical_usages.${index}.chemical_product`}>
|
||||
<Field
|
||||
name={`chemical_usages.${index}.chemical_product`}
|
||||
id={`chemical_usages.${index}.chemical_product`}
|
||||
component={SelectField}
|
||||
itemRef="chemical_products"
|
||||
showField="name"
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Quantity Used" labelFor={`chemical_usages.${index}.quantity_used`}>
|
||||
<Field
|
||||
type="number"
|
||||
name={`chemical_usages.${index}.quantity_used`}
|
||||
id={`chemical_usages.${index}.quantity_used`}
|
||||
placeholder="e.g. 5.5"
|
||||
step="0.01"
|
||||
/>
|
||||
</FormField>
|
||||
<div className="mb-4">
|
||||
<BaseButton
|
||||
type="button"
|
||||
color="danger"
|
||||
label="Remove"
|
||||
icon={mdiTrashCan}
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<BaseButton
|
||||
type="button"
|
||||
color="success"
|
||||
label="Add Chemical"
|
||||
icon={mdiPlus}
|
||||
onClick={() => push({ chemical_product: '', quantity_used: '' })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</FieldArray>
|
||||
|
||||
<BaseDivider />
|
||||
<BaseButtons>
|
||||
<BaseButton type="submit" color="info" label="Submit Work Log" />
|
||||
@ -180,4 +135,4 @@ LogWorkPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
|
||||
};
|
||||
|
||||
export default LogWorkPage;
|
||||
export default LogWorkPage;
|
||||
|
||||
@ -30,10 +30,20 @@ const Payroll_runsView = () => {
|
||||
const { id } = router.query;
|
||||
|
||||
function removeLastCharacter(str) {
|
||||
console.log(str,`str`)
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
|
||||
function formatEmployeeName(employee) {
|
||||
if (!employee) return 'No data';
|
||||
|
||||
const fullName = [employee.firstName, employee.lastName]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
return fullName || employee.email || 'No data';
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetch({ id }));
|
||||
}, [dispatch, id]);
|
||||
@ -292,72 +302,28 @@ const Payroll_runsView = () => {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th>Employee</th>
|
||||
<th>TotalHours</th>
|
||||
|
||||
|
||||
|
||||
<th>GrossPay</th>
|
||||
|
||||
|
||||
|
||||
<th>TotalCommissionBase</th>
|
||||
|
||||
|
||||
|
||||
<th>TotalClientPaid</th>
|
||||
|
||||
|
||||
|
||||
<th>Summary</th>
|
||||
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payroll_runs.payroll_line_items_payroll_run && Array.isArray(payroll_runs.payroll_line_items_payroll_run) &&
|
||||
payroll_runs.payroll_line_items_payroll_run.map((item: any) => (
|
||||
<tr key={item.id} onClick={() => router.push(`/payroll_line_items/payroll_line_items-view/?id=${item.id}`)}>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td data-label="employee">
|
||||
{formatEmployeeName(item.employee)}
|
||||
</td>
|
||||
<td data-label="total_hours">
|
||||
{ item.total_hours }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="gross_pay">
|
||||
{ item.gross_pay }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="total_commission_base">
|
||||
{ item.total_commission_base }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="total_client_paid">
|
||||
{ item.total_client_paid }
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
<td data-label="summary">
|
||||
{ item.summary }
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user