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 = () => { PhoneCar ModelCar Year + ModificationsActions @@ -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 */} - - {selectedCustomer ? 'Edit Customer' : 'Add New Customer'} - -
- - - - - - - Car Model - - - - - - - - -
+ {selectedCustomer ? 'Edit Customer' : 'Add Customer'} + + + + + + + + + + + + + + + + + + Car Model + + + + + + + + + + + + + +
+ + {/* Modification Form Dialog */} + + Add Modification + + + + + + + + + + + + + + + Category + + + + + + + + + +
); 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'); } });