This commit is contained in:
2026-02-23 15:31:32 -05:00
parent 116c3e8259
commit f3839b000b
19 changed files with 741 additions and 345 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@@ -48,6 +48,23 @@ exports.getAllVehicleRepairs = (req, res) => {
}); });
} }
exports.updateVehicleRepair = (req, res) => {
const id = req.params.id;
VehicleRepair.findByIdAndUpdate(id, req.body, { new: true })
.then(data => {
if (!data) {
res.status(404).send({ message: `Cannot update Vehicle Repair Record with id=${id}.` });
} else {
res.send(data);
}
})
.catch(err => {
res.status(500).send({
message: err.message || "Could not update Vehicle Repair Record."
});
});
}
exports.deleteVehicleRepair = (req, res) => { exports.deleteVehicleRepair = (req, res) => {
const id = req.params.id; const id = req.params.id;
VehicleRepair.findByIdAndDelete(id) VehicleRepair.findByIdAndDelete(id)

View File

@@ -11,6 +11,7 @@ module.exports = app => {
var router = require("express").Router(); var router = require("express").Router();
router.get("/", [authJwt.verifyToken], vehicleRepairs.getAllVehicleRepairs); router.get("/", [authJwt.verifyToken], vehicleRepairs.getAllVehicleRepairs);
router.post("/", [authJwt.verifyToken], vehicleRepairs.createVehicleRepair); router.post("/", [authJwt.verifyToken], vehicleRepairs.createVehicleRepair);
router.put("/:id", [authJwt.verifyToken], vehicleRepairs.updateVehicleRepair);
router.delete("/:id", [authJwt.verifyToken], vehicleRepairs.deleteVehicleRepair); router.delete("/:id", [authJwt.verifyToken], vehicleRepairs.deleteVehicleRepair);
app.use('/api/vehicle-repairs', router); app.use('/api/vehicle-repairs', router);
}; };

View File

@@ -1,16 +1,16 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.46cc12be.css", "main.css": "/static/css/main.46cc12be.css",
"main.js": "/static/js/main.671a9950.js", "main.js": "/static/js/main.c98c5007.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.46cc12be.css.map": "/static/css/main.46cc12be.css.map", "main.46cc12be.css.map": "/static/css/main.46cc12be.css.map",
"main.671a9950.js.map": "/static/js/main.671a9950.js.map", "main.c98c5007.js.map": "/static/js/main.c98c5007.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.46cc12be.css", "static/css/main.46cc12be.css",
"static/js/main.671a9950.js" "static/js/main.c98c5007.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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.671a9950.js"></script><link href="/static/css/main.46cc12be.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="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"><link rel="manifest" href="/manifest.json"/><title>Worldshine Transportation</title><script defer="defer" src="/static/js/main.c98c5007.js"></script><link href="/static/css/main.46cc12be.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

BIN
client/.DS_Store vendored

Binary file not shown.

View File

@@ -14,6 +14,10 @@ import RouteEdit from "./components/trans-routes/RouteEdit";
import RoutesSchedule from "./components/trans-routes/RoutesSchedule"; import RoutesSchedule from "./components/trans-routes/RoutesSchedule";
import CreateVehicle from "./components/vehicles/CreateVehicle"; import CreateVehicle from "./components/vehicles/CreateVehicle";
import UpdateVehicle from "./components/vehicles/UpdateVehicle"; import UpdateVehicle from "./components/vehicles/UpdateVehicle";
import AddVehicleInspection from "./components/vehicles/AddVehicleInspection";
import EditVehicleInspection from "./components/vehicles/EditVehicleInspection";
import AddRepairRecord from "./components/vehicles/AddRepairRecord";
import EditRepairRecord from "./components/vehicles/EditRepairRecord";
import CreateEmployee from "./components/employees/CreateEmployee"; import CreateEmployee from "./components/employees/CreateEmployee";
import UpdateEmployee from "./components/employees/UpdateEmployee"; import UpdateEmployee from "./components/employees/UpdateEmployee";
import EmployeeList from "./components/employees/EmployeeList"; import EmployeeList from "./components/employees/EmployeeList";
@@ -138,6 +142,10 @@ function App() {
<Route path="/vehicles/edit/:id" element={<UpdateVehicle />} /> <Route path="/vehicles/edit/:id" element={<UpdateVehicle />} />
<Route path="/vehicles/list" element={<VehicleList/> } /> <Route path="/vehicles/list" element={<VehicleList/> } />
<Route path="/vehicles/:id" element={<ViewVehicle/>}/> <Route path="/vehicles/:id" element={<ViewVehicle/>}/>
<Route path="/vehicles/:id/inspections/:type/add" element={<AddVehicleInspection />} />
<Route path="/vehicles/:id/inspections/:type/edit" element={<EditVehicleInspection />} />
<Route path="/vehicles/:id/repairs/add" element={<AddRepairRecord />} />
<Route path="/vehicles/:id/repairs/edit/:repairId" element={<EditRepairRecord />} />
<Route path="/messages" element={<CreateMessage /> } /> <Route path="/messages" element={<CreateMessage /> } />
<Route path="/messages/edit/:id" element={<UpdateMessage />} /> <Route path="/messages/edit/:id" element={<UpdateMessage />} />

View File

@@ -0,0 +1,230 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { AuthService, VehicleRepairService, VehicleService } from "../../services";
import { Upload, Trash } from "react-bootstrap-icons";
import { Breadcrumb } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
import {
REPAIR_PART_NAME,
REPAIR_PART_NAME_TEXT,
QUANTITY_OPTIONS
} from "../../shared";
const AddRepairRecord = () => {
const navigate = useNavigate();
const params = useParams();
const [currentVehicle, setCurrentVehicle] = useState(null);
const [existingRepairs, setExistingRepairs] = useState([]);
const [partName, setPartName] = useState('');
const [replacementDate, setReplacementDate] = useState(null);
const [mileage, setMileage] = useState('');
const [quantity, setQuantity] = useState('');
const [cost, setCost] = useState('');
const [location, setLocation] = useState('');
const [receiptFile, setReceiptFile] = useState(null);
const [nextReminder, setNextReminder] = useState('');
useEffect(() => {
if (!AuthService.canAddOrEditVechiles()) {
window.alert('You haven\'t login yet OR this user does not have access to this page.');
AuthService.logout();
navigate('/login');
}
VehicleService.getVehicle(params.id).then((data) => {
setCurrentVehicle(data.data);
});
}, []);
useEffect(() => {
if (currentVehicle) {
fetchRepairs();
}
}, [currentVehicle]);
const fetchRepairs = () => {
VehicleRepairService.getAll(currentVehicle.id)
.then(res => setExistingRepairs(res.data || []))
.catch(() => setExistingRepairs([]));
};
const formatDateForBackend = (date) => {
if (!date) return '';
return moment(date).format('MM/DD/YYYY');
};
const handleSave = () => {
if (!partName) {
window.alert('Please select a part name.');
return;
}
const data = {
vehicle: currentVehicle?.id,
part_name: partName,
repair_date: formatDateForBackend(replacementDate),
mileage_at_replacement: mileage,
quantity,
repair_price: cost,
repair_location: location,
next_replacement_reminder: nextReminder
};
VehicleRepairService.createNewVehicleRepair(data).then(result => {
const record = result.data;
const uploadPromise = receiptFile
? VehicleService.uploadVechileFile(
(() => { const fd = new FormData(); fd.append('file', receiptFile); return fd; })(),
currentVehicle.id, record.id, 'repair', replacementDate || new Date()
)
: Promise.resolve();
uploadPromise.then(() => {
setPartName('');
setReplacementDate(null);
setMileage('');
setQuantity('');
setCost('');
setLocation('');
setReceiptFile(null);
setNextReminder('');
fetchRepairs();
});
});
};
const deleteRepair = (repairId) => {
if (window.confirm('Are you sure you want to delete this repair record?')) {
VehicleRepairService.deleteVehicleRepair(repairId).then(() => fetchRepairs());
}
};
const goBack = () => {
navigate(`/vehicles/${params.id}?tab=documents`);
};
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item href="/trans-routes/dashboard">Transportation</Breadcrumb.Item>
<Breadcrumb.Item href="/vehicles/list">Vehicles Information</Breadcrumb.Item>
<Breadcrumb.Item href={`/vehicles/${params.id}?tab=documents`}>View Vehicle Information</Breadcrumb.Item>
<Breadcrumb.Item active>Add Repair Record</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>Add Repair Record <button className="btn btn-link btn-sm" onClick={goBack}>Back</button></h4>
</div>
</div>
<div className="app-main-content-list-container form-page">
<div className="app-main-content-list-func-container">
<h6 className="text-primary">Repair & Maintenance Record</h6>
{existingRepairs.length > 0 && (
<>
<div className="field-label mb-2">Existing Records</div>
<table className="table table-sm table-bordered mb-4">
<thead>
<tr>
<th>Part Name</th>
<th>Date</th>
<th>Mileage</th>
<th>Qty</th>
<th>Cost</th>
<th>Location</th>
<th>Next Reminder</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{existingRepairs.map((repair) => (
<tr key={repair.id}>
<td>{REPAIR_PART_NAME_TEXT[repair.part_name] || repair.repair_description || repair.part_name || '-'}</td>
<td>{repair.repair_date || '-'}</td>
<td>{repair.mileage_at_replacement || '-'}</td>
<td>{repair.quantity || '-'}</td>
<td>{repair.repair_price || '-'}</td>
<td>{repair.repair_location || '-'}</td>
<td>{repair.next_replacement_reminder || '-'}</td>
<td>
<button className="btn btn-link btn-sm p-0 text-danger" onClick={() => deleteRepair(repair.id)}>
<Trash size={14} />
</button>
</td>
</tr>
))}
</tbody>
</table>
</>
)}
<div className="field-label mb-2">Add New Record</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Part Name</div>
<select value={partName} onChange={e => setPartName(e.target.value)}>
<option value="">Select...</option>
{Object.keys(REPAIR_PART_NAME).map(key => (
<option key={key} value={REPAIR_PART_NAME[key]}>{REPAIR_PART_NAME_TEXT[REPAIR_PART_NAME[key]]}</option>
))}
</select>
</div>
<div className="me-4">
<div className="field-label">Replacement Date</div>
<DatePicker
selected={replacementDate}
onChange={(date) => setReplacementDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Mileage at Replacement</div>
<input type="text" placeholder="e.g., 48,000" value={mileage} onChange={e => setMileage(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Quantity</div>
<select value={quantity} onChange={e => setQuantity(e.target.value)}>
<option value="">Select...</option>
{QUANTITY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Cost</div>
<input type="text" placeholder="e.g., $250.00" value={cost} onChange={e => setCost(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Location</div>
<input type="text" placeholder="e.g., Rockville Auto Center" value={location} onChange={e => setLocation(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Receipt Upload</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2" /> Upload
<input type="file" onChange={(e) => setReceiptFile(e.target.files[0])} />
</label>
<div className="file-name">{receiptFile?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Next Replacement Reminder</div>
<input type="text" placeholder="e.g., 78,000" value={nextReminder} onChange={e => setNextReminder(e.target.value)} />
</div>
</div>
<div className="list row mb-5 mt-3">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={goBack}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={handleSave}> Save </button>
</div>
</div>
</div>
</div>
</>
);
};
export default AddRepairRecord;

View File

@@ -0,0 +1,143 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { AuthService, VehicleService } from "../../services";
import { Upload } from "react-bootstrap-icons";
import { Breadcrumb } from "react-bootstrap";
import DatePicker from "react-datepicker";
const AddVehicleInspection = () => {
const navigate = useNavigate();
const params = useParams();
const inspectionType = params.type; // 'yearly' or 'monthly'
const isYearly = inspectionType === 'yearly';
const title = isYearly ? 'Yearly Inspection' : 'Monthly Inspection';
const [currentVehicle, setCurrentVehicle] = useState(null);
const [inspectionDate, setInspectionDate] = useState(null);
const [selectedFile, setSelectedFile] = useState(null);
const [existingFiles, setExistingFiles] = useState([]);
useEffect(() => {
if (!AuthService.canAddOrEditVechiles()) {
window.alert('You haven\'t login yet OR this user does not have access to this page.');
AuthService.logout();
navigate('/login');
}
VehicleService.getVehicle(params.id).then((data) => {
setCurrentVehicle(data.data);
});
}, []);
useEffect(() => {
if (currentVehicle) {
fetchFiles();
}
}, [currentVehicle]);
const fetchFiles = () => {
const fileType = isYearly ? 'yearlyInspection' : 'monthlyInspection';
VehicleService.getAllVechileFiles(currentVehicle.id, currentVehicle.vehicle_number, fileType)
.then(res => setExistingFiles(res.data?.data?.files || []))
.catch(() => setExistingFiles([]));
};
const getFileDownloadUrl = (fileUrl) => {
const baseUrl = require('../../http-common').default?.defaults?.baseURL || '';
return baseUrl.replace('/api', '') + fileUrl;
};
const handleSave = () => {
if (!selectedFile || !inspectionDate) {
window.alert('Please select a date and a file.');
return;
}
const formData = new FormData();
formData.append('file', selectedFile);
const fileType = isYearly ? 'yearlyInspection' : 'monthlyInspection';
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, fileType, inspectionDate).then(() => {
setSelectedFile(null);
setInspectionDate(null);
fetchFiles();
});
};
const goBack = () => {
navigate(`/vehicles/${params.id}?tab=documents`);
};
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item href="/trans-routes/dashboard">Transportation</Breadcrumb.Item>
<Breadcrumb.Item href="/vehicles/list">Vehicles Information</Breadcrumb.Item>
<Breadcrumb.Item href={`/vehicles/${params.id}?tab=documents`}>View Vehicle Information</Breadcrumb.Item>
<Breadcrumb.Item active>Add {title}</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>Add {title} <button className="btn btn-link btn-sm" onClick={goBack}>Back</button></h4>
</div>
</div>
<div className="app-main-content-list-container form-page">
<div className="app-main-content-list-func-container">
<h6 className="text-primary">{title}</h6>
{existingFiles.length > 0 && (
<>
<div className="field-label mb-2">Existing Records</div>
<table className="table table-sm table-bordered mb-4">
<thead>
<tr>
<th>File Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{existingFiles.map((file, idx) => (
<tr key={idx}>
<td>{file.name}</td>
<td>
<a href={getFileDownloadUrl(file.url)} target="_blank" rel="noopener noreferrer" className="btn btn-link btn-sm p-0">Download</a>
</td>
</tr>
))}
</tbody>
</table>
</>
)}
<div className="field-label mb-2">Add New Record</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Inspection Date</div>
<DatePicker
selected={inspectionDate}
onChange={(date) => setInspectionDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Inspection File</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2" /> Upload
<input type="file" onChange={(e) => setSelectedFile(e.target.files[0])} />
</label>
<div className="file-name">{selectedFile?.name}</div>
</div>
</div>
<div className="list row mb-5 mt-3">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={goBack}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={handleSave}> Save </button>
</div>
</div>
</div>
</div>
</>
);
};
export default AddVehicleInspection;

View File

@@ -0,0 +1,175 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { AuthService, VehicleRepairService, VehicleService } from "../../services";
import { Upload } from "react-bootstrap-icons";
import { Breadcrumb } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
import {
REPAIR_PART_NAME,
REPAIR_PART_NAME_TEXT,
QUANTITY_OPTIONS
} from "../../shared";
const EditRepairRecord = () => {
const navigate = useNavigate();
const params = useParams();
const [currentVehicle, setCurrentVehicle] = useState(null);
const [partName, setPartName] = useState('');
const [replacementDate, setReplacementDate] = useState(null);
const [mileage, setMileage] = useState('');
const [quantity, setQuantity] = useState('');
const [cost, setCost] = useState('');
const [location, setLocation] = useState('');
const [receiptFile, setReceiptFile] = useState(null);
const [nextReminder, setNextReminder] = useState('');
useEffect(() => {
if (!AuthService.canAddOrEditVechiles()) {
window.alert('You haven\'t login yet OR this user does not have access to this page.');
AuthService.logout();
navigate('/login');
}
VehicleService.getVehicle(params.id).then((data) => {
setCurrentVehicle(data.data);
});
VehicleRepairService.getAll(params.id).then(res => {
const repair = (res.data || []).find(r => r.id === params.repairId);
if (repair) {
setPartName(repair.part_name || '');
if (repair.repair_date) {
const parsed = moment(repair.repair_date, 'MM/DD/YYYY');
if (parsed.isValid()) setReplacementDate(parsed.toDate());
}
setMileage(repair.mileage_at_replacement || '');
setQuantity(repair.quantity || '');
setCost(repair.repair_price || '');
setLocation(repair.repair_location || '');
setNextReminder(repair.next_replacement_reminder || '');
}
});
}, []);
const formatDateForBackend = (date) => {
if (!date) return '';
return moment(date).format('MM/DD/YYYY');
};
const handleSave = () => {
if (!partName) {
window.alert('Please select a part name.');
return;
}
const data = {
part_name: partName,
repair_date: formatDateForBackend(replacementDate),
mileage_at_replacement: mileage,
quantity,
repair_price: cost,
repair_location: location,
next_replacement_reminder: nextReminder
};
VehicleRepairService.updateVehicleRepair(params.repairId, data).then(() => {
if (receiptFile) {
const fd = new FormData();
fd.append('file', receiptFile);
VehicleService.uploadVechileFile(fd, params.id, params.repairId, 'repair', replacementDate || new Date())
.then(() => goBack());
} else {
goBack();
}
});
};
const goBack = () => {
navigate(`/vehicles/${params.id}?tab=documents`);
};
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item href="/trans-routes/dashboard">Transportation</Breadcrumb.Item>
<Breadcrumb.Item href="/vehicles/list">Vehicles Information</Breadcrumb.Item>
<Breadcrumb.Item href={`/vehicles/${params.id}?tab=documents`}>View Vehicle Information</Breadcrumb.Item>
<Breadcrumb.Item active>Edit Repair Record</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>Edit Repair Record <button className="btn btn-link btn-sm" onClick={goBack}>Back</button></h4>
</div>
</div>
<div className="app-main-content-list-container form-page">
<div className="app-main-content-list-func-container">
<h6 className="text-primary">Repair & Maintenance Record</h6>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Part Name</div>
<select value={partName} onChange={e => setPartName(e.target.value)}>
<option value="">Select...</option>
{Object.keys(REPAIR_PART_NAME).map(key => (
<option key={key} value={REPAIR_PART_NAME[key]}>{REPAIR_PART_NAME_TEXT[REPAIR_PART_NAME[key]]}</option>
))}
</select>
</div>
<div className="me-4">
<div className="field-label">Replacement Date</div>
<DatePicker
selected={replacementDate}
onChange={(date) => setReplacementDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Mileage at Replacement</div>
<input type="text" placeholder="e.g., 48,000" value={mileage} onChange={e => setMileage(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Quantity</div>
<select value={quantity} onChange={e => setQuantity(e.target.value)}>
<option value="">Select...</option>
{QUANTITY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Cost</div>
<input type="text" placeholder="e.g., $250.00" value={cost} onChange={e => setCost(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Location</div>
<input type="text" placeholder="e.g., Rockville Auto Center" value={location} onChange={e => setLocation(e.target.value)} />
</div>
<div className="me-4">
<div className="field-label">Receipt Upload</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2" /> Upload
<input type="file" onChange={(e) => setReceiptFile(e.target.files[0])} />
</label>
<div className="file-name">{receiptFile?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Next Replacement Reminder</div>
<input type="text" placeholder="e.g., 78,000" value={nextReminder} onChange={e => setNextReminder(e.target.value)} />
</div>
</div>
<div className="list row mb-5 mt-3">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={goBack}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={handleSave}> Save </button>
</div>
</div>
</div>
</div>
</>
);
};
export default EditRepairRecord;

View File

@@ -0,0 +1,121 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { AuthService, VehicleService } from "../../services";
import { Upload } from "react-bootstrap-icons";
import { Breadcrumb } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
const EditVehicleInspection = () => {
const navigate = useNavigate();
const params = useParams();
const [searchParams] = useSearchParams();
const inspectionType = params.type;
const isYearly = inspectionType === 'yearly';
const title = isYearly ? 'Yearly Inspection' : 'Monthly Inspection';
const [currentVehicle, setCurrentVehicle] = useState(null);
const [inspectionDate, setInspectionDate] = useState(null);
const [selectedFile, setSelectedFile] = useState(null);
const [currentFileName, setCurrentFileName] = useState('');
useEffect(() => {
if (!AuthService.canAddOrEditVechiles()) {
window.alert('You haven\'t login yet OR this user does not have access to this page.');
AuthService.logout();
navigate('/login');
}
VehicleService.getVehicle(params.id).then((data) => {
setCurrentVehicle(data.data);
});
const fileName = searchParams.get('fileName');
const dateStr = searchParams.get('date');
if (fileName) setCurrentFileName(decodeURIComponent(fileName));
if (dateStr) {
const parsed = moment(dateStr, 'MM/DD/YYYY');
if (parsed.isValid()) setInspectionDate(parsed.toDate());
}
}, []);
const handleSave = () => {
if (!selectedFile) {
window.alert('Please select a file to upload.');
return;
}
if (!inspectionDate) {
window.alert('Please select an inspection date.');
return;
}
const formData = new FormData();
formData.append('file', selectedFile);
const fileType = isYearly ? 'yearlyInspection' : 'monthlyInspection';
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, fileType, inspectionDate).then(() => {
goBack();
});
};
const goBack = () => {
navigate(`/vehicles/${params.id}?tab=documents`);
};
return (
<>
<div className="list row mb-4">
<Breadcrumb>
<Breadcrumb.Item href="/trans-routes/dashboard">Transportation</Breadcrumb.Item>
<Breadcrumb.Item href="/vehicles/list">Vehicles Information</Breadcrumb.Item>
<Breadcrumb.Item href={`/vehicles/${params.id}?tab=documents`}>View Vehicle Information</Breadcrumb.Item>
<Breadcrumb.Item active>Edit {title}</Breadcrumb.Item>
</Breadcrumb>
<div className="col-md-12 text-primary">
<h4>Edit {title} <button className="btn btn-link btn-sm" onClick={goBack}>Back</button></h4>
</div>
</div>
<div className="app-main-content-list-container form-page">
<div className="app-main-content-list-func-container">
<h6 className="text-primary">{title}</h6>
{currentFileName && (
<div className="app-main-content-fields-section mb-3">
<div className="field-body">
<div className="field-label">Current File</div>
<div className="field-value">{currentFileName}</div>
</div>
</div>
)}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Inspection Date</div>
<DatePicker
selected={inspectionDate}
onChange={(date) => setInspectionDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Upload Replacement File</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2" /> Upload
<input type="file" onChange={(e) => setSelectedFile(e.target.files[0])} />
</label>
<div className="file-name">{selectedFile?.name}</div>
</div>
</div>
<div className="list row mb-5 mt-3">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-default btn-sm float-right" onClick={goBack}> Cancel </button>
<button className="btn btn-primary btn-sm float-right" onClick={handleSave}> Save </button>
</div>
</div>
</div>
</div>
</>
);
};
export default EditVehicleInspection;

View File

@@ -2,8 +2,8 @@ import React, {useEffect, useState} from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { vehicleSlice, selectVehicleError } from "./../../store"; import { vehicleSlice, selectVehicleError } from "./../../store";
import { AuthService, VehicleRepairService, VehicleService, DriverService } from "../../services"; import { AuthService, VehicleService, DriverService } from "../../services";
import { Archive, Upload, Trash } from "react-bootstrap-icons"; import { Archive, Upload } from "react-bootstrap-icons";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Modal, Button } from "react-bootstrap"; import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Modal, Button } from "react-bootstrap";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import Select from 'react-select'; import Select from 'react-select';
@@ -12,9 +12,7 @@ import {
SEATING_CAPACITY_OPTIONS, SEATING_CAPACITY_OPTIONS,
FUEL_TYPE, FUEL_TYPE_TEXT, FUEL_TYPE, FUEL_TYPE_TEXT,
VEHICLE_TITLE, VEHICLE_TITLE_TEXT, VEHICLE_TITLE, VEHICLE_TITLE_TEXT,
LIFT_EQUIPPED, LIFT_EQUIPPED_TEXT, LIFT_EQUIPPED, LIFT_EQUIPPED_TEXT
REPAIR_PART_NAME, REPAIR_PART_NAME_TEXT,
QUANTITY_OPTIONS
} from "../../shared"; } from "../../shared";
const UpdateVehicle = () => { const UpdateVehicle = () => {
@@ -22,7 +20,9 @@ const UpdateVehicle = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const params = useParams(); const params = useParams();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [activeTab, setActiveTab] = useState(searchParams.get('tab') || 'basicInfo'); const validTabs = ['basicInfo', 'complianceDeadlines'];
const tabFromUrl = searchParams.get('tab');
const [activeTab, setActiveTab] = useState(validTabs.includes(tabFromUrl) ? tabFromUrl : 'basicInfo');
const vehicles = useSelector((state) => state.vehicles && state.vehicles.vehicles); const vehicles = useSelector((state) => state.vehicles && state.vehicles.vehicles);
const currentVehicle = vehicles.find(item => item.id === params.id ) || undefined; const currentVehicle = vehicles.find(item => item.id === params.id ) || undefined;
const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions; const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions;
@@ -58,25 +58,6 @@ const UpdateVehicle = () => {
// Drivers list // Drivers list
const [drivers, setDrivers] = useState([]); const [drivers, setDrivers] = useState([]);
// Documents
const [selectedMonthlyFile, setSelectedMonthlyFile] = useState();
const [selectedYearlyFile, setSelectedYearlyFile] = useState();
const [monthlyInspectionDate, setMonthlyInspectionDate] = useState(null);
const [yearlyInspectionDate, setYearlyInspectionDate] = useState(null);
const [existingYearlyFiles, setExistingYearlyFiles] = useState([]);
const [existingMonthlyFiles, setExistingMonthlyFiles] = useState([]);
// Repair & Maintenance Record
const [repairPartName, setRepairPartName] = useState('');
const [repairReplacementDate, setRepairReplacementDate] = useState(null);
const [repairMileage, setRepairMileage] = useState('');
const [repairQuantity, setRepairQuantity] = useState('');
const [repairCost, setRepairCost] = useState('');
const [repairLocation, setRepairLocation] = useState('');
const [repairReceiptFile, setRepairReceiptFile] = useState(null);
const [repairNextReminder, setRepairNextReminder] = useState('');
const [existingRepairs, setExistingRepairs] = useState([]);
// Modal // Modal
const [showDeleteModal, setShowDeleteModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false);
@@ -130,29 +111,9 @@ const UpdateVehicle = () => {
// Additional Information // Additional Information
setNote(currentVehicle.note || ''); setNote(currentVehicle.note || '');
// Fetch existing inspection files
fetchInspectionFiles(currentVehicle);
// Fetch existing repair records
fetchRepairRecords(currentVehicle.id);
} }
}, [currentVehicle]); }, [currentVehicle]);
const fetchInspectionFiles = (vehicle) => {
VehicleService.getAllVechileFiles(vehicle.id, vehicle.vehicle_number, 'yearlyInspection')
.then(res => setExistingYearlyFiles(res.data?.data?.files || []))
.catch(() => setExistingYearlyFiles([]));
VehicleService.getAllVechileFiles(vehicle.id, vehicle.vehicle_number, 'monthlyInspection')
.then(res => setExistingMonthlyFiles(res.data?.data?.files || []))
.catch(() => setExistingMonthlyFiles([]));
};
const fetchRepairRecords = (vehicleId) => {
VehicleRepairService.getAll(vehicleId)
.then(res => setExistingRepairs(res.data || []))
.catch(() => setExistingRepairs([]));
};
const redirectTo = () => { const redirectTo = () => {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const redirect = params.get('redirect'); const redirect = params.get('redirect');
@@ -263,83 +224,6 @@ const UpdateVehicle = () => {
redirectTo(); redirectTo();
} }
const saveYearlyInspection = () => {
if (!selectedYearlyFile || !yearlyInspectionDate) {
window.alert('Please select a date and a file for yearly inspection.');
return;
}
const formData = new FormData();
formData.append('file', selectedYearlyFile);
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, 'yearlyInspection', yearlyInspectionDate).then(() => {
setSelectedYearlyFile(undefined);
setYearlyInspectionDate(null);
fetchInspectionFiles(currentVehicle);
});
}
const saveMonthlyInspection = () => {
if (!selectedMonthlyFile || !monthlyInspectionDate) {
window.alert('Please select a date and a file for monthly inspection.');
return;
}
const formData = new FormData();
formData.append('file', selectedMonthlyFile);
VehicleService.uploadVechileFile(formData, currentVehicle.id, currentVehicle.vehicle_number, 'monthlyInspection', monthlyInspectionDate).then(() => {
setSelectedMonthlyFile(undefined);
setMonthlyInspectionDate(null);
fetchInspectionFiles(currentVehicle);
});
}
const saveRepair = () => {
if (!repairPartName) {
window.alert('Please select a part name.');
return;
}
const data = {
vehicle: currentVehicle?.id,
part_name: repairPartName,
repair_date: formatDateForBackend(repairReplacementDate),
mileage_at_replacement: repairMileage,
quantity: repairQuantity,
repair_price: repairCost,
repair_location: repairLocation,
next_replacement_reminder: repairNextReminder
};
VehicleRepairService.createNewVehicleRepair(data).then(result => {
const record = result.data;
const uploadPromise = repairReceiptFile
? VehicleService.uploadVechileFile(
(() => { const fd = new FormData(); fd.append('file', repairReceiptFile); return fd; })(),
currentVehicle.id, record.id, 'repair', repairReplacementDate || new Date()
)
: Promise.resolve();
uploadPromise.then(() => {
setRepairPartName('');
setRepairReplacementDate(null);
setRepairMileage('');
setRepairQuantity('');
setRepairCost('');
setRepairLocation('');
setRepairReceiptFile(null);
setRepairNextReminder('');
fetchRepairRecords(currentVehicle.id);
});
});
}
const deleteRepairRecord = (repairId) => {
if (window.confirm('Are you sure you want to delete this repair record?')) {
VehicleRepairService.deleteVehicleRepair(repairId).then(() => {
fetchRepairRecords(currentVehicle.id);
});
}
}
const getFileDownloadUrl = (fileUrl) => {
const baseUrl = require('../../http-common').default?.defaults?.baseURL || '';
return baseUrl.replace('/api', '') + fileUrl;
}
// Custom styles for react-select // Custom styles for react-select
const selectStyles = { const selectStyles = {
@@ -565,202 +449,6 @@ const UpdateVehicle = () => {
</div> </div>
</Tab> </Tab>
<Tab eventKey="documents" title="Documents & Records">
{/* ===== Yearly Inspection ===== */}
<h6 className="text-primary">Yearly Inspection</h6>
{existingYearlyFiles.length > 0 && (
<table className="table table-sm table-bordered mb-3">
<thead>
<tr>
<th>File Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{existingYearlyFiles.map((file, idx) => (
<tr key={idx}>
<td>{file.name}</td>
<td>
<a href={getFileDownloadUrl(file.url)} target="_blank" rel="noopener noreferrer" className="btn btn-link btn-sm p-0">Download</a>
</td>
</tr>
))}
</tbody>
</table>
)}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Yearly Inspection Date</div>
<DatePicker
selected={yearlyInspectionDate}
onChange={(date) => setYearlyInspectionDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Yearly Inspection File</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<input type="file" onChange={(e) => setSelectedYearlyFile(e.target.files[0])}/>
</label>
<div className="file-name">{selectedYearlyFile?.name}</div>
</div>
<div className="me-4 d-flex align-items-end">
<button className="btn btn-primary btn-sm" onClick={() => saveYearlyInspection()}>Add Yearly Inspection</button>
</div>
</div>
{/* ===== Monthly Inspection ===== */}
<h6 className="text-primary mt-4">Monthly Inspection</h6>
{existingMonthlyFiles.length > 0 && (
<table className="table table-sm table-bordered mb-3">
<thead>
<tr>
<th>File Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{existingMonthlyFiles.map((file, idx) => (
<tr key={idx}>
<td>{file.name}</td>
<td>
<a href={getFileDownloadUrl(file.url)} target="_blank" rel="noopener noreferrer" className="btn btn-link btn-sm p-0">Download</a>
</td>
</tr>
))}
</tbody>
</table>
)}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Monthly Inspection Date</div>
<DatePicker
selected={monthlyInspectionDate}
onChange={(date) => setMonthlyInspectionDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Monthly Inspection File</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<input type="file" onChange={(e) => setSelectedMonthlyFile(e.target.files[0])}/>
</label>
<div className="file-name">{selectedMonthlyFile?.name}</div>
</div>
<div className="me-4 d-flex align-items-end">
<button className="btn btn-primary btn-sm" onClick={() => saveMonthlyInspection()}>Add Monthly Inspection</button>
</div>
</div>
{/* ===== Repair & Maintenance Record ===== */}
<h6 className="text-primary mt-4">Repair & Maintenance Record</h6>
{existingRepairs.length > 0 && (
<table className="table table-sm table-bordered mb-3">
<thead>
<tr>
<th>Part Name</th>
<th>Date</th>
<th>Mileage</th>
<th>Qty</th>
<th>Cost</th>
<th>Location</th>
<th>Next Reminder</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{existingRepairs.map((repair) => (
<tr key={repair.id}>
<td>{REPAIR_PART_NAME_TEXT[repair.part_name] || repair.repair_description || repair.part_name || '-'}</td>
<td>{repair.repair_date || '-'}</td>
<td>{repair.mileage_at_replacement || '-'}</td>
<td>{repair.quantity || '-'}</td>
<td>{repair.repair_price || '-'}</td>
<td>{repair.repair_location || '-'}</td>
<td>{repair.next_replacement_reminder || '-'}</td>
<td>
<button className="btn btn-link btn-sm p-0 text-danger" onClick={() => deleteRepairRecord(repair.id)}>
<Trash size={14} />
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Part Name</div>
<select value={repairPartName} onChange={e => setRepairPartName(e.target.value)}>
<option value="">Select...</option>
{Object.keys(REPAIR_PART_NAME).map(key => (
<option key={key} value={REPAIR_PART_NAME[key]}>{REPAIR_PART_NAME_TEXT[REPAIR_PART_NAME[key]]}</option>
))}
</select>
</div>
<div className="me-4">
<div className="field-label">Replacement Date</div>
<DatePicker
selected={repairReplacementDate}
onChange={(date) => setRepairReplacementDate(date)}
dateFormat="MM/dd/yyyy"
placeholderText="e.g., 03/01/2024"
className="form-control"
/>
</div>
<div className="me-4">
<div className="field-label">Mileage at Replacement</div>
<input type="text" placeholder="e.g., 48,000" value={repairMileage} onChange={e => setRepairMileage(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Quantity</div>
<select value={repairQuantity} onChange={e => setRepairQuantity(e.target.value)}>
<option value="">Select...</option>
{QUANTITY_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
</div>
<div className="app-main-content-fields-section">
<div className="me-4">
<div className="field-label">Cost</div>
<input type="text" placeholder="e.g., $250.00" value={repairCost} onChange={e => setRepairCost(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Location</div>
<input type="text" placeholder="e.g., Rockville Auto Center" value={repairLocation} onChange={e => setRepairLocation(e.target.value)}/>
</div>
<div className="me-4">
<div className="field-label">Receipt Upload</div>
<label className="custom-file-upload">
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload
<input type="file" onChange={(e) => setRepairReceiptFile(e.target.files[0])}/>
</label>
<div className="file-name">{repairReceiptFile?.name}</div>
</div>
<div className="me-4">
<div className="field-label">Next Replacement Reminder</div>
<input type="text" placeholder="e.g., 78,000" value={repairNextReminder} onChange={e => setRepairNextReminder(e.target.value)}/>
</div>
</div>
{error && <div className="col-md-12 mb-4 alert alert-danger" role="alert">
{error}
</div>}
<div className="list row mb-5">
<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={() => saveRepair()}> Add Repair Record </button>
</div>
</div>
</Tab>
</Tabs> </Tabs>
<div className="list-func-panel"> <div className="list-func-panel">
<button className="btn btn-primary" onClick={() => triggerShowDeleteModal()}><Archive size={16} className="me-2"></Archive>Archive</button> <button className="btn btn-primary" onClick={() => triggerShowDeleteModal()}><Archive size={16} className="me-2"></Archive>Archive</button>

View File

@@ -311,7 +311,7 @@ const ViewVehicle = () => {
<td className="td-checkbox"><input type="checkbox" checked={selectedItemsMonthly.includes(doc?.url)} onClick={() => toggleItemMonthly(doc?.url)}/></td> <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 className="td-index">{index + 1}</td>
<td> <td>
<PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(currentVehicle?.id)}></PencilSquare> <PencilSquare size={14} className="clickable me-2" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/inspections/monthly/edit?fileName=${encodeURIComponent(doc?.name)}&date=${encodeURIComponent(doc?.inspectionDate || '')}`)} />
<a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a> <a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a>
</td> </td>
<td>{doc?.inspectionDate}</td> <td>{doc?.inspectionDate}</td>
@@ -345,7 +345,7 @@ const ViewVehicle = () => {
<td className="td-checkbox"><input type="checkbox" checked={selectedItemsYearly.includes(doc?.url)} onClick={() => toggleItemYearly(doc?.url)}/></td> <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 className="td-index">{index + 1}</td>
<td> <td>
<PencilSquare size={16} className="clickable me-2" onClick={() => goToEdit(currentVehicle?.id)}></PencilSquare> <PencilSquare size={14} className="clickable me-2" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/inspections/yearly/edit?fileName=${encodeURIComponent(doc?.name)}&date=${encodeURIComponent(doc?.inspectionDate || '')}`)} />
<a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a> <a className="btn btn-link btn-sm" href={doc?.url} target="_blank">{doc?.name}</a>
</td> </td>
<td>{doc?.inspectionDate}</td> <td>{doc?.inspectionDate}</td>
@@ -378,7 +378,10 @@ const ViewVehicle = () => {
<tr key={repair.id}> <tr key={repair.id}>
<td className="td-checkbox"><input type="checkbox" checked={selectedItemsRepair.includes(repair?.id)} onClick={() => toggleItemRepair(repair?.id)}/></td> <td className="td-checkbox"><input type="checkbox" checked={selectedItemsRepair.includes(repair?.id)} onClick={() => toggleItemRepair(repair?.id)}/></td>
<td className="td-index">{index + 1}</td> <td className="td-index">{index + 1}</td>
<td>{REPAIR_PART_NAME_TEXT[repair?.part_name] || repair?.part_name || repair?.repair_description}</td> <td>
<PencilSquare size={14} className="clickable me-2" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/repairs/edit/${repair?.id}`)} />
{REPAIR_PART_NAME_TEXT[repair?.part_name] || repair?.part_name || repair?.repair_description}
</td>
<td>{repair?.repair_date}</td> <td>{repair?.repair_date}</td>
<td>{repair?.mileage_at_replacement}</td> <td>{repair?.mileage_at_replacement}</td>
<td>{repair?.quantity}</td> <td>{repair?.quantity}</td>
@@ -514,31 +517,37 @@ const ViewVehicle = () => {
</Tab> </Tab>
<Tab eventKey="documents" title="Documents & Records"> <Tab eventKey="documents" title="Documents & Records">
<h6 className="text-primary">Yearly Inspection</h6> <div className="d-flex justify-content-between align-items-center mb-3">
<h6 className="text-primary mb-0">Yearly Inspection</h6>
<button className="btn btn-primary btn-sm" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/inspections/yearly/add`)}>+ Add Yearly Inspection</button>
</div>
{tableYearly} {tableYearly}
<h6 className="text-primary">Monthly Inspection</h6> <div className="d-flex justify-content-between align-items-center mb-3 mt-4">
<h6 className="text-primary mb-0">Monthly Inspection</h6>
<button className="btn btn-primary btn-sm" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/inspections/monthly/add`)}>+ Add Monthly Inspection</button>
</div>
{tableMonthly} {tableMonthly}
</Tab> <div className="d-flex justify-content-between align-items-center mb-3 mt-4">
<h6 className="text-primary mb-0">Repair & Maintenance Record</h6>
<Tab eventKey="repairRecords" title="Repair & Maintenance"> <button className="btn btn-primary btn-sm" onClick={() => navigate(`/vehicles/${currentVehicle?.id}/repairs/add`)}>+ Add Repair Record</button>
</div>
{tableRepair} {tableRepair}
</Tab> </Tab>
</Tabs> </Tabs>
<div className="list-func-panel"> <div className="list-func-panel">
{(currentTab === 'documents' || currentTab === 'repairRecords') && (
<input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
)}
{currentTab === 'documents' && ( {currentTab === 'documents' && (
<button className="btn btn-primary" onClick={() => download()}><Download size={16} className="me-2"></Download>Download</button> <>
<input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
<Export
columns={columnsRepair}
data={filteredRepairs}
filename={`vehicle-${currentVehicle?.vehicle_number}-repairs`}
/>
</>
)} )}
{currentTab === 'repairRecords' && ( {currentTab !== 'documents' && (
<Export <button className="btn btn-primary ms-2" onClick={() => goToEdit(currentVehicle?.id)}><PencilSquare className="me-2" size={16}></PencilSquare>Edit</button>
columns={columnsRepair}
data={filteredRepairs}
filename={`vehicle-${currentVehicle?.vehicle_number}-repairs`}
/>
)} )}
<button className="btn btn-primary ms-2" onClick={() => goToEdit(currentVehicle?.id)}><PencilSquare className="me-2" size={16}></PencilSquare>Edit</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -5,6 +5,9 @@ const getAll = (vehicle) => {
const createNewVehicleRepair = (data) => { const createNewVehicleRepair = (data) => {
return http.post('/vehicle-repairs', data); return http.post('/vehicle-repairs', data);
}; };
const updateVehicleRepair = (id, data) => {
return http.put(`/vehicle-repairs/${id}`, data);
};
const deleteVehicleRepair = (id) => { const deleteVehicleRepair = (id) => {
return http.delete(`/vehicle-repairs/${id}`); return http.delete(`/vehicle-repairs/${id}`);
}; };
@@ -12,5 +15,6 @@ const deleteVehicleRepair = (id) => {
export const VehicleRepairService = { export const VehicleRepairService = {
getAll, getAll,
createNewVehicleRepair, createNewVehicleRepair,
updateVehicleRepair,
deleteVehicleRepair deleteVehicleRepair
}; };