This commit is contained in:
2026-02-23 15:01:58 -05:00
parent 6062e191f1
commit 116c3e8259
7 changed files with 204 additions and 34 deletions

BIN
.DS_Store vendored

Binary file not shown.

BIN
app/.DS_Store vendored

Binary file not shown.

View File

@@ -3,8 +3,8 @@ const db = require("../models");
const VehicleRepair = db.vehicle_repair;
exports.createVehicleRepair = (req, res) => {
if (!req.body.repair_description) {
res.status(400).send({ message: "Content can not be empty!" });
if (!req.body.part_name && !req.body.repair_description) {
res.status(400).send({ message: "Part name or description is required." });
return;
}
@@ -13,11 +13,15 @@ exports.createVehicleRepair = (req, res) => {
const vehicleRepair = new VehicleRepair({
repair_date: req.body.repair_date,
site,
repair_description: req.body.repair_description || '',
repair_description: req.body.repair_description || req.body.part_name || '',
part_name: req.body.part_name || '',
mileage_at_replacement: req.body.mileage_at_replacement || '',
quantity: req.body.quantity || '',
repair_price: req.body.repair_price || '',
repair_location: req.body.repair_location || '',
next_replacement_reminder: req.body.next_replacement_reminder || '',
vehicle: req.body.vehicle,
create_date: new Date()
create_date: new Date()
});
vehicleRepair.save(vehicleRepair).then(data => res.send(data)).catch(err => {
@@ -32,7 +36,7 @@ exports.getAllVehicleRepairs = (req, res) => {
const vehicle = req.query.vehicle;
condition = splitSite.splitSiteGet(req, condition);
condition.vehicle = vehicle;
VehicleRepair.find(condition)
VehicleRepair.find(condition).sort({ create_date: -1 })
.then(data => {
res.send(data);
})
@@ -42,4 +46,21 @@ exports.getAllVehicleRepairs = (req, res) => {
err.message || "Some error occurred while retrieving Vehicle Repair Records."
});
});
}
exports.deleteVehicleRepair = (req, res) => {
const id = req.params.id;
VehicleRepair.findByIdAndDelete(id)
.then(data => {
if (!data) {
res.status(404).send({ message: `Cannot delete Vehicle Repair Record with id=${id}.` });
} else {
res.send({ message: "Vehicle Repair Record was deleted successfully!" });
}
})
.catch(err => {
res.status(500).send({
message: err.message || "Could not delete Vehicle Repair Record."
});
});
};

View File

@@ -4,10 +4,14 @@ module.exports = mongoose => {
repair_date: String,
site: Number,
repair_description: String,
part_name: String,
mileage_at_replacement: String,
quantity: String,
repair_price: String,
repair_location: String,
next_replacement_reminder: String,
vehicle: String,
create_date: Date
create_date: Date
},
{ collection: 'vehicle_repair', timestamps: true }
);

View File

@@ -9,9 +9,8 @@ module.exports = app => {
next();
});
var router = require("express").Router();
// Retrieve all vehicle records
router.get("/", [authJwt.verifyToken], vehicleRepairs.getAllVehicleRepairs);
// Create a new vehicle record
router.post("/", [authJwt.verifyToken], vehicleRepairs.createVehicleRepair);
router.delete("/:id", [authJwt.verifyToken], vehicleRepairs.deleteVehicleRepair);
app.use('/api/vehicle-repairs', router);
};

View File

@@ -3,7 +3,7 @@ import { useSelector, useDispatch } from "react-redux";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { vehicleSlice, selectVehicleError } from "./../../store";
import { AuthService, VehicleRepairService, VehicleService, DriverService } from "../../services";
import { Archive, Upload } from "react-bootstrap-icons";
import { Archive, Upload, Trash } from "react-bootstrap-icons";
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab, Modal, Button } from "react-bootstrap";
import DatePicker from "react-datepicker";
import Select from 'react-select';
@@ -63,6 +63,8 @@ const UpdateVehicle = () => {
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('');
@@ -73,6 +75,7 @@ const UpdateVehicle = () => {
const [repairLocation, setRepairLocation] = useState('');
const [repairReceiptFile, setRepairReceiptFile] = useState(null);
const [repairNextReminder, setRepairNextReminder] = useState('');
const [existingRepairs, setExistingRepairs] = useState([]);
// Modal
const [showDeleteModal, setShowDeleteModal] = useState(false);
@@ -127,9 +130,29 @@ const UpdateVehicle = () => {
// Additional Information
setNote(currentVehicle.note || '');
// Fetch existing inspection files
fetchInspectionFiles(currentVehicle);
// Fetch existing repair records
fetchRepairRecords(currentVehicle.id);
}
}, [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 params = new URLSearchParams(window.location.search);
const redirect = params.get('redirect');
@@ -240,21 +263,39 @@ const UpdateVehicle = () => {
redirectTo();
}
const saveDocuments = () => {
if (selectedMonthlyFile && monthlyInspectionDate) {
const monthlyFormData = new FormData();
monthlyFormData.append('file', selectedMonthlyFile);
VehicleService.uploadVechileFile(monthlyFormData, currentVehicle.id, currentVehicle.vehicle_number, 'monthlyInspection', monthlyInspectionDate);
const saveYearlyInspection = () => {
if (!selectedYearlyFile || !yearlyInspectionDate) {
window.alert('Please select a date and a file for yearly inspection.');
return;
}
if (selectedYearlyFile && yearlyInspectionDate) {
const yearlyFormData = new FormData();
yearlyFormData.append('file', selectedYearlyFile);
VehicleService.uploadVechileFile(yearlyFormData, currentVehicle.id, currentVehicle.vehicle_number, 'yearlyInspection', yearlyInspectionDate);
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;
}
redirectTo();
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,
@@ -267,16 +308,39 @@ const UpdateVehicle = () => {
};
VehicleRepairService.createNewVehicleRepair(data).then(result => {
const record = result.data;
if (repairReceiptFile) {
const formData = new FormData();
formData.append('file', repairReceiptFile);
VehicleService.uploadVechileFile(formData, currentVehicle.id, record.id, 'repair', repairReplacementDate).then(() => redirectTo());
} else {
redirectTo();
}
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
const selectStyles = {
control: (baseStyles) => ({
@@ -502,7 +566,28 @@ const UpdateVehicle = () => {
</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>
@@ -522,9 +607,33 @@ const UpdateVehicle = () => {
</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>
<h6 className="text-primary">Monthly Inspection</h6>
{/* ===== 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>
@@ -544,14 +653,47 @@ const UpdateVehicle = () => {
</label>
<div className="file-name">{selectedMonthlyFile?.name}</div>
</div>
</div>
<div className="list row mb-3">
<div className="col-md-12 col-sm-12 col-xs-12">
<button className="btn btn-primary btn-sm" onClick={() => saveDocuments()}> Upload Documents </button>
<div className="me-4 d-flex align-items-end">
<button className="btn btn-primary btn-sm" onClick={() => saveMonthlyInspection()}>Add Monthly Inspection</button>
</div>
</div>
<h6 className="text-primary">Repair & Maintenance Record</h6>
{/* ===== 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>
@@ -615,7 +757,7 @@ const UpdateVehicle = () => {
<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()}> Save Repair Record </button>
<button className="btn btn-primary btn-sm float-right" onClick={() => saveRepair()}> Add Repair Record </button>
</div>
</div>
</Tab>

View File

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