More update for Vehicle

This commit is contained in:
Yang Li 2025-06-08 18:34:14 -04:00
parent e70fb22ac3
commit f18f213f6b
34 changed files with 1504 additions and 700 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@ -167,7 +167,14 @@ exports.updateRouteInProgress = (req, res) => {
finalCustomerList.push(cust); finalCustomerList.push(cust);
} }
} }
const finalBody = Object.assign({}, routeBody, {route_customer_list: finalCustomerList}) let finalBody = {};
if (finalCustomerList?.length > 0) {
finalBody = Object.assign({}, routeBody, {route_customer_list: finalCustomerList})
} else {
const newBody = Object.assign({}, routeBody);
delete newBody.route_customer_list;
finalBody = Object.assign({}, newBody)
}
RoutePath.findByIdAndUpdate(currentRoute.id, finalBody, { useFindAndModify: false }) RoutePath.findByIdAndUpdate(currentRoute.id, finalBody, { useFindAndModify: false })
.then(data => { .then(data => {
// console.log('success', data.id); // console.log('success', data.id);

View File

@ -1,10 +1,19 @@
const upload = require("../middlewares/upload"); const upload = require("../middlewares/upload");
// const uploadPhysical = require("../middlewares/upload_physical");
const dbConfig = require("../config/db.config"); const dbConfig = require("../config/db.config");
const MongoClient = require("mongodb").MongoClient; const MongoClient = require("mongodb").MongoClient;
const GridFSBucket = require("mongodb").GridFSBucket; const GridFSBucket = require("mongodb").GridFSBucket;
const util = require("util");
var fs = require('fs');
var multer = require('multer');
const path = require("path");
const url = dbConfig.url; const url = dbConfig.url;
const baseUrl = dbConfig.fileUrl; const baseUrl = dbConfig.fileUrl;
const mongoClient = new MongoClient(url); const mongoClient = new MongoClient(url);
const readdir = util.promisify(fs.readdir);
const stat = util.promisify(fs.stat);
const uploadFiles = async (req, res) => { const uploadFiles = async (req, res) => {
try { try {
await upload(req, res); await upload(req, res);
@ -63,8 +72,91 @@ const deleteFile = async (req, res) => {
} }
}; };
const uploadPhysicalFile = async (req, res) => {
try {
// Destination and file name would be adjusted to be the same with original file system
const {objectId, name, fileType, model} = req.query;
const uploadedFile = req.file;
if (!uploadedFile) {
return res.status(400).send({
message: 'No file uploaded.'
})
}
return res.status(200).send({
message: `File ${fileType} Uploaded Successfully for ${model} ${objectId}-${name}`
})
} catch (error) {
return res.status(500).send({
message: error.message,
});
}
}
const getFilesByType = async (req, res) => {
try {
const {objectId, fileType, name, model} = req.params;
if (!objectId || !name || !fileType || !model) {
return res.status(400).send({message: 'Required fields missed'});
}
const BASE_UPLOAD_DIR = `/www/wwwroot/upload/`;
const filesDir = `${BASE_UPLOAD_DIR}${model}/${objectId}/${fileType}/`;
if (!fs.existsSync(filesDir)) {
return res.status(200).send({
data: {
objectId,
model,
fileType,
name,
files: [],
count: 0
}
})
}
const fileEntries = await readdir(filesDir, { withFileTypes: true});
const filePromises = fileEntries.filter(entry => entry.isFile()).map(async (entry) => {
const filePath = path.join(filesDir, entry.name);
const stats = await stat(filePath);
const fileUrl = `/files/${model}/${objectId}/${fileType}/${entry.name}`;
return {
name: entry.name,
url: fileUrl,
size: stats.size,
extension: path.extname(entry.name),
createdAt: stats.birthtime,
modifiedAt: stats.mtime
}
});
const files = await Promise.all(filePromises);
return res.status(200).json({
data: {
objectId,
fileType,
name,
files,
count: files.length
}
})
} catch(err) {
return res.status(500).send({
message: err.message
})
}
}
module.exports = { module.exports = {
uploadFiles, uploadFiles,
getFile, getFile,
deleteFile deleteFile,
uploadPhysicalFile,
getFilesByType
}; };

View File

@ -21,7 +21,15 @@ exports.createVehicle = (req, res) => {
year: req.body.year || '', year: req.body.year || '',
checklist: req.body.checklist || '', checklist: req.body.checklist || '',
status: 'active', status: 'active',
site site,
has_lift_equip: req.body.has_lift_equip,
vin: req.body.vin || '',
note: req.body.note || '',
insurance_expire_on: req.body.insurance_expire_on,
title_registration_on: req.body.title_registration_on,
emission_test_on: req.body.emission_test_on,
oil_change_mileage: req.body.oil_change_mileage,
oil_change_date: req.body.oil_change_date
}); });
// Save Vehicle in the database // Save Vehicle in the database

View File

@ -1,6 +1,8 @@
const authJwt = require("./authJwt"); const authJwt = require("./authJwt");
const splitSite = require("./splitSite"); const splitSite = require("./splitSite");
const uploadPhysicalFile = require("./upload_physical");
module.exports = { module.exports = {
authJwt, authJwt,
splitSite splitSite,
uploadPhysicalFile
}; };

View File

@ -0,0 +1,93 @@
var fs = require('fs');
var multer = require('multer');
const path = require("path");
const BASE_UPLOAD_DIR = `/www/wwwroot/upload/`;
// try {
// if (!fs.existsSync(BASE_UPLOAD_DIR)) {
// fs.mkdirSync(BASE_UPLOAD_DIR, {recursive: true});
// console.log (`Created upload directory: ${BASE_UPLOAD_DIR}`);
// }
// } catch(err) {
// console.error(`Error creating directory ${BASE_UPLOAD_DIR}`)
// }
const storage = multer.diskStorage({
destination: (req, file, cb) => {
const { objectId, fileType, name, model } = req.query;
if (!objectId || !name || !fileType || !model) {
return res.status(400).send({message: 'Required fields missed'});
}
const uploadDir = `${BASE_UPLOAD_DIR}${model}/${objectId}/${fileType}/`;
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, {recursive: true});
console.log (`Created upload directory: ${uploadDir}/`);
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const { objectId, fileType, name, date} = req.query;
const uniqueSuffix = date || Date.now();
const payload = req.body;
console.log('is the payload', payload);
cb(null, `${name.replaceAll(' ', '_')}_${objectId}_${fileType}_${uniqueSuffix}${path.extname(file.originalname)}`)
}
})
const uploadPhysicalFile = multer({
storage: storage,
limits: {fileSize: 10 * 1024* 1024},
fileFilter: (req, file, cb) => cb(null, true)
}).single('file');
// const memoryStorage = multer.memoryStorage();
// const upload = multer({
// storage: memoryStorage,
// limits: { fileSize: 10 * 1024* 1024}
// });
// const uploadPhysicalFile = (req, res, next) => {
// upload.single('file')(req, res, async (err) => {
// if (err) {
// return res.status(400).send({message: `Uplodad Error: ${err}`});
// }
// if (!req.file) {
// return res.status(400).send({message: 'No File uploaded'});
// }
// try {
// const {objectId, name, fileType} = req.body;
// if (!objectId || !name || fileType) {
// return res.status(400).send({message: 'Required fields missed'});
// }
// const uploadDir = `${BASE_UPLOAD_DIR}${objectId}/${fileType}/`;
// if (!fs.existsSync(uploadDir)) {
// fs.mkdirSync(uploadDir, {recursive: true});
// console.log (`Created upload directory: ${uploadDir}`);
// }
// const uniqueSuffix = Date.now();
// const fileExension = path.extname(req.file.originalname);
// const fileName = `${name.replaceAll(' ', '_')}_${objectId}_${fileType}_${uniqueSuffix}${fileExension}`;
// const filePath = path.join(uploadDir, fileName);
// fs.writeFileSync(filePath, req.file.buffer);
// req.fileInfo = {
// filename: fileName,
// path: filePath,
// originalname: req.file.originalname,
// size: req.file.size
// };
// next();
// } catch (err) {
// return res.status(500).send({message: `Upload Parsing error: ${err}`});
// }
// })
// }
module.exports = uploadPhysicalFile;

View File

@ -1,4 +1,3 @@
const uniqueValidator = require('mongoose-unique-validator');
module.exports = mongoose => { module.exports = mongoose => {
var schema = mongoose.Schema( var schema = mongoose.Schema(
{ {

View File

@ -25,7 +25,15 @@ module.exports = mongoose => {
type: String type: String
}], }],
status: String, status: String,
site: Number site: Number,
vin: String,
has_lift_equip: Boolean,
insurance_expire_on: String,
title_registration_on: String,
emission_test_on: String,
oil_change_mileage: Number,
oil_change_date: String,
note: String
}, },
{ collection: 'vehicle', timestamps: true } { collection: 'vehicle', timestamps: true }
); );

View File

@ -1,8 +1,12 @@
const {uploadPhysicalFile} = require("../middlewares");
module.exports = app => { module.exports = app => {
const upload = require("../controllers/upload.controller.js"); const upload = require("../controllers/upload.controller.js");
var router = require("express").Router(); var router = require("express").Router();
router.get("/:name", upload.getFile); router.get("/:name", upload.getFile);
router.post("/upload/:filename", upload.uploadFiles); router.post("/upload/:filename", upload.uploadFiles);
router.post("/upload-physical", [uploadPhysicalFile], upload.uploadPhysicalFile);
router.post("/delete", upload.deleteFile); router.post("/delete", upload.deleteFile);
router.get("/uploadedDocs/:model/:objectId/type/:fileType/name/:name", upload.getFilesByType);
app.use('/api/files', router); app.use('/api/files', router);
}; };

View File

@ -1,16 +1,16 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.2fa2d232.css", "main.css": "/static/css/main.e5412702.css",
"main.js": "/static/js/main.da419ea1.js", "main.js": "/static/js/main.26b7d753.js",
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js", "static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png", "static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
"index.html": "/index.html", "index.html": "/index.html",
"main.2fa2d232.css.map": "/static/css/main.2fa2d232.css.map", "main.e5412702.css.map": "/static/css/main.e5412702.css.map",
"main.da419ea1.js.map": "/static/js/main.da419ea1.js.map", "main.26b7d753.js.map": "/static/js/main.26b7d753.js.map",
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map" "787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.2fa2d232.css", "static/css/main.e5412702.css",
"static/js/main.da419ea1.js" "static/js/main.26b7d753.js"
] ]
} }

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.da419ea1.js"></script><link href="/static/css/main.2fa2d232.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.26b7d753.js"></script><link href="/static/css/main.e5412702.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,126 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* Signature Pad v2.3.2
* https://github.com/szimek/signature_pad
*
* Copyright 2017 Szymon Nowak
* Released under the MIT license
*
* The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
* http://corner.squareup.com/2012/07/smoother-signatures.html
*
* Implementation of interpolation using cubic Bézier curves is taken from:
* http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
*
* Algorithm for approximated length of a Bézier curve is taken from:
* http://www.lemoda.net/maths/bezier-length/index.html
*
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim/with-selector.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* React Router v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//! moment.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,126 @@
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/*!
* Signature Pad v2.3.2
* https://github.com/szimek/signature_pad
*
* Copyright 2017 Szymon Nowak
* Released under the MIT license
*
* The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
* http://corner.squareup.com/2012/07/smoother-signatures.html
*
* Implementation of interpolation using cubic Bézier curves is taken from:
* http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
*
* Algorithm for approximated length of a Bézier curve is taken from:
* http://www.lemoda.net/maths/bezier-length/index.html
*
*/
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim/with-selector.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* React Router v6.3.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//! moment.js

File diff suppressed because one or more lines are too long

737
client/package-lock.json generated
View File

@ -8,11 +8,14 @@
"name": "client", "name": "client",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@aldabil/react-scheduler": "^2.9.2",
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"@emotion/react": "^11.11.4", "@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5", "@emotion/styled": "^11.11.5",
"@reduxjs/toolkit": "^1.8.1", "@reduxjs/toolkit": "^1.8.1",
"@schedule-x/event-modal": "^2.30.0",
"@schedule-x/events-service": "^2.30.0",
"@schedule-x/react": "^2.30.0",
"@schedule-x/theme-default": "^2.30.0",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0", "@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -53,18 +56,6 @@
"tape": "^5.5.3" "tape": "^5.5.3"
} }
}, },
"node_modules/@aldabil/react-scheduler": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@aldabil/react-scheduler/-/react-scheduler-2.9.2.tgz",
"integrity": "sha512-WederN8Pmhg7nQdT7685UITFUPk/FDjMC0bSuyeqgHZSn2nhVMqiLVFC4q0kHh+v75lQCPqkFKr8FBnEoZNRig==",
"peerDependencies": {
"@mui/icons-material": ">=5.0.0",
"@mui/material": ">=5.0.0",
"@mui/x-date-pickers": ">=6.19.0",
"date-fns": ">=3.2.0",
"react": ">=17.0.0"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@ -2352,44 +2343,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
"integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
"peer": true,
"dependencies": {
"@floating-ui/utils": "^0.2.0"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
"integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
"peer": true,
"dependencies": {
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.0"
}
},
"node_modules/@floating-ui/react-dom": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz",
"integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==",
"peer": true,
"dependencies": {
"@floating-ui/dom": "^1.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==",
"peer": true
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.9.5", "version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
@ -3118,338 +3071,6 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
"integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==" "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg=="
}, },
"node_modules/@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
"integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@floating-ui/react-dom": "^2.0.8",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"@popperjs/core": "^2.11.8",
"clsx": "^2.1.0",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/core-downloads-tracker": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz",
"integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
}
},
"node_modules/@mui/icons-material": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz",
"integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@mui/material": "^5.0.0",
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/material": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz",
"integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/base": "5.0.0-beta.40",
"@mui/core-downloads-tracker": "^5.15.16",
"@mui/system": "^5.15.15",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"@types/react-transition-group": "^4.4.10",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1",
"react-is": "^18.2.0",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/material/node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"peer": true
},
"node_modules/@mui/private-theming": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
"integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/utils": "^5.15.14",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/styled-engine": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
"integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@emotion/cache": "^11.11.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
}
}
},
"node_modules/@mui/system": {
"version": "5.15.15",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
"integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@mui/private-theming": "^5.15.14",
"@mui/styled-engine": "^5.15.14",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/types": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
"integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
"peer": true,
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/utils": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
"integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.23.9",
"@types/prop-types": "^15.7.11",
"prop-types": "^15.8.1",
"react-is": "^18.2.0"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@mui/utils/node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"peer": true
},
"node_modules/@mui/x-date-pickers": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.3.2.tgz",
"integrity": "sha512-i7JaDs1eXSZWyJihfszUHVV0t/C2HvtdMv5tHwv3E3enMx5Hup1vkJ64vZAH2fgGrTHQH8mjxvVsmI6jhDXIUg==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.24.0",
"@mui/base": "^5.0.0-beta.40",
"@mui/system": "^5.15.14",
"@mui/utils": "^5.15.14",
"@types/react-transition-group": "^4.4.10",
"clsx": "^2.1.0",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.15.14",
"date-fns": "^2.25.0 || ^3.2.0",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
"moment": "^2.29.4",
"moment-hijri": "^2.1.2",
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"date-fns": {
"optional": true
},
"date-fns-jalali": {
"optional": true
},
"dayjs": {
"optional": true
},
"luxon": {
"optional": true
},
"moment": {
"optional": true
},
"moment-hijri": {
"optional": true
},
"moment-jalaali": {
"optional": true
}
}
},
"node_modules/@nicolo-ribaudo/chokidar-2": { "node_modules/@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents.3", "version": "2.1.8-no-fsevents.3",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
@ -3547,6 +3168,32 @@
"url": "https://opencollective.com/popperjs" "url": "https://opencollective.com/popperjs"
} }
}, },
"node_modules/@preact/signals": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@preact/signals/-/signals-2.0.4.tgz",
"integrity": "sha512-9241aGnIv7y0IGzaq2vkBMe8/0jGnnmEEUeFmAoTWsaj8q/BW2PVekL8nHVJcy69bBww6rwEy3A1tc6yPE0sJA==",
"peer": true,
"dependencies": {
"@preact/signals-core": "^1.7.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
},
"peerDependencies": {
"preact": ">= 10.25.0"
}
},
"node_modules/@preact/signals-core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.8.0.tgz",
"integrity": "sha512-OBvUsRZqNmjzCZXWLxkZfhcgT+Fk8DDcT/8vD6a1xhDemodyy87UJRJfASMuSD8FaAIeGgGm85ydXhm7lr4fyA==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/@react-dnd/asap": { "node_modules/@react-dnd/asap": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
@ -3753,6 +3400,45 @@
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz",
"integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==" "integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw=="
}, },
"node_modules/@schedule-x/calendar": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/calendar/-/calendar-2.30.0.tgz",
"integrity": "sha512-+yAVnY+4T8+iTrVp03V3usVYKhLuBzWEEtYOKsTnbFapD3LaL02Rv+0Gmv+4Vpk1H5yd3znrSxQvDLNBVNmUhg==",
"peer": true,
"peerDependencies": {
"@preact/signals": "^2.0.2",
"preact": "^10.19.2"
}
},
"node_modules/@schedule-x/event-modal": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/event-modal/-/event-modal-2.30.0.tgz",
"integrity": "sha512-RS/p5/rg/wCooKR5y05orJBJjxf7Ls7/VcGsz5MemJCQCXhu5pbU0g+OVVSlh6tJ+OoKF5frm4lIhXu82gd6/Q==",
"peerDependencies": {
"@preact/signals": "^2.0.2",
"preact": "^10.19.2"
}
},
"node_modules/@schedule-x/events-service": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/events-service/-/events-service-2.30.0.tgz",
"integrity": "sha512-WztKcKSj9uqvjvVr5bVe5tAzR4FKDBPQ/FFnuDWE2Y76yrxs7Hy4RROG/UCmteGrzJeXv3BV+TU2l63scJUADA=="
},
"node_modules/@schedule-x/react": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/react/-/react-2.30.0.tgz",
"integrity": "sha512-9eVcoE7xYDYHnS+2JwgtRoEZtfzoJdfNHb+VMgvgJ0Ur+ItGifN5DyrFlyQ3l18E848ny0+wp1J8qRvlV5ikMw==",
"peerDependencies": {
"@schedule-x/calendar": "^2.25.0",
"react": "^16.7.0 || ^17 || ^18 || ^19",
"react-dom": "^16.7.0 || ^17 || ^18 || ^19"
}
},
"node_modules/@schedule-x/theme-default": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/theme-default/-/theme-default-2.30.0.tgz",
"integrity": "sha512-vRUDV3fg7SoETiWQktv3DwYKjFZQMHwuYw+VKW5T3dzEJ8YfatuJbg0sj0JCiu9XQYcjs0hdmzrQvIDFy93r/w=="
},
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.23.5", "version": "0.23.5",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz",
@ -7026,15 +6712,6 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/co": { "node_modules/co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -7945,23 +7622,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/dayjs": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==",
"optional": true,
"peer": true
},
"node_modules/debounce": { "node_modules/debounce": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@ -16991,6 +16651,16 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"node_modules/preact": {
"version": "10.26.6",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz",
"integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -22218,12 +21888,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@aldabil/react-scheduler": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@aldabil/react-scheduler/-/react-scheduler-2.9.2.tgz",
"integrity": "sha512-WederN8Pmhg7nQdT7685UITFUPk/FDjMC0bSuyeqgHZSn2nhVMqiLVFC4q0kHh+v75lQCPqkFKr8FBnEoZNRig==",
"requires": {}
},
"@ampproject/remapping": { "@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@ -23777,40 +23441,6 @@
} }
} }
}, },
"@floating-ui/core": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz",
"integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==",
"peer": true,
"requires": {
"@floating-ui/utils": "^0.2.0"
}
},
"@floating-ui/dom": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz",
"integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==",
"peer": true,
"requires": {
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.0"
}
},
"@floating-ui/react-dom": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz",
"integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==",
"peer": true,
"requires": {
"@floating-ui/dom": "^1.0.0"
}
},
"@floating-ui/utils": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==",
"peer": true
},
"@humanwhocodes/config-array": { "@humanwhocodes/config-array": {
"version": "0.9.5", "version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
@ -24351,146 +23981,6 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
"integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg==" "integrity": "sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg=="
}, },
"@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
"integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@floating-ui/react-dom": "^2.0.8",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"@popperjs/core": "^2.11.8",
"clsx": "^2.1.0",
"prop-types": "^15.8.1"
}
},
"@mui/core-downloads-tracker": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz",
"integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg==",
"peer": true
},
"@mui/icons-material": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz",
"integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9"
}
},
"@mui/material": {
"version": "5.15.16",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz",
"integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@mui/base": "5.0.0-beta.40",
"@mui/core-downloads-tracker": "^5.15.16",
"@mui/system": "^5.15.15",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"@types/react-transition-group": "^4.4.10",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1",
"react-is": "^18.2.0",
"react-transition-group": "^4.4.5"
},
"dependencies": {
"react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"peer": true
}
}
},
"@mui/private-theming": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
"integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@mui/utils": "^5.15.14",
"prop-types": "^15.8.1"
}
},
"@mui/styled-engine": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
"integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@emotion/cache": "^11.11.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
}
},
"@mui/system": {
"version": "5.15.15",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
"integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@mui/private-theming": "^5.15.14",
"@mui/styled-engine": "^5.15.14",
"@mui/types": "^7.2.14",
"@mui/utils": "^5.15.14",
"clsx": "^2.1.0",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
}
},
"@mui/types": {
"version": "7.2.14",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
"integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
"peer": true,
"requires": {}
},
"@mui/utils": {
"version": "5.15.14",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
"integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
"peer": true,
"requires": {
"@babel/runtime": "^7.23.9",
"@types/prop-types": "^15.7.11",
"prop-types": "^15.8.1",
"react-is": "^18.2.0"
},
"dependencies": {
"react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"peer": true
}
}
},
"@mui/x-date-pickers": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.3.2.tgz",
"integrity": "sha512-i7JaDs1eXSZWyJihfszUHVV0t/C2HvtdMv5tHwv3E3enMx5Hup1vkJ64vZAH2fgGrTHQH8mjxvVsmI6jhDXIUg==",
"peer": true,
"requires": {
"@babel/runtime": "^7.24.0",
"@mui/base": "^5.0.0-beta.40",
"@mui/system": "^5.15.14",
"@mui/utils": "^5.15.14",
"@types/react-transition-group": "^4.4.10",
"clsx": "^2.1.0",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
}
},
"@nicolo-ribaudo/chokidar-2": { "@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents.3", "version": "2.1.8-no-fsevents.3",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
@ -24542,6 +24032,21 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
}, },
"@preact/signals": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@preact/signals/-/signals-2.0.4.tgz",
"integrity": "sha512-9241aGnIv7y0IGzaq2vkBMe8/0jGnnmEEUeFmAoTWsaj8q/BW2PVekL8nHVJcy69bBww6rwEy3A1tc6yPE0sJA==",
"peer": true,
"requires": {
"@preact/signals-core": "^1.7.0"
}
},
"@preact/signals-core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.8.0.tgz",
"integrity": "sha512-OBvUsRZqNmjzCZXWLxkZfhcgT+Fk8DDcT/8vD6a1xhDemodyy87UJRJfASMuSD8FaAIeGgGm85ydXhm7lr4fyA==",
"peer": true
},
"@react-dnd/asap": { "@react-dnd/asap": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
@ -24702,6 +24207,35 @@
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz",
"integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==" "integrity": "sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw=="
}, },
"@schedule-x/calendar": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/calendar/-/calendar-2.30.0.tgz",
"integrity": "sha512-+yAVnY+4T8+iTrVp03V3usVYKhLuBzWEEtYOKsTnbFapD3LaL02Rv+0Gmv+4Vpk1H5yd3znrSxQvDLNBVNmUhg==",
"peer": true,
"requires": {}
},
"@schedule-x/event-modal": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/event-modal/-/event-modal-2.30.0.tgz",
"integrity": "sha512-RS/p5/rg/wCooKR5y05orJBJjxf7Ls7/VcGsz5MemJCQCXhu5pbU0g+OVVSlh6tJ+OoKF5frm4lIhXu82gd6/Q==",
"requires": {}
},
"@schedule-x/events-service": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/events-service/-/events-service-2.30.0.tgz",
"integrity": "sha512-WztKcKSj9uqvjvVr5bVe5tAzR4FKDBPQ/FFnuDWE2Y76yrxs7Hy4RROG/UCmteGrzJeXv3BV+TU2l63scJUADA=="
},
"@schedule-x/react": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/react/-/react-2.30.0.tgz",
"integrity": "sha512-9eVcoE7xYDYHnS+2JwgtRoEZtfzoJdfNHb+VMgvgJ0Ur+ItGifN5DyrFlyQ3l18E848ny0+wp1J8qRvlV5ikMw==",
"requires": {}
},
"@schedule-x/theme-default": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/@schedule-x/theme-default/-/theme-default-2.30.0.tgz",
"integrity": "sha512-vRUDV3fg7SoETiWQktv3DwYKjFZQMHwuYw+VKW5T3dzEJ8YfatuJbg0sj0JCiu9XQYcjs0hdmzrQvIDFy93r/w=="
},
"@sinclair/typebox": { "@sinclair/typebox": {
"version": "0.23.5", "version": "0.23.5",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.23.5.tgz",
@ -27274,12 +26808,6 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"peer": true
},
"co": { "co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -27974,19 +27502,6 @@
"whatwg-url": "^8.0.0" "whatwg-url": "^8.0.0"
} }
}, },
"date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"peer": true
},
"dayjs": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
"integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==",
"optional": true,
"peer": true
},
"debounce": { "debounce": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@ -34541,6 +34056,12 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"preact": {
"version": "10.26.6",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz",
"integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==",
"peer": true
},
"prelude-ls": { "prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",

View File

@ -3,11 +3,14 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@aldabil/react-scheduler": "^2.9.2",
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"@emotion/react": "^11.11.4", "@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5", "@emotion/styled": "^11.11.5",
"@reduxjs/toolkit": "^1.8.1", "@reduxjs/toolkit": "^1.8.1",
"@schedule-x/event-modal": "^2.30.0",
"@schedule-x/events-service": "^2.30.0",
"@schedule-x/react": "^2.30.0",
"@schedule-x/theme-default": "^2.30.0",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0", "@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",

View File

@ -660,11 +660,107 @@ input[type="checkbox"] {
padding-right: 8px; padding-right: 8px;
} }
.sx__month-grid-day {
min-height: 150px;
}
.sx__event-modal {
box-shadow: 1px -1px 20px rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
.sx__event-modal__title {
padding: 8px;
}
.sx__event-modal__time {
padding: 0 8px 8px 8px;
}
.sx__event-modal__time.with-padding {
padding-left: 8px;
}
.event-item-flex {
display: flex;
justify-content: space-between;
padding: 4px 8px;
}
.event-list-item-container {
border-radius: 8px;
}
.event-red {
border-inline-start: 4px solid rgb(249, 28, 69) !important;
color: rgb(89, 0, 13) !important;
background-color: rgb(255, 210, 220) !important;
}
.event-brown {
border-inline-start: 4px solid #d0b316 !important;
color: #594800 !important;
background-color: #fff5aa !important;
}
.event-green {
border-inline-start: 4px solid #004d3d !important;
color: #004d3d !important;
background-color: #dafff0 !important;
}
.event-blue {
border-inline-start: 4px solid rgb(28, 125, 249) !important;
color: rgb(0, 40, 89) !important;
background-color: rgb(210, 231, 255) !important;
}
.event-black {
border-inline-start: 4px solid black !important;
color: black !important;
background-color: #aaa !important;
}
.event-purple {
border-inline-start: 4px solid #6750a4 !important;
color: #21005e !important;
background-color: #eaddff !important;
}
.event-primary {
border-inline-start: 4px solid #6750a4 !important;
color: #21005e !important;
background-color: #eaddff !important;
}
.event-gray {
border-inline-start: 4px solid #777 !important;
color: #555 !important;
background-color: #eee !important;
}
.event-orange {
border-inline-start: 4px solid #F76806 !important;
color: #C35214 !important;
background-color: #FED8B1 !important;
}
.event-pink {
border-inline-start: 4px solid #FF6EC7 !important;
color: #A94064 !important;
background-color: #FFD1DC !important;
}
.btn-no-deco { .btn-no-deco {
text-decoration: none!important; text-decoration: none!important;
padding-top: 6px; padding-top: 6px;
} }
.small-dropdown-item {
font-size: 12px;
color: #555;
}
.personnel-info-table .red { .personnel-info-table .red {
background: #dc3545; background: #dc3545;
color: rgb(25, 23, 23); color: rgb(25, 23, 23);

View File

@ -38,6 +38,7 @@ const UpdateEmployee = () => {
const [status, setStatus] = useState(''); const [status, setStatus] = useState('');
const [tags, setTags] = useState(''); const [tags, setTags] = useState('');
const [showDeleteModal, setShowDeleteModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false);
const [selectedFile, setSelectedFile] = useState();
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const redirectTo = () => { const redirectTo = () => {
const redirect = params.get('redirect'); const redirect = params.get('redirect');
@ -186,6 +187,16 @@ const UpdateEmployee = () => {
} }
const formData = new FormData();
formData.append("file", selectedFile);
// formData.append('employeeId', currentEmployee?.id);
// formData.append('name', `${currentEmployee?.lastname}_${currentEmployee?.firstname}`);
// formData.append('fileType', 'I9');
if (selectedFile) {
EmployeeService.uploadEmployeeFile(formData, currentEmployee?.id, `${currentEmployee?.lastname}_${currentEmployee?.firstname}`, 'I9');
}
if (params.get('type') === 'driver') { if (params.get('type') === 'driver') {
dispatch(updateDriver({id: urlParams.id, data, currentEmployee})); dispatch(updateDriver({id: urlParams.id, data, currentEmployee}));
} else { } else {
@ -288,6 +299,13 @@ const UpdateEmployee = () => {
<div className="col-md-4 mb-4"> <div className="col-md-4 mb-4">
<div>Tags(Please use ',' between each tags):</div> <input type="text" value={tags || ''} onChange={e => setTags(e.target.value)}/> <div>Tags(Please use ',' between each tags):</div> <input type="text" value={tags || ''} onChange={e => setTags(e.target.value)}/>
</div> </div>
<div className="col-md-4 mb-4">
<div>Upload I9:</div>
<input
type="file"
onChange={(e) => setSelectedFile(e.target.files[0])}
/>
</div>
</div> </div>
<div className="list row mb-5"> <div className="list row mb-5">
<div className="col-md-6 col-sm-6 col-xs-12"> <div className="col-md-6 col-sm-6 col-xs-12">

View File

@ -9,6 +9,7 @@ const ViewEmployee = () => {
const urlParams = useParams(); const urlParams = useParams();
const [currentEmployee, setCurrentEmployee] = useState(undefined); const [currentEmployee, setCurrentEmployee] = useState(undefined);
const [i9Files, setI9Files] = useState([]);
const redirectTo = () => { const redirectTo = () => {
navigate(`/employees/list`) navigate(`/employees/list`)
@ -27,6 +28,12 @@ const ViewEmployee = () => {
} }
}, []); }, []);
useEffect(() => {
if (currentEmployee) {
EmployeeService.getAllEmployeeFiles(currentEmployee?.id, currentEmployee?.name, 'I9').then(data => { setI9Files(data?.data?.data?.files)} )
}
}, [currentEmployee])
return ( return (
<> <>
<div className="list row mb-4"> <div className="list row mb-4">
@ -92,6 +99,14 @@ const ViewEmployee = () => {
<div className="col-md-4 mb-4"> <div className="col-md-4 mb-4">
<div>Tags: {currentEmployee?.tags?.join(', ')}</div> <div>Tags: {currentEmployee?.tags?.join(', ')}</div>
</div> </div>
<div className="col-md-4 mb-4">
<div>I9: </div>
<div>
{
i9Files?.map(item => <div><a href={`${window.location.origin}${item?.url}`} target="_blank">{item?.name}</a></div> )
}
</div>
</div>
</div> </div>
</> </>
); );

View File

@ -2,8 +2,23 @@ import React, {useState, useEffect} from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, EventsService, CustomerService, ResourceService } from "../../services"; import { AuthService, EventsService, CustomerService, ResourceService } from "../../services";
import moment from 'moment'; import moment from 'moment';
import { Button, Modal } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Button, Modal, Dropdown } from "react-bootstrap";
import { Scheduler } from "@aldabil/react-scheduler"; import { useCalendarApp, ScheduleXCalendar } from '@schedule-x/react';
import {
viewWeek,
viewDay,
viewMonthGrid,
viewMonthAgenda,
createViewDay,
createViewMonthAgenda,
createViewWeek,
createViewMonthGrid
} from '@schedule-x/calendar';
import { createEventsServicePlugin } from '@schedule-x/events-service';
import { createEventModalPlugin} from '@schedule-x/event-modal';
import '@schedule-x/theme-default/dist/calendar.css';
import { Archive, PencilSquare, Filter } from "react-bootstrap-icons";
// import { Scheduler } from "@aldabil/react-scheduler";
const EventsCalendar = () => { const EventsCalendar = () => {
@ -18,6 +33,43 @@ const EventsCalendar = () => {
const [currentTotalResource, setCurrentTotalResource] = useState(0); const [currentTotalResource, setCurrentTotalResource] = useState(0);
const [showDeletedItems, setShowDeletedItems] = useState(false); const [showDeletedItems, setShowDeletedItems] = useState(false);
const [timeData, setTimeData] = useState([]); const [timeData, setTimeData] = useState([]);
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
const eventsServicePlugin = createEventsServicePlugin();
const eventModalService = createEventModalPlugin();
const [groupedEvents, setGroupedEvents] = useState(new Map());
const calendar = useCalendarApp({
views: [createViewMonthGrid(), createViewDay(), createViewMonthAgenda()],
monthGridOptions: {
/**
* Number of events to display in a day cell before the "+ N events" button is shown
* */
nEventsPerDay: 50,
},
defaultView: viewMonthGrid.name,
skipValidation: true,
selectedDate: moment(new Date()).format('YYYY-MM-DD HH:mm'),
events: events,
plugins: [eventModalService, eventsServicePlugin]
})
const getGroupedEvents = () => {
const eventsDateMap = new Map();
console.log('events', events);
for (const eventItem of events) {
const dateString = moment(eventItem.start_time).format('MMM Do, YYYY');
if (eventsDateMap.has(dateString)) {
eventsDateMap.set(dateString, [...eventsDateMap.get(dateString), eventItem]);
} else {
const value = [];
value.push(eventItem);
eventsDateMap.set(dateString, value);
}
}
console.log('eventsMap', eventsDateMap);
return eventsDateMap;
}
useEffect(() => { useEffect(() => {
if (!AuthService.canAccessLegacySystem()) { if (!AuthService.canAccessLegacySystem()) {
@ -52,15 +104,17 @@ const EventsCalendar = () => {
item.newPatient = item?.data?.new_patient || ''; item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || ''; item.needId = item?.data?.need_id || '';
item.disability = item?.data?.disability || ''; item.disability = item?.data?.disability || '';
item.startTime = item?.start_time? `${new Date(item?.start_time).toLocaleDateString()} ${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ; item.startTime = item?.start_time? `${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ;
item.endTime = item?.start_time? `${moment(new Date(item?.end_time)).format('hh:mm A')}` : '' ;
item.fasting = item?.data?.fasting || ''; item.fasting = item?.data?.fasting || '';
item.transportation = item?.link_event_name || ''; item.transportation = item?.link_event_name || '';
item.title = `${moment(new Date(item?.start_time)).format('hh:mm A') || ''} ${doctorField} / ${customerField}`; item.title = `${customerField}, provider: ${doctorField}`;
item.start = new Date(item.start_time); item.start = item?.start_time? `${moment(new Date(item?.start_time)).format('YYYY-MM-DD HH:mm')}` : '';
item.end = item.stop_time? new Date(item.stop_time): new Date(item.start_time); item.end = item.stop_time? `${moment(new Date(item?.stop_time)).format('YYYY-MM-DD HH:mm')}` : '';
const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData); const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData);
const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo; const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo;
item.color = item?.color; item.color = item?.color;
item._options = { additionalClasses: [`event-${item?.color || 'primary'}`]};
item.showWarnings = isFutureEvent; item.showWarnings = isFutureEvent;
item.maxTranslate1 = maxTranslate1; item.maxTranslate1 = maxTranslate1;
item.maxTranslate2 = maxTranslate2; item.maxTranslate2 = maxTranslate2;
@ -73,10 +127,17 @@ const EventsCalendar = () => {
setCurrentTotalResource(item.totalResource); setCurrentTotalResource(item.totalResource);
return item; return item;
}).filter(item => item.type === 'medical')); }).filter(item => item.type === 'medical'));
})} })}
}, [fromDate, toDate, customers, resources, timeData]); }, [fromDate, toDate, customers, resources, timeData]);
useEffect(() => {
if (events) {
events?.forEach((item) => calendar.eventsService.add(item));
setGroupedEvents(getGroupedEvents());
}
}, [events]);
const redirectToAdmin = () => { const redirectToAdmin = () => {
navigate(`/medical`) navigate(`/medical`)
} }
@ -102,6 +163,8 @@ const EventsCalendar = () => {
navigate(`/medical/events/${id}`) navigate(`/medical/events/${id}`)
} }
const disableEvent = (id) => { const disableEvent = (id) => {
const currentEvent = events.find(item => item.id === id); const currentEvent = events.find(item => item.id === id);
EventsService.disableEvent(id, { status: 'inactive', edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, EventsService.disableEvent(id, { status: 'inactive', edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
@ -121,12 +184,14 @@ const EventsCalendar = () => {
item.newPatient = item?.data?.new_patient || ''; item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || ''; item.needId = item?.data?.need_id || '';
item.disability = item?.data?.disability || ''; item.disability = item?.data?.disability || '';
item.startTime = item?.start_time? `${new Date(item?.start_time).toLocaleDateString()} ${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ; item.startTime = item?.start_time? `${moment(new Date(item?.start_time)).format('hh:mm A')}` : '' ;
item.endTime = item?.start_time? `${moment(new Date(item?.end_time)).format('hh:mm A')}` : '' ;
item.fasting = item?.data?.fasting || ''; item.fasting = item?.data?.fasting || '';
item.transportation = item?.link_event_name || ''; item.transportation = item?.link_event_name || '';
item.title = `${moment(new Date(item?.start_time)).format('hh:mm A') || ''} ${doctorField} / ${customerField}`; item.title = `${customerField}, provider: ${doctorField}`;
item.start = new Date(item.start_time); item.start = new Date(item.start_time);
item.end = item.stop_time? new Date(item.stop_time): new Date(item.start_time); item.end = item.stop_time? new Date(item.stop_time): new Date(item.start_time);
item._options = { additionalClasses: [`event-${item?.color || 'primary'}`]};
const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData); const transportationInfo = EventsService.getTransportationInfo(data.data, item, timeData);
const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo; const { isFutureEvent, maxTranslate1, maxTranslate2, maxResource, totalTranslate1, totalTranslate2, totalResource} = transportationInfo;
item.color = item?.color; item.color = item?.color;
@ -146,51 +211,120 @@ const EventsCalendar = () => {
}); });
} }
const FilterAndClose = () => {
setShowFilterDropdown(false);
}
const cleanFilterAndClose = () => {
setShowFilterDropdown(false);
setShowDeletedItems(false);
}
const customComponents = {
eventModal: ({calendarEvent}) => {
return <>
<div className="sx__event-modal__title">{calendarEvent?.customer}</div>
<div className="sx__event-modal__time">{`${calendarEvent?.doctor}`}</div>
<div className="sx__event-modal__time">{`${calendarEvent?.startTime}`}</div>
<div className="sx__event-modal__time">
<PencilSquare size={16} onClick={() => goToEdit(calendarEvent?.id)} className="me-4"></PencilSquare>
<Archive size={16} onClick={() =>{disableEvent(calendarEvent?.id)}}></Archive> </div>
</>
}
};
const customMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }, ref) => {
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<h6>Filter By</h6>
<div className="app-main-content-fields-section margin-sm dropdown-container">
<div className="me-4">
<div className="field-label">Show Deleted Events</div>
<input type="checkbox" value={showDeletedItems} checked={showDeletedItems === true} onClick={() => setShowDeletedItems(!showDeletedItems)} />
</div>
</div>
<div className="list row">
<div className="col-md-12">
<button className="btn btn-default btn-sm float-right" onClick={() => cleanFilterAndClose()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => FilterAndClose()}> Filter </button>
</div>
</div>
</div>
);
},
);
return ( return (
<> <>
<div className="list row mb-4"> <div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item>Medical</Breadcrumb.Item>
<Breadcrumb.Item active>
Medical Event Calendar
</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary"> <div className="col-md-12 text-primary">
<h5>Medical Event Calendar <button className="btn btn-link btn-sm" onClick={() => {redirectToAdmin()}}>Back</button></h5> <h4>
Medical Event Calendar
</h4>
</div> </div>
</div> </div>
<div className="list row mb-4">
<div className="col-md-12 col-sm-12 mb-4">
Show Deleted Events: <input type="checkbox" value={showDeletedItems} checked={showDeletedItems === true} onClick={() => setShowDeletedItems(!showDeletedItems)} />
<div className="app-main-content-list-container" style={{"min-width": "1500px"}}>
<div className="app-main-content-list-func-container">
<Tabs defaultActiveKey="eventsCalendar" id="events-calendar-tab">
<Tab eventKey="eventsCalendar" title="Medical Appointments">
<div className="multi-columns-container">
<div className="column-container" style={{'minWidth': '1000px'}}>
{calendar && <ScheduleXCalendar customComponents={customComponents} calendarApp={calendar} />}
</div>
<div className="column-container">
<div className="column-card">
<h6 className="text-primary me-4">List</h6>
{
Array.from(groupedEvents?.keys())?.map((key) => {
return <>
<h6 className="text-primary me-2">{key}</h6>
{
groupedEvents.get(key).map(eventItem => <div className={`event-${eventItem.color || 'primary'} mb-4 event-list-item-container`}>
<div className="event-item-flex">
<div className="sx__month-agenda-event__title">{eventItem.customer}</div>
<div className="sx__event-modal__time">{`${moment(eventItem?.start_time).format('hh:mm A')} ${eventItem?.end_time ? `- ${moment(eventItem?.end_time).format('hh:mm A')}` : '' }`}</div>
</div>
<div className="sx__event-modal__time with-padding">{`provider: ${eventItem?.doctor}`}</div>
</div>)
}
</>
})
}
</div>
</div>
</div>
</Tab>
</Tabs>
<div className="list-func-panel">
<Dropdown
key={'event-calendar-filter'}
id="event-calendar-filter"
show={showFilterDropdown}
onToggle={() => setShowFilterDropdown(!showFilterDropdown)}
autoClose={false}
>
<Dropdown.Toggle variant="primary">
<Filter size={16} className="me-2"></Filter>Filter
</Dropdown.Toggle>
<Dropdown.Menu as={customMenu}/>
</Dropdown>
</div>
</div> </div>
<div className="col-md-12 col-sm-12 mb-4"> </div>
<Scheduler
view="month"
events={showDeletedItems ? events : events.filter(event => event.status === 'active')}
month = {{
weekDays: [0, 1, 2, 3, 4, 5, 6],
weekStartOn: 0,
startHour: 0,
endHour: 24,
navigation: true,
disableGoToDay: false
}}
day = {{
startHour: 0,
endHour: 24,
step:1440,
navigation: true
}}
onSelectedDateChange = {(date) => {setFromDate(new Date(new Date(date).getFullYear(), new Date(date).getMonth(), 1)); setToDate(new Date(new Date(date).getFullYear(), new Date(date).getMonth() + 1, 0))}}
onEventEdit = {(event) => goToEdit(event.event_id)}
onDelete = {(id) => disableEvent(id)}
viewerExtraComponent = {(fields, event) => {
return (<div>
<div>{`Client: ${event.customer}`}</div>
<div>{`Doctor: ${event.doctor}`}</div>
<div>{`Contact: ${event.contact}`} </div>
<div>{`Address: ${event.address}`} </div>
<hr />
</div>)
}}
/>
</div>
</div>
</> </>
) )
}; };

View File

@ -2,11 +2,12 @@ import { Bell, ChevronDown, PersonCircle } from 'react-bootstrap-icons';
import { Outlet, useLocation, Navigate } from 'react-router-dom'; import { Outlet, useLocation, Navigate } from 'react-router-dom';
import { AuthService } from '../../services'; import { AuthService } from '../../services';
import SideMenu from './menu'; import SideMenu from './menu';
import { Dropdown } from "react-bootstrap";
function Layout() { function Layout() {
const location = useLocation(); const location = useLocation();
const showMenu = location.pathname !== '/login' && location.pathname !== '/landing'; // Example: Hide menu on login page const showMenu = location.pathname !== '/login' && location.pathname !== '/landing'; // Example: Hide menu on login page
const user = localStorage.getItem('user') || {}; const user = localStorage.getItem('user');
const getLogoSuffix = () => { const getLogoSuffix = () => {
return (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2') || window.location.host.includes('ws2') ||window.location.hostname.includes('localhost')) ? "Care LLC" : ((window.location.hostname.includes('worldshine3.mayo.llc') ||window.location.hostname.includes('site3') || window.location.hostname.includes('ws3')) ? "Cloverleaf LLC" : "International LLC"); return (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2') || window.location.host.includes('ws2') ||window.location.hostname.includes('localhost')) ? "Care LLC" : ((window.location.hostname.includes('worldshine3.mayo.llc') ||window.location.hostname.includes('site3') || window.location.hostname.includes('ws3')) ? "Cloverleaf LLC" : "International LLC");
} }
@ -20,12 +21,21 @@ function Layout() {
<div className="app-menu-user-profile-container"> <div className="app-menu-user-profile-container">
<Bell size={16} color="#0066B1"/> <Bell size={16} color="#0066B1"/>
<div className="app-menu-user-profile ms-2"> <div className="app-menu-user-profile ms-2">
<PersonCircle size={24}/> <PersonCircle size={24}/>
<div className="user-info-container me-2"> <div className="user-info-container me-2">
<div className="user-name">{JSON.parse(user).username}</div> <div className="user-name">{user && JSON.parse(user)?.username}</div>
<div className="user-role">{JSON.parse(user).roles[0]}</div> <div className="user-role">{user && JSON.parse(user)?.roles[0]}</div>
</div> </div>
<ChevronDown size={12} color="#555"></ChevronDown> <Dropdown>
<Dropdown.Toggle variant="tertiary" id="user-basic">
{/* <ChevronDown size={12} color="#555"></ChevronDown> */}
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item className="small-dropdown-item" onClick={() => AuthService.logout()}>Logout</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</div> </div>
</div> </div>

View File

@ -206,7 +206,6 @@ const SideMenu = () => {
} }
const goToLink = (link) => { const goToLink = (link) => {
console.log('this is lik', link);
navigate(link); navigate(link);
} }

View File

@ -2,8 +2,10 @@ import React, {useEffect, useState} from "react";
import { useSelector,useDispatch } from "react-redux"; import { useSelector,useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { vehicleSlice, selectVehicleError } from "./../../store"; import { vehicleSlice, selectVehicleError } from "./../../store";
import { AuthService } from "../../services"; import { AuthService, VehicleService } from "../../services";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from 'moment';
const CreateVehicle = () => { const CreateVehicle = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -26,6 +28,18 @@ const CreateVehicle = () => {
const [mileage, setMileage] = useState(); const [mileage, setMileage] = useState();
const [capacity, setCapacity] = useState(); const [capacity, setCapacity] = useState();
const [checklist, setChecklist] = useState(['']); const [checklist, setChecklist] = useState(['']);
const [hasLiftEquip, setHasLiftEquip] = useState(undefined);
const [insuranceExpireOn, setInsuranceExpireOn] = useState(undefined);
const [titleRegistrationOn, setTitleRegistrationOn] = useState(undefined);
const [emissionTestOn, setEmissionTestOn] = useState(undefined);
const [oilChangeMileage, setOilChangeMileage] = useState(undefined);
const [oilChangeDate, setOilChangeDate] = useState(undefined);
const [vin, setVin] = useState('');
const [note, setNote] = useState('');
// const [selectedMothlyFile, setSelectedMonthlyFile] = useState();
// const [selectedYearlyFile, setSelectedYearlyFile] = useState();
// const [monthlyInspectionDate, setMonthlyInspectionDate] = useState();
// const [yearlyInspectionDate, setYearlyInspectionDate] = useState();
const error = useSelector(selectVehicleError); const error = useSelector(selectVehicleError);
const redirectTo = () => { const redirectTo = () => {
@ -60,11 +74,27 @@ const CreateVehicle = () => {
make, make,
vehicle_model: vehicleModel, vehicle_model: vehicleModel,
status: 'active', status: 'active',
checklist checklist,
note,
vin,
has_lift_equip: hasLiftEquip === 'true',
insurance_expire_on: moment(insuranceExpireOn).format('MM/DD/YYYY'),
title_registration_on: moment(titleRegistrationOn).format('MM/DD/YYYY'),
emission_test_on: moment(emissionTestOn).format('MM/DD/YYYY'),
oil_change_date: moment(oilChangeDate).format('MM/DD/YYYY')
}; };
dispatch(createVehicle({data, redirectFun: redirectTo})); dispatch(createVehicle({data, redirectFun: redirectTo}));
} }
// const saveDocument = () => {
// if (selectedMothlyFile && monthlyInspectionDate) {
// VehicleService.uploadVechileFile(formData, currentVechile.id, currentVehchile.vehicle_number, 'monthlyInspection', monthlyInspectionDate);
// }
// if (selectedYearlyFile && yearlyInspectionDate) {
// VehicleService.uploadVechileFile(formData, currentVechile.id, currentVehchile.vehicle_number, 'yearlyInspection', yearlyInspectionDate);
// }
// }
return ( return (
<> <>
@ -101,20 +131,57 @@ const CreateVehicle = () => {
</div> </div>
<div className="app-main-content-fields-section"> <div className="app-main-content-fields-section">
<div className="me-4"><div className="field-label">Year</div><input type="text" value={year || ''} placeholder="e.g.,2016" onChange={e => setYear(e.target.value)}/></div> <div className="me-4"><div className="field-label">Year</div><input type="text" value={year || ''} placeholder="e.g.,2016" onChange={e => setYear(e.target.value)}/></div>
<div className="me-4"><div className="field-label">Vin Number</div><input type="text" value={vin || ''} placeholder="e.g, 1FBAX2CM9KA34959" onChange={e => setVin(e.target.value)}/></div>
<div className="me-4"><div className="field-label">Licence Plate</div><input type="text" value={tag || ''} placeholder="e.g.,91579HT" onChange={e => setTag(e.target.value)}/></div> <div className="me-4"><div className="field-label">Licence Plate</div><input type="text" value={tag || ''} placeholder="e.g.,91579HT" onChange={e => setTag(e.target.value)}/></div>
<div className="me-4"><div className="field-label">GPS ID</div><input type="text" value={gps || ''} placeholder="e.g.,609671" onChange={e => setGps(e.target.value)}/></div> <div className="me-4"><div className="field-label">GPS ID</div><input type="text" value={gps || ''} placeholder="e.g.,609671" onChange={e => setGps(e.target.value)}/></div>
<div className="me-4"><div className="field-label">EZPass</div><input type="text" value={ezpass || ''} placeholder="e.g.,NY12345" onChange={e => setEzpass(e.target.value)}/></div> <div className="me-4"><div className="field-label">EZPass</div><input type="text" value={ezpass || ''} placeholder="e.g.,NY12345" onChange={e => setEzpass(e.target.value)}/></div>
{/* Lift Equipped & VIN Number would be added later */} <div className="me-4">
<div className="field-label">Lift Equipped</div>
<select value={hasLiftEquip} onChange={e => setHasLiftEquip(e.target.value)}>
<option value=""></option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
</div> </div>
{/* Vehicle Maintenance & Compliance would be added later */} <h6 className="text-primary">Vehicle Maintenance & Compliance</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Last Oil Change Date <span className="field-blurb float-right">1-month due cycle </span></div>
<DatePicker selected={oilChangeDate} onChange={(v) => setOilChangeDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Last Emissions Inspection Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={emissionTestOn} onChange={(v) => setEmissionTestOn(v)} />
</div>
<div className="me-4">
<div className="field-label">Insurance Expiration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={insuranceExpireOn} onChange={(v) => setInsuranceExpireOn(v)} />
</div>
<div className="me-4">
<div className="field-label">Title Registration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={titleRegistrationOn} onChange={(v) => setTitleRegistrationOn(v)} />
</div>
</div>
<h6 className="text-primary">Check List</h6> <h6 className="text-primary">Check List</h6>
<div className="app-main-content-fields-section column"> <div className="app-main-content-fields-section column">
{checklist.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/> {checklist.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/>
<button className="btn btn-link btn-sm" onClick={(e) => setChecklist([...checklist].filter((value, index1) => index1 != index))}>Remove</button> <button className="btn btn-link btn-sm" onClick={(e) => setChecklist([...checklist].filter((value, index1) => index1 != index))}>Remove</button>
</div>))} </div>))}
<button className="btn btn-link" onClick={() => addItemToArray()}>+Add New Item</button> <button className="btn btn-link" onClick={() => addItemToArray()}>+Add New Item</button>
{/* Note would be added later */}
</div> </div>
<h6 className="text-primary">Additional Information</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Note</div>
<textarea placeholder="Any Extra Details" value={note || ''} onChange={e => setNote(e.target.value)}/>
</div>
</div>
{error && <div className="col-md-12 mb-4 alert alert-danger" role="alert"> {error && <div className="col-md-12 mb-4 alert alert-danger" role="alert">
{error} {error}
</div>} </div>}
@ -124,12 +191,53 @@ const CreateVehicle = () => {
</div> </div>
</Tab> </Tab>
<Tab eventKey="documents" title="Documents"> {/* <Tab eventKey="documents" title="Documents">
Coming soon... <h6 className="text-primary">Yearly Vehicle Inspection</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Vehicle Inspection Date<span className="required">*</span></div>
<DatePicker selected={yearlyInspectionDate} onChange={(v) => setYearlyInspectionDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Yearly Vehicle Inspection Sheet<span className="required">*</span></div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload Files
<input
type="file"
onChange={(e) => setSelectedYearlyFile(e.target.files[0])}
/>
</label>
<div className="file-name">{ selectedYearlyFile && selectedYearlyFile?.name }</div>
</div>
</div>
<h6 className="text-primary">Monthly Vehicle Inspection</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Vehicle Inspection Date <span className="required">*</span></div>
<DatePicker selected={monthlyInspectionDate} onChange={(v) => setMonthlyInspectionDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Monthly Vehicle Inspection Sheet<span className="required">*</span></div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload Files
<input
type="file"
onChange={(e) => setSelectedMonthlyFile(e.target.files[0])}
/>
</label>
<div className="file-name">{ selectedMothlyFile && selectedMothlyFile?.name }</div>
</div>
</div>
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={() => redirectTo()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => saveDocuments()}> Save </button>
</div>
</Tab> </Tab>
<Tab eventKey="Repair Records" title="Repair Records"> <Tab eventKey="Repair Records" title="Repair Records">
Coming soon... Coming soon...
</Tab> </Tab> */}
</Tabs> </Tabs>
</div> </div>
</div> </div>

View File

@ -2,9 +2,12 @@ import React, {useEffect, useState} from "react";
import { useSelector,useDispatch } from "react-redux"; import { useSelector,useDispatch } from "react-redux";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { vehicleSlice, selectVehicleError } from "./../../store"; import { vehicleSlice, selectVehicleError } from "./../../store";
import { AuthService } from "../../services"; import { AuthService, VehicleService } from "../../services";
import { Archive } from "react-bootstrap-icons"; import { Archive, Upload } from "react-bootstrap-icons";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from 'moment';
const UpdateVehicle = () => { const UpdateVehicle = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -23,6 +26,18 @@ const UpdateVehicle = () => {
const [mileage, setMileage] = useState(); const [mileage, setMileage] = useState();
const [capacity, setCapacity] = useState(); const [capacity, setCapacity] = useState();
const [checklist, setChecklist] = useState(['']); const [checklist, setChecklist] = useState(['']);
const [hasLiftEquip, setHasLiftEquip] = useState(undefined);
const [insuranceExpireOn, setInsuranceExpireOn] = useState(undefined);
const [titleRegistrationOn, setTitleRegistrationOn] = useState(undefined);
const [emissionTestOn, setEmissionTestOn] = useState(undefined);
const [oilChangeMileage, setOilChangeMileage] = useState(undefined);
const [oilChangeDate, setOilChangeDate] = useState(undefined);
const [vin, setVin] = useState('');
const [note, setNote] = useState('');
const [selectedMothlyFile, setSelectedMonthlyFile] = useState();
const [selectedYearlyFile, setSelectedYearlyFile] = useState();
const [monthlyInspectionDate, setMonthlyInspectionDate] = useState();
const [yearlyInspectionDate, setYearlyInspectionDate] = useState();
const error = useSelector(selectVehicleError); const error = useSelector(selectVehicleError);
useEffect(() => { useEffect(() => {
@ -48,6 +63,13 @@ const UpdateVehicle = () => {
setMileage(currentVehicle.mileage); setMileage(currentVehicle.mileage);
setCapacity(currentVehicle.capacity); setCapacity(currentVehicle.capacity);
setChecklist(currentVehicle.checklist); setChecklist(currentVehicle.checklist);
setHasLiftEquip(currentVehicle.has_lift_equip === true ? 'true': (currentVehicle?.has_lift_equip === false ? 'false' : undefined) );
setNote(currentVehicle?.note);
setVin(currentVehicle?.vin);
setInsuranceExpireOn(currentVehicle.insurance_expire_on && VehicleService.convertToDate(currentVehicle.insurance_expire_on));
setTitleRegistrationOn(currentVehicle?.title_registration_on && VehicleService.convertToDate(currentVehicle?.title_registration_on));
setEmissionTestOn(currentVehicle.title_registration_on && VehicleService.convertToDate(currentVehicle.title_registration_on));
setOilChangeDate(currentVehicle.oil_change_date && VehicleService.convertToDate(currentVehicle.oil_change_date));
} }
}, [currentVehicle]) }, [currentVehicle])
@ -83,7 +105,14 @@ const UpdateVehicle = () => {
make, make,
vehicle_model: vehicleModel, vehicle_model: vehicleModel,
status: 'active', status: 'active',
checklist checklist,
note,
vin,
has_lift_equip: hasLiftEquip === 'true',
insurance_expire_on: moment(insuranceExpireOn).format('MM/DD/YYYY'),
title_registration_on: moment(titleRegistrationOn).format('MM/DD/YYYY'),
emission_test_on: moment(emissionTestOn).format('MM/DD/YYYY'),
oil_change_date: moment(oilChangeDate).format('MM/DD/YYYY')
}; };
dispatch(updateVehicle({id: params.id, data, redirectFun: redirectTo})); dispatch(updateVehicle({id: params.id, data, redirectFun: redirectTo}));
} }
@ -106,6 +135,20 @@ const UpdateVehicle = () => {
redirectTo(); redirectTo();
} }
const saveDocuments = () => {
if (selectedMothlyFile && monthlyInspectionDate) {
const monthlyFormData = new FormData();
monthlyFormData.append('file', selectedMothlyFile);
VehicleService.uploadVechileFile(monthlyFormData, currentVehicle.id, currentVehicle.vehicle_number, 'monthlyInspection', monthlyInspectionDate);
}
if (selectedYearlyFile && yearlyInspectionDate) {
const yearlyFormData = new FormData();
yearlyFormData.append('file', selectedYearlyFile);
VehicleService.uploadVechileFile(yearlyFormData, currentVehicle.id, currentVehicle.vehicle_number, 'yearlyInspection', yearlyInspectionDate);
}
redirectTo();
}
return ( return (
<> <>
@ -142,19 +185,51 @@ const UpdateVehicle = () => {
</div> </div>
<div className="app-main-content-fields-section"> <div className="app-main-content-fields-section">
<div className="me-4"><div className="field-label">Year</div><input type="text" value={year || ''} placeholder="e.g.,2016" onChange={e => setYear(e.target.value)}/></div> <div className="me-4"><div className="field-label">Year</div><input type="text" value={year || ''} placeholder="e.g.,2016" onChange={e => setYear(e.target.value)}/></div>
<div className="me-4"><div className="field-label">Vin Number</div><input type="text" value={vin || ''} placeholder="e.g, 1FBAX2CM9KA34959" onChange={e => setVin(e.target.value)}/></div>
<div className="me-4"><div className="field-label">Licence Plate</div><input type="text" value={tag || ''} placeholder="e.g.,91579HT" onChange={e => setTag(e.target.value)}/></div> <div className="me-4"><div className="field-label">Licence Plate</div><input type="text" value={tag || ''} placeholder="e.g.,91579HT" onChange={e => setTag(e.target.value)}/></div>
<div className="me-4"><div className="field-label">GPS ID</div><input type="text" value={gps || ''} placeholder="e.g.,609671" onChange={e => setGps(e.target.value)}/></div> <div className="me-4"><div className="field-label">GPS ID</div><input type="text" value={gps || ''} placeholder="e.g.,609671" onChange={e => setGps(e.target.value)}/></div>
<div className="me-4"><div className="field-label">EZPass</div><input type="text" value={ezpass || ''} placeholder="e.g.,NY12345" onChange={e => setEzpass(e.target.value)}/></div> <div className="me-4"><div className="field-label">EZPass</div><input type="text" value={ezpass || ''} placeholder="e.g.,NY12345" onChange={e => setEzpass(e.target.value)}/></div>
{/* Lift Equipped & VIN Number would be added later */} <div className="me-4">
<div className="field-label">Lift Equipped</div>
<select value={hasLiftEquip} onChange={e => setHasLiftEquip(e.target.value)}>
<option value=""></option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
</div>
</div>
<h6 className="text-primary">Vehicle Maintenance & Compliance</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Last Oil Change Date <span className="field-blurb float-right">1-month due cycle </span></div>
<DatePicker selected={oilChangeDate} onChange={(v) => setOilChangeDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Last Emissions Inspection Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={emissionTestOn} onChange={(v) => setEmissionTestOn(v)} />
</div>
<div className="me-4">
<div className="field-label">Insurance Expiration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={insuranceExpireOn} onChange={(v) => setInsuranceExpireOn(v)} />
</div>
<div className="me-4">
<div className="field-label">Title Registration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<DatePicker selected={titleRegistrationOn} onChange={(v) => setTitleRegistrationOn(v)} />
</div>
</div> </div>
{/* Vehicle Maintenance & Compliance would be added later */}
<h6 className="text-primary">Check List</h6> <h6 className="text-primary">Check List</h6>
<div className="app-main-content-fields-section column"> <div className="app-main-content-fields-section column">
{checklist.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/> {checklist.map((item, index) => (<div className="mb-4" key={index}><input type="text" value={item} onChange={(e) => setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/>
<button className="btn btn-link btn-sm" onClick={(e) => setChecklist([...checklist].filter((value, index1) => index1 != index))}>Remove</button> <button className="btn btn-link btn-sm" onClick={(e) => setChecklist([...checklist].filter((value, index1) => index1 != index))}>Remove</button>
</div>))} </div>))}
<button className="btn btn-link" onClick={() => addItemToArray()}>+Add New Item</button> <button className="btn btn-link" onClick={() => addItemToArray()}>+Add New Item</button>
{/* Note would be added later */} </div>
<h6 className="text-primary">Additional Information</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Note</div>
<textarea placeholder="Any Extra Details" value={note || ''} onChange={e => setNote(e.target.value)}/>
</div>
</div> </div>
{error && <div className="col-md-12 mb-4 alert alert-danger" role="alert"> {error && <div className="col-md-12 mb-4 alert alert-danger" role="alert">
{error} {error}
@ -166,7 +241,48 @@ const UpdateVehicle = () => {
</div> </div>
</Tab> </Tab>
<Tab eventKey="documents" title="Documents"> <Tab eventKey="documents" title="Documents">
Coming soon... <h6 className="text-primary">Yearly Vehicle Inspection</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Vehicle Inspection Date<span className="required">*</span></div>
<DatePicker selected={yearlyInspectionDate} onChange={(v) => setYearlyInspectionDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Yearly Vehicle Inspection Sheet<span className="required">*</span></div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload Files
<input
type="file"
onChange={(e) => setSelectedYearlyFile(e.target.files[0])}
/>
</label>
<div className="file-name">{ selectedYearlyFile && selectedYearlyFile?.name }</div>
</div>
</div>
<h6 className="text-primary">Monthly Vehicle Inspection</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Vehicle Inspection Date <span className="required">*</span></div>
<DatePicker selected={monthlyInspectionDate} onChange={(v) => setMonthlyInspectionDate(v)} />
</div>
<div className="me-4">
<div className="field-label">Monthly Vehicle Inspection Sheet<span className="required">*</span></div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload Files
<input
type="file"
onChange={(e) => setSelectedMonthlyFile(e.target.files[0])}
/>
</label>
<div className="file-name">{ selectedMothlyFile && selectedMothlyFile?.name }</div>
</div>
</div>
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={() => redirectTo()}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => saveDocuments()}> Save </button>
</div>
</Tab> </Tab>
<Tab eventKey="Repair Records" title="Repair Records"> <Tab eventKey="Repair Records" title="Repair Records">
Coming soon... Coming soon...

View File

@ -4,14 +4,23 @@ import { useSelector,useDispatch } from "react-redux";
import { AuthService, VehicleService } from "../../services"; import { AuthService, VehicleService } from "../../services";
import { vehicleSlice, selectVehicleError } from "./../../store"; import { vehicleSlice, selectVehicleError } from "./../../store";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
import { Download, Pencil, Archive } from "react-bootstrap-icons"; import { Download, PencilSquare, Archive } from "react-bootstrap-icons";
import moment from "moment";
const ViewVehicle = () => { const ViewVehicle = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const urlParams = useParams(); const urlParams = useParams();
const [currentVehicle, setCurrentVehicle] = useState(undefined); const [currentVehicle, setCurrentVehicle] = useState(undefined);
const [monthlyDocs, setMonthlyDocs] = useState([]);
const [yearlyDocs, setYearlyDocs] = useState([]);
const [keyword, setKeyword] = useState('');
const [sortingMonthly, setSortingMonthly] = useState({key: '', order: ''});
const [sortingYearly, setSortingYearly] = useState({key: '', order: ''});
const [selectedItemsMonthly, setSelectedItemsMonthly] = useState([]);
const [selectedItemsYearly, setSelectedItemsYearly] = useState([]);
const [filteredMonthlyDocs, setFilteredMonthlyDocs] = useState(monthlyDocs);
const [filteredYearlyDocs, setFilteredYearlyDocs] = useState(yearlyDocs);
const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions; const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions;
const redirectTo = () => { const redirectTo = () => {
@ -22,6 +31,18 @@ const ViewVehicle = () => {
navigate(`/vehicles/edit/${id}?redirect=list`) navigate(`/vehicles/edit/${id}?redirect=list`)
} }
const download = () => {
const downloadArr = [...selectedItemsMonthly, ...selectedItemsYearly];
downloadArr.forEach((url) => {
const a = document.createElement('a');
a.href= url;
a.download = '';
document.body.append(a);
a.click();
document.body.removeChild(a);
})
}
const deactivateVehicle = () => { const deactivateVehicle = () => {
const data = { const data = {
status: 'inactive' status: 'inactive'
@ -31,6 +52,24 @@ const ViewVehicle = () => {
} }
useEffect(() => { useEffect(() => {
const getInspectionDate = (name) => {
const arr1 = name.split('.');
const prefix = arr1[0];
if (prefix) {
const arr2 = prefix.split('_');
const dateNumber = arr2[arr2.length - 1];
return dateNumber ? new Date(parseInt(dateNumber)).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'}) : moment().format('MM/DD/YYYY');
} else {
return moment().format('MM/DD/YYYY');
}
}
const getAllDocuments = async (vid, name) => {
const monthlyInspectionDocs = (await VehicleService.getAllVechileFiles(vid, name, 'monthlyInspection'))?.data?.data?.files || [];
const yearlyInspectionDocs = (await VehicleService.getAllVechileFiles(vid, name, 'yearlyInspection'))?.data?.data?.files || [];
setMonthlyDocs(monthlyInspectionDocs?.map(item => ({ ...item, inspectionDate: getInspectionDate(item?.name) })));
setYearlyDocs(yearlyInspectionDocs?.map(item => ({ ...item, inspectionDate: getInspectionDate(item?.name) })));
};
if (!AuthService.canViewVechiles()) { if (!AuthService.canViewVechiles()) {
window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.')
AuthService.logout(); AuthService.logout();
@ -39,10 +78,208 @@ const ViewVehicle = () => {
if (!currentVehicle) { if (!currentVehicle) {
VehicleService.getVehicle(urlParams.id).then((data) => { VehicleService.getVehicle(urlParams.id).then((data) => {
setCurrentVehicle(data.data); setCurrentVehicle(data.data);
getAllDocuments(data.data?.id, data.data?.vehicle_number);
}) })
} else {
getAllDocuments(currentVehicle?.id, currentVehicle?.vehicle_number);
} }
}, []); }, []);
useEffect(() => {
setFilteredMonthlyDocs(monthlyDocs.filter(item => item?.name?.toLowerCase().includes(keyword.toLowerCase()) || item?.inspectionDate?.includes(keyword.toLowerCase())));
setFilteredYearlyDocs(yearlyDocs.filter(item => item?.name?.toLowerCase().includes(keyword.toLowerCase()) || item?.inspectionDate?.includes(keyword.toLowerCase())));
}, [keyword, yearlyDocs, monthlyDocs]);
useEffect(() => {
const newYearlyDocs = [...yearlyDocs];
const sortedYearlyDocs = sortingYearly.key === '' ? newYearlyDocs : newYearlyDocs.sort((a, b) => {
return a[sortingYearly.key]?.localeCompare(b[sortingYearly.key]);
});
setYearlyDocs(
sortingMonthly.order === 'asc' ? sortedYearlyDocs : sortedYearlyDocs.reverse()
)
}, [sortingYearly]);
useEffect(() => {
const newMonthlyDocs = [...monthlyDocs];
const sortedMonthlyDocs = sortingMonthly.key === '' ? newMonthlyDocs : newMonthlyDocs.sort((a, b) => {
return a[sortingMonthly.key]?.localeCompare(b[sortingMonthly.key]);
});
setMonthlyDocs(
sortingMonthly.order === 'asc' ? sortedMonthlyDocs : sortedMonthlyDocs.reverse()
)
}, [sortingMonthly]);
const columnsMonthly = [
{
key: 'name',
label: 'Monthly Vehicle Inspection Sheet'
},
{
key: 'inspectionDate',
label: 'Vehicle Inspection Date'
},
{
key: 'createdAt',
label: 'Date Added'
}
];
const columnsYearly = [
{
key: 'name',
label: 'Yearly Vehicle Inspection Sheet'
},
{
key: 'inspectionDate',
label: 'Vehicle Inspection Date'
},
{
key: 'createdAt',
label: 'Date Added'
}
];
const sortTableWithFieldMonthly = (key) => {
let newSorting = {
key,
order: 'asc',
}
if (sortingMonthly.key === key && sortingMonthly.order === 'asc') {
newSorting = {...newSorting, order: 'desc'};
}
setSortingMonthly(newSorting);
}
const sortTableWithFieldYearly = (key) => {
let newSorting = {
key,
order: 'asc',
}
if (sortingYearly.key === key && sortingYearly.order === 'asc') {
newSorting = {...newSorting, order: 'desc'};
}
setSortingYearly(newSorting);
}
const toggleSelectedAllItemsMonthly = () => {
if (selectedItemsMonthly.length !== filteredMonthlyDocs.length || selectedItemsMonthly.length === 0) {
const newSelectedItems = [...filteredMonthlyDocs].map((doc) => doc.url);
setSelectedItemsMonthly(newSelectedItems);
} else {
setSelectedItemsMonthly([]);
}
}
const toggleSelectedAllItemsYearly = () => {
if (selectedItemsYearly.length !== filteredYearlyDocs.length || selectedItemsYearly.length === 0) {
const newSelectedItems = [...filteredYearlyDocs].map((doc) => doc.url);
setSelectedItemsYearly(newSelectedItems);
} else {
setSelectedItemsYearly([]);
}
}
const toggleItemYearly = (id) => {
if (selectedItemsYearly.includes(id)) {
const newSelectedItems = [...selectedItemsYearly].filter((item) => item !== id);
setSelectedItemsYearly(newSelectedItems);
} else {
const newSelectedItems = [...selectedItemsYearly, id];
setSelectedItemsYearly(newSelectedItems);
}
}
const toggleItemMonthly = (id) => {
if (selectedItemsMonthly.includes(id)) {
const newSelectedItems = [...selectedItemsMonthly].filter((item) => item !== id);
setSelectedItemsMonthly(newSelectedItems);
} else {
const newSelectedItems = [...selectedItemsMonthly, id];
setSelectedItemsMonthly(newSelectedItems);
}
}
const getSortingImgMonthly = (key) => {
return sortingMonthly.key === key ? (sortingMonthly.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
}
const getSortingImgYearly = (key) => {
return sortingYearly.key === key ? (sortingYearly.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
}
const checkSelectAllMonthly = () => {
return selectedItemsMonthly.length === filteredMonthlyDocs.length && selectedItemsMonthly.length > 0;
}
const checkSelectAllYearly = () => {
return selectedItemsYearly.length === filteredYearlyDocs.length && selectedItemsYearly.length > 0;
}
const tableMonthly = <div className="list row mb-4">
<div className="col-md-12">
<table className="personnel-info-table">
<thead>
<tr>
<th className="th-checkbox"><input type="checkbox" checked={checkSelectAllMonthly()} onClick={() => toggleSelectedAllItemsMonthly()}></input></th>
<th className="th-index">No.</th>
{
columnsMonthly.map((column, index) => <th className="sortable-header" key={index}>
{column.label} <span className="float-right" onClick={() => sortTableWithFieldMonthly(column.key)}><img src={`/images/${getSortingImgMonthly(column.key)}.png`}></img></span>
</th>)
}
</tr>
</thead>
<tbody>
{
filteredMonthlyDocs.map((doc, index) => <tr key={doc.url}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItemsMonthly.includes(doc?.url)} onClick={()=>toggleItemMonthly(doc?.url)}/></td>
<td className="td-index">{index + 1}</td>
<td> <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(currentVehicle?.id)}></PencilSquare>
<a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a> </td>
<td>{doc?.inspectionDate}</td>
<td>{new Date(doc?.createdAt).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'})}</td>
</tr>)
}
</tbody>
</table>
</div>
</div>;
const tableYearly = <div className="list row mb-4">
<div className="col-md-12">
<table className="personnel-info-table">
<thead>
<tr>
<th className="th-checkbox"><input type="checkbox" checked={checkSelectAllYearly()} onClick={() => toggleSelectedAllItemsYearly()}></input></th>
<th className="th-index">No.</th>
{
columnsYearly.map((column, index) => <th className="sortable-header" key={index}>
{column.label} <span className="float-right" onClick={() => sortTableWithFieldYearly(column.key)}><img src={`/images/${getSortingImgYearly(column.key)}.png`}></img></span>
</th>)
}
</tr>
</thead>
<tbody>
{
filteredYearlyDocs.map((doc, index) => <tr key={doc.url}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItemsYearly.includes(doc?.url)} onClick={()=>toggleItemYearly(doc?.url)}/></td>
<td className="td-index">{index + 1}</td>
<td> <PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(currentVehicle?.id)}></PencilSquare>
<a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a> </td>
<td>{doc?.inspectionDate}</td>
<td>{new Date(doc?.createdAt).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'})}</td>
</tr>)
}
</tbody>
</table>
</div>
</div>;
return ( return (
<> <>
<div className="list row mb-4"> <div className="list row mb-4">
@ -91,6 +328,10 @@ const ViewVehicle = () => {
<div className="field-label">Year</div> <div className="field-label">Year</div>
<div className="field-value">{currentVehicle?.year}</div> <div className="field-value">{currentVehicle?.year}</div>
</div> </div>
<div className="field-body">
<div className="field-label">Vin Number</div>
<div className="field-value">{currentVehicle?.vin}</div>
</div>
<div className="field-body"> <div className="field-body">
<div className="field-label">Licence Plate</div> <div className="field-label">Licence Plate</div>
<div className="field-value">{currentVehicle?.tag}</div> <div className="field-value">{currentVehicle?.tag}</div>
@ -103,25 +344,59 @@ const ViewVehicle = () => {
<div className="field-label">EZPass</div> <div className="field-label">EZPass</div>
<div className="field-value">{currentVehicle?.ezpass}</div> <div className="field-value">{currentVehicle?.ezpass}</div>
</div> </div>
<div className="field-body">
<div className="field-label">Lift Equipped</div>
<div className="field-value">{currentVehicle?.has_lift_equip === true ? 'Yes' : (currentVehicle?.has_lift_equip === false? 'No' : '')}</div>
</div>
</div> </div>
<h6 className="text-primary">Vehicle Maintenance & Compliance</h6>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Last Oil Change Date <span className="field-blurb float-right">1-month due cycle </span></div>
<div className="field-value">{currentVehicle?.oil_change_date}</div>
</div>
<div className="field-body">
<div className="field-label">Last Emissions Inspection Date <span className="field-blurb float-right">1-year due cycle </span></div>
<div className="field-value">{currentVehicle?.emission_test_on}</div>
</div>
<div className="field-body">
<div className="field-label">Insurance Expiration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<div className="field-value">{currentVehicle?.insurance_expire_on}</div>
</div>
<div className="field-body">
<div className="field-label">Title Registration Date <span className="field-blurb float-right">1-year due cycle </span></div>
<div className="field-value">{currentVehicle?.title_registration_on}</div>
</div>
</div>
<h6 className="text-primary">Check List</h6> <h6 className="text-primary">Check List</h6>
<div className="app-main-content-fields-section column"> <div className="app-main-content-fields-section column">
<ul> <ul>
{currentVehicle?.checklist?.map((item) => <li>{item}</li>)} {currentVehicle?.checklist?.map((item) => <li>{item}</li>)}
</ul> </ul>
</div> </div>
<h6 className="text-primary">Additional Information</h6>
<div className="app-main-content-fields-section">
<div className="field-body">
<div className="field-label">Note</div>
<div className="field-value">{currentVehicle?.note}</div>
</div>
</div>
</Tab> </Tab>
<Tab eventKey="documents" title="Documents"> <Tab eventKey="documents" title="Documents">
Coming soon... <h6 className="text-primary">Yearly Vehicle Inspection</h6>
{tableYearly}
<h6 className="text-primary">Monthly Vehicle Inspection</h6>
{tableMonthly}
</Tab> </Tab>
<Tab eventKey="Repair Records" title="Repair Records"> <Tab eventKey="Repair Records" title="Repair Records">
Coming soon... Coming soon...
</Tab> </Tab>
</Tabs> </Tabs>
<div className="list-func-panel"> <div className="list-func-panel">
<button className="btn btn-primary me-2" onClick={() => goToEdit(currentVehicle?.id)}><Pencil size={16} className="me-2"></Pencil>Edit</button> <input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
<button className="btn btn-primary me-2" onClick={() => deactivateVehicle()}><Archive size={16} className="me-2"></Archive>Archive</button> <button className="btn btn-primary" onClick={() => download()}><Download size={16} className="me-2"></Download>Download</button>
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -30,6 +30,14 @@ const deleteEmployee = (id, data) => {
return http.put(`/employees/${id}`, data); return http.put(`/employees/${id}`, data);
} }
const uploadEmployeeFile = (data, employeeId, name, fileType) => {
return http.post(`/files/upload-physical?objectId=${employeeId}&name=${name}&fileType=${fileType}&model=employee`, data)
}
const getAllEmployeeFiles = (employeeId, name, fileType) => {
return http.get(`/files/uploadedDocs/employee/${employeeId}/type/${fileType}/name/${name}`)
}
const validatePassword = (password = '') => { const validatePassword = (password = '') => {
const lowercaseRegex = /[a-z]/; const lowercaseRegex = /[a-z]/;
const uppercaseRegex = /[A-Z]/; const uppercaseRegex = /[A-Z]/;
@ -59,5 +67,7 @@ export const EmployeeService = {
updateEmployee, updateEmployee,
deleteEmployee, deleteEmployee,
getEmployee, getEmployee,
validatePassword validatePassword,
uploadEmployeeFile,
getAllEmployeeFiles
}; };

View File

@ -18,11 +18,27 @@ const getVehicle = (id) => {
return http.get(`/vehicles/${id}`); return http.get(`/vehicles/${id}`);
}; };
const convertToDate = (dateString) => {
const [month, day, year] = dateString.split('/');
return new Date(Number(year), Number(month)-1, Number(day))
}
const uploadVechileFile = (data, vehicleId, name, fileType, date) => {
return http.post(`/files/upload-physical?objectId=${vehicleId}&name=${name}&fileType=${fileType}&model=vehicle&date=${date.getTime()}`, data)
}
const getAllVechileFiles = (vehicleId, name, fileType) => {
return http.get(`/files/uploadedDocs/vehicle/${vehicleId}/type/${fileType}/name/${name}`)
}
export const VehicleService = { export const VehicleService = {
getAll, getAll,
getAllActiveVehicles, getAllActiveVehicles,
updateVehicles, updateVehicles,
createNewVehicles, createNewVehicles,
deleteVehicles, deleteVehicles,
getVehicle getVehicle,
convertToDate,
uploadVechileFile,
getAllVechileFiles
}; };

View File

@ -8,6 +8,8 @@ var corsOptions = {
// const path = __dirname + '/client/build/'; // const path = __dirname + '/client/build/';
const path = __dirname + '/app/views/' const path = __dirname + '/app/views/'
const employeeUploadBasePath = '/www/wwwroot/upload/';
app.use('/files', express.static(employeeUploadBasePath));
app.use(cors(corsOptions)); app.use(cors(corsOptions));
// parse requests of content-type - application/json // parse requests of content-type - application/json
app.use(bodyParser.json()); app.use(bodyParser.json());