diff --git a/api-update-sale.tar.gz b/api-update-sale.tar.gz new file mode 100644 index 0000000..b99fd73 Binary files /dev/null and b/api-update-sale.tar.gz differ diff --git a/api/server.js b/api/server.js index f571424..04738c4 100644 --- a/api/server.js +++ b/api/server.js @@ -153,7 +153,7 @@ app.post('/api/filaments', authenticateToken, async (req, res) => { app.put('/api/filaments/:id', authenticateToken, async (req, res) => { const { id } = req.params; - const { tip, finish, boja, boja_hex, refill, spulna, cena } = req.body; + const { tip, finish, boja, boja_hex, refill, spulna, cena, sale_percentage, sale_active, sale_start_date, sale_end_date } = req.body; try { // Ensure refill and spulna are numbers @@ -165,9 +165,12 @@ app.put('/api/filaments/:id', authenticateToken, async (req, res) => { `UPDATE filaments SET tip = $1, finish = $2, boja = $3, boja_hex = $4, refill = $5, spulna = $6, kolicina = $7, cena = $8, + sale_percentage = $9, sale_active = $10, + sale_start_date = $11, sale_end_date = $12, updated_at = CURRENT_TIMESTAMP - WHERE id = $9 RETURNING *`, - [tip, finish, boja, boja_hex, refillNum, spulnaNum, kolicina, cena, id] + WHERE id = $13 RETURNING *`, + [tip, finish, boja, boja_hex, refillNum, spulnaNum, kolicina, cena, + sale_percentage || 0, sale_active || false, sale_start_date, sale_end_date, id] ); res.json(result.rows[0]); } catch (error) { @@ -188,6 +191,51 @@ app.delete('/api/filaments/:id', authenticateToken, async (req, res) => { } }); +// Bulk sale update endpoint +app.post('/api/filaments/sale/bulk', authenticateToken, async (req, res) => { + const { filamentIds, salePercentage, saleStartDate, saleEndDate, enableSale } = req.body; + + try { + let query; + let params; + + if (filamentIds && filamentIds.length > 0) { + // Update specific filaments + query = ` + UPDATE filaments + SET sale_percentage = $1, + sale_active = $2, + sale_start_date = $3, + sale_end_date = $4, + updated_at = CURRENT_TIMESTAMP + WHERE id = ANY($5) + RETURNING *`; + params = [salePercentage || 0, enableSale || false, saleStartDate, saleEndDate, filamentIds]; + } else { + // Update all filaments + query = ` + UPDATE filaments + SET sale_percentage = $1, + sale_active = $2, + sale_start_date = $3, + sale_end_date = $4, + updated_at = CURRENT_TIMESTAMP + RETURNING *`; + params = [salePercentage || 0, enableSale || false, saleStartDate, saleEndDate]; + } + + const result = await pool.query(query, params); + res.json({ + success: true, + updatedCount: result.rowCount, + updatedFilaments: result.rows + }); + } catch (error) { + console.error('Error updating sale:', error); + res.status(500).json({ error: 'Failed to update sale' }); + } +}); + app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); \ No newline at end of file diff --git a/app/upadaj/dashboard/page.tsx b/app/upadaj/dashboard/page.tsx index 99a4cae..e9f37f3 100644 --- a/app/upadaj/dashboard/page.tsx +++ b/app/upadaj/dashboard/page.tsx @@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'; import { filamentService, colorService } from '@/src/services/api'; import { Filament } from '@/src/types/filament'; import { trackEvent } from '@/src/components/MatomoAnalytics'; +import { SaleManager } from '@/src/components/SaleManager'; // Removed unused imports for Bambu Lab color categorization import '@/src/styles/select.css'; @@ -339,6 +340,11 @@ export default function AdminDashboard() { Obriši izabrane ({selectedFilaments.size}) )} + + + {showSaleModal && ( +
+
+

+ Upravljanje popustima +

+ +
+
+ + + {!applyToAll && ( +

+ Izabrano: {selectedFilaments.size} filament{selectedFilaments.size === 1 ? '' : 'a'} +

+ )} +
+ +
+ + setSalePercentage(parseInt(e.target.value) || 0)} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" + /> +
+ +
+ + setSaleStartDate(e.target.value)} + min={getCurrentDateTime()} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" + /> +
+ +
+ + setSaleEndDate(e.target.value)} + min={saleStartDate || getCurrentDateTime()} + className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100" + /> +
+ +
+ +
+
+ +
+ + +
+
+
+ )} + + ); +} \ No newline at end of file diff --git a/src/services/api.ts b/src/services/api.ts index 73b2bd2..394859c 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -87,6 +87,17 @@ export const filamentService = { const response = await api.delete(`/filaments/${id}`); return response.data; }, + + updateBulkSale: async (data: { + filamentIds?: string[]; + salePercentage: number; + saleStartDate?: string; + saleEndDate?: string; + enableSale: boolean; + }) => { + const response = await api.post('/filaments/sale/bulk', data); + return response.data; + }, }; export default api; \ No newline at end of file diff --git a/src/types/filament.ts b/src/types/filament.ts index 7e7d092..1b62ea6 100644 --- a/src/types/filament.ts +++ b/src/types/filament.ts @@ -11,4 +11,8 @@ export interface Filament { status?: string; created_at?: string; // Using snake_case to match database updated_at?: string; // Using snake_case to match database + sale_percentage?: number; + sale_active?: boolean; + sale_start_date?: string; + sale_end_date?: string; } \ No newline at end of file