diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..9db8f88 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,2 @@ +# API Configuration +VITE_API_URL=http://localhost:8080 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 55722f7..0ab1438 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Truck } from 'lucide-react'; import { Dashboard } from './pages/Dashboard'; import { DeliveryListPage } from './pages/DeliveryListPage'; @@ -10,8 +10,17 @@ function App() { const [selectedDate, setSelectedDate] = useState(''); const [isFormOpen, setIsFormOpen] = useState(false); const [formDate, setFormDate] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); const addDelivery = useDeliveryStore(state => state.addDelivery); + const fetchDeliveryCounts = useDeliveryStore(state => state.fetchDeliveryCounts); + + // Refresh counts when form closes + useEffect(() => { + if (!isFormOpen) { + fetchDeliveryCounts(); + } + }, [isFormOpen, fetchDeliveryCounts]); const handleDateSelect = (date: string) => { setSelectedDate(date); @@ -29,13 +38,22 @@ function App() { setIsFormOpen(true); }; - const handleFormSubmit = (data: Parameters[0]) => { - addDelivery(data); - setIsFormOpen(false); - - if (data.date !== new Date().toLocaleDateString('ru-RU').split('.').join('-')) { - setSelectedDate(data.date); - setView('delivery-list'); + const handleFormSubmit = async (data: Parameters[0]) => { + setIsSubmitting(true); + try { + await addDelivery(data); + setIsFormOpen(false); + + // If created for different date, navigate to that date + const today = new Date().toLocaleDateString('ru-RU').split('.').join('-'); + if (data.date !== today) { + setSelectedDate(data.date); + setView('delivery-list'); + } + } catch { + // Error is handled by store + } finally { + setIsSubmitting(false); } }; @@ -76,6 +94,7 @@ function App() { onClose={() => setIsFormOpen(false)} onSubmit={handleFormSubmit} defaultDate={formDate} + isSubmitting={isSubmitting} /> ); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts new file mode 100644 index 0000000..6cfc25f --- /dev/null +++ b/frontend/src/api/client.ts @@ -0,0 +1,58 @@ +const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080'; + +export class ApiError extends Error { + status: number; + details?: unknown; + + constructor(message: string, status: number, details?: unknown) { + super(message); + this.name = 'ApiError'; + this.status = status; + this.details = details; + } +} + +async function fetchApi( + endpoint: string, + options?: RequestInit +): Promise { + const url = `${API_BASE_URL}${endpoint}`; + + const response = await fetch(url, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers, + }, + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => null); + throw new ApiError( + errorData?.error || `HTTP ${response.status}`, + response.status, + errorData?.details + ); + } + + return response.json(); +} + +export const api = { + get: (endpoint: string) => fetchApi(endpoint, { method: 'GET' }), + + post: (endpoint: string, data: unknown) => + fetchApi(endpoint, { + method: 'POST', + body: JSON.stringify(data), + }), + + patch: (endpoint: string, data?: unknown) => + fetchApi(endpoint, { + method: 'PATCH', + body: data ? JSON.stringify(data) : undefined, + }), + + delete: (endpoint: string) => + fetchApi(endpoint, { method: 'DELETE' }), +}; diff --git a/frontend/src/api/deliveries.ts b/frontend/src/api/deliveries.ts new file mode 100644 index 0000000..bbc32d5 --- /dev/null +++ b/frontend/src/api/deliveries.ts @@ -0,0 +1,148 @@ +import { api } from './client'; +import type { Delivery, PickupLocation, DeliveryStatus } from '../types'; + +// Types matching backend responses +interface BackendDelivery { + id: string; + date: string; // YYYY-MM-DD from pgtype.Date + pickup_location: PickupLocation; + product_name: string; + address: string; + phone: string; + additional_phone: string | null; + has_elevator: boolean; + comment: string; + status: DeliveryStatus; + created_at: string; // ISO timestamp + updated_at: string; // ISO timestamp +} + +interface DeliveryCount { + date: string; // YYYY-MM-DD + count: number; +} + +// API Response types +interface GetDeliveriesResponse { + deliveries: BackendDelivery[]; +} + +interface GetDeliveryResponse { + delivery: BackendDelivery; +} + +interface GetDeliveryCountResponse { + counts: DeliveryCount[]; +} + +interface CreateDeliveryResponse { + message: string; + id: string; +} + +interface UpdateDeliveryResponse { + message: string; +} + +// Convert backend date format (YYYY-MM-DD) to frontend format (DD-MM-YYYY) +function backendDateToFrontend(dateStr: string): string { + const [year, month, day] = dateStr.split('-'); + return `${day}-${month}-${year}`; +} + +// Convert frontend date format (DD-MM-YYYY) to backend format (YYYY-MM-DD) +export function frontendDateToBackend(dateStr: string): string { + const [day, month, year] = dateStr.split('-'); + return `${year}-${month}-${day}`; +} + +// Map backend delivery to frontend delivery +function mapBackendToFrontend(backend: BackendDelivery): Delivery { + return { + id: backend.id, + date: backendDateToFrontend(backend.date), + pickupLocation: backend.pickup_location, + productName: backend.product_name, + address: backend.address, + phone: backend.phone, + additionalPhone: backend.additional_phone || undefined, + hasElevator: backend.has_elevator, + comment: backend.comment, + status: backend.status, + createdAt: new Date(backend.created_at).getTime(), + updatedAt: new Date(backend.updated_at).getTime(), + }; +} + +// Delivery API methods +export const deliveriesApi = { + // Get deliveries by date (DD-MM-YYYY) + getByDate: async (date: string): Promise => { + const response = await api.get( + `/api/deliveries?date=${encodeURIComponent(date)}` + ); + return response.deliveries.map(mapBackendToFrontend); + }, + + // Get single delivery by ID + getById: async (id: string): Promise => { + const response = await api.get(`/api/deliveries/${id}`); + return mapBackendToFrontend(response.delivery); + }, + + // Get delivery counts by date + getCounts: async (): Promise> => { + const response = await api.get('/api/deliveries/count'); + const counts: Record = {}; + response.counts.forEach(({ date, count }) => { + counts[backendDateToFrontend(date)] = count; + }); + return counts; + }, + + // Create delivery + create: async ( + data: Omit + ): Promise => { + const payload = { + date: data.date, + pickup_location: data.pickupLocation, + product_name: data.productName, + address: data.address, + phone: data.phone, + additional_phone: data.additionalPhone || '', + has_elevator: data.hasElevator, + comment: data.comment, + }; + const response = await api.post('/api/deliveries', payload); + return response.id; + }, + + // Update delivery + update: async ( + id: string, + data: Omit + ): Promise => { + const payload = { + date: data.date, + pickup_location: data.pickupLocation, + product_name: data.productName, + address: data.address, + phone: data.phone, + additional_phone: data.additionalPhone || '', + has_elevator: data.hasElevator, + comment: data.comment, + }; + await api.patch(`/api/deliveries/${id}`, payload); + }, + + // Update delivery status + updateStatus: async (id: string, status: DeliveryStatus): Promise => { + await api.patch(`/api/deliveries/${id}/status`, { status }); + }, + + // Delete delivery + delete: async (id: string): Promise => { + await api.delete(`/api/deliveries/${id}`); + }, +}; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..6c887d1 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,2 @@ +export { api, ApiError } from './client'; +export { deliveriesApi, frontendDateToBackend } from './deliveries'; diff --git a/frontend/src/components/delivery/DeliveryForm.tsx b/frontend/src/components/delivery/DeliveryForm.tsx index f0ed8c9..2c97d9c 100644 --- a/frontend/src/components/delivery/DeliveryForm.tsx +++ b/frontend/src/components/delivery/DeliveryForm.tsx @@ -6,9 +6,10 @@ import { pickupLocationLabels } from '../../types'; interface DeliveryFormProps { isOpen: boolean; onClose: () => void; - onSubmit: (delivery: Omit) => void; + onSubmit: (delivery: Omit) => void | Promise; initialData?: Delivery | null; defaultDate?: string; + isSubmitting?: boolean; } const pickupOptions: { value: PickupLocation; label: string }[] = [ @@ -18,7 +19,7 @@ const pickupOptions: { value: PickupLocation; label: string }[] = [ { value: 'galaktika', label: pickupLocationLabels.galaktika }, ]; -export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDate }: DeliveryFormProps) => { +export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDate, isSubmitting }: DeliveryFormProps) => { const [formData, setFormData] = useState({ date: defaultDate || new Date().toLocaleDateString('ru-RU').split('.').join('-'), pickupLocation: 'warehouse' as PickupLocation, @@ -85,11 +86,11 @@ export const DeliveryForm = ({ isOpen, onClose, onSubmit, initialData, defaultDa title={initialData ? 'Редактировать доставку' : 'Новая доставка'} footer={ <> - - } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 2d3facb..bef5202 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -2,17 +2,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' -import { mockDeliveries } from './utils/mockData' -import { useDeliveryStore } from './stores/deliveryStore' - -// Seed mock data if no data exists -const stored = localStorage.getItem('delivery-tracker-data') -if (!stored) { - const store = useDeliveryStore.getState() - mockDeliveries.forEach(delivery => { - store.addDelivery(delivery) - }) -} createRoot(document.getElementById('root')!).render( diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 793bc7b..49af7b7 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -1,8 +1,9 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Plus, Printer, ChevronRight, CalendarDays } from 'lucide-react'; import { format, startOfMonth, endOfMonth, eachDayOfInterval, isToday } from 'date-fns'; import { ru } from 'date-fns/locale'; import { useDeliveryStore } from '../stores/deliveryStore'; +import type { Delivery } from '../types'; import { Button } from '../components/ui/Button'; import { Card } from '../components/ui/Card'; @@ -12,21 +13,36 @@ interface DashboardProps { } export const Dashboard = ({ onDateSelect, onAddDelivery }: DashboardProps) => { - const deliveries = useDeliveryStore(state => state.deliveries); + const deliveryCounts = useDeliveryStore(state => state.deliveryCounts); + const fetchDeliveryCounts = useDeliveryStore(state => state.fetchDeliveryCounts); const [currentMonth, setCurrentMonth] = useState(new Date()); + // Fetch counts on mount + useEffect(() => { + fetchDeliveryCounts(); + }, [fetchDeliveryCounts]); + const monthStart = startOfMonth(currentMonth); const monthEnd = endOfMonth(currentMonth); const days = eachDayOfInterval({ start: monthStart, end: monthEnd }); const getCountForDate = (date: Date) => { const dateStr = format(date, 'dd-MM-yyyy'); - return deliveries.filter(d => d.date === dateStr).length; + return deliveryCounts[dateStr] || 0; }; const handlePrintDay = (date: Date) => { const dateStr = format(date, 'dd-MM-yyyy'); - const dayDeliveries = deliveries.filter(d => d.date === dateStr); + const fetchDeliveriesByDate = useDeliveryStore.getState().fetchDeliveriesByDate; + + // Fetch and print + fetchDeliveriesByDate(dateStr).then(() => { + const deliveries = useDeliveryStore.getState().deliveries; + printDeliveries(date, deliveries); + }); + }; + + const printDeliveries = (date: Date, dayDeliveries: Delivery[]) => { const printWindow = window.open('', '_blank'); if (!printWindow) return; @@ -57,7 +73,7 @@ export const Dashboard = ({ onDateSelect, onAddDelivery }: DashboardProps) => { Телефон Комментарий - ${dayDeliveries.map(d => ` + ${dayDeliveries.map((d: Delivery) => ` ${d.status === 'new' ? 'Новое' : 'Доставлено'} ${d.pickupLocation === 'warehouse' ? 'Склад' : d.pickupLocation === 'symbat' ? 'Сымбат' : d.pickupLocation === 'nursaya' ? 'Нурсая' : 'Галактика'} @@ -74,7 +90,7 @@ export const Dashboard = ({ onDateSelect, onAddDelivery }: DashboardProps) => { printWindow.document.write(html); printWindow.document.close(); - printWindow.print(); + printWindow?.print(); }; const navigateMonth = (direction: 'prev' | 'next') => { diff --git a/frontend/src/pages/DeliveryListPage.tsx b/frontend/src/pages/DeliveryListPage.tsx index cf2af1a..e076360 100644 --- a/frontend/src/pages/DeliveryListPage.tsx +++ b/frontend/src/pages/DeliveryListPage.tsx @@ -1,5 +1,5 @@ -import { useState } from 'react'; -import { ArrowLeft, Filter } from 'lucide-react'; +import { useState, useEffect } from 'react'; +import { ArrowLeft, Filter, Loader2, AlertCircle } from 'lucide-react'; import { useDeliveryStore } from '../stores/deliveryStore'; import { DeliveryList as DeliveryListComponent } from '../components/delivery/DeliveryList'; import { DeliveryForm } from '../components/delivery/DeliveryForm'; @@ -15,16 +15,26 @@ interface DeliveryListPageProps { export const DeliveryListPage = ({ selectedDate, onBack }: DeliveryListPageProps) => { const deliveries = useDeliveryStore(state => state.deliveries); + const isLoading = useDeliveryStore(state => state.isLoading); + const error = useDeliveryStore(state => state.error); + const fetchDeliveriesByDate = useDeliveryStore(state => state.fetchDeliveriesByDate); const toggleStatus = useDeliveryStore(state => state.toggleStatus); const deleteDelivery = useDeliveryStore(state => state.deleteDelivery); const updateDelivery = useDeliveryStore(state => state.updateDelivery); const addDelivery = useDeliveryStore(state => state.addDelivery); + const clearError = useDeliveryStore(state => state.clearError); + + // Fetch deliveries when date changes + useEffect(() => { + fetchDeliveriesByDate(selectedDate); + }, [selectedDate, fetchDeliveriesByDate]); const [isFormOpen, setIsFormOpen] = useState(false); const [editingDelivery, setEditingDelivery] = useState(null); const [pickupFilter, setPickupFilter] = useState('all'); - const dayDeliveries = deliveries.filter(d => d.date === selectedDate); + // Use all deliveries from store (already filtered by API) + const dayDeliveries = deliveries; const filteredDeliveries = pickupFilter === 'all' ? dayDeliveries : dayDeliveries.filter(d => d.pickupLocation === pickupFilter); @@ -37,8 +47,11 @@ export const DeliveryListPage = ({ selectedDate, onBack }: DeliveryListPageProps { value: 'galaktika', label: pickupLocationLabels.galaktika }, ]; - const handleStatusChange = (id: string) => { - toggleStatus(id); + const handleStatusChange = async (id: string) => { + const delivery = deliveries.find(d => d.id === id); + if (delivery) { + await toggleStatus(id, delivery.status); + } }; const handleEdit = (delivery: Delivery) => { @@ -46,19 +59,27 @@ export const DeliveryListPage = ({ selectedDate, onBack }: DeliveryListPageProps setIsFormOpen(true); }; - const handleDelete = (id: string) => { + const handleDelete = async (id: string) => { if (confirm('Удалить эту доставку?')) { - deleteDelivery(id); + try { + await deleteDelivery(id); + } catch { + // Error is handled by store + } } }; - const handleSubmit = (data: Omit) => { - if (editingDelivery) { - updateDelivery(editingDelivery.id, data); - } else { - addDelivery(data); + const handleSubmit = async (data: Omit) => { + try { + if (editingDelivery) { + await updateDelivery(editingDelivery.id, data); + } else { + await addDelivery(data); + } + setEditingDelivery(null); + } catch { + // Error is handled by store } - setEditingDelivery(null); }; const handleAdd = () => { @@ -94,14 +115,30 @@ export const DeliveryListPage = ({ selectedDate, onBack }: DeliveryListPageProps - + {isLoading ? ( +
+ +
+ ) : error ? ( +
+ +
+

{error}

+
+ +
+ ) : ( + + )} ) => void; - updateDelivery: (id: string, updates: Partial) => void; - deleteDelivery: (id: string) => void; - toggleStatus: (id: string) => void; + deliveryCounts: Record; + + // Loading states + isLoading: boolean; + isLoadingCounts: boolean; + error: string | null; + + // Actions + fetchDeliveriesByDate: (date: string) => Promise; + fetchDeliveryCounts: () => Promise; + addDelivery: (delivery: Omit) => Promise; + updateDelivery: (id: string, updates: Omit) => Promise; + deleteDelivery: (id: string) => Promise; + toggleStatus: (id: string, currentStatus: DeliveryStatus) => Promise; getDeliveriesByDate: (date: string) => Delivery[]; getDeliveriesByDateRange: (startDate: string, endDate: string) => Delivery[]; getDeliveryCountsByDate: () => Record; + clearError: () => void; } -const STORAGE_KEY = 'delivery-tracker-data'; +export const useDeliveryStore = create()((set, get) => ({ + // Initial state + deliveries: [], + deliveryCounts: {}, + isLoading: false, + isLoadingCounts: false, + error: null, -export const useDeliveryStore = create()( - persist( - (set, get) => ({ - deliveries: [], - - addDelivery: (delivery) => { - const now = Date.now(); - const newDelivery: Delivery = { - ...delivery, - id: crypto.randomUUID(), - createdAt: now, - updatedAt: now, - }; - set((state) => ({ - deliveries: [...state.deliveries, newDelivery], - })); - }, - - updateDelivery: (id, updates) => { - set((state) => ({ - deliveries: state.deliveries.map((d) => - d.id === id ? { ...d, ...updates, updatedAt: Date.now() } : d - ), - })); - }, - - deleteDelivery: (id) => { - set((state) => ({ - deliveries: state.deliveries.filter((d) => d.id !== id), - })); - }, - - toggleStatus: (id) => { - set((state) => ({ - deliveries: state.deliveries.map((d) => - d.id === id - ? { ...d, status: d.status === 'new' ? 'delivered' : 'new', updatedAt: Date.now() } - : d - ), - })); - }, - - getDeliveriesByDate: (date) => { - return get().deliveries.filter((d) => d.date === date); - }, - - getDeliveriesByDateRange: (startDate, endDate) => { - return get().deliveries.filter((d) => { - const date = d.date; - return date >= startDate && date <= endDate; - }); - }, - - getDeliveryCountsByDate: () => { - const counts: Record = {}; - get().deliveries.forEach((d) => { - counts[d.date] = (counts[d.date] || 0) + 1; - }); - return counts; - }, - }), - { - name: STORAGE_KEY, + // Fetch deliveries for a specific date + fetchDeliveriesByDate: async (date: string) => { + set({ isLoading: true, error: null }); + try { + const deliveries = await deliveriesApi.getByDate(date); + set({ deliveries, isLoading: false }); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to fetch deliveries', + isLoading: false, + }); } - ) -); + }, + + // Fetch delivery counts for calendar + fetchDeliveryCounts: async () => { + set({ isLoadingCounts: true, error: null }); + try { + const counts = await deliveriesApi.getCounts(); + set({ deliveryCounts: counts, isLoadingCounts: false }); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to fetch counts', + isLoadingCounts: false, + }); + } + }, + + // Add new delivery + addDelivery: async (delivery) => { + set({ isLoading: true, error: null }); + try { + await deliveriesApi.create(delivery); + // Refresh deliveries for that date + await get().fetchDeliveriesByDate(delivery.date); + // Refresh counts + await get().fetchDeliveryCounts(); + set({ isLoading: false }); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to create delivery', + isLoading: false, + }); + throw err; + } + }, + + // Update delivery + updateDelivery: async (id, updates) => { + set({ isLoading: true, error: null }); + try { + await deliveriesApi.update(id, updates); + // Refresh deliveries for that date + await get().fetchDeliveriesByDate(updates.date); + // Refresh counts (in case date changed) + await get().fetchDeliveryCounts(); + set({ isLoading: false }); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to update delivery', + isLoading: false, + }); + throw err; + } + }, + + // Delete delivery + deleteDelivery: async (id) => { + set({ isLoading: true, error: null }); + try { + await deliveriesApi.delete(id); + // Remove from local state + set((state) => ({ + deliveries: state.deliveries.filter((d) => d.id !== id), + isLoading: false, + })); + // Refresh counts + await get().fetchDeliveryCounts(); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to delete delivery', + isLoading: false, + }); + throw err; + } + }, + + // Toggle delivery status + toggleStatus: async (id, currentStatus) => { + set({ isLoading: true, error: null }); + try { + const newStatus = currentStatus === 'new' ? 'delivered' : 'new'; + await deliveriesApi.updateStatus(id, newStatus); + // Update local state + set((state) => ({ + deliveries: state.deliveries.map((d) => + d.id === id + ? { ...d, status: newStatus, updatedAt: Date.now() } + : d + ), + isLoading: false, + })); + } catch (err) { + set({ + error: err instanceof Error ? err.message : 'Failed to update status', + isLoading: false, + }); + throw err; + } + }, + + // Getters (local filtering) + getDeliveriesByDate: (date) => { + return get().deliveries.filter((d) => d.date === date); + }, + + getDeliveriesByDateRange: (startDate, endDate) => { + return get().deliveries.filter((d) => { + const date = d.date; + return date >= startDate && date <= endDate; + }); + }, + + getDeliveryCountsByDate: () => { + return get().deliveryCounts; + }, + + clearError: () => set({ error: null }), +})); diff --git a/frontend/src/utils/mockData.ts b/frontend/src/utils/mockData.ts deleted file mode 100644 index 651cf29..0000000 --- a/frontend/src/utils/mockData.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { Delivery } from '../types'; - -export const mockDeliveries: Omit[] = [ - { - date: new Date().toLocaleDateString('ru-RU').split('.').join('-'), - pickupLocation: 'symbat', - productName: 'Диван прямой Милан', - address: 'ул. Ленина, д. 10, кв. 25', - phone: '+7 (771)-123-45-67', - additionalPhone: '', - hasElevator: true, - comment: 'Доставить после 18:00', - status: 'new', - }, - { - date: new Date().toLocaleDateString('ru-RU').split('.').join('-'), - pickupLocation: 'warehouse', - productName: 'Шкаф двухдверный', - address: 'ул. Гагарина, д. 5, офис 304', - phone: '+7 (777)-234-56-78', - additionalPhone: '+7 (702)-111-22-33', - hasElevator: false, - comment: 'Предварительно позвонить', - status: 'new', - }, - { - date: new Date(Date.now() + 86400000).toLocaleDateString('ru-RU').split('.').join('-'), - pickupLocation: 'nursaya', - productName: 'Стол обеденный + 4 стула', - address: 'пр. Мира, д. 15', - phone: '+7 (705)-345-67-89', - additionalPhone: '', - hasElevator: true, - comment: '', - status: 'new', - }, - { - date: new Date(Date.now() - 86400000).toLocaleDateString('ru-RU').split('.').join('-'), - pickupLocation: 'galaktika', - productName: 'Матрас ортопедический 160x200', - address: 'ул. Пушкина, д. 20', - phone: '+7 (701)-456-78-90', - additionalPhone: '', - hasElevator: false, - comment: 'Доставлено успешно', - status: 'delivered', - }, - { - date: new Date(Date.now() + 172800000).toLocaleDateString('ru-RU').split('.').join('-'), - pickupLocation: 'warehouse', - productName: 'Кресло реклайнер', - address: 'ул. Чехова, д. 8, кв. 12', - phone: '+7 (776)-567-89-01', - additionalPhone: '', - hasElevator: true, - comment: 'Подъезд с торца', - status: 'new', - }, -];