diff --git a/client/src/App.js b/client/src/App.js
index 377a0cf..f979769 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -3,13 +3,16 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-d
import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Login from './components/Login';
+import Register from './components/Register';
import Dashboard from './components/Dashboard';
import CustomerList from './components/CustomerList';
import CustomerDetail from './components/CustomerDetail';
import CarModifications from './components/CarModifications';
import ContactHistory from './components/ContactHistory';
+import CustomerManagement from './components/CustomerManagement';
import PrivateRoute from './components/PrivateRoute';
import UserManagement from './components/UserManagement';
+import BackToHome from './components/BackToHome';
// Create a theme that matches the "stoer en snel" (tough and fast) requirement
const theme = createTheme({
@@ -55,6 +58,7 @@ function App() {
} />
+ } />
-
+
}
/>
@@ -105,6 +109,7 @@ function App() {
/>
} />
+
);
diff --git a/client/src/components/BackToHome.js b/client/src/components/BackToHome.js
new file mode 100644
index 0000000..5867acb
--- /dev/null
+++ b/client/src/components/BackToHome.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Button, Box } from '@mui/material';
+import HomeIcon from '@mui/icons-material/Home';
+
+const BackToHome = () => {
+ const navigate = useNavigate();
+
+ return (
+
+ }
+ onClick={() => navigate('/')}
+ sx={{
+ borderRadius: '50px',
+ padding: '10px 20px',
+ boxShadow: 3,
+ '&:hover': {
+ transform: 'scale(1.05)',
+ transition: 'transform 0.2s ease-in-out',
+ },
+ }}
+ >
+ Home
+
+
+ );
+};
+
+export default BackToHome;
\ No newline at end of file
diff --git a/client/src/components/CustomerManagement.js b/client/src/components/CustomerManagement.js
index f0bf083..ffdc1f1 100644
--- a/client/src/components/CustomerManagement.js
+++ b/client/src/components/CustomerManagement.js
@@ -18,12 +18,57 @@ import {
Paper,
IconButton,
Alert,
+ MenuItem,
+ Select,
+ FormControl,
+ InputLabel,
} 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 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' },
+];
+
+// Create axios instance with default config
+const api = axios.create({
+ baseURL: 'http://localhost:5000/api',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+});
+
+// Add request interceptor to add token to all requests
+api.interceptors.request.use(
+ (config) => {
+ const token = localStorage.getItem('token');
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`;
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ }
+);
+
const CustomerManagement = () => {
const [customers, setCustomers] = useState([]);
const [open, setOpen] = useState(false);
@@ -45,7 +90,7 @@ const CustomerManagement = () => {
const fetchCustomers = async () => {
try {
- const response = await axios.get('http://localhost:5000/api/customers');
+ const response = await api.get('/customers');
setCustomers(response.data);
} catch (err) {
setError('Failed to fetch customers');
@@ -87,10 +132,10 @@ const CustomerManagement = () => {
e.preventDefault();
try {
if (selectedCustomer) {
- await axios.put(`http://localhost:5000/api/customers/${selectedCustomer._id}`, formData);
+ await api.put(`/customers/${selectedCustomer._id}`, formData);
setSuccess('Customer updated successfully');
} else {
- await axios.post('http://localhost:5000/api/customers', formData);
+ await api.post('/customers', formData);
setSuccess('Customer added successfully');
}
fetchCustomers();
@@ -103,7 +148,7 @@ const CustomerManagement = () => {
const handleDelete = async (id) => {
if (window.confirm('Are you sure you want to delete this customer?')) {
try {
- await axios.delete(`http://localhost:5000/api/customers/${id}`);
+ await api.delete(`/customers/${id}`);
setSuccess('Customer deleted successfully');
fetchCustomers();
} catch (err) {
@@ -218,15 +263,21 @@ const CustomerManagement = () => {
multiline
rows={2}
/>
-
+
+ Car Model
+
+
{
useEffect(() => {
const checkAdminStatus = async () => {
try {
- const response = await axios.get('http://localhost:5000/api/users/me', {
- headers: {
- Authorization: `Bearer ${localStorage.getItem('token')}`,
- },
- });
+ const response = await axios.get('http://localhost:5000/api/users/me');
setIsAdmin(response.data.role === 'admin');
- } catch (error) {
- console.error('Error checking admin status:', error);
+ } catch (err) {
+ console.error('Error checking admin status:', err);
}
};
-
checkAdminStatus();
}, []);
const menuItems = [
{
title: 'Customers',
+ description: 'Manage customer information and car details',
icon: ,
- description: 'View and manage customer information',
path: '/customers',
},
{
title: 'Car Modifications',
- icon: ,
- description: 'Browse available car modifications',
+ description: 'Browse available car modifications and upgrades',
+ icon: ,
path: '/modifications',
},
{
title: 'Contact History',
- icon: ,
- description: 'View customer interaction history',
+ description: 'View and manage customer interactions',
+ icon: ,
path: '/contacts',
},
...(isAdmin ? [{
title: 'User Management',
- icon: ,
description: 'Manage system users and permissions',
+ icon: ,
path: '/users',
}] : []),
];
@@ -68,7 +63,7 @@ const Dashboard = () => {
{menuItems.map((item) => (
-
+
{
{item.icon}
-
- {item.title}
-
-
- {item.description}
-
+
+ {item.title}
+
+
+ {item.description}
+
))}
diff --git a/client/src/components/Register.js b/client/src/components/Register.js
new file mode 100644
index 0000000..d041e56
--- /dev/null
+++ b/client/src/components/Register.js
@@ -0,0 +1,164 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Container,
+ Paper,
+ Typography,
+ TextField,
+ Button,
+ Box,
+ Alert,
+} from '@mui/material';
+import axios from 'axios';
+
+const Register = () => {
+ const navigate = useNavigate();
+ const [formData, setFormData] = useState({
+ name: '',
+ email: '',
+ password: '',
+ confirmPassword: '',
+ });
+ const [error, setError] = useState('');
+ const [success, setSuccess] = useState('');
+
+ const handleChange = (e) => {
+ setFormData({
+ ...formData,
+ [e.target.name]: e.target.value,
+ });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setError('');
+ setSuccess('');
+
+ if (formData.password !== formData.confirmPassword) {
+ setError('Passwords do not match');
+ return;
+ }
+
+ try {
+ const response = await axios.post('http://localhost:5000/api/users/register', {
+ name: formData.name,
+ email: formData.email,
+ password: formData.password,
+ });
+
+ setSuccess('Registration successful! Redirecting to login...');
+ setTimeout(() => {
+ navigate('/login');
+ }, 2000);
+ } catch (err) {
+ setError(err.response?.data?.message || 'Registration failed');
+ }
+ };
+
+ return (
+
+
+
+
+ Register
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+ {success && (
+
+ {success}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Register;
\ No newline at end of file