Seating Page
This commit is contained in:
parent
f18f213f6b
commit
4237cc4a7c
99
app/controllers/label.controller.js
Normal file
99
app/controllers/label.controller.js
Normal file
@ -0,0 +1,99 @@
|
||||
const { splitSite } = require("../middlewares");
|
||||
const db = require("../models");
|
||||
const Label = db.label;
|
||||
|
||||
// Create a new Label Item
|
||||
exports.createNewLabel = (req, res) => {
|
||||
// Validate request
|
||||
if (!req.body.label_name) {
|
||||
res.status(400).send({ message: "Name can not be empty!" });
|
||||
return;
|
||||
}
|
||||
const site = splitSite.findSiteNumber(req);
|
||||
// Create an Label Item
|
||||
const label = new Label({
|
||||
label_name: req.body.label_name,
|
||||
label_color: req.body.label_color,
|
||||
status: 'active',
|
||||
site
|
||||
});
|
||||
// Save label Item in the database
|
||||
label
|
||||
.save(label)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Label Record."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Retrive all Label Records from database.
|
||||
exports.getAllLabels = (req, res) => {
|
||||
var params = req.query;
|
||||
var condition = {};
|
||||
if (params.status) {
|
||||
condition.status = params.status;
|
||||
}
|
||||
|
||||
condition = splitSite.splitSiteGet(req, condition);
|
||||
Label.find(condition)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving Labels."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Update a Label by the id in the request
|
||||
exports.updateLabel = (req, res) => {
|
||||
if (!req.body) {
|
||||
return res.status(400).send({
|
||||
message: "Data to update can not be empty!"
|
||||
});
|
||||
}
|
||||
const id = req.params.id;
|
||||
Label.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot update Label with id=${id}. Maybe Label was not found!`
|
||||
});
|
||||
} else res.send({ success: true, message: "Label was updated successfully." });
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Error updating Label with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Delete a Label by id
|
||||
exports.deleteLabel= (req, res) => {
|
||||
const id = req.params.id;
|
||||
Label.findByIdAndRemove(id)
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot delete Label with id=${id}. Maybe Label was not found!`
|
||||
});
|
||||
} else {
|
||||
res.send({
|
||||
message: "Label was deleted successfully!"
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Could not delete Label with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
102
app/controllers/seating.controller.js
Normal file
102
app/controllers/seating.controller.js
Normal file
@ -0,0 +1,102 @@
|
||||
const { splitSite } = require("../middlewares");
|
||||
const db = require("../models");
|
||||
const Seating = db.seating;
|
||||
|
||||
// Create a new Seating Item
|
||||
exports.createNewSeating = (req, res) => {
|
||||
// Validate request
|
||||
if (!req.body.seating_assignment) {
|
||||
res.status(400).send({ message: "Assignment can not be empty!" });
|
||||
return;
|
||||
}
|
||||
const site = splitSite.findSiteNumber(req);
|
||||
// Create an Seating Item
|
||||
const seating = new Seating({
|
||||
seating_assignment: req.body.seating_assignment,
|
||||
date: req.body.date,
|
||||
create_by: req.body.create_by,
|
||||
create_date: req.body.create_date,
|
||||
update_by: req.body.update_by,
|
||||
update_date: req.body.update_date,
|
||||
site
|
||||
});
|
||||
// Save seating Item in the database
|
||||
seating
|
||||
.save(seating)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while creating the Seating Record."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Retrive all Seating Records from database.
|
||||
exports.getAllSeatings = (req, res) => {
|
||||
var params = req.query;
|
||||
var condition = {};
|
||||
if (params.date) {
|
||||
condition.date = params.date;
|
||||
}
|
||||
|
||||
condition = splitSite.splitSiteGet(req, condition);
|
||||
Seating.find(condition)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving Seatings."
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Update a Seating by the id in the request
|
||||
exports.updateSeating = (req, res) => {
|
||||
if (!req.body) {
|
||||
return res.status(400).send({
|
||||
message: "Data to update can not be empty!"
|
||||
});
|
||||
}
|
||||
const id = req.params.id;
|
||||
Seating.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot update Seating with id=${id}. Maybe Seating was not found!`
|
||||
});
|
||||
} else res.send({ success: true, message: "Seating was updated successfully." });
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Error updating Seating with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Delete a Seating by id
|
||||
exports.deleteSeating= (req, res) => {
|
||||
const id = req.params.id;
|
||||
Seating.findByIdAndRemove(id)
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
res.status(404).send({
|
||||
message: `Cannot delete Seating with id=${id}. Maybe Seating was not found!`
|
||||
});
|
||||
} else {
|
||||
res.send({
|
||||
message: "Seating was deleted successfully!"
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message: "Could not delete Seating with id=" + id
|
||||
});
|
||||
});
|
||||
};
|
||||
45
app/controllers/vehicle-repair.controller.js
Normal file
45
app/controllers/vehicle-repair.controller.js
Normal file
@ -0,0 +1,45 @@
|
||||
const { splitSite } = require("../middlewares");
|
||||
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!" });
|
||||
return;
|
||||
}
|
||||
|
||||
const site = splitSite.findSiteNumber(req);
|
||||
|
||||
const vehicleRepair = new VehicleRepair({
|
||||
repair_date: req.body.repair_date,
|
||||
site,
|
||||
repair_description: req.body.repair_description || '',
|
||||
repair_price: req.body.repair_price || '',
|
||||
repair_location: req.body.repair_location || '',
|
||||
vehicle: req.body.vehicle,
|
||||
create_date: new Date()
|
||||
});
|
||||
|
||||
vehicleRepair.save(vehicleRepair).then(data => res.send(data)).catch(err => {
|
||||
res.status(500).send({
|
||||
message: err.message || "Some error occurred while creating the Vehicle Repair Record."
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
exports.getAllVehicleRepairs = (req, res) => {
|
||||
var condition = {};
|
||||
const vehicle = req.query.vehicle;
|
||||
condition = splitSite.splitSiteGet(req, condition);
|
||||
condition.vehicle = vehicle;
|
||||
VehicleRepair.find(condition)
|
||||
.then(data => {
|
||||
res.send(data);
|
||||
})
|
||||
.catch(err => {
|
||||
res.status(500).send({
|
||||
message:
|
||||
err.message || "Some error occurred while retrieving Vehicle Repair Records."
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -27,4 +27,7 @@ db.event_request = require("./event-request.model")(mongoose);
|
||||
db.signature_request = require("./signature-request.model")(mongoose);
|
||||
db.lunch = require("./lunch.model")(mongoose);
|
||||
db.snack = require("./snack.model")(mongoose);
|
||||
db.vehicle_repair = require("./vehicle-repair.model")(mongoose);
|
||||
db.label = require("./label.model")(mongoose);
|
||||
db.seating = require("./seating.model")(mongoose);
|
||||
module.exports = db;
|
||||
18
app/models/label.model.js
Normal file
18
app/models/label.model.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = mongoose => {
|
||||
var schema = mongoose.Schema(
|
||||
{
|
||||
label_name: String,
|
||||
label_color: String,
|
||||
site: Number,
|
||||
status: String
|
||||
},
|
||||
{ collection: 'label', timestamps: true }
|
||||
);
|
||||
schema.method("toJSON", function() {
|
||||
const { __v, _id, ...object } = this.toObject();
|
||||
object.id = _id;
|
||||
return object;
|
||||
});
|
||||
const Label = mongoose.model("label", schema);
|
||||
return Label;
|
||||
};
|
||||
21
app/models/seating.model.js
Normal file
21
app/models/seating.model.js
Normal file
@ -0,0 +1,21 @@
|
||||
module.exports = mongoose => {
|
||||
var schema = mongoose.Schema(
|
||||
{
|
||||
date: String,
|
||||
seating_assignment: Object,
|
||||
create_by: String,
|
||||
create_date: Date,
|
||||
update_by: String,
|
||||
update_date: Date,
|
||||
site: Number
|
||||
},
|
||||
{ collection: 'seating', timestamps: true }
|
||||
);
|
||||
schema.method("toJSON", function() {
|
||||
const { __v, _id, ...object } = this.toObject();
|
||||
object.id = _id;
|
||||
return object;
|
||||
});
|
||||
const Seating = mongoose.model("seating", schema);
|
||||
return Seating;
|
||||
};
|
||||
22
app/models/vehicle-repair.model.js
Normal file
22
app/models/vehicle-repair.model.js
Normal file
@ -0,0 +1,22 @@
|
||||
module.exports = mongoose => {
|
||||
var schema = mongoose.Schema(
|
||||
{
|
||||
repair_date: String,
|
||||
site: Number,
|
||||
repair_description: String,
|
||||
repair_price: String,
|
||||
repair_location: String,
|
||||
vehicle: String,
|
||||
create_date: Date
|
||||
},
|
||||
{ collection: 'vehicle_repair', timestamps: true }
|
||||
);
|
||||
schema.method("toJSON", function() {
|
||||
const { __v, _id, ...object } = this.toObject();
|
||||
object.id = _id;
|
||||
return object;
|
||||
});
|
||||
schema.index({tag: 1, site:1}, {unique: true});
|
||||
const VehicleRepair = mongoose.model("vehicle_repair", schema);
|
||||
return VehicleRepair;
|
||||
};
|
||||
19
app/routes/label.routes.js
Normal file
19
app/routes/label.routes.js
Normal file
@ -0,0 +1,19 @@
|
||||
const {authJwt} = require("../middlewares");
|
||||
module.exports = app => {
|
||||
const label = require("../controllers/label.controller.js");
|
||||
app.use((req, res, next) => {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
var router = require("express").Router();
|
||||
// Retrieve all labels
|
||||
router.get("/", [authJwt.verifyToken], label.getAllLabels);
|
||||
// Create a new label
|
||||
router.post("/", [authJwt.verifyToken], label.createNewLabel);
|
||||
router.put('/:id', [authJwt.verifyToken], label.updateLabel);
|
||||
router.delete('/:id', [authJwt.verifyToken], label.deleteLabel)
|
||||
app.use('/api/labels', router);
|
||||
};
|
||||
19
app/routes/seating.routes.js
Normal file
19
app/routes/seating.routes.js
Normal file
@ -0,0 +1,19 @@
|
||||
const {authJwt} = require("../middlewares");
|
||||
module.exports = app => {
|
||||
const seating = require("../controllers/seating.controller.js");
|
||||
app.use((req, res, next) => {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
next();
|
||||
});
|
||||
var router = require("express").Router();
|
||||
// Retrieve all seatings
|
||||
router.get("/", [authJwt.verifyToken],seating.getAllSeatings);
|
||||
// Create a new seating
|
||||
router.post("/", [authJwt.verifyToken], seating.createNewSeating);
|
||||
router.put('/:id', [authJwt.verifyToken], seating.updateSeating);
|
||||
router.delete('/:id', [authJwt.verifyToken], seating.deleteSeating)
|
||||
app.use('/api/seatings', router);
|
||||
};
|
||||
17
app/routes/vehicle-repair.route.js
Normal file
17
app/routes/vehicle-repair.route.js
Normal file
@ -0,0 +1,17 @@
|
||||
const {authJwt} = require("../middlewares");
|
||||
module.exports = app => {
|
||||
const vehicleRepairs = require("../controllers/vehicle-repair.controller.js");
|
||||
app.use((req, res, next) => {
|
||||
res.header(
|
||||
"Access-Control-Allow-Headers",
|
||||
"x-access-token, Origin, Content-Type, Accept"
|
||||
);
|
||||
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);
|
||||
app.use('/api/vehicle-repairs', router);
|
||||
};
|
||||
@ -1,16 +1,16 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.e5412702.css",
|
||||
"main.js": "/static/js/main.26b7d753.js",
|
||||
"main.css": "/static/css/main.11c89bb3.css",
|
||||
"main.js": "/static/js/main.1ecf349a.js",
|
||||
"static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js",
|
||||
"static/media/landing.png": "/static/media/landing.d4c6072db7a67dff6a78.png",
|
||||
"index.html": "/index.html",
|
||||
"main.e5412702.css.map": "/static/css/main.e5412702.css.map",
|
||||
"main.26b7d753.js.map": "/static/js/main.26b7d753.js.map",
|
||||
"main.11c89bb3.css.map": "/static/css/main.11c89bb3.css.map",
|
||||
"main.1ecf349a.js.map": "/static/js/main.1ecf349a.js.map",
|
||||
"787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.e5412702.css",
|
||||
"static/js/main.26b7d753.js"
|
||||
"static/css/main.11c89bb3.css",
|
||||
"static/js/main.1ecf349a.js"
|
||||
]
|
||||
}
|
||||
@ -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.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>
|
||||
<!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.1ecf349a.js"></script><link href="/static/css/main.11c89bb3.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
1
app/views/static/css/main.11c89bb3.css.map
Normal file
1
app/views/static/css/main.11c89bb3.css.map
Normal file
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
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
@ -22,6 +22,14 @@ body {
|
||||
color: #0066B1;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
background: white;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
align-items: baseline;
|
||||
}
|
||||
@ -511,6 +519,11 @@ table .group td {
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.app-main-content-fields-section.dropdown-container input[type=number] {
|
||||
width: 210px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.app-main-content-fields-section.dropdown-container select {
|
||||
width: 210px;
|
||||
height: 35px;
|
||||
@ -1261,6 +1274,175 @@ input[type="checkbox"] {
|
||||
left:0;
|
||||
}
|
||||
|
||||
.circular-table-container {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.table-circle {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
padding-left: 40px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.table-number {
|
||||
color: #0066B1;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.label-delete-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 13px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.btn-custom-label {
|
||||
min-width: 225px;
|
||||
}
|
||||
|
||||
.seat-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.seat-circle {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: #ccc;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.guest-name {
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.seating-chart-container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
.seating-stage {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stage {
|
||||
color: #0066B1;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
background: #eee;
|
||||
padding: 12px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.seating-row-container {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.manage-seating-chart-container {
|
||||
background: #D0E1F8;
|
||||
/* right: -250px; */
|
||||
/* top: 0; */
|
||||
height: 100vh;
|
||||
min-width: 300px;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
margin-top: -40px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.manage-seating-chart-title-container {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.manage-seating-chart-tables-container {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.manage-seating-chart-tables-container .title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.seating-page-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.table-config-container {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.table-config-item-title {
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table-config-item {
|
||||
padding: 4px 8px 4px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-config-item-title:focus, .table-config-item-title:hover, .table-config-item:focus, .table-config-item:hover {
|
||||
background: rgb(210, 231, 255);
|
||||
border-inline-start: 4px solid rgb(28, 125, 249) !important;
|
||||
}
|
||||
|
||||
.seating-popover {
|
||||
padding:20px;
|
||||
position: absolute;
|
||||
background: white;
|
||||
top: 32px;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1200px) {
|
||||
/* .container {
|
||||
max-width: 1200px;
|
||||
|
||||
@ -66,6 +66,8 @@ import RouteReportWithSignature from './components/trans-routes/RouteReportWithS
|
||||
import Layout from "./components/home/layout";
|
||||
import Home from "./components/home/home";
|
||||
|
||||
import Seating from "./components/seating/Seating";
|
||||
|
||||
|
||||
function App() {
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
@ -146,6 +148,8 @@ function App() {
|
||||
<Route path="" element={<Navigate replace to="customer-report" />} />
|
||||
<Route path="customer-report" element={<CustomerReport/>} />
|
||||
</Route>
|
||||
|
||||
<Route path="/seating" element={<Seating />} />
|
||||
|
||||
<Route path="/medical" element={<Medical />}>
|
||||
<Route path="" element={<Navigate replace to="index" />} />
|
||||
|
||||
@ -183,7 +183,7 @@ const SideMenu = () => {
|
||||
},
|
||||
{
|
||||
name: 'Seating Chart',
|
||||
link: '#',
|
||||
link: '/seating',
|
||||
roleFunc: () => true
|
||||
}
|
||||
]
|
||||
|
||||
45
client/src/components/seating/CircularTable.js
Normal file
45
client/src/components/seating/CircularTable.js
Normal file
@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
|
||||
const CircularTable = ({tableNumber, guests = []}) => {
|
||||
const getPositions = () => {
|
||||
const positions = [];
|
||||
const seatCount = 8;
|
||||
const radius = 60;
|
||||
|
||||
for (let i=0; i< seatCount; i++) {
|
||||
const angle = ((i*360) / seatCount - 90) * (Math.PI / 180);
|
||||
|
||||
const x = radius * Math.cos(angle);
|
||||
const y = radius * Math.sin(angle);
|
||||
|
||||
positions.push({x, y, angle})
|
||||
|
||||
}
|
||||
return positions;
|
||||
}
|
||||
|
||||
const seatPositions = getPositions();
|
||||
// const defaultNames = ['Guest 1', 'Guest 2', 'Guest 3', 'Guest 4', 'Guest 5', 'Guest 6', 'Guest 7', 'Guest 8' ];
|
||||
const defaultSeatNumber = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
||||
|
||||
// This is just for test purpose
|
||||
// const guestNames = [...guests];
|
||||
// while (guestNames.length < 8) (guestNames.push(defaultNames[guestNames.length]));
|
||||
|
||||
return (
|
||||
<div className="circular-table-container">
|
||||
<div className="table-circle">
|
||||
<div className="table-number">{tableNumber}</div>
|
||||
|
||||
{seatPositions.map((pos, index) => {
|
||||
return (<div className="seat-container" key={index} style={{transform: `translate(${pos.x}px, ${pos.y}px)`}}>
|
||||
<div className="seat-circle" style={guests[index]?.label?.label_color && { background: guests[index]?.label?.label_color}}>{defaultSeatNumber[index]}</div>
|
||||
<div className="guest-name">{guests[index]?.customerName}</div>
|
||||
</div>)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CircularTable;
|
||||
690
client/src/components/seating/Seating.js
Normal file
690
client/src/components/seating/Seating.js
Normal file
@ -0,0 +1,690 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import CircularTable from './CircularTable';
|
||||
import { Breadcrumb, Tabs, Tab, Dropdown } from "react-bootstrap";
|
||||
import { Columns, Download, Filter, PencilSquare, ArrowBarRight, ChevronDown, ChevronRight, FileX } from "react-bootstrap-icons";
|
||||
import { CustomerService, LabelService, SeatingService } from '../../services';
|
||||
import moment from 'moment';
|
||||
import Select from 'react-select';
|
||||
|
||||
|
||||
const Seating = () => {
|
||||
const initialValue = {
|
||||
rows: [{
|
||||
id: 1,
|
||||
tables: 1
|
||||
}],
|
||||
tables: {
|
||||
'table-1': {
|
||||
id: 1,
|
||||
tableId: 'table-1',
|
||||
seats: [
|
||||
{
|
||||
id: '1-A',
|
||||
name: 'A',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-B',
|
||||
name: 'B',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-C',
|
||||
name: 'C',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-D',
|
||||
name: 'D',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-E',
|
||||
name: 'E',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-F',
|
||||
name: 'F',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-G',
|
||||
name: 'G',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
},
|
||||
{
|
||||
id: '1-H',
|
||||
name: 'H',
|
||||
customerId: '',
|
||||
customerName: '',
|
||||
label: {},
|
||||
rowIndex: 1
|
||||
}
|
||||
],
|
||||
expanded: false,
|
||||
rowIndex: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
const [tableLayout, setTableLayout] = useState({
|
||||
rows: [],
|
||||
tables: {}
|
||||
}
|
||||
// rows: [
|
||||
// {
|
||||
// itemsLength: 5,
|
||||
// id: 'row-1'
|
||||
// }, {
|
||||
// itemsLength: 5,
|
||||
// id: 'row-2'
|
||||
// }, {
|
||||
// itemsLength: 5,
|
||||
// id: 'row-3'
|
||||
// }
|
||||
// ],
|
||||
// tables:
|
||||
);
|
||||
|
||||
const [rowCount, setRowCount] = useState(1);
|
||||
|
||||
const [currentDateRecord, setCurrentDateRecord] = useState(null);
|
||||
|
||||
const [tableCountsPerRow, setTableCountsPerRow] = useState([1]);
|
||||
|
||||
const [editingSeat, setEditingSeat] = useState(null);
|
||||
|
||||
const [showTableLayoutConfig, setShowTableLayoutConfig] = useState(false);
|
||||
|
||||
const [currentLabels, setCurrentLabels] = useState([]);
|
||||
|
||||
const [newLabel, setNewLabel] = useState({ label_name: undefined, label_color: undefined});
|
||||
|
||||
const [customers, setCustomers] = useState([]);
|
||||
|
||||
const [showLabelConfig, setShowLabelConfig] = useState(false);
|
||||
|
||||
const [originalLabelsList, setOriginalLabelsList] = useState([]);
|
||||
|
||||
const [startAddLabel, setStartAddLabel] = useState(false);
|
||||
const [deletedItems, setDeletedItems] = useState([]);
|
||||
|
||||
const [selectedCustomer, setSelectedCustomer] = useState(undefined);
|
||||
const [selectedCustomerLabel, setSelectedCustomerLabel] = useState(undefined);
|
||||
const [selectedLabelColor, setSelectedLabelColor] = useState(undefined);
|
||||
|
||||
const colorsList = ['#AD967A', '#BD816E', '#2B76E5', '#66CCFF', '#0A7E22', '#00C875', '#9CD326', '#FFCB00', '#FF642E', '#FD314D', '#BB3354', '#FF158A', '#9B51E0', '#BDA8F9'];
|
||||
|
||||
useEffect(() => {
|
||||
CustomerService.getAllActiveCustomers().then(data => setCustomers(data?.data));
|
||||
LabelService.getAll('active').then((data) => {setCurrentLabels(data?.data); setOriginalLabelsList(data?.data)});
|
||||
SeatingService.getAll(moment().format('MM/DD/YYYY')).then(data => {
|
||||
const result = data?.data;
|
||||
if (result?.length > 0) {
|
||||
setTableLayout(result[0]?.seating_assignment);
|
||||
setCurrentDateRecord(result[0]);
|
||||
setRowCount(result[0]?.seating_assignment?.rows?.length);
|
||||
setTableCountsPerRow(result[0]?.seating_assignment?.rows?.map(item => item.tables));
|
||||
}
|
||||
});
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const newRows = Array.from({length: rowCount}, (_, index) => ({
|
||||
id: index+1,
|
||||
tables: tableCountsPerRow[index] || 0
|
||||
}));
|
||||
if (rowCount !== tableLayout.rows?.length) {
|
||||
setTableLayout(prevLayout => ({
|
||||
...prevLayout,
|
||||
rows: newRows
|
||||
}));
|
||||
if (tableCountsPerRow.length !== rowCount) {
|
||||
setTableCountsPerRow(prev => {
|
||||
const newCounts = [...prev];
|
||||
while(newCounts.length < newCounts) {
|
||||
newCounts.push(0);
|
||||
}
|
||||
return newCounts.slice(0, rowCount);
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [rowCount]);
|
||||
|
||||
useEffect(() => {
|
||||
const newTables = {};
|
||||
|
||||
let tableNumber = 1;
|
||||
|
||||
let startUpdate = false;
|
||||
|
||||
if (tableCountsPerRow.length !== tableLayout.rows.length) {
|
||||
startUpdate = true;
|
||||
} else {
|
||||
for (let i = 0; i<tableLayout?.rows.length; i++) {
|
||||
if (tableLayout.rows[i].tables !== tableCountsPerRow[i]) {
|
||||
startUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (startUpdate) {
|
||||
tableLayout.rows.forEach((row, rowIndex) => {
|
||||
const tableCount = tableCountsPerRow[rowIndex] || 0;
|
||||
row.tables = tableCount;
|
||||
for (let tableIndex = 0; tableIndex < tableCount; tableIndex++) {
|
||||
|
||||
const tableId = `table-${tableNumber}`
|
||||
|
||||
if (!newTables[tableId]) {
|
||||
const seats = Array.from({length: 8}, (_, seatIndex) => {
|
||||
const seatLetter = String.fromCharCode(65 + seatIndex);
|
||||
return {
|
||||
id: `${tableNumber}-${seatLetter}`,
|
||||
name: seatLetter,
|
||||
customerName: '',
|
||||
customerId: '',
|
||||
rowIndex: rowIndex + 1,
|
||||
label: {label_name: undefined, label_color: undefined}
|
||||
}
|
||||
});
|
||||
|
||||
newTables[tableId] = {
|
||||
id: tableNumber,
|
||||
tableId: tableId,
|
||||
seats,
|
||||
expanded: false,
|
||||
rowIndex: rowIndex + 1
|
||||
}
|
||||
} else {
|
||||
newTables[tableId] = tableLayout.tables[tableId];
|
||||
}
|
||||
tableNumber++;
|
||||
}
|
||||
})
|
||||
|
||||
setTableLayout(prevLayout => ({
|
||||
...prevLayout,
|
||||
rows: prevLayout.rows.map((item, index) => ({...item, tables: tableCountsPerRow[index]}) ),
|
||||
tables: newTables
|
||||
}));
|
||||
}
|
||||
}, [tableCountsPerRow, tableLayout.rows]);
|
||||
|
||||
const handleRowCountChange = (e) => {
|
||||
const count = parseInt(e.target.value) || 0;
|
||||
setRowCount(count);
|
||||
}
|
||||
|
||||
const handleTableCountChange = (rowIndex, count) => {
|
||||
setTableCountsPerRow(prev => {
|
||||
const newCounts = [...prev];
|
||||
newCounts[rowIndex] = parseInt(count) || 0;
|
||||
return newCounts;
|
||||
})
|
||||
}
|
||||
|
||||
const toggleTableExpansion = (tableId) => {
|
||||
setTableLayout(prevLayout => ({
|
||||
...prevLayout,
|
||||
tables: {
|
||||
...prevLayout.tables,
|
||||
[tableId]: {
|
||||
...prevLayout.tables[tableId],
|
||||
expanded: !prevLayout.tables[tableId].expanded
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const cleanAndClose = () => {
|
||||
setTableLayout(currentDateRecord?.seat_assignment || initialValue);
|
||||
setRowCount(currentDateRecord?.seat_assignment?.rows?.length || 1);
|
||||
setTableCountsPerRow(currentDateRecord?.seat_assignment?.rows?.map(item => item.tables));
|
||||
setShowTableLayoutConfig(false);
|
||||
setSelectedCustomer(undefined);
|
||||
setSelectedCustomerLabel(undefined);
|
||||
}
|
||||
|
||||
const saveAndClose = () => {
|
||||
if (currentDateRecord) {
|
||||
SeatingService.updateSeating(currentDateRecord?.id, { seating_assignment: tableLayout, update_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, update_date: new Date() })
|
||||
} else {
|
||||
SeatingService.createNewSeating({
|
||||
date: moment().format('MM/DD/YYYY'),
|
||||
seating_assignment: tableLayout,
|
||||
create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
|
||||
update_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name,
|
||||
create_date: new Date(),
|
||||
update_date: new Date()
|
||||
}).then((data) => {
|
||||
const result = data?.data;
|
||||
setCurrentDateRecord(result);
|
||||
})
|
||||
}
|
||||
|
||||
setShowTableLayoutConfig(false);
|
||||
setSelectedCustomer(undefined);
|
||||
setSelectedCustomerLabel(undefined);
|
||||
|
||||
}
|
||||
|
||||
const [keyword, setKeyword] = useState('');
|
||||
|
||||
const getTotalTableNumber = () => tableCountsPerRow.reduce((acc, curr) => acc + curr, 0);
|
||||
|
||||
const tablesByRow = () => tableLayout.rows.map((row, rowIndex) => {
|
||||
const rowTables = Object.values(tableLayout.tables).filter(table => table.rowIndex === rowIndex + 1).sort((a,b) => a.id - b.id);
|
||||
|
||||
return {
|
||||
rowId: row.id,
|
||||
tables: rowTables
|
||||
}
|
||||
})
|
||||
|
||||
const startEditingSeat = (tableId, seatId) => {
|
||||
const seat = tableLayout.tables[tableId].seats.find(seat => seat.id === seatId);
|
||||
setEditingSeat({
|
||||
tableId,
|
||||
seatId,
|
||||
customerId: seat?.customerId,
|
||||
label: seat?.label,
|
||||
customerName: seat?.customerName
|
||||
});
|
||||
setSelectedCustomer({value: seat?.customerId, label: seat?.customerName});
|
||||
setSelectedCustomerLabel({value: seat?.label?.id, label: seat?.label?.label_name});
|
||||
};
|
||||
|
||||
const onCustomerChange = (selectedCustomer) => {
|
||||
setEditingSeat({
|
||||
...editingSeat,
|
||||
customerId: selectedCustomer.value,
|
||||
customerName: selectedCustomer.label
|
||||
});
|
||||
setSelectedCustomer(selectedCustomer)
|
||||
}
|
||||
|
||||
const handleLabelChange = (selectedLabel) => {
|
||||
setEditingSeat({
|
||||
...editingSeat,
|
||||
label: { label_name: selectedLabel.label, id: selectedLabel.value, label_color: currentLabels.find(item => item.id === selectedLabel.value)?.label_color }
|
||||
})
|
||||
setSelectedCustomerLabel(selectedLabel);
|
||||
}
|
||||
|
||||
const handleNewLabelNameChange = (e) => {
|
||||
setNewLabel(prevLabel => ({
|
||||
...prevLabel,
|
||||
label_name: e.target.value
|
||||
}))
|
||||
}
|
||||
|
||||
const handleNewLabelColorChange = (selectedColor) => {
|
||||
setSelectedLabelColor(selectedColor);
|
||||
setNewLabel(prevLabel => ({
|
||||
...prevLabel,
|
||||
label_color: selectedColor.value
|
||||
}))
|
||||
}
|
||||
|
||||
const saveSeatEdit = () =>{
|
||||
if (!editingSeat) return;
|
||||
|
||||
const {tableId, seatId, customerName, customerId, label} = editingSeat;
|
||||
|
||||
setTableLayout(prevLayout => {
|
||||
const updatedSeats = prevLayout.tables[tableId].seats.map(seat => seat.id === seatId ? {...seat, customerName, customerId, label} : seat);
|
||||
const finalResult = {
|
||||
...prevLayout,
|
||||
tables: {
|
||||
...prevLayout.tables,
|
||||
[tableId]: {
|
||||
...prevLayout.tables[tableId],
|
||||
seats: updatedSeats
|
||||
}
|
||||
}
|
||||
};
|
||||
SeatingService.updateSeating(currentDateRecord?.id, { seating_assignment: finalResult, update_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, update_date: new Date()});
|
||||
CustomerService.updateCustomer(customerId, { table_id: tableId, seating: seatId, tags:[...customers.find(item => item.id === customerId)?.tags, label?.label_name], edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, edit_date: new Date()})
|
||||
return finalResult;
|
||||
});
|
||||
|
||||
setEditingSeat(null);
|
||||
}
|
||||
|
||||
const cancelSeatEdit = () => {
|
||||
setEditingSeat(null);
|
||||
}
|
||||
|
||||
const saveLabelAndClose = () => {
|
||||
if (newLabel.label_color && newLabel.label_name) {
|
||||
LabelService.createNewLabel(newLabel).then(() => {
|
||||
LabelService.getAll('active').then(data => {
|
||||
setCurrentLabels(data?.data);
|
||||
setOriginalLabelsList(data?.data);
|
||||
setShowLabelConfig(false);
|
||||
setStartAddLabel(false);
|
||||
setNewLabel({ label_color: undefined, label_name: undefined})
|
||||
});
|
||||
});
|
||||
}
|
||||
if (deletedItems.length > 0) {
|
||||
Promise.all(deletedItems.map(item => LabelService.updateLabel(item.id, {status: 'inactive'}))).then(() => setDeletedItems([]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const cleanLabelAndClose = () => {
|
||||
setCurrentLabels(originalLabelsList);
|
||||
setShowLabelConfig(false);
|
||||
setStartAddLabel(false);
|
||||
}
|
||||
|
||||
const onDeleteLabel = (item) => {
|
||||
setCurrentLabels(prevLabels => ({
|
||||
...prevLabels.filter(lb => lb.id !== item.id)
|
||||
}))
|
||||
setDeletedItems(prevLabels => ([
|
||||
...prevLabels,
|
||||
item
|
||||
]))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="seating-page-container">
|
||||
<div className="list row mb-4">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>Lobby</Breadcrumb.Item>
|
||||
<Breadcrumb.Item active>
|
||||
Seating Chart
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="col-md-12 text-primary">
|
||||
<h4>
|
||||
Seating Chart
|
||||
</h4>
|
||||
</div>
|
||||
<div className="app-main-content-list-container">
|
||||
<div className="app-main-content-list-func-container">
|
||||
<Tabs defaultActiveKey="seatingChart" id="seatingTabs">
|
||||
<Tab eventKey="seatingChart" title="Seating Chart">
|
||||
<div className="seating-chart-container">
|
||||
<div className="seating-stage">
|
||||
<div className="stage">Stage</div>
|
||||
</div>
|
||||
{
|
||||
tablesByRow().map((rowItem, rowIndex) => {
|
||||
return (<div className="seating-row-container" id={`seating-row-${rowIndex}`} key={`seating-row-${rowIndex}`}>
|
||||
{
|
||||
rowItem?.tables?.map((item, itemIndex) => <CircularTable className="me-4" key={`seating-table-${rowIndex}-${itemIndex}`} id={`seating-table-${rowIndex}-${itemIndex}`} tableNumber={item?.id} guests={item?.seats} />)
|
||||
}
|
||||
</div>)
|
||||
})
|
||||
}
|
||||
<div className="seating-stage mt-4">
|
||||
{
|
||||
|
||||
currentLabels.map((item) => <div><span style={{width: '16px', height: '16px', borderRadius: '16px', display: 'inline-block', background: item.label_color, marginRight: '8px' }}></span>{item.label_name}</div>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<div className="list-func-panel">
|
||||
<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"><Filter size={16} className="me-2"></Filter>Filter</button>
|
||||
<button className="btn btn-primary me-2"><Columns size={16} className="me-2"></Columns>Manage Labels</button>
|
||||
<button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="manage-seating-chart-container">
|
||||
<div className="manage-seating-chart-title-container">
|
||||
<h6>Manage Seating Chart</h6>
|
||||
{/* <ArrowBarRight color="#777" size={20}></ArrowBarRight> */}
|
||||
</div>
|
||||
<div className="manage-seating-chart-tables-container">
|
||||
<div className="title">
|
||||
<strong>Table Layout</strong>
|
||||
<PencilSquare color="#777" size={16} onClick={() => setShowTableLayoutConfig(true)}/>
|
||||
{
|
||||
showTableLayoutConfig && <div className="seating-popover">
|
||||
<h6>Manage Table Layout</h6>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Number of Rows</div>
|
||||
<input type="number" min="0" value={rowCount} onChange={handleRowCountChange}></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
rowCount>0 && Array.from({length: rowCount}).map((_, rowIndex) => (
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">{`Row ${rowIndex + 1}`}</div>
|
||||
<input type="number" min="0" value={tableCountsPerRow[rowIndex] || 0} onChange={(e) => handleTableCountChange(rowIndex, e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
<div className="list row">
|
||||
<div className="col-md-12">
|
||||
<button className="btn btn-default btn-sm float-right" onClick={() => cleanAndClose()}> Cancel </button>
|
||||
<button className="btn btn-primary btn-sm float-right" onClick={() => saveAndClose()}> Save </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
Object.keys(tableLayout.tables).map((tableId) => (
|
||||
<div className="table-config-container">
|
||||
<div className="table-config-item-title">
|
||||
{!tableLayout.tables[tableId]?.expanded && <ChevronDown className="me-2" color="#777" size={12} onClick={() => toggleTableExpansion(tableId)} />}
|
||||
{tableLayout.tables[tableId]?.expanded && <ChevronRight className="me-2" color="#777" size={12} onClick={() => toggleTableExpansion(tableId)} />}
|
||||
{tableId.replace('-', ' ')}
|
||||
</div>
|
||||
{tableLayout.tables[tableId]?.expanded && tableLayout.tables[tableId]?.seats.map(seat => <div className="table-config-item">
|
||||
{`${seat.name}. ${seat.customerName}`} <PencilSquare color="#777" size={16} onClick={() => startEditingSeat(tableId, seat.id)} />
|
||||
{
|
||||
editingSeat && editingSeat.tableId === tableId && editingSeat.seatId === seat.id && <div className="seating-popover">
|
||||
<h6>Seat Assignment</h6>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Customer Name</div>
|
||||
<Select styles={{
|
||||
control: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: '210px',
|
||||
height: '35px',
|
||||
'padding-top': 0,
|
||||
'padding-bottom': 0,
|
||||
'margin-top': 0,
|
||||
'margin-bottom': 0
|
||||
}),
|
||||
indicatorSeparator: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: 0
|
||||
}),
|
||||
indicatorsContainer: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px'
|
||||
}),
|
||||
placeholder: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
}),
|
||||
singleValue: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
})
|
||||
}} value={selectedCustomer} onChange={newSelectedCustomer => onCustomerChange(newSelectedCustomer)} options={[{value: '', label: ''}, ...customers.map(customer => ({
|
||||
value: customer?.id || '',
|
||||
label: customer?.name || ''
|
||||
}))]}></Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Customer Label</div>
|
||||
<Select styles={{
|
||||
control: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: '210px',
|
||||
height: '35px',
|
||||
'padding-top': 0,
|
||||
'padding-bottom': 0,
|
||||
'margin-top': 0,
|
||||
'margin-bottom': 0
|
||||
}),
|
||||
indicatorSeparator: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: 0
|
||||
}),
|
||||
indicatorsContainer: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px'
|
||||
}),
|
||||
placeholder: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
}),
|
||||
singleValue: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
})
|
||||
}} value={ selectedCustomerLabel} onChange={selectedLabel => handleLabelChange(selectedLabel)} options={[{value: '', label: ''}, ...currentLabels.map(label => ({
|
||||
value: label?.id|| '',
|
||||
label: label?.label_name || ''
|
||||
}))]}></Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="list row">
|
||||
<div className="col-md-12">
|
||||
<button className="btn btn-default btn-sm float-right" onClick={() => cancelSeatEdit()}> Cancel </button>
|
||||
<button className="btn btn-primary btn-sm float-right" onClick={() => saveSeatEdit()}> Save </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
<div className="title">
|
||||
<strong>Customer Label</strong>
|
||||
<PencilSquare color="#777" size={16} onClick={() => setShowLabelConfig(true)}/>
|
||||
{
|
||||
showLabelConfig && <div className="seating-popover">
|
||||
<h6>Customer Labels</h6>
|
||||
<div className="mb-4">
|
||||
{
|
||||
currentLabels.map((item) => <div className="label-delete-item"><span style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color }}></span>{item.label_name} <FileX size={16} onClick={(item) => onDeleteLabel(item)}></FileX></div>)
|
||||
}
|
||||
</div>
|
||||
{!startAddLabel && <button className="btn btn-tertiary btn-custom-label btn-sm mb-4" onClick={() => setStartAddLabel(true)}>+ Add New Label</button>}
|
||||
{startAddLabel && <>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Label Name</div>
|
||||
<input type="text" value={newLabel.label_name} onChange={handleNewLabelNameChange}></input>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section margin-sm dropdown-container">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Label Color</div>
|
||||
{/* <select value={newLabel.label_color} onChange={handleNewLabelColorChange}>
|
||||
<option value=""></option>
|
||||
{
|
||||
colorsList.map((item) => <option style={{backgroundColor: item}} value={item}><div style={{backgroundColor: item, width: '4px', height: '4px'}}></div>{item}</option>)
|
||||
}
|
||||
</select> */}
|
||||
<Select styles={{
|
||||
control: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: '210px',
|
||||
height: '35px',
|
||||
'padding-top': 0,
|
||||
'padding-bottom': 0,
|
||||
'margin-top': 0,
|
||||
'margin-bottom': 0
|
||||
}),
|
||||
indicatorSeparator: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
width: 0
|
||||
}),
|
||||
indicatorsContainer: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px'
|
||||
}),
|
||||
placeholder: (baseStyles) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
}),
|
||||
singleValue: (baseStyles, state) => ({
|
||||
...baseStyles,
|
||||
'margin-top': '-10px',
|
||||
'font-size': '13px'
|
||||
}),
|
||||
option: (baseStyles, {data}) => ({
|
||||
...baseStyles,
|
||||
backgroundColor: data?.value
|
||||
})
|
||||
}} value={selectedLabelColor} onChange={selectedColor => handleNewLabelColorChange(selectedColor)} options={[{value: '', label: ''}, ...colorsList.map(color => ({
|
||||
value: color|| '',
|
||||
label: color || ''
|
||||
}))]}></Select>
|
||||
</div>
|
||||
</div>
|
||||
</>}
|
||||
|
||||
<div className="list row">
|
||||
<div className="col-md-12">
|
||||
<button className="btn btn-default btn-sm float-right" onClick={() => cleanLabelAndClose()}> Cancel </button>
|
||||
<button className="btn btn-primary btn-sm float-right" onClick={() => saveLabelAndClose()}> Save </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
currentLabels.map((item) => <div className="mb-4" style={{fontSize: '12px'}}><span style={{width: '16px', height: '16px', borderRadius: '16px', background: item.label_color, display: 'inline-block', marginRight: '8px' }}></span>{item.label_name}</div>)
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Seating;
|
||||
@ -2,7 +2,7 @@ import React, {useEffect, useState} from "react";
|
||||
import { useSelector,useDispatch } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { vehicleSlice, selectVehicleError } from "./../../store";
|
||||
import { AuthService, VehicleService } from "../../services";
|
||||
import { AuthService, VehicleRepairService, VehicleService } from "../../services";
|
||||
import { Archive, Upload } from "react-bootstrap-icons";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
|
||||
import DatePicker from "react-datepicker";
|
||||
@ -38,7 +38,12 @@ const UpdateVehicle = () => {
|
||||
const [selectedYearlyFile, setSelectedYearlyFile] = useState();
|
||||
const [monthlyInspectionDate, setMonthlyInspectionDate] = useState();
|
||||
const [yearlyInspectionDate, setYearlyInspectionDate] = useState();
|
||||
const [repairDate, setRepairDate] = useState();
|
||||
const [repairDescription, setRepairDescription] = useState('');
|
||||
const [repairPrice, setRepairPrice] = useState('');
|
||||
const [repairLocation, setRepairLocation] = useState('');
|
||||
const error = useSelector(selectVehicleError);
|
||||
const [selectedRepairFile, setSelectedRepairFile] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
if (!AuthService.canAddOrEditVechiles()) {
|
||||
@ -149,6 +154,22 @@ const UpdateVehicle = () => {
|
||||
redirectTo();
|
||||
}
|
||||
|
||||
const saveRepair = () => {
|
||||
const data = {
|
||||
vehicle: currentVehicle?.id,
|
||||
repair_date: moment(repairDate).format('MM/DD/YYYY'),
|
||||
repair_description: repairDescription,
|
||||
repair_location: repairLocation,
|
||||
repair_price: repairPrice
|
||||
}
|
||||
VehicleRepairService.createNewVehicleRepair(data).then(result => {
|
||||
const record = result.data;
|
||||
const formData = new FormData();
|
||||
formData.append('file', selectedRepairFile);
|
||||
VehicleService.uploadVechileFile(formData, currentVehicle.id, record.id, 'repair', repairDate).then(() => redirectTo());
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -285,7 +306,41 @@ const UpdateVehicle = () => {
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab eventKey="Repair Records" title="Repair Records">
|
||||
Coming soon...
|
||||
<h6 className="text-primary">Repair Log</h6>
|
||||
<div className="app-main-content-fields-section">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Repair Date <span className="required">*</span></div>
|
||||
<DatePicker selected={repairDate} onChange={(v) => setRepairDate(v)} />
|
||||
</div>
|
||||
<div className="me-4"><div className="field-label">Cost <span className="required">*</span></div>
|
||||
<input type="text" value={repairPrice || ''} placeholder="e.g.,$75" onChange={e => setRepairPrice(e.target.value)}/>
|
||||
</div>
|
||||
<div className="me-4"><div className="field-label">Repair Location <span className="required">*</span></div>
|
||||
<input type="text" value={repairLocation || ''} placeholder="e.g.,LocalGarage" onChange={e => setRepairLocation(e.target.value)}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-main-content-fields-section">
|
||||
<div className="me-4">
|
||||
<div className="field-label">Description <span className="required">*</span></div>
|
||||
<textarea value={repairDescription || ''} onChange={e => setRepairDescription(e.target.value)}/>
|
||||
</div>
|
||||
<div className="me-4">
|
||||
<div className="field-label">Upload Maintenance Files</div>
|
||||
<label className="custom-file-upload">
|
||||
<Upload width={20} color={"#fff"} className="me-2"></Upload> Upload Files
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => setSelectedRepairFile(e.target.files[0])}
|
||||
/>
|
||||
</label>
|
||||
<div className="file-name">{ selectedRepairFile && selectedRepairFile?.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={() => saveRepair()}> Save </button>
|
||||
|
||||
</div>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<div className="list-func-panel">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useSelector,useDispatch } from "react-redux";
|
||||
import { AuthService, VehicleService } from "../../services";
|
||||
import { AuthService, VehicleRepairService, VehicleService } from "../../services";
|
||||
import { vehicleSlice, selectVehicleError } from "./../../store";
|
||||
import { Spinner, Breadcrumb, BreadcrumbItem, Tabs, Tab } from "react-bootstrap";
|
||||
import { Download, PencilSquare, Archive } from "react-bootstrap-icons";
|
||||
@ -14,14 +14,19 @@ const ViewVehicle = () => {
|
||||
const [currentVehicle, setCurrentVehicle] = useState(undefined);
|
||||
const [monthlyDocs, setMonthlyDocs] = useState([]);
|
||||
const [yearlyDocs, setYearlyDocs] = useState([]);
|
||||
const [repairs, setRepairs] = useState([]);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [sortingMonthly, setSortingMonthly] = useState({key: '', order: ''});
|
||||
const [sortingYearly, setSortingYearly] = useState({key: '', order: ''});
|
||||
const [sortingRepair, setSortingRepair] = useState({key: '', order: ''});
|
||||
const [selectedItemsMonthly, setSelectedItemsMonthly] = useState([]);
|
||||
const [selectedItemsYearly, setSelectedItemsYearly] = useState([]);
|
||||
const [selectedItemsRepair, setSelectedItemsRepair] = useState([]);
|
||||
const [filteredMonthlyDocs, setFilteredMonthlyDocs] = useState(monthlyDocs);
|
||||
const [filteredYearlyDocs, setFilteredYearlyDocs] = useState(yearlyDocs);
|
||||
const [filteredRepairs, setFilteredRepairs] = useState(repairs);
|
||||
const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions;
|
||||
const [currentTab, setCurrentTab] = useState('basicInfo')
|
||||
|
||||
const redirectTo = () => {
|
||||
navigate(`/vehicles/list`)
|
||||
@ -70,6 +75,11 @@ const ViewVehicle = () => {
|
||||
setMonthlyDocs(monthlyInspectionDocs?.map(item => ({ ...item, inspectionDate: getInspectionDate(item?.name) })));
|
||||
setYearlyDocs(yearlyInspectionDocs?.map(item => ({ ...item, inspectionDate: getInspectionDate(item?.name) })));
|
||||
};
|
||||
const getAllRepairs = async (vid) => {
|
||||
const v_repairs = (await VehicleRepairService.getAll(vid)).data;
|
||||
console.log('repairs', v_repairs);
|
||||
setRepairs(v_repairs);
|
||||
}
|
||||
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.')
|
||||
AuthService.logout();
|
||||
@ -79,17 +89,23 @@ const ViewVehicle = () => {
|
||||
VehicleService.getVehicle(urlParams.id).then((data) => {
|
||||
setCurrentVehicle(data.data);
|
||||
getAllDocuments(data.data?.id, data.data?.vehicle_number);
|
||||
getAllRepairs(data.data?.id);
|
||||
})
|
||||
} else {
|
||||
getAllDocuments(currentVehicle?.id, currentVehicle?.vehicle_number);
|
||||
getAllRepairs(currentVehicle?.id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
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())));
|
||||
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(() => {
|
||||
setFilteredRepairs(repairs?.filter(item => item?.repair_description?.toLowerCase().includes(keyword.toLowerCase()) || item?.repair_date?.includes(keyword.toLowerCase()) || item?.repair_location?.toLowerCase().includes(keyword.toLowerCase()) || item?.repair_price?.toLowerCase().includes(keyword.toLowerCase()) ));
|
||||
}, [keyword, repairs])
|
||||
|
||||
useEffect(() => {
|
||||
const newYearlyDocs = [...yearlyDocs];
|
||||
const sortedYearlyDocs = sortingYearly.key === '' ? newYearlyDocs : newYearlyDocs.sort((a, b) => {
|
||||
@ -110,6 +126,16 @@ const ViewVehicle = () => {
|
||||
)
|
||||
}, [sortingMonthly]);
|
||||
|
||||
useEffect(() => {
|
||||
const newRepairs = [...repairs];
|
||||
const sortedRepairs = sortingRepair.key === '' ? newRepairs : newRepairs.sort((a, b) => {
|
||||
return a[sortingRepair.key]?.localeCompare(b[sortingRepair.key]);
|
||||
});
|
||||
setRepairs(
|
||||
sortingRepair.order === 'asc' ? sortedRepairs : sortedRepairs.reverse()
|
||||
)
|
||||
}, [sortingRepair]);
|
||||
|
||||
const columnsMonthly = [
|
||||
{
|
||||
key: 'name',
|
||||
@ -140,6 +166,29 @@ const ViewVehicle = () => {
|
||||
}
|
||||
];
|
||||
|
||||
const columnsRepair = [
|
||||
{
|
||||
key: 'repair_description',
|
||||
label: 'Repair Description'
|
||||
},
|
||||
{
|
||||
key: 'repair_date',
|
||||
label: 'Repair Date'
|
||||
},
|
||||
{
|
||||
key: 'repair_price',
|
||||
label: 'Cost'
|
||||
},
|
||||
{
|
||||
key: 'repair_location',
|
||||
label: 'Repair Location'
|
||||
},
|
||||
{
|
||||
key: 'create_date',
|
||||
label: 'Date Added'
|
||||
}
|
||||
];
|
||||
|
||||
const sortTableWithFieldMonthly = (key) => {
|
||||
let newSorting = {
|
||||
key,
|
||||
@ -164,6 +213,18 @@ const ViewVehicle = () => {
|
||||
setSortingYearly(newSorting);
|
||||
}
|
||||
|
||||
const sortTableWithFieldRepair = (key) => {
|
||||
let newSorting = {
|
||||
key,
|
||||
order: 'asc',
|
||||
}
|
||||
|
||||
if (sortingRepair.key === key && sortingRepair.order === 'asc') {
|
||||
newSorting = {...newSorting, order: 'desc'};
|
||||
}
|
||||
setSortingRepair(newSorting);
|
||||
}
|
||||
|
||||
const toggleSelectedAllItemsMonthly = () => {
|
||||
if (selectedItemsMonthly.length !== filteredMonthlyDocs.length || selectedItemsMonthly.length === 0) {
|
||||
const newSelectedItems = [...filteredMonthlyDocs].map((doc) => doc.url);
|
||||
@ -182,6 +243,15 @@ const ViewVehicle = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const toggleSelectedAllItemsRepair = () => {
|
||||
if (selectedItemsRepair.length !== filteredRepairs.length || filteredRepairs.length === 0) {
|
||||
const newSelectedItems = [...filteredRepairs].map((doc) => doc.id);
|
||||
selectedItemsRepair(newSelectedItems);
|
||||
} else {
|
||||
selectedItemsRepair([]);
|
||||
}
|
||||
}
|
||||
|
||||
const toggleItemYearly = (id) => {
|
||||
if (selectedItemsYearly.includes(id)) {
|
||||
const newSelectedItems = [...selectedItemsYearly].filter((item) => item !== id);
|
||||
@ -202,6 +272,16 @@ const ViewVehicle = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const toggleItemRepair = (id) => {
|
||||
if (selectedItemsRepair.includes(id)) {
|
||||
const newSelectedItems = [...selectedItemsRepair].filter((item) => item !== id);
|
||||
setSelectedItemsRepair(newSelectedItems);
|
||||
} else {
|
||||
const newSelectedItems = [...selectedItemsRepair, id];
|
||||
setSelectedItemsRepair(newSelectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
const getSortingImgMonthly = (key) => {
|
||||
return sortingMonthly.key === key ? (sortingMonthly.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
|
||||
}
|
||||
@ -210,6 +290,10 @@ const ViewVehicle = () => {
|
||||
return sortingYearly.key === key ? (sortingYearly.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
|
||||
}
|
||||
|
||||
const getSortingImgRepair = (key) => {
|
||||
return sortingRepair.key === key ? (sortingRepair.order === 'asc' ? 'up_arrow' : 'down_arrow') : 'default';
|
||||
}
|
||||
|
||||
const checkSelectAllMonthly = () => {
|
||||
return selectedItemsMonthly.length === filteredMonthlyDocs.length && selectedItemsMonthly.length > 0;
|
||||
}
|
||||
@ -218,6 +302,18 @@ const ViewVehicle = () => {
|
||||
return selectedItemsYearly.length === filteredYearlyDocs.length && selectedItemsYearly.length > 0;
|
||||
}
|
||||
|
||||
const checkSelectAllRepair = () => {
|
||||
return selectedItemsRepair.length === selectedItemsRepair.length && selectedItemsRepair.length > 0;
|
||||
}
|
||||
|
||||
const changeTab = (k) => {
|
||||
setCurrentTab(k);
|
||||
setKeyword('');
|
||||
setSortingMonthly({key: '', order: ''});
|
||||
setSortingYearly({key: '', order: ''});
|
||||
setSortingRepair({key: '', order: ''});
|
||||
}
|
||||
|
||||
const tableMonthly = <div className="list row mb-4">
|
||||
<div className="col-md-12">
|
||||
<table className="personnel-info-table">
|
||||
@ -279,6 +375,39 @@ const tableYearly = <div className="list row mb-4">
|
||||
</table>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
|
||||
const tableRepair = <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={checkSelectAllRepair()} onClick={() => toggleSelectedAllItemsRepair()}></input></th>
|
||||
<th className="th-index">No.</th>
|
||||
{
|
||||
columnsRepair.map((column, index) => <th className="sortable-header" key={index}>
|
||||
{column.label} <span className="float-right" onClick={() => sortTableWithFieldRepair(column.key)}><img src={`/images/${getSortingImgRepair(column.key)}.png`}></img></span>
|
||||
</th>)
|
||||
}
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
filteredRepairs?.map((repair, index) => <tr key={repair.id}>
|
||||
<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> {repair?.repair_description}</td>
|
||||
<td>{repair?.repair_date}</td>
|
||||
<td>{repair?.repair_price}</td>
|
||||
<td>{repair?.repair_location}</td>
|
||||
<td>{repair?.create_date ? new Date(repair?.create_date).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'}) : repair?.repair_date}</td>
|
||||
</tr>)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -298,7 +427,7 @@ const tableYearly = <div className="list row mb-4">
|
||||
</div>
|
||||
<div className="app-main-content-list-container">
|
||||
<div className="app-main-content-list-func-container">
|
||||
<Tabs defaultActiveKey="basicInfo" id="customers-tab">
|
||||
<Tabs defaultActiveKey="basicInfo" id="customers-tab" onSelect={k => changeTab(k)}>
|
||||
<Tab eventKey="basicInfo" title="Basic Information">
|
||||
<h6 className="text-primary">Vehicle Information</h6>
|
||||
<div className="app-main-content-fields-section">
|
||||
@ -390,13 +519,15 @@ const tableYearly = <div className="list row mb-4">
|
||||
<h6 className="text-primary">Monthly Vehicle Inspection</h6>
|
||||
{tableMonthly}
|
||||
</Tab>
|
||||
<Tab eventKey="Repair Records" title="Repair Records">
|
||||
Coming soon...
|
||||
<Tab eventKey="repairRecords" title="Repair Records">
|
||||
{tableRepair}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<div className="list-func-panel">
|
||||
<input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />
|
||||
<button className="btn btn-primary" onClick={() => download()}><Download size={16} className="me-2"></Download>Download</button>
|
||||
{currentTab !== 'basicInfo' && <input className="me-2 with-search-icon" type="text" placeholder="Search" value={keyword} onChange={(e) => setKeyword(e.currentTarget.value)} />}
|
||||
{currentTab === 'documents' && <button className="btn btn-primary" onClick={() => download()}><Download size={16} className="me-2"></Download>Download</button>}
|
||||
{currentTab === 'repairRecords' && <button className="btn btn-primary"><Download size={16} className="me-2"></Download>Export</button>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
28
client/src/services/LabelService.js
Normal file
28
client/src/services/LabelService.js
Normal file
@ -0,0 +1,28 @@
|
||||
import http from "../http-common";
|
||||
const getAll = (status) => {
|
||||
const params = {};
|
||||
if (status) {
|
||||
params.status = status;
|
||||
}
|
||||
return http.get("/labels", {params});
|
||||
};
|
||||
|
||||
const createNewLabel = (data) => {
|
||||
data.status = 'active';
|
||||
return http.post('/labels', data);
|
||||
};
|
||||
|
||||
const updateLabel = (id, data) => {
|
||||
return http.put(`/labels/${id}`, data);
|
||||
}
|
||||
|
||||
const deleteLabel = (id) => {
|
||||
return http.delete(`/labels/${id}`)
|
||||
}
|
||||
|
||||
export const LabelService = {
|
||||
getAll,
|
||||
createNewLabel,
|
||||
updateLabel,
|
||||
deleteLabel
|
||||
}
|
||||
28
client/src/services/SeatingService.js
Normal file
28
client/src/services/SeatingService.js
Normal file
@ -0,0 +1,28 @@
|
||||
import http from "../http-common";
|
||||
const getAll = (date) => {
|
||||
const params = {};
|
||||
if (date) {
|
||||
params.date = date;
|
||||
}
|
||||
return http.get("/seatings", {params});
|
||||
};
|
||||
|
||||
const createNewSeating = (data) => {
|
||||
data.status = 'active';
|
||||
return http.post('/seatings', data);
|
||||
};
|
||||
|
||||
const updateSeating = (id, data) => {
|
||||
return http.put(`/seatings/${id}`, data);
|
||||
}
|
||||
|
||||
const deleteSeating = (id) => {
|
||||
return http.delete(`/seatings/${id}`)
|
||||
}
|
||||
|
||||
export const SeatingService = {
|
||||
getAll,
|
||||
createNewSeating,
|
||||
updateSeating,
|
||||
deleteSeating
|
||||
}
|
||||
12
client/src/services/VehicleRepairService.js
Normal file
12
client/src/services/VehicleRepairService.js
Normal file
@ -0,0 +1,12 @@
|
||||
import http from "../http-common";
|
||||
const getAll = (vehicle) => {
|
||||
return http.get(`/vehicle-repairs?vehicle=${vehicle}`);
|
||||
};
|
||||
const createNewVehicleRepair = (data) => {
|
||||
return http.post('/vehicle-repairs', data);
|
||||
};
|
||||
|
||||
export const VehicleRepairService = {
|
||||
getAll,
|
||||
createNewVehicleRepair
|
||||
};
|
||||
@ -13,4 +13,7 @@ export * from './CenterPhoneService';
|
||||
export * from './ResourceService';
|
||||
export * from './EventsService';
|
||||
export * from './EventRequestService';
|
||||
export * from './SignatureRequestService';
|
||||
export * from './SignatureRequestService';
|
||||
export * from './VehicleRepairService';
|
||||
export * from './LabelService';
|
||||
export * from './SeatingService';
|
||||
@ -189,6 +189,9 @@ app.get('/medical/resources/:id', function (req,res) {
|
||||
app.get('/signature/:id', function (req,res) {
|
||||
res.sendFile(path + "index.html");
|
||||
});
|
||||
app.get('/seating', function (req,res) {
|
||||
res.sendFile(path + "index.html");
|
||||
});
|
||||
require("./app/routes/user.routes")(app);
|
||||
require("./app/routes/auth.routes")(app);
|
||||
require("./app/routes/employee.routes")(app);
|
||||
@ -213,6 +216,9 @@ require("./app/routes/event-request.routes")(app);
|
||||
require("./app/routes/signature-request.routes")(app);
|
||||
require("./app/routes/snack.routes")(app);
|
||||
require("./app/routes/lunch.routes")(app);
|
||||
require("./app/routes/vehicle-repair.route")(app);
|
||||
require("./app/routes/seating.routes")(app);
|
||||
require("./app/routes/label.routes")(app);
|
||||
|
||||
// set port, listen for requests
|
||||
const PORT = process.env.PORT || 8080;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user