From 1ac852f2a157d73b92dc0d124ac16bbeb05e4bbc Mon Sep 17 00:00:00 2001
From: Alvin <524715@vistacollege.nl>
Date: Tue, 10 Jun 2025 11:38:27 +0200
Subject: [PATCH] Update customer management functionality by adding
modification support, enhancing the customer model to include modifications,
and improving the UI for customer and modification forms. Refactor API routes
for better error handling and streamline customer data management.
---
client/public/favicon.svg | 14 +
client/public/index.html | 8 +-
client/src/components/CustomerManagement.js | 375 +++++++++++++-------
models/Customer.js | 32 +-
routes/customers.js | 45 ++-
5 files changed, 329 insertions(+), 145 deletions(-)
create mode 100644 client/public/favicon.svg
diff --git a/client/public/favicon.svg b/client/public/favicon.svg
new file mode 100644
index 0000000..740de80
--- /dev/null
+++ b/client/public/favicon.svg
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/client/public/index.html b/client/public/index.html
index aa069f2..10b4f00 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -2,14 +2,14 @@
-
+
-
+
- React App
+ AutoTune Pro | Car Tuning Management
diff --git a/client/src/components/CustomerManagement.js b/client/src/components/CustomerManagement.js
index ffdc1f1..78bb3b0 100644
--- a/client/src/components/CustomerManagement.js
+++ b/client/src/components/CustomerManagement.js
@@ -22,40 +22,47 @@ import {
Select,
FormControl,
InputLabel,
+ Grid,
+ Card,
+ CardContent,
+ Divider
} from '@mui/material';
-import EditIcon from '@mui/icons-material/Edit';
-import DeleteIcon from '@mui/icons-material/Delete';
-import AddIcon from '@mui/icons-material/Add';
+import { Edit as EditIcon, Delete as DeleteIcon, Add as AddIcon } from '@mui/icons-material';
import axios from 'axios';
const carModels = [
- { value: 'bmw_m3', label: 'BMW M3' },
- { value: 'bmw_m4', label: 'BMW M4' },
- { value: 'audi_rs3', label: 'Audi RS3' },
- { value: 'audi_rs4', label: 'Audi RS4' },
- { value: 'mercedes_c63', label: 'Mercedes-AMG C63' },
- { value: 'mercedes_e63', label: 'Mercedes-AMG E63' },
- { value: 'volkswagen_golf_r', label: 'Volkswagen Golf R' },
- { value: 'volkswagen_arteon_r', label: 'Volkswagen Arteon R' },
- { value: 'toyota_supra', label: 'Toyota Supra' },
- { value: 'nissan_gtr', label: 'Nissan GT-R' },
- { value: 'porsche_911', label: 'Porsche 911' },
- { value: 'porsche_cayman', label: 'Porsche Cayman' },
- { value: 'honda_civic_type_r', label: 'Honda Civic Type R' },
- { value: 'subaru_wrx_sti', label: 'Subaru WRX STI' },
- { value: 'mitsubishi_evo', label: 'Mitsubishi Lancer Evolution' },
- { value: 'other', label: 'Other' },
+ 'BMW M3',
+ 'BMW M4',
+ 'Audi RS3',
+ 'Audi RS4',
+ 'Mercedes-AMG C63',
+ 'Mercedes-AMG E63',
+ 'Volkswagen Golf R',
+ 'Volkswagen Arteon R',
+ 'Toyota Supra',
+ 'Nissan GT-R',
+ 'Porsche 911',
+ 'Porsche Cayman',
+ 'Honda Civic Type R',
+ 'Subaru WRX STI',
+ 'Mitsubishi Lancer Evolution',
+ 'Other'
+];
+
+const categories = [
+ 'Engine',
+ 'Exhaust',
+ 'Suspension',
+ 'Brakes',
+ 'Wheels'
];
// Create axios instance with default config
const api = axios.create({
- baseURL: 'http://localhost:5000/api',
- headers: {
- 'Content-Type': 'application/json',
- },
+ baseURL: 'http://localhost:5000/api'
});
-// Add request interceptor to add token to all requests
+// Add request interceptor to include token
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
@@ -72,17 +79,24 @@ api.interceptors.request.use(
const CustomerManagement = () => {
const [customers, setCustomers] = useState([]);
const [open, setOpen] = useState(false);
+ const [openModDialog, setOpenModDialog] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
+ const [error, setError] = useState('');
+ const [success, setSuccess] = useState('');
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
address: '',
carModel: '',
- carYear: '',
+ carYear: ''
+ });
+ const [modificationData, setModificationData] = useState({
+ name: '',
+ description: '',
+ price: '',
+ category: ''
});
- const [error, setError] = useState('');
- const [success, setSuccess] = useState('');
useEffect(() => {
fetchCustomers();
@@ -99,32 +113,56 @@ const CustomerManagement = () => {
const handleOpen = (customer = null) => {
if (customer) {
- setSelectedCustomer(customer);
setFormData(customer);
+ setSelectedCustomer(customer);
} else {
- setSelectedCustomer(null);
setFormData({
name: '',
email: '',
phone: '',
address: '',
carModel: '',
- carYear: '',
+ carYear: ''
});
+ setSelectedCustomer(null);
}
setOpen(true);
};
+ const handleOpenModDialog = (customer) => {
+ setSelectedCustomer(customer);
+ setModificationData({
+ name: '',
+ description: '',
+ price: '',
+ category: ''
+ });
+ setOpenModDialog(true);
+ };
+
const handleClose = () => {
setOpen(false);
setError('');
setSuccess('');
};
- const handleChange = (e) => {
+ const handleCloseModDialog = () => {
+ setOpenModDialog(false);
+ setError('');
+ setSuccess('');
+ };
+
+ const handleInputChange = (e) => {
setFormData({
...formData,
- [e.target.name]: e.target.value,
+ [e.target.name]: e.target.value
+ });
+ };
+
+ const handleModInputChange = (e) => {
+ setModificationData({
+ ...modificationData,
+ [e.target.name]: e.target.value
});
};
@@ -145,6 +183,18 @@ const CustomerManagement = () => {
}
};
+ const handleAddModification = async (e) => {
+ e.preventDefault();
+ try {
+ await api.put(`/customers/${selectedCustomer._id}/modifications`, modificationData);
+ setSuccess('Modification added successfully');
+ fetchCustomers();
+ handleCloseModDialog();
+ } catch (err) {
+ setError(err.response?.data?.message || 'An error occurred');
+ }
+ };
+
const handleDelete = async (id) => {
if (window.confirm('Are you sure you want to delete this customer?')) {
try {
@@ -159,8 +209,8 @@ const CustomerManagement = () => {
return (
-
-
+
+
Customer Management
- {error && (
-
- {error}
-
- )}
-
- {success && (
-
- {success}
-
- )}
+ {error && {error}}
+ {success && {success}}
@@ -194,6 +235,7 @@ const CustomerManagement = () => {
Phone
Car Model
Car Year
+ Modifications
Actions
@@ -205,6 +247,24 @@ const CustomerManagement = () => {
{customer.phone}
{customer.carModel}
{customer.carYear}
+
+
+ {customer.modifications && customer.modifications.length > 0 && (
+
+ {customer.modifications.map((mod, index) => (
+
+ {mod.name} - {mod.category}
+
+ ))}
+
+ )}
+
handleOpen(customer)} color="primary">
@@ -219,82 +279,157 @@ const CustomerManagement = () => {
+ {/* Customer Form Dialog */}
+
+ {/* Modification Form Dialog */}
+
);
diff --git a/models/Customer.js b/models/Customer.js
index c027459..9ea3dbe 100644
--- a/models/Customer.js
+++ b/models/Customer.js
@@ -20,6 +20,7 @@ const customerSchema = new mongoose.Schema({
},
address: {
type: String,
+ required: true,
trim: true
},
carModel: {
@@ -28,10 +29,31 @@ const customerSchema = new mongoose.Schema({
trim: true
},
carYear: {
- type: String,
- required: true,
- trim: true
+ type: Number,
+ required: true
},
+ modifications: [{
+ name: {
+ type: String,
+ required: true
+ },
+ description: {
+ type: String,
+ required: true
+ },
+ price: {
+ type: Number,
+ required: true
+ },
+ category: {
+ type: String,
+ required: true
+ },
+ dateAdded: {
+ type: Date,
+ default: Date.now
+ }
+ }],
createdAt: {
type: Date,
default: Date.now
@@ -48,4 +70,6 @@ customerSchema.pre('save', function(next) {
next();
});
-module.exports = mongoose.model('Customer', customerSchema);
\ No newline at end of file
+const Customer = mongoose.model('Customer', customerSchema);
+
+module.exports = Customer;
\ No newline at end of file
diff --git a/routes/customers.js b/routes/customers.js
index ca15ee8..39b6f53 100644
--- a/routes/customers.js
+++ b/routes/customers.js
@@ -9,7 +9,7 @@ router.get('/', auth, async (req, res) => {
const customers = await Customer.find().sort({ name: 1 });
res.json(customers);
} catch (err) {
- res.status(500).json({ message: err.message });
+ res.status(500).send('Server Error');
}
});
@@ -22,24 +22,16 @@ router.get('/:id', auth, async (req, res) => {
}
res.json(customer);
} catch (err) {
- res.status(500).json({ message: err.message });
+ res.status(500).send('Server Error');
}
});
// Create customer
router.post('/', auth, async (req, res) => {
- const customer = new Customer({
- name: req.body.name,
- email: req.body.email,
- phone: req.body.phone,
- address: req.body.address,
- carModel: req.body.carModel,
- carYear: req.body.carYear,
- });
-
try {
- const newCustomer = await customer.save();
- res.status(201).json(newCustomer);
+ const customer = new Customer(req.body);
+ await customer.save();
+ res.status(201).json(customer);
} catch (err) {
res.status(400).json({ message: err.message });
}
@@ -53,9 +45,28 @@ router.put('/:id', auth, async (req, res) => {
return res.status(404).json({ message: 'Customer not found' });
}
- Object.assign(customer, req.body);
- const updatedCustomer = await customer.save();
- res.json(updatedCustomer);
+ Object.keys(req.body).forEach(key => {
+ customer[key] = req.body[key];
+ });
+
+ await customer.save();
+ res.json(customer);
+ } catch (err) {
+ res.status(400).json({ message: err.message });
+ }
+});
+
+// Add modification to customer
+router.put('/:id/modifications', auth, async (req, res) => {
+ try {
+ const customer = await Customer.findById(req.params.id);
+ if (!customer) {
+ return res.status(404).json({ message: 'Customer not found' });
+ }
+
+ customer.modifications.push(req.body);
+ await customer.save();
+ res.json(customer);
} catch (err) {
res.status(400).json({ message: err.message });
}
@@ -72,7 +83,7 @@ router.delete('/:id', auth, async (req, res) => {
await customer.remove();
res.json({ message: 'Customer deleted' });
} catch (err) {
- res.status(500).json({ message: err.message });
+ res.status(500).send('Server Error');
}
});