Forced merge: merge ai-dev into master

This commit is contained in:
Flatlogic Bot 2025-07-12 09:49:15 +00:00
commit 0a30b833e2
21 changed files with 2580 additions and 378 deletions

File diff suppressed because one or more lines are too long

View File

@ -152,6 +152,14 @@ module.exports = class EmployeesDBApi {
const output = employees.get({ plain: true });
output.fuelcard_driver = await employees.getFuelcard_driver({
transaction,
});
output.fuellog_driver = await employees.getFuellog_driver({
transaction,
});
output.organizations = await employees.getOrganizations({
transaction,
});

View File

@ -0,0 +1,421 @@
const db = require('../models');
const FileDBApi = require('./file');
const crypto = require('crypto');
const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
module.exports = class FuelcardDBApi {
static async create(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const fuelcard = await db.fuelcard.create(
{
id: data.id || undefined,
card_number: data.card_number || null,
limit: data.limit || null,
importHash: data.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
},
{ transaction },
);
await fuelcard.setOrganizations(data.organizations || null, {
transaction,
});
await fuelcard.setVehicle(data.vehicle || null, {
transaction,
});
await fuelcard.setDriver(data.driver || null, {
transaction,
});
return fuelcard;
}
static async bulkImport(data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
// Prepare data - wrapping individual data transformations in a map() method
const fuelcardData = data.map((item, index) => ({
id: item.id || undefined,
card_number: item.card_number || null,
limit: item.limit || null,
importHash: item.importHash || null,
createdById: currentUser.id,
updatedById: currentUser.id,
createdAt: new Date(Date.now() + index * 1000),
}));
// Bulk create items
const fuelcard = await db.fuelcard.bulkCreate(fuelcardData, {
transaction,
});
// For each item created, replace relation files
return fuelcard;
}
static async update(id, data, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const globalAccess = currentUser.app_role?.globalAccess;
const fuelcard = await db.fuelcard.findByPk(id, {}, { transaction });
const updatePayload = {};
if (data.card_number !== undefined)
updatePayload.card_number = data.card_number;
if (data.limit !== undefined) updatePayload.limit = data.limit;
updatePayload.updatedById = currentUser.id;
await fuelcard.update(updatePayload, { transaction });
if (data.organizations !== undefined) {
await fuelcard.setOrganizations(
data.organizations,
{ transaction },
);
}
if (data.vehicle !== undefined) {
await fuelcard.setVehicle(
data.vehicle,
{ transaction },
);
}
if (data.driver !== undefined) {
await fuelcard.setDriver(
data.driver,
{ transaction },
);
}
return fuelcard;
}
static async deleteByIds(ids, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const fuelcard = await db.fuelcard.findAll({
where: {
id: {
[Op.in]: ids,
},
},
transaction,
});
await db.sequelize.transaction(async (transaction) => {
for (const record of fuelcard) {
await record.update({ deletedBy: currentUser.id }, { transaction });
}
for (const record of fuelcard) {
await record.destroy({ transaction });
}
});
return fuelcard;
}
static async remove(id, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const fuelcard = await db.fuelcard.findByPk(id, options);
await fuelcard.update(
{
deletedBy: currentUser.id,
},
{
transaction,
},
);
await fuelcard.destroy({
transaction,
});
return fuelcard;
}
static async findBy(where, options) {
const transaction = (options && options.transaction) || undefined;
const fuelcard = await db.fuelcard.findOne({ where }, { transaction });
if (!fuelcard) {
return fuelcard;
}
const output = fuelcard.get({ plain: true });
output.fuellog_fuel_card = await fuelcard.getFuellog_fuel_card({
transaction,
});
output.organizations = await fuelcard.getOrganizations({
transaction,
});
output.vehicle = await fuelcard.getVehicle({
transaction,
});
output.driver = await fuelcard.getDriver({
transaction,
});
return output;
}
static async findAll(filter, globalAccess, options) {
const limit = filter.limit || 0;
let offset = 0;
let where = {};
const currentPage = +filter.page;
const user = (options && options.currentUser) || null;
const userOrganizations = (user && user.organizations?.id) || null;
if (userOrganizations) {
if (options?.currentUser?.organizationsId) {
where.organizationsId = options.currentUser.organizationsId;
}
}
offset = currentPage * limit;
const orderBy = null;
const transaction = (options && options.transaction) || undefined;
let include = [
{
model: db.organizations,
as: 'organizations',
},
{
model: db.vehicle,
as: 'vehicle',
where: filter.vehicle
? {
[Op.or]: [
{
id: {
[Op.in]: filter.vehicle
.split('|')
.map((term) => Utils.uuid(term)),
},
},
{
license_plate: {
[Op.or]: filter.vehicle
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
},
},
],
}
: {},
},
{
model: db.employees,
as: 'driver',
where: filter.driver
? {
[Op.or]: [
{
id: {
[Op.in]: filter.driver
.split('|')
.map((term) => Utils.uuid(term)),
},
},
{
first_name: {
[Op.or]: filter.driver
.split('|')
.map((term) => ({ [Op.iLike]: `%${term}%` })),
},
},
],
}
: {},
},
];
if (filter) {
if (filter.id) {
where = {
...where,
['id']: Utils.uuid(filter.id),
};
}
if (filter.card_number) {
where = {
...where,
[Op.and]: Utils.ilike('fuelcard', 'card_number', filter.card_number),
};
}
if (filter.limitRange) {
const [start, end] = filter.limitRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
limit: {
...where.limit,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
limit: {
...where.limit,
[Op.lte]: end,
},
};
}
}
if (filter.active !== undefined) {
where = {
...where,
active: filter.active === true || filter.active === 'true',
};
}
if (filter.organizations) {
const listItems = filter.organizations.split('|').map((item) => {
return Utils.uuid(item);
});
where = {
...where,
organizationsId: { [Op.or]: listItems },
};
}
if (filter.createdAtRange) {
const [start, end] = filter.createdAtRange;
if (start !== undefined && start !== null && start !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.gte]: start,
},
};
}
if (end !== undefined && end !== null && end !== '') {
where = {
...where,
['createdAt']: {
...where.createdAt,
[Op.lte]: end,
},
};
}
}
}
if (globalAccess) {
delete where.organizationsId;
}
const queryOptions = {
where,
include,
distinct: true,
order:
filter.field && filter.sort
? [[filter.field, filter.sort]]
: [['createdAt', 'desc']],
transaction: options?.transaction,
logging: console.log,
};
if (!options?.countOnly) {
queryOptions.limit = limit ? Number(limit) : undefined;
queryOptions.offset = offset ? Number(offset) : undefined;
}
try {
const { rows, count } = await db.fuelcard.findAndCountAll(queryOptions);
return {
rows: options?.countOnly ? [] : rows,
count: count,
};
} catch (error) {
console.error('Error executing query:', error);
throw error;
}
}
static async findAllAutocomplete(
query,
limit,
offset,
globalAccess,
organizationId,
) {
let where = {};
if (!globalAccess && organizationId) {
where.organizationId = organizationId;
}
if (query) {
where = {
[Op.or]: [
{ ['id']: Utils.uuid(query) },
Utils.ilike('fuelcard', 'card_number', query),
],
};
}
const records = await db.fuelcard.findAll({
attributes: ['id', 'card_number'],
where,
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['card_number', 'ASC']],
});
return records.map((record) => ({
id: record.id,
label: record.card_number,
}));
}
};

View File

@ -0,0 +1,54 @@
module.exports = {
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async up(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'fuelcard',
'driverId',
{
type: Sequelize.DataTypes.UUID,
references: {
model: 'employees',
key: 'id',
},
},
{ transaction },
);
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
/**
* @param {QueryInterface} queryInterface
* @param {Sequelize} Sequelize
* @returns {Promise<void>}
*/
async down(queryInterface, Sequelize) {
/**
* @type {Transaction}
*/
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('fuelcard', 'driverId', {
transaction,
});
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

View File

@ -46,6 +46,22 @@ module.exports = function (sequelize, DataTypes) {
employees.associate = (db) => {
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
db.employees.hasMany(db.fuelcard, {
as: 'fuelcard_driver',
foreignKey: {
name: 'driverId',
},
constraints: false,
});
db.employees.hasMany(db.fuellog, {
as: 'fuellog_driver',
foreignKey: {
name: 'driverId',
},
constraints: false,
});
//end loop
db.employees.belongsTo(db.organizations, {

View File

@ -0,0 +1,85 @@
const config = require('../../config');
const providers = config.providers;
const crypto = require('crypto');
const bcrypt = require('bcrypt');
const moment = require('moment');
module.exports = function (sequelize, DataTypes) {
const fuelcard = sequelize.define(
'fuelcard',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
card_number: {
type: DataTypes.TEXT,
},
limit: {
type: DataTypes.INTEGER,
},
importHash: {
type: DataTypes.STRING(255),
allowNull: true,
unique: true,
},
},
{
timestamps: true,
paranoid: true,
freezeTableName: true,
},
);
fuelcard.associate = (db) => {
/// loop through entities and it's fields, and if ref === current e[name] and create relation has many on parent entity
db.fuelcard.hasMany(db.fuellog, {
as: 'fuellog_fuel_card',
foreignKey: {
name: 'fuel_cardId',
},
constraints: false,
});
//end loop
db.fuelcard.belongsTo(db.organizations, {
as: 'organizations',
foreignKey: {
name: 'organizationsId',
},
constraints: false,
});
db.fuelcard.belongsTo(db.vehicle, {
as: 'vehicle',
foreignKey: {
name: 'vehicleId',
},
constraints: false,
});
db.fuelcard.belongsTo(db.employees, {
as: 'driver',
foreignKey: {
name: 'driverId',
},
constraints: false,
});
db.fuelcard.belongsTo(db.users, {
as: 'createdBy',
});
db.fuelcard.belongsTo(db.users, {
as: 'updatedBy',
});
};
return fuelcard;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
import React from 'react';
import ImageField from '../ImageField';
import ListActionsPopover from '../ListActionsPopover';
import { useAppSelector } from '../../stores/hooks';
import dataFormatter from '../../helpers/dataFormatter';
import { Pagination } from '../Pagination';
import { saveFile } from '../../helpers/fileSaver';
import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link';
import { hasPermission } from '../../helpers/userPermissions';
type Props = {
fuelcard: any[];
loading: boolean;
onDelete: (id: string) => void;
currentPage: number;
numPages: number;
onPageChange: (page: number) => void;
};
const CardFuelcard = ({
fuelcard,
loading,
onDelete,
currentPage,
numPages,
onPageChange,
}: Props) => {
const asideScrollbarsStyle = useAppSelector(
(state) => state.style.asideScrollbarsStyle,
);
const bgColor = useAppSelector((state) => state.style.cardsColor);
const darkMode = useAppSelector((state) => state.style.darkMode);
const corners = useAppSelector((state) => state.style.corners);
const focusRing = useAppSelector((state) => state.style.focusRingColor);
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_FUELCARD');
return (
<div className={'p-4'}>
{loading && <LoadingSpinner />}
<ul
role='list'
className='grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 2xl:grid-cols-4 xl:gap-x-8'
>
{!loading &&
fuelcard.map((item, index) => (
<li
key={item.id}
className={`overflow-hidden ${
corners !== 'rounded-full' ? corners : 'rounded-3xl'
} border ${focusRing} border-gray-200 dark:border-dark-700 ${
darkMode ? 'aside-scrollbars-[slate]' : asideScrollbarsStyle
}`}
>
<div
className={`flex items-center ${bgColor} p-6 gap-x-4 border-b border-gray-900/5 bg-gray-50 dark:bg-dark-800 relative`}
>
<Link
href={`/fuelcard/fuelcard-view/?id=${item.id}`}
className='text-lg font-bold leading-6 line-clamp-1'
>
{item.card_number}
</Link>
<div className='ml-auto '>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/fuelcard/fuelcard-edit/?id=${item.id}`}
pathView={`/fuelcard/fuelcard-view/?id=${item.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>
</div>
<dl className='divide-y divide-stone-300 dark:divide-dark-700 px-6 py-4 text-sm leading-6 h-64 overflow-y-auto'>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Card number
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{item.card_number}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>Limit</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>{item.limit}</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Vehicle
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{dataFormatter.vehicleOneListFormatter(item.vehicle)}
</div>
</dd>
</div>
<div className='flex justify-between gap-x-4 py-3'>
<dt className=' text-gray-500 dark:text-dark-600'>
Driver
</dt>
<dd className='flex items-start gap-x-2'>
<div className='font-medium line-clamp-4'>
{dataFormatter.employeesOneListFormatter(item.driver)}
</div>
</dd>
</div>
</dl>
</li>
))}
{!loading && fuelcard.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p>
</div>
)}
</ul>
<div className={'flex items-center justify-center my-6'}>
<Pagination
currentPage={currentPage}
numPages={numPages}
setCurrentPage={onPageChange}
/>
</div>
</div>
);
};
export default CardFuelcard;

View File

@ -0,0 +1,106 @@
import React from 'react';
import CardBox from '../CardBox';
import ImageField from '../ImageField';
import dataFormatter from '../../helpers/dataFormatter';
import { saveFile } from '../../helpers/fileSaver';
import ListActionsPopover from '../ListActionsPopover';
import { useAppSelector } from '../../stores/hooks';
import { Pagination } from '../Pagination';
import LoadingSpinner from '../LoadingSpinner';
import Link from 'next/link';
import { hasPermission } from '../../helpers/userPermissions';
type Props = {
fuelcard: any[];
loading: boolean;
onDelete: (id: string) => void;
currentPage: number;
numPages: number;
onPageChange: (page: number) => void;
};
const ListFuelcard = ({
fuelcard,
loading,
onDelete,
currentPage,
numPages,
onPageChange,
}: Props) => {
const currentUser = useAppSelector((state) => state.auth.currentUser);
const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_FUELCARD');
const corners = useAppSelector((state) => state.style.corners);
const bgColor = useAppSelector((state) => state.style.cardsColor);
return (
<>
<div className='relative overflow-x-auto p-4 space-y-4'>
{loading && <LoadingSpinner />}
{!loading &&
fuelcard.map((item) => (
<div key={item.id}>
<CardBox hasTable isList className={'rounded shadow-none'}>
<div
className={`flex rounded dark:bg-dark-900 border border-stone-300 items-center overflow-hidden`}
>
<Link
href={`/fuelcard/fuelcard-view/?id=${item.id}`}
className={
'flex-1 px-4 py-6 h-24 flex divide-x-2 divide-stone-300 items-center overflow-hidden`}> dark:divide-dark-700 overflow-x-auto'
}
>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Card number</p>
<p className={'line-clamp-2'}>{item.card_number}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Limit</p>
<p className={'line-clamp-2'}>{item.limit}</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Vehicle</p>
<p className={'line-clamp-2'}>
{dataFormatter.vehicleOneListFormatter(item.vehicle)}
</p>
</div>
<div className={'flex-1 px-3'}>
<p className={'text-xs text-gray-500 '}>Driver</p>
<p className={'line-clamp-2'}>
{dataFormatter.employeesOneListFormatter(item.driver)}
</p>
</div>
</Link>
<ListActionsPopover
onDelete={onDelete}
itemId={item.id}
pathEdit={`/fuelcard/fuelcard-edit/?id=${item.id}`}
pathView={`/fuelcard/fuelcard-view/?id=${item.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>
</CardBox>
</div>
))}
{!loading && fuelcard.length === 0 && (
<div className='col-span-full flex items-center justify-center h-40'>
<p className=''>No data to display</p>
</div>
)}
</div>
<div className={'flex items-center justify-center my-6'}>
<Pagination
currentPage={currentPage}
numPages={numPages}
setCurrentPage={onPageChange}
/>
</div>
</>
);
};
export default ListFuelcard;

View File

@ -0,0 +1,128 @@
import React from 'react';
import BaseIcon from '../BaseIcon';
import { mdiEye, mdiTrashCan, mdiPencilOutline } from '@mdi/js';
import axios from 'axios';
import {
GridActionsCellItem,
GridRowParams,
GridValueGetterParams,
} from '@mui/x-data-grid';
import ImageField from '../ImageField';
import { saveFile } from '../../helpers/fileSaver';
import dataFormatter from '../../helpers/dataFormatter';
import DataGridMultiSelect from '../DataGridMultiSelect';
import ListActionsPopover from '../ListActionsPopover';
import { hasPermission } from '../../helpers/userPermissions';
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;
} catch (error) {
console.log(error);
return [];
}
}
const hasUpdatePermission = hasPermission(user, 'UPDATE_FUELCARD');
return [
{
field: 'card_number',
headerName: 'Card number',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
},
{
field: 'limit',
headerName: 'Limit',
flex: 1,
minWidth: 120,
filterable: false,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
editable: hasUpdatePermission,
type: 'number',
},
{
field: 'vehicle',
headerName: 'Vehicle',
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('vehicle'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
{
field: 'driver',
headerName: 'Driver',
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('employees'),
valueGetter: (params: GridValueGetterParams) =>
params?.value?.id ?? params?.value,
},
{
field: 'actions',
type: 'actions',
minWidth: 30,
headerClassName: 'datagrid--header',
cellClassName: 'datagrid--cell',
getActions: (params: GridRowParams) => {
return [
<div key={params?.row?.id}>
<ListActionsPopover
onDelete={onDelete}
itemId={params?.row?.id}
pathEdit={`/fuelcard/fuelcard-edit/?id=${params?.row?.id}`}
pathView={`/fuelcard/fuelcard-view/?id=${params?.row?.id}`}
hasUpdatePermission={hasUpdatePermission}
/>
</div>,
];
},
},
];
};

View File

@ -17,9 +17,9 @@ export default function WebSiteFooter({ projectName }: WebSiteFooterProps) {
const borders = useAppSelector((state) => state.style.borders);
const websiteHeder = useAppSelector((state) => state.style.websiteHeder);
const style = FooterStyle.WITH_PROJECT_NAME;
const style = FooterStyle.WITH_PAGES;
const design = FooterDesigns.DEFAULT_DESIGN;
const design = FooterDesigns.DESIGN_DIVERSITY;
return (
<div

View File

@ -19,7 +19,7 @@ export default function WebSiteHeader({ projectName }: WebSiteHeaderProps) {
const style = HeaderStyle.PAGES_RIGHT;
const design = HeaderDesigns.DESIGN_DIVERSITY;
const design = HeaderDesigns.DEFAULT_DESIGN;
return (
<header id='websiteHeader' className='overflow-hidden'>
<div

View File

@ -84,6 +84,100 @@ const EmployeesView = () => {
<p>{employees?.organizations?.name ?? 'No data'}</p>
</div>
<>
<p className={'block font-bold mb-2'}>Fuelcard Driver</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Card number</th>
<th>Limit</th>
</tr>
</thead>
<tbody>
{employees.fuelcard_driver &&
Array.isArray(employees.fuelcard_driver) &&
employees.fuelcard_driver.map((item: any) => (
<tr
key={item.id}
onClick={() =>
router.push(
`/fuelcard/fuelcard-view/?id=${item.id}`,
)
}
>
<td data-label='card_number'>{item.card_number}</td>
<td data-label='limit'>{item.limit}</td>
</tr>
))}
</tbody>
</table>
</div>
{!employees?.fuelcard_driver?.length && (
<div className={'text-center py-4'}>No data</div>
)}
</CardBox>
</>
<>
<p className={'block font-bold mb-2'}>Fuellog Driver</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Date</th>
<th>Quantity</th>
<th>Unit cost</th>
<th>Total cost</th>
<th>Odometer</th>
</tr>
</thead>
<tbody>
{employees.fuellog_driver &&
Array.isArray(employees.fuellog_driver) &&
employees.fuellog_driver.map((item: any) => (
<tr
key={item.id}
onClick={() =>
router.push(`/fuellog/fuellog-view/?id=${item.id}`)
}
>
<td data-label='date'>
{dataFormatter.dateTimeFormatter(item.date)}
</td>
<td data-label='quantity'>{item.quantity}</td>
<td data-label='unit_cost'>{item.unit_cost}</td>
<td data-label='total_cost'>{item.total_cost}</td>
<td data-label='odometer'>{item.odometer}</td>
</tr>
))}
</tbody>
</table>
</div>
{!employees?.fuellog_driver?.length && (
<div className={'text-center py-4'}>No data</div>
)}
</CardBox>
</>
<BaseDivider />
<BaseButton

View File

@ -0,0 +1,173 @@
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';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import { Field, Form, Formik } from 'formik';
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/fuelcard/fuelcardSlice';
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';
import { hasPermission } from '../../helpers/userPermissions';
const EditFuelcard = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const initVals = {
organizations: null,
card_number: '',
limit: '',
vehicle: null,
driver: null,
};
const [initialValues, setInitialValues] = useState(initVals);
const { fuelcard } = useAppSelector((state) => state.fuelcard);
const { currentUser } = useAppSelector((state) => state.auth);
const { fuelcardId } = router.query;
useEffect(() => {
dispatch(fetch({ id: fuelcardId }));
}, [fuelcardId]);
useEffect(() => {
if (typeof fuelcard === 'object') {
setInitialValues(fuelcard);
}
}, [fuelcard]);
useEffect(() => {
if (typeof fuelcard === 'object') {
const newInitialVal = { ...initVals };
Object.keys(initVals).forEach((el) => (newInitialVal[el] = fuelcard[el]));
setInitialValues(newInitialVal);
}
}, [fuelcard]);
const handleSubmit = async (data) => {
await dispatch(update({ id: fuelcardId, data }));
await router.push('/fuelcard/fuelcard-list');
};
return (
<>
<Head>
<title>{getPageTitle('Edit fuelcard')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title={'Edit fuelcard'}
main
>
{''}
</SectionTitleLineWithButton>
<CardBox>
<Formik
enableReinitialize
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='organizations' labelFor='organizations'>
<Field
name='organizations'
id='organizations'
component={SelectField}
options={initialValues.organizations}
itemRef={'organizations'}
showField={'name'}
></Field>
</FormField>
<FormField label='Card number'>
<Field name='card_number' placeholder='Card number' />
</FormField>
<FormField label='Limit'>
<Field type='number' name='limit' placeholder='Limit' />
</FormField>
<FormField label='Vehicle' labelFor='vehicle'>
<Field
name='vehicle'
id='vehicle'
component={SelectField}
options={initialValues.vehicle}
itemRef={'vehicle'}
showField={'license_plate'}
></Field>
</FormField>
<FormField label='Driver' labelFor='driver'>
<Field
name='driver'
id='driver'
component={SelectField}
options={initialValues.driver}
itemRef={'employees'}
showField={'first_name'}
></Field>
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />
<BaseButton type='reset' color='info' outline label='Reset' />
<BaseButton
type='reset'
color='danger'
outline
label='Cancel'
onClick={() => router.push('/fuelcard/fuelcard-list')}
/>
</BaseButtons>
</Form>
</Formik>
</CardBox>
</SectionMain>
</>
);
};
EditFuelcard.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'UPDATE_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default EditFuelcard;

View File

@ -0,0 +1,171 @@
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';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import { Field, Form, Formik } from 'formik';
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/fuelcard/fuelcardSlice';
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';
import { hasPermission } from '../../helpers/userPermissions';
const EditFuelcardPage = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const initVals = {
organizations: null,
card_number: '',
limit: '',
vehicle: null,
driver: null,
};
const [initialValues, setInitialValues] = useState(initVals);
const { fuelcard } = useAppSelector((state) => state.fuelcard);
const { currentUser } = useAppSelector((state) => state.auth);
const { id } = router.query;
useEffect(() => {
dispatch(fetch({ id: id }));
}, [id]);
useEffect(() => {
if (typeof fuelcard === 'object') {
setInitialValues(fuelcard);
}
}, [fuelcard]);
useEffect(() => {
if (typeof fuelcard === 'object') {
const newInitialVal = { ...initVals };
Object.keys(initVals).forEach((el) => (newInitialVal[el] = fuelcard[el]));
setInitialValues(newInitialVal);
}
}, [fuelcard]);
const handleSubmit = async (data) => {
await dispatch(update({ id: id, data }));
await router.push('/fuelcard/fuelcard-list');
};
return (
<>
<Head>
<title>{getPageTitle('Edit fuelcard')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title={'Edit fuelcard'}
main
>
{''}
</SectionTitleLineWithButton>
<CardBox>
<Formik
enableReinitialize
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='organizations' labelFor='organizations'>
<Field
name='organizations'
id='organizations'
component={SelectField}
options={initialValues.organizations}
itemRef={'organizations'}
showField={'name'}
></Field>
</FormField>
<FormField label='Card number'>
<Field name='card_number' placeholder='Card number' />
</FormField>
<FormField label='Limit'>
<Field type='number' name='limit' placeholder='Limit' />
</FormField>
<FormField label='Vehicle' labelFor='vehicle'>
<Field
name='vehicle'
id='vehicle'
component={SelectField}
options={initialValues.vehicle}
itemRef={'vehicle'}
showField={'license_plate'}
></Field>
</FormField>
<FormField label='Driver' labelFor='driver'>
<Field
name='driver'
id='driver'
component={SelectField}
options={initialValues.driver}
itemRef={'employees'}
showField={'first_name'}
></Field>
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />
<BaseButton type='reset' color='info' outline label='Reset' />
<BaseButton
type='reset'
color='danger'
outline
label='Cancel'
onClick={() => router.push('/fuelcard/fuelcard-list')}
/>
</BaseButtons>
</Form>
</Formik>
</CardBox>
</SectionMain>
</>
);
};
EditFuelcardPage.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'UPDATE_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default EditFuelcardPage;

View File

@ -0,0 +1,169 @@
import { mdiChartTimelineVariant } from '@mdi/js';
import Head from 'next/head';
import { uniqueId } from 'lodash';
import React, { ReactElement, useState } from 'react';
import CardBox from '../../components/CardBox';
import LayoutAuthenticated from '../../layouts/Authenticated';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import TableFuelcard from '../../components/Fuelcard/TableFuelcard';
import BaseButton from '../../components/BaseButton';
import axios from 'axios';
import Link from 'next/link';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
import CardBoxModal from '../../components/CardBoxModal';
import DragDropFilePicker from '../../components/DragDropFilePicker';
import { setRefetch, uploadCsv } from '../../stores/fuelcard/fuelcardSlice';
import { hasPermission } from '../../helpers/userPermissions';
const FuelcardTablesPage = () => {
const [filterItems, setFilterItems] = useState([]);
const [csvFile, setCsvFile] = useState<File | null>(null);
const [isModalActive, setIsModalActive] = useState(false);
const [showTableView, setShowTableView] = useState(false);
const { currentUser } = useAppSelector((state) => state.auth);
const dispatch = useAppDispatch();
const [filters] = useState([
{ label: 'Card number', title: 'card_number' },
{ label: 'Limit', title: 'limit', number: 'true' },
{ label: 'Vehicle', title: 'vehicle' },
{ label: 'Driver', title: 'driver' },
]);
const hasCreatePermission =
currentUser && hasPermission(currentUser, 'CREATE_FUELCARD');
const addFilter = () => {
const newItem = {
id: uniqueId(),
fields: {
filterValue: '',
filterValueFrom: '',
filterValueTo: '',
selectedField: '',
},
};
newItem.fields.selectedField = filters[0].title;
setFilterItems([...filterItems, newItem]);
};
const getFuelcardCSV = async () => {
const response = await axios({
url: '/fuelcard?filetype=csv',
method: 'GET',
responseType: 'blob',
});
const type = response.headers['content-type'];
const blob = new Blob([response.data], { type: type });
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'fuelcardCSV.csv';
link.click();
};
const onModalConfirm = async () => {
if (!csvFile) return;
await dispatch(uploadCsv(csvFile));
dispatch(setRefetch(true));
setCsvFile(null);
setIsModalActive(false);
};
const onModalCancel = () => {
setCsvFile(null);
setIsModalActive(false);
};
return (
<>
<Head>
<title>{getPageTitle('Fuelcard')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title='Fuelcard'
main
>
{''}
</SectionTitleLineWithButton>
<CardBox className='mb-6' cardBoxClassName='flex flex-wrap'>
{hasCreatePermission && (
<BaseButton
className={'mr-3'}
href={'/fuelcard/fuelcard-new'}
color='info'
label='New Item'
/>
)}
<BaseButton
className={'mr-3'}
color='info'
label='Filter'
onClick={addFilter}
/>
<BaseButton
className={'mr-3'}
color='info'
label='Download CSV'
onClick={getFuelcardCSV}
/>
{hasCreatePermission && (
<BaseButton
color='info'
label='Upload CSV'
onClick={() => setIsModalActive(true)}
/>
)}
<div className='md:inline-flex items-center ms-auto'>
<div id='delete-rows-button'></div>
</div>
</CardBox>
<CardBox className='mb-6' hasTable>
<TableFuelcard
filterItems={filterItems}
setFilterItems={setFilterItems}
filters={filters}
showGrid={false}
/>
</CardBox>
</SectionMain>
<CardBoxModal
title='Upload CSV'
buttonColor='info'
buttonLabel={'Confirm'}
// buttonLabel={false ? 'Deleting...' : 'Confirm'}
isActive={isModalActive}
onConfirm={onModalConfirm}
onCancel={onModalCancel}
>
<DragDropFilePicker
file={csvFile}
setFile={setCsvFile}
formats={'.csv'}
/>
</CardBoxModal>
</>
);
};
FuelcardTablesPage.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'READ_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default FuelcardTablesPage;

View File

@ -0,0 +1,140 @@
import {
mdiAccount,
mdiChartTimelineVariant,
mdiMail,
mdiUpload,
} from '@mdi/js';
import Head from 'next/head';
import React, { ReactElement } from 'react';
import CardBox from '../../components/CardBox';
import LayoutAuthenticated from '../../layouts/Authenticated';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import { Field, Form, Formik } from 'formik';
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 { SwitchField } from '../../components/SwitchField';
import { SelectField } from '../../components/SelectField';
import { SelectFieldMany } from '../../components/SelectFieldMany';
import { RichTextField } from '../../components/RichTextField';
import { create } from '../../stores/fuelcard/fuelcardSlice';
import { useAppDispatch } from '../../stores/hooks';
import { useRouter } from 'next/router';
import moment from 'moment';
const initialValues = {
organizations: '',
card_number: '',
limit: '',
vehicle: '',
driver: '',
};
const FuelcardNew = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const handleSubmit = async (data) => {
await dispatch(create(data));
await router.push('/fuelcard/fuelcard-list');
};
return (
<>
<Head>
<title>{getPageTitle('New Item')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title='New Item'
main
>
{''}
</SectionTitleLineWithButton>
<CardBox>
<Formik
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
<Form>
<FormField label='organizations' labelFor='organizations'>
<Field
name='organizations'
id='organizations'
component={SelectField}
options={[]}
itemRef={'organizations'}
></Field>
</FormField>
<FormField label='Card number'>
<Field name='card_number' placeholder='Card number' />
</FormField>
<FormField label='Limit'>
<Field type='number' name='limit' placeholder='Limit' />
</FormField>
<FormField label='Vehicle' labelFor='vehicle'>
<Field
name='vehicle'
id='vehicle'
component={SelectField}
options={[]}
itemRef={'vehicle'}
></Field>
</FormField>
<FormField label='Driver' labelFor='driver'>
<Field
name='driver'
id='driver'
component={SelectField}
options={[]}
itemRef={'employees'}
></Field>
</FormField>
<BaseDivider />
<BaseButtons>
<BaseButton type='submit' color='info' label='Submit' />
<BaseButton type='reset' color='info' outline label='Reset' />
<BaseButton
type='reset'
color='danger'
outline
label='Cancel'
onClick={() => router.push('/fuelcard/fuelcard-list')}
/>
</BaseButtons>
</Form>
</Formik>
</CardBox>
</SectionMain>
</>
);
};
FuelcardNew.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'CREATE_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default FuelcardNew;

View File

@ -0,0 +1,168 @@
import { mdiChartTimelineVariant } from '@mdi/js';
import Head from 'next/head';
import { uniqueId } from 'lodash';
import React, { ReactElement, useState } from 'react';
import CardBox from '../../components/CardBox';
import LayoutAuthenticated from '../../layouts/Authenticated';
import SectionMain from '../../components/SectionMain';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import { getPageTitle } from '../../config';
import TableFuelcard from '../../components/Fuelcard/TableFuelcard';
import BaseButton from '../../components/BaseButton';
import axios from 'axios';
import Link from 'next/link';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';
import CardBoxModal from '../../components/CardBoxModal';
import DragDropFilePicker from '../../components/DragDropFilePicker';
import { setRefetch, uploadCsv } from '../../stores/fuelcard/fuelcardSlice';
import { hasPermission } from '../../helpers/userPermissions';
const FuelcardTablesPage = () => {
const [filterItems, setFilterItems] = useState([]);
const [csvFile, setCsvFile] = useState<File | null>(null);
const [isModalActive, setIsModalActive] = useState(false);
const [showTableView, setShowTableView] = useState(false);
const { currentUser } = useAppSelector((state) => state.auth);
const dispatch = useAppDispatch();
const [filters] = useState([
{ label: 'Card number', title: 'card_number' },
{ label: 'Limit', title: 'limit', number: 'true' },
{ label: 'Vehicle', title: 'vehicle' },
{ label: 'Driver', title: 'driver' },
]);
const hasCreatePermission =
currentUser && hasPermission(currentUser, 'CREATE_FUELCARD');
const addFilter = () => {
const newItem = {
id: uniqueId(),
fields: {
filterValue: '',
filterValueFrom: '',
filterValueTo: '',
selectedField: '',
},
};
newItem.fields.selectedField = filters[0].title;
setFilterItems([...filterItems, newItem]);
};
const getFuelcardCSV = async () => {
const response = await axios({
url: '/fuelcard?filetype=csv',
method: 'GET',
responseType: 'blob',
});
const type = response.headers['content-type'];
const blob = new Blob([response.data], { type: type });
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'fuelcardCSV.csv';
link.click();
};
const onModalConfirm = async () => {
if (!csvFile) return;
await dispatch(uploadCsv(csvFile));
dispatch(setRefetch(true));
setCsvFile(null);
setIsModalActive(false);
};
const onModalCancel = () => {
setCsvFile(null);
setIsModalActive(false);
};
return (
<>
<Head>
<title>{getPageTitle('Fuelcard')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title='Fuelcard'
main
>
{''}
</SectionTitleLineWithButton>
<CardBox className='mb-6' cardBoxClassName='flex flex-wrap'>
{hasCreatePermission && (
<BaseButton
className={'mr-3'}
href={'/fuelcard/fuelcard-new'}
color='info'
label='New Item'
/>
)}
<BaseButton
className={'mr-3'}
color='info'
label='Filter'
onClick={addFilter}
/>
<BaseButton
className={'mr-3'}
color='info'
label='Download CSV'
onClick={getFuelcardCSV}
/>
{hasCreatePermission && (
<BaseButton
color='info'
label='Upload CSV'
onClick={() => setIsModalActive(true)}
/>
)}
<div className='md:inline-flex items-center ms-auto'>
<div id='delete-rows-button'></div>
</div>
</CardBox>
<CardBox className='mb-6' hasTable>
<TableFuelcard
filterItems={filterItems}
setFilterItems={setFilterItems}
filters={filters}
showGrid={true}
/>
</CardBox>
</SectionMain>
<CardBoxModal
title='Upload CSV'
buttonColor='info'
buttonLabel={'Confirm'}
// buttonLabel={false ? 'Deleting...' : 'Confirm'}
isActive={isModalActive}
onConfirm={onModalConfirm}
onCancel={onModalCancel}
>
<DragDropFilePicker
file={csvFile}
setFile={setCsvFile}
formats={'.csv'}
/>
</CardBoxModal>
</>
);
};
FuelcardTablesPage.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'READ_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default FuelcardTablesPage;

View File

@ -0,0 +1,163 @@
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/fuelcard/fuelcardSlice';
import { saveFile } from '../../helpers/fileSaver';
import dataFormatter from '../../helpers/dataFormatter';
import ImageField from '../../components/ImageField';
import LayoutAuthenticated from '../../layouts/Authenticated';
import { getPageTitle } from '../../config';
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton';
import SectionMain from '../../components/SectionMain';
import CardBox from '../../components/CardBox';
import BaseButton from '../../components/BaseButton';
import BaseDivider from '../../components/BaseDivider';
import { mdiChartTimelineVariant } from '@mdi/js';
import { SwitchField } from '../../components/SwitchField';
import FormField from '../../components/FormField';
import { hasPermission } from '../../helpers/userPermissions';
const FuelcardView = () => {
const router = useRouter();
const dispatch = useAppDispatch();
const { fuelcard } = useAppSelector((state) => state.fuelcard);
const { currentUser } = useAppSelector((state) => state.auth);
const { id } = router.query;
function removeLastCharacter(str) {
console.log(str, `str`);
return str.slice(0, -1);
}
useEffect(() => {
dispatch(fetch({ id }));
}, [dispatch, id]);
return (
<>
<Head>
<title>{getPageTitle('View fuelcard')}</title>
</Head>
<SectionMain>
<SectionTitleLineWithButton
icon={mdiChartTimelineVariant}
title={removeLastCharacter('View fuelcard')}
main
>
<BaseButton
color='info'
label='Edit'
href={`/fuelcard/fuelcard-edit/?id=${id}`}
/>
</SectionTitleLineWithButton>
<CardBox>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>organizations</p>
<p>{fuelcard?.organizations?.name ?? 'No data'}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Card number</p>
<p>{fuelcard?.card_number}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Limit</p>
<p>{fuelcard?.limit || 'No data'}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Vehicle</p>
<p>{fuelcard?.vehicle?.license_plate ?? 'No data'}</p>
</div>
<div className={'mb-4'}>
<p className={'block font-bold mb-2'}>Driver</p>
<p>{fuelcard?.driver?.first_name ?? 'No data'}</p>
</div>
<>
<p className={'block font-bold mb-2'}>Fuellog Fuel_card</p>
<CardBox
className='mb-6 border border-gray-300 rounded overflow-hidden'
hasTable
>
<div className='overflow-x-auto'>
<table>
<thead>
<tr>
<th>Date</th>
<th>Quantity</th>
<th>Unit cost</th>
<th>Total cost</th>
<th>Odometer</th>
</tr>
</thead>
<tbody>
{fuelcard.fuellog_fuel_card &&
Array.isArray(fuelcard.fuellog_fuel_card) &&
fuelcard.fuellog_fuel_card.map((item: any) => (
<tr
key={item.id}
onClick={() =>
router.push(`/fuellog/fuellog-view/?id=${item.id}`)
}
>
<td data-label='date'>
{dataFormatter.dateTimeFormatter(item.date)}
</td>
<td data-label='quantity'>{item.quantity}</td>
<td data-label='unit_cost'>{item.unit_cost}</td>
<td data-label='total_cost'>{item.total_cost}</td>
<td data-label='odometer'>{item.odometer}</td>
</tr>
))}
</tbody>
</table>
</div>
{!fuelcard?.fuellog_fuel_card?.length && (
<div className={'text-center py-4'}>No data</div>
)}
</CardBox>
</>
<BaseDivider />
<BaseButton
color='info'
label='Back'
onClick={() => router.push('/fuelcard/fuelcard-list')}
/>
</CardBox>
</SectionMain>
</>
);
};
FuelcardView.getLayout = function getLayout(page: ReactElement) {
return (
<LayoutAuthenticated permission={'READ_FUELCARD'}>
{page}
</LayoutAuthenticated>
);
};
export default FuelcardView;

View File

@ -111,7 +111,7 @@ export default function WebSite() {
<FeaturesSection
projectName={'Transport Managment'}
image={['Streamlined operations dashboard']}
withBg={0}
withBg={1}
features={features_points}
mainText={`Explore ${projectName} Key Features`}
subTitle={`Discover how ${projectName} can transform your manufacturing operations with its powerful features.`}

View File

@ -74,7 +74,7 @@ export default function WebSite() {
<FeaturesSection
projectName={'Transport Managment'}
image={['Efficient workflow illustration']}
withBg={0}
withBg={1}
features={features_points}
mainText={`Unleash the Power of ${projectName}`}
subTitle={`Explore the key features of ${projectName} that drive efficiency and innovation in your manufacturing operations.`}