diff --git a/api/server.js b/api/server.js index 04738c4..22d6c77 100644 --- a/api/server.js +++ b/api/server.js @@ -236,6 +236,128 @@ app.post('/api/filaments/sale/bulk', authenticateToken, async (req, res) => { } }); +// Color request endpoints + +// Get all color requests (admin only) +app.get('/api/color-requests', authenticateToken, async (req, res) => { + try { + const result = await pool.query( + 'SELECT * FROM color_requests ORDER BY created_at DESC' + ); + res.json(result.rows); + } catch (error) { + console.error('Error fetching color requests:', error); + res.status(500).json({ error: 'Failed to fetch color requests' }); + } +}); + +// Submit a new color request (public) +app.post('/api/color-requests', async (req, res) => { + try { + const { + color_name, + material_type, + finish_type, + user_email, + user_name, + description, + reference_url + } = req.body; + + // Check if similar request already exists + const existingRequest = await pool.query( + `SELECT id, request_count FROM color_requests + WHERE LOWER(color_name) = LOWER($1) + AND material_type = $2 + AND (finish_type = $3 OR (finish_type IS NULL AND $3 IS NULL)) + AND status = 'pending'`, + [color_name, material_type, finish_type] + ); + + if (existingRequest.rows.length > 0) { + // Increment request count for existing request + const result = await pool.query( + `UPDATE color_requests + SET request_count = request_count + 1, + updated_at = CURRENT_TIMESTAMP + WHERE id = $1 + RETURNING *`, + [existingRequest.rows[0].id] + ); + res.json({ + message: 'Your request has been added to an existing request for this color', + request: result.rows[0] + }); + } else { + // Create new request + const result = await pool.query( + `INSERT INTO color_requests + (color_name, material_type, finish_type, user_email, user_name, description, reference_url) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING *`, + [color_name, material_type, finish_type, user_email, user_name, description, reference_url] + ); + res.json({ + message: 'Color request submitted successfully', + request: result.rows[0] + }); + } + } catch (error) { + console.error('Error creating color request:', error); + res.status(500).json({ error: 'Failed to submit color request' }); + } +}); + +// Update color request status (admin only) +app.put('/api/color-requests/:id', authenticateToken, async (req, res) => { + try { + const { id } = req.params; + const { status, admin_notes } = req.body; + + const result = await pool.query( + `UPDATE color_requests + SET status = $1, + admin_notes = $2, + processed_at = CURRENT_TIMESTAMP, + processed_by = $3, + updated_at = CURRENT_TIMESTAMP + WHERE id = $4 + RETURNING *`, + [status, admin_notes, req.user.username, id] + ); + + if (result.rows.length === 0) { + return res.status(404).json({ error: 'Color request not found' }); + } + + res.json(result.rows[0]); + } catch (error) { + console.error('Error updating color request:', error); + res.status(500).json({ error: 'Failed to update color request' }); + } +}); + +// Delete color request (admin only) +app.delete('/api/color-requests/:id', authenticateToken, async (req, res) => { + try { + const { id } = req.params; + + const result = await pool.query( + 'DELETE FROM color_requests WHERE id = $1 RETURNING *', + [id] + ); + + if (result.rows.length === 0) { + return res.status(404).json({ error: 'Color request not found' }); + } + + res.json({ message: 'Color request deleted successfully' }); + } catch (error) { + console.error('Error deleting color request:', error); + res.status(500).json({ error: 'Failed to delete color request' }); + } +}); + app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index f3ce6b9..6f08546 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import { FilamentTableV2 } from '../src/components/FilamentTableV2'; import { SaleCountdown } from '../src/components/SaleCountdown'; +import ColorRequestModal from '../src/components/ColorRequestModal'; import { Filament } from '../src/types/filament'; import { filamentService } from '../src/services/api'; import { trackEvent } from '../src/components/MatomoAnalytics'; @@ -14,6 +15,7 @@ export default function Home() { const [darkMode, setDarkMode] = useState(false); const [mounted, setMounted] = useState(false); const [resetKey, setResetKey] = useState(0); + const [showColorRequestModal, setShowColorRequestModal] = useState(false); // Removed V1/V2 toggle - now only using V2 // Initialize dark mode from localStorage after mounting @@ -173,6 +175,19 @@ export default function Home() { Pozovi +381 67 710 2845 + + + {/* Color Request Modal */} + setShowColorRequestModal(false)} + /> ); } \ No newline at end of file diff --git a/app/upadaj/dashboard/page.tsx b/app/upadaj/dashboard/page.tsx index 0f40e8e..72dc5b0 100644 --- a/app/upadaj/dashboard/page.tsx +++ b/app/upadaj/dashboard/page.tsx @@ -377,6 +377,18 @@ export default function AdminDashboard() { selectedFilaments={selectedFilaments} onSaleUpdate={fetchFilaments} /> + +