diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..f8b4888 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..2698c3b --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,10 @@ +{ + "ExpandedNodes": [ + "", + "\\public", + "\\src", + "\\src\\pages" + ], + "SelectedNode": "\\src\\App.tsx", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/logistics-app/FileContentIndex/07dfedf3-4b38-4be1-b6d6-5119e0f01a73.vsidx b/.vs/logistics-app/FileContentIndex/07dfedf3-4b38-4be1-b6d6-5119e0f01a73.vsidx new file mode 100644 index 0000000..3872d21 Binary files /dev/null and b/.vs/logistics-app/FileContentIndex/07dfedf3-4b38-4be1-b6d6-5119e0f01a73.vsidx differ diff --git a/.vs/logistics-app/FileContentIndex/7e5bbd21-0bd4-47e0-8ee6-9684a8c31103.vsidx b/.vs/logistics-app/FileContentIndex/7e5bbd21-0bd4-47e0-8ee6-9684a8c31103.vsidx new file mode 100644 index 0000000..796f7a6 Binary files /dev/null and b/.vs/logistics-app/FileContentIndex/7e5bbd21-0bd4-47e0-8ee6-9684a8c31103.vsidx differ diff --git a/.vs/logistics-app/FileContentIndex/c5a6c98d-7f4d-4b71-b220-310ab288f298.vsidx b/.vs/logistics-app/FileContentIndex/c5a6c98d-7f4d-4b71-b220-310ab288f298.vsidx new file mode 100644 index 0000000..607184a Binary files /dev/null and b/.vs/logistics-app/FileContentIndex/c5a6c98d-7f4d-4b71-b220-310ab288f298.vsidx differ diff --git a/.vs/logistics-app/FileContentIndex/f8f06d21-6130-4351-a6b3-f29d63051768.vsidx b/.vs/logistics-app/FileContentIndex/f8f06d21-6130-4351-a6b3-f29d63051768.vsidx new file mode 100644 index 0000000..ade0caa Binary files /dev/null and b/.vs/logistics-app/FileContentIndex/f8f06d21-6130-4351-a6b3-f29d63051768.vsidx differ diff --git a/.vs/logistics-app/FileContentIndex/feeecddc-f0ee-4bfe-9db7-a14e32700516.vsidx b/.vs/logistics-app/FileContentIndex/feeecddc-f0ee-4bfe-9db7-a14e32700516.vsidx new file mode 100644 index 0000000..8cb7ff0 Binary files /dev/null and b/.vs/logistics-app/FileContentIndex/feeecddc-f0ee-4bfe-9db7-a14e32700516.vsidx differ diff --git a/.vs/logistics-app/config/applicationhost.config b/.vs/logistics-app/config/applicationhost.config new file mode 100644 index 0000000..269dc55 --- /dev/null +++ b/.vs/logistics-app/config/applicationhost.config @@ -0,0 +1,1021 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.vs/logistics-app/v17/.wsuo b/.vs/logistics-app/v17/.wsuo new file mode 100644 index 0000000..d82f322 Binary files /dev/null and b/.vs/logistics-app/v17/.wsuo differ diff --git a/.vs/logistics-app/v17/DocumentLayout.json b/.vs/logistics-app/v17/DocumentLayout.json new file mode 100644 index 0000000..c78ecf0 --- /dev/null +++ b/.vs/logistics-app/v17/DocumentLayout.json @@ -0,0 +1,248 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\KhasanovAM\\logistics-app\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\App.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\App.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\tsconfig.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:tsconfig.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\OrdersPage.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\pages\\OrdersPage.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\public\\index.html||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:public\\index.html||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\VehicleDetailPage.tsx||{8B382828-6202-11D1-8870-0000F87579D2}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\pages\\VehicleDetailPage.tsx||{8B382828-6202-11D1-8870-0000F87579D2}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\services\\api.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\services\\api.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\index.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\index.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\index.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\index.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\setupTests.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\setupTests.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\App.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\App.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\KhasanovAM\\logistics-app\\src\\components\\WaybillWidget.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:src\\components\\WaybillWidget.tsx||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 15, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:129:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}" + }, + { + "$type": "Bookmark", + "Name": "ST:130:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}" + }, + { + "$type": "Bookmark", + "Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{4a9b7e51-aa16-11d0-a8c5-00a0c921a4d2}" + }, + { + "$type": "Bookmark", + "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "tsconfig.json", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\tsconfig.json", + "RelativeDocumentMoniker": "tsconfig.json", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\tsconfig.json", + "RelativeToolTip": "tsconfig.json", + "ViewState": "AgIAAAAAAAAAAAAAAAAAABoAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|", + "WhenOpened": "2025-11-21T04:11:35.007Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 3, + "Title": "index.html", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\public\\index.html", + "RelativeDocumentMoniker": "public\\index.html", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\public\\index.html", + "RelativeToolTip": "public\\index.html", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAB4AAAAZAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001512|", + "WhenOpened": "2025-11-21T04:08:55.71Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 2, + "Title": "OrdersPage.tsx", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\OrdersPage.tsx", + "RelativeDocumentMoniker": "src\\pages\\OrdersPage.tsx", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\OrdersPage.tsx", + "RelativeToolTip": "src\\pages\\OrdersPage.tsx", + "ViewState": "AgIAAAwAAAAAAAAAAAAAACsAAAAcAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T04:02:02.174Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 6, + "Title": "api.ts", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\services\\api.ts", + "RelativeDocumentMoniker": "src\\services\\api.ts", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\services\\api.ts", + "RelativeToolTip": "src\\services\\api.ts", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAA0AAAACAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T04:00:40.26Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 7, + "Title": "index.tsx", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\index.tsx", + "RelativeDocumentMoniker": "src\\index.tsx", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\index.tsx", + "RelativeToolTip": "src\\index.tsx", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T04:00:23.973Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 8, + "Title": "index.css", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\index.css", + "RelativeDocumentMoniker": "src\\index.css", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\index.css", + "RelativeToolTip": "src\\index.css", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2025-11-21T04:00:18.151Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 4, + "Title": "package.json", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\package.json", + "RelativeDocumentMoniker": "package.json", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\package.json", + "RelativeToolTip": "package.json", + "ViewState": "AgIAABUAAAAAAAAAAAAAACEAAAAFAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|", + "WhenOpened": "2025-11-21T03:58:43.953Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 9, + "Title": "setupTests.ts", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\setupTests.ts", + "RelativeDocumentMoniker": "src\\setupTests.ts", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\setupTests.ts", + "RelativeToolTip": "src\\setupTests.ts", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T03:56:29.748Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 10, + "Title": "App.css", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\App.css", + "RelativeDocumentMoniker": "src\\App.css", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\App.css", + "RelativeToolTip": "src\\App.css", + "ViewState": "AgIAAAAAAAAAAAAAAAAAABYAAAAgAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2025-11-21T03:54:33.459Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "App.tsx", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\App.tsx", + "RelativeDocumentMoniker": "src\\App.tsx", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\App.tsx", + "RelativeToolTip": "src\\App.tsx", + "ViewState": "AgIAAAAAAAAAAAAAAAAAADYAAAAGAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T03:50:44.515Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 5, + "Title": "VehicleDetailPage.tsx", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\VehicleDetailPage.tsx", + "RelativeDocumentMoniker": "src\\pages\\VehicleDetailPage.tsx", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\pages\\VehicleDetailPage.tsx", + "RelativeToolTip": "src\\pages\\VehicleDetailPage.tsx", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAB4AAAA/AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T03:50:29.56Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 11, + "Title": "WaybillWidget.tsx", + "DocumentMoniker": "C:\\Users\\KhasanovAM\\logistics-app\\src\\components\\WaybillWidget.tsx", + "RelativeDocumentMoniker": "src\\components\\WaybillWidget.tsx", + "ToolTip": "C:\\Users\\KhasanovAM\\logistics-app\\src\\components\\WaybillWidget.tsx", + "RelativeToolTip": "src\\components\\WaybillWidget.tsx", + "ViewState": "AgIAALAAAAAAAAAAAAAIwMgAAAA1AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|", + "WhenOpened": "2025-11-21T03:50:12.585Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..4e2397e Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..42b8b46 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + + + { + "name": "Debug React App", + "type": "firefox", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}/src", + "sourceMapPathOverrides": { + "webpack:///src/*": "${webRoot}/*" + } + } + ] +} \ No newline at end of file diff --git a/db.json b/db.json new file mode 100644 index 0000000..5cf59bf --- /dev/null +++ b/db.json @@ -0,0 +1,212 @@ +{ + "orders": [ + { + "id": "1", + "clientName": "Иванов Иван Иванович", + "orderCost": 15000, + "orderDate": "2025-01-15", + "status": "in_progress", + "address": "ул. Ленина, 10", + "description": "Доставка строительных материалов" + }, + { + "id": "2", + "clientName": "ООО 'СтройМир'", + "orderCost": 25000, + "orderDate": "2025-01-16", + "status": "in_progress", + "address": "пр. Мира, 25", + "description": "Перевозка оборудования" + }, + { + "id": "3", + "clientName": "ЗАО 'ТехноПром'", + "orderCost": 18000, + "orderDate": "2025-01-17", + "status": "completed", + "address": "ул. Промышленная, 45", + "description": "Доставка офисной мебели" + }, + { + "id": "4", + "clientName": "Сергеев Алексей Петрович", + "orderCost": 12000, + "orderDate": "2025-01-18", + "status": "pending", + "address": "пр. Космонавтов, 78", + "description": "Переезд квартиры" + }, + { + "id": "5", + "clientName": "ИП Козлова Марина Сергеевна", + "orderCost": 22000, + "orderDate": "2025-01-19", + "status": "in_progress", + "address": "ул. Торговая, 12", + "description": "Доставка товаров для магазина" + }, + { + "id": "6", + "clientName": "ООО 'СтройГрад'", + "orderCost": 35000, + "orderDate": "2025-01-20", + "status": "pending", + "address": "ул. Строителей, 33", + "description": "Перевозка кирпича" + }, + { + "id": "7", + "clientName": "Федоров Дмитрий Иванович", + "orderCost": 8000, + "orderDate": "2025-01-21", + "status": "completed", + "address": "ул. Садовая, 15", + "description": "Перевозка бытовой техники" + }, + { + "id": "8", + "clientName": "ООО 'ПромСнаб'", + "orderCost": 28000, + "orderDate": "2025-01-22", + "status": "cancelled", + "address": "пр. Заводской, 67", + "description": "Доставка станков" + }, + { + "id": "9", + "clientName": "Александрова Ольга Викторовна", + "orderCost": 9500, + "orderDate": "2025-01-23", + "status": "pending", + "address": "ул. Центральная, 89", + "description": "Перевозка личных вещей" + }, + { + "id": "10", + "clientName": "ООО 'ТоргСервис'", + "orderCost": 19500, + "orderDate": "2025-01-24", + "status": "in_progress", + "address": "ул. Коммерческая, 56", + "description": "Доставка продуктов питания" + }, + { + "id": "11", + "clientName": "Васильев Павел Сергеевич", + "orderCost": 11000, + "orderDate": "2025-01-25", + "status": "completed", + "address": "ул. Молодежная, 23", + "description": "Переезд на новую квартиру" + }, + { + "id": "12", + "clientName": "ЗАО 'ПромИнвест'", + "orderCost": 42000, + "orderDate": "2025-01-26", + "status": "pending", + "address": "пр. Индустриальный, 44", + "description": "Перевозка промышленного оборудования" + } + ], + "vehicles": [ + { + "id": "1", + "driverName": "Петров Петр Петрович", + "vehicleType": "Газель", + "licensePlate": "А123БВ77" + }, + { + "id": "2", + "driverName": "Сидоров Алексей Владимирович", + "vehicleType": "Камаз", + "licensePlate": "В456ГД77" + }, + { + "id": "3", + "driverName": "Кузнецов Михаил Иванович", + "vehicleType": "Газель", + "licensePlate": "С789ЕЖ77" + }, + { + "id": "4", + "driverName": "Николаев Андрей Сергеевич", + "vehicleType": "ЗИЛ", + "licensePlate": "Д321ФГ77" + }, + { + "id": "5", + "driverName": "Морозов Виктор Павлович", + "vehicleType": "Камаз", + "licensePlate": "Е654ХЦ77" + }, + { + "id": "6", + "driverName": "Орлов Денис Александрович", + "vehicleType": "Газель", + "licensePlate": "Ж987ЧШ77" + }, + { + "id": "7", + "driverName": "Белов Артем Игоревич", + "vehicleType": "Фургон", + "licensePlate": "З159ЩР77" + }, + { + "id": "8", + "driverName": "Громов Сергей Викторович", + "vehicleType": "ЗИЛ", + "licensePlate": "И753ЪЫ77" + } + ], + "waybillEntries": [ + { + "id": "cdf9", + "vehicleId": "6", + "orderId": "1", + "startTime": "20:00", + "endTime": "22:00", + "date": "2025-11-21" + }, + { + "id": "9c84", + "vehicleId": "6", + "orderId": "4", + "startTime": "14:00", + "endTime": "16:00", + "date": "2025-11-21" + }, + { + "id": "c4a6", + "vehicleId": "6", + "orderId": "2", + "startTime": "10:00", + "endTime": "12:00", + "date": "2025-11-21" + }, + { + "id": "e320", + "vehicleId": "1", + "orderId": "1", + "startTime": "08:00", + "endTime": "10:00", + "date": "2025-11-21" + }, + { + "id": "08a1", + "vehicleId": "2", + "orderId": "2", + "startTime": "20:00", + "endTime": "22:00", + "date": "2025-11-21" + }, + { + "id": "27df", + "vehicleId": "2", + "orderId": "4", + "startTime": "08:00", + "endTime": "09:00", + "date": "2025-11-21" + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index aa22123..b8680da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,11 +16,19 @@ "@types/node": "^16.18.126", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", + "axios": "^1.13.2", + "bootstrap": "^5.3.8", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-router-dom": "^7.9.6", "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/bootstrap": "^5.2.10", + "@types/react-router-dom": "^5.3.3", + "json-server": "^1.0.0-beta.3" } }, "node_modules/@adobe/css-tools": { @@ -2977,6 +2985,23 @@ } } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3421,6 +3446,327 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tinyhttp/accepts": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.2.3.tgz", + "integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "4.0.4", + "negotiator": "^0.6.3" + }, + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/accepts/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/app": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-2.5.2.tgz", + "integrity": "sha512-DcB3Y8GQppLQlO2VxRYF7LzTEAoZb+VRQXuIsErcu2fNaM1xdx6NQZDso5rlZUiaeg6KYYRfU34N4XkZbv6jSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/proxy-addr": "2.2.1", + "@tinyhttp/req": "2.2.5", + "@tinyhttp/res": "2.2.5", + "@tinyhttp/router": "2.2.3", + "header-range-parser": "1.1.3", + "regexparam": "^2.0.2" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", + "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-type": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz", + "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4" + } + }, + "node_modules/@tinyhttp/cookie": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz", + "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/cookie-signature": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz", + "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/cors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz", + "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/vary": "^0.1.3" + }, + "engines": { + "node": ">=12.20 || 14.x || >=16" + } + }, + "node_modules/@tinyhttp/encode-url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz", + "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/etag": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz", + "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/forwarded": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.2.tgz", + "integrity": "sha512-9H/eulJ68ElY/+zYpTpNhZ7vxGV+cnwaR6+oQSm7bVgZMyuQfgROW/qvZuhmgDTIxnGMXst+Ba4ij6w6Krcs3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/logger": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.1.0.tgz", + "integrity": "sha512-Ma1fJ9CwUbn9r61/4HW6+nflsVoslpOnCrfQ6UeZq7GGIgwLzofms3HoSVG7M+AyRMJpxlfcDdbH5oFVroDMKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.20", + "dayjs": "^1.11.13", + "http-status-emojis": "^2.2.0" + }, + "engines": { + "node": ">=14.18 || >=16.20" + } + }, + "node_modules/@tinyhttp/proxy-addr": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-2.2.1.tgz", + "integrity": "sha512-BicqMqVI91hHq2BQmnqJUh0FQUnx7DncwSGgu2ghlh+JZG2rHK2ZN/rXkfhrx1rrUw6hnd0L36O8GPMh01+dDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/forwarded": "2.1.2", + "ipaddr.js": "^2.2.0" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/req": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.5.tgz", + "integrity": "sha512-trfsXwtmsNjMcGKcLJ+45h912kLRqBQCQD06ams3Tq0kf4gHLxjHjoYOC1Z9yGjOn81XllRx8wqvnvr+Kbe3gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/accepts": "2.2.3", + "@tinyhttp/type-is": "2.2.4", + "@tinyhttp/url": "2.1.1", + "header-range-parser": "^1.1.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/res": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.5.tgz", + "integrity": "sha512-yBsqjWygpuKAVz4moWlP4hqzwiDDqfrn2mA0wviJAcgvGiyOErtlQwXY7aj3aPiCpURvxvEFO//Gdy6yV+xEpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-disposition": "2.2.2", + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/cookie-signature": "2.1.1", + "@tinyhttp/encode-url": "2.1.1", + "@tinyhttp/req": "2.2.5", + "@tinyhttp/send": "2.2.3", + "@tinyhttp/vary": "^0.1.3", + "es-escape-html": "^0.1.1", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/res/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/router": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.3.tgz", + "integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.3.tgz", + "integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "@tinyhttp/etag": "2.1.2", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/type-is": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.4.tgz", + "integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/type-is/node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@tinyhttp/url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz", + "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/vary": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz", + "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -3505,6 +3851,16 @@ "@types/node": "*" } }, + "node_modules/@types/bootstrap": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz", + "integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.2" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3595,6 +3951,13 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -3731,6 +4094,29 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -4830,6 +5216,33 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -5224,6 +5637,25 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -6385,6 +6817,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -6727,6 +7166,35 @@ "tslib": "^2.0.3" } }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dotenv": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", @@ -6957,6 +7425,16 @@ "node": ">= 0.4" } }, + "node_modules/es-escape-html": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/es-escape-html/-/es-escape-html-0.1.1.tgz", + "integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.x" + } + }, "node_modules/es-iterator-helpers": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", @@ -7704,6 +8182,19 @@ "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -8751,6 +9242,16 @@ "he": "bin/he" } }, + "node_modules/header-range-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.3.tgz", + "integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -8994,6 +9495,13 @@ } } }, + "node_modules/http-status-emojis": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz", + "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==", + "dev": true, + "license": "MIT" + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -9139,6 +9647,16 @@ "node": ">=8" } }, + "node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -10845,6 +11363,77 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "license": "MIT" }, + "node_modules/json-server": { + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.3.tgz", + "integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==", + "dev": true, + "license": "SEE LICENSE IN ./LICENSE", + "dependencies": { + "@tinyhttp/app": "^2.4.0", + "@tinyhttp/cors": "^2.0.1", + "@tinyhttp/logger": "^2.0.0", + "chalk": "^5.3.0", + "chokidar": "^4.0.1", + "dot-prop": "^9.0.0", + "eta": "^3.5.0", + "inflection": "^3.0.0", + "json5": "^2.2.3", + "lowdb": "^7.0.1", + "milliparsec": "^4.0.0", + "sirv": "^2.0.4", + "sort-on": "^6.1.0" + }, + "bin": { + "json-server": "lib/bin.js" + }, + "engines": { + "node": ">=18.3" + } + }, + "node_modules/json-server/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/json-server/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/json-server/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -11110,6 +11699,22 @@ "loose-envify": "cli.js" } }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "license": "MIT", + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -11261,6 +11866,16 @@ "node": ">=8.6" } }, + "node_modules/milliparsec": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-4.0.0.tgz", + "integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -11371,6 +11986,16 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -13447,6 +14072,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -13738,6 +14369,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz", + "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz", + "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.6" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -13943,6 +14621,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/regexpu-core": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", @@ -14632,6 +15320,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -14795,6 +15489,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -14821,6 +15530,22 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sort-on": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-6.1.1.tgz", + "integrity": "sha512-PB8pVvXAoRBijBCvuKJnmo06D8mSnQlLij0abfB2VdOpfFm29sPGYD4ft2prUPo1AZXTnkn3pP48AppRWyMkrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -15071,6 +15796,19 @@ "node": ">= 0.8" } }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -15928,6 +16666,16 @@ "node": ">=0.6" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", diff --git a/package.json b/package.json index 9d43cdf..123c660 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,11 @@ "@types/node": "^16.18.126", "@types/react": "^19.2.6", "@types/react-dom": "^19.2.3", + "axios": "^1.13.2", + "bootstrap": "^5.3.8", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-router-dom": "^7.9.6", "react-scripts": "5.0.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" @@ -21,7 +24,8 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "server": "json-server --watch db.json --port 3001" }, "eslintConfig": { "extends": [ @@ -40,5 +44,10 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/bootstrap": "^5.2.10", + "@types/react-router-dom": "^5.3.3", + "json-server": "^1.0.0-beta.3" } } diff --git a/src/App.tsx b/src/App.tsx index a53698a..1faee8c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,58 @@ import React from 'react'; -import logo from './logo.svg'; +import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; +import OrdersPage from './pages/OrdersPage'; +import VehiclesPage from './pages/VehiclesPage'; +import VehicleDetailPage from './pages/VehicleDetailPage'; +import 'bootstrap/dist/css/bootstrap.min.css'; import './App.css'; -function App() { +const App: React.FC = () => { return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- ); -} + +
+ {/* Навигация */} + -export default App; + {/* Основной контент */} +
+ + } /> + } /> + } /> + } /> + +
+
+
+ ); +}; + +export default App; \ No newline at end of file diff --git a/src/components/ErrorAlert.tsx b/src/components/ErrorAlert.tsx new file mode 100644 index 0000000..bb66a84 --- /dev/null +++ b/src/components/ErrorAlert.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +interface ErrorAlertProps { + message: string; + onRetry?: () => void; +} + +const ErrorAlert: React.FC = ({ message, onRetry }) => { + return ( +
+

Ошибка!

+

{message}

+ {onRetry && ( + + )} +
+ ); +}; + +export default ErrorAlert; \ No newline at end of file diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx new file mode 100644 index 0000000..2c94502 --- /dev/null +++ b/src/components/Loading.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +const Loading: React.FC = () => { + return ( +
+
+ Загрузка... +
+

Загрузка данных...

+
+ ); +}; + +export default Loading; \ No newline at end of file diff --git a/src/components/OrderFilters.tsx b/src/components/OrderFilters.tsx new file mode 100644 index 0000000..122c1df --- /dev/null +++ b/src/components/OrderFilters.tsx @@ -0,0 +1,97 @@ +import React from 'react'; + +interface OrderFiltersProps { + filters: { + clientName: string; + minCost: string; + maxCost: string; + orderDate: string; + status: string; + }; + onFiltersChange: (filters: any) => void; +} + +const OrderFilters: React.FC = ({ filters, onFiltersChange }) => { + const handleChange = (field: string, value: string) => { + onFiltersChange({ + ...filters, + [field]: value + }); + }; + + return ( +
+
+
Фильтры заказов
+
+
+
+
+ + handleChange('clientName', e.target.value)} + placeholder="ФИО или наименование" + /> +
+ +
+ + handleChange('minCost', e.target.value)} + placeholder="0" + /> +
+ +
+ + handleChange('maxCost', e.target.value)} + placeholder="100000" + /> +
+ +
+ + handleChange('orderDate', e.target.value)} + /> +
+ +
+ + +
+
+
+
+ ); +}; + +export default OrderFilters; \ No newline at end of file diff --git a/src/components/VehicleFilters.tsx b/src/components/VehicleFilters.tsx new file mode 100644 index 0000000..b7fae7e --- /dev/null +++ b/src/components/VehicleFilters.tsx @@ -0,0 +1,68 @@ +import React from 'react'; + +interface VehicleFiltersProps { + filters: { + driverName: string; + vehicleType: string; + licensePlate: string; + }; + onFiltersChange: (filters: any) => void; +} + +const VehicleFilters: React.FC = ({ filters, onFiltersChange }) => { + const handleChange = (field: string, value: string) => { + onFiltersChange({ + ...filters, + [field]: value + }); + }; + + return ( +
+
+
Фильтры машин
+
+
+
+
+ + handleChange('driverName', e.target.value)} + placeholder="ФИО водителя" + /> +
+ +
+ + handleChange('vehicleType', e.target.value)} + placeholder="Тип машины" + /> +
+ +
+ + handleChange('licensePlate', e.target.value)} + placeholder="Гос. номер" + /> +
+
+
+
+ ); +}; + +export default VehicleFilters; \ No newline at end of file diff --git a/src/components/WaybillWidget.tsx b/src/components/WaybillWidget.tsx new file mode 100644 index 0000000..e911814 --- /dev/null +++ b/src/components/WaybillWidget.tsx @@ -0,0 +1,311 @@ +import React, { useState, useEffect } from 'react'; +import { Order, WaybillEntry } from '../types'; +import { waybillApi, ordersApi } from '../services/api'; +import Loading from '../components/Loading'; +import ErrorAlert from '../components/ErrorAlert'; + +interface WaybillWidgetProps { + vehicleId: number; + date: string; +} + +let durationOrder; + +const WaybillWidget: React.FC = ({ vehicleId, date }) => { + const [entries, setEntries] = useState([]); + const [orders, setOrders] = useState([]); + const [availableOrders, setAvailableOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [selectedOrder, setSelectedOrder] = useState(null); + const [selectedTimeSlot, setSelectedTimeSlot] = useState(null); + const [duration, setDuration] = useState(2); + + // Часовые интервалы с 8:00 до 20:00 + const timeSlots = Array.from({ length: 13 }, (_, i) => { + const hour = i + 8; + return `${hour.toString().padStart(2, '0')}:00`; + }); + + const loadData = async () => { + try { + const response = await ordersApi.getOrders(); + setOrders(response.data); + setLoading(true); + setError(null); + + // Загружаем записи путевого листа + const entriesResponse = await waybillApi.getEntries(); + const vehicleEntries = entriesResponse.data.filter( + entry => entry.vehicleId === vehicleId && entry.date === date + ); + setEntries(vehicleEntries); + + // Загружаем доступные заказы + const ordersResponse = await ordersApi.getOrders(); + const assignedOrderIds = new Set(vehicleEntries.map(entry => entry.orderId)); + const available = ordersResponse.data.filter( + order => !assignedOrderIds.has(order.id) && order.status !== 'completed' + ); + setAvailableOrders(available); + + } catch (err) { + setError('Не удалось загрузить данные путевого листа'); + console.error('Error loading waybill data:', err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadData(); + }, [vehicleId, date]); + + const getOrderForTimeSlot = (timeSlot: string): WaybillEntry | undefined => { + return entries.find(entry => { + const startHour = parseInt(entry.startTime.split(':')[0]); + const endHour = parseInt(entry.endTime.split(':')[0]); + const slotHour = parseInt(timeSlot.split(':')[0]); + return slotHour >= startHour && slotHour < endHour; + }); + }; + + const getOrderInfo = (orderId: number, allOrders: Order[]): Order | undefined => { + // Ищем заказ среди всех заказов + const foundOrder = allOrders.find(order => order.id === orderId); + if (foundOrder) { + return foundOrder; + } + + // Если заказ не найден, но есть в путевом листе - создаем базовый объект + const waybillEntry = entries.find(entry => entry.orderId === orderId); + if (waybillEntry) { + const basicOrder: Order = { + id: waybillEntry.orderId, + clientName: `Заказ #${waybillEntry.orderId}`, + orderCost: 0, + orderDate: new Date().toISOString().split('T')[0], + status: 'in_progress' as const, + address: 'Адрес не указан', + description: 'Заказ запланирован в путевом листе' + }; + return basicOrder; + } + + // Если заказ не найден нигде + return undefined; + }; + + const handleTimeSlotClick = async (timeSlot: string) => { + if (selectedOrder) { + try { + const startHour = parseInt(timeSlot.split(':')[0]); + const endHour = startHour + duration; // Используем выбранную длительность + + const newEntry: Omit = { + vehicleId, + orderId: selectedOrder.id, + startTime: timeSlot, + endTime: `${endHour.toString().padStart(2, '0')}:00`, + date + }; + + await waybillApi.createEntry(newEntry); + setSelectedOrder(null); + setSelectedTimeSlot(null); + await loadData(); + + } catch (err) { + setError('Не удалось добавить заказ в путевой лист'); + console.error('Error creating waybill entry:', err); + } + } else { + setSelectedTimeSlot(timeSlot); + } + }; + + const handleOrderSelect = async (order: Order) => { + if (selectedTimeSlot) { + try { + const startHour = parseInt(selectedTimeSlot.split(':')[0]); + const endHour = startHour + duration; // Используем выбранную длительность + + const newEntry: Omit = { + vehicleId, + orderId: order.id, + startTime: selectedTimeSlot, + endTime: `${endHour.toString().padStart(2, '0')}:00`, + date + }; + + await waybillApi.createEntry(newEntry); + setSelectedTimeSlot(null); + setSelectedOrder(null); + await loadData(); + + } catch (err) { + setError('Не удалось добавить заказ в путевой лист'); + console.error('Error creating waybill entry:', err); + } + } else { + setSelectedOrder(order); + } + }; + + const handleRemoveEntry = async (entryId: number) => { + try { + await waybillApi.deleteEntry(entryId); + await loadData(); + } catch (err) { + setError('Не удалось удалить запись из путевого листа'); + console.error('Error deleting waybill entry:', err); + } + }; + + if (loading) return ; + if (error) return ; + + return ( +
+
+
+
Путевой лист на {new Date(date).toLocaleDateString()}
+
+
+ {/* Состояние выбора */} +
+ {selectedOrder && !selectedTimeSlot && ( +
+

Выбран заказ: {selectedOrder.clientName}. Теперь выберите время.

+
+ + setDuration(parseInt(e.target.value) || 1)} + style={{ width: '100px', display: 'inline-block', marginLeft: '10px' }} + /> +
+
+ )} + {selectedTimeSlot && !selectedOrder && ( +
+

Выбрано время: {selectedTimeSlot}. Теперь выберите заказ.

+
+ + setDuration(parseInt(e.target.value) || 1)} + style={{ width: '100px', display: 'inline-block', marginLeft: '10px' }} + /> +
+
+ )} + {!selectedOrder && !selectedTimeSlot && ( +

Выберите время или заказ для планирования доставки.

+ )} + {selectedOrder && selectedTimeSlot && ( +

+ Готово к добавлению: {selectedOrder.clientName} на время {selectedTimeSlot} + продолжительностью {duration} час(ов) +

+ )} +
+ {/* Таблица временных интервалов */} +
+ + + + + + + + + + {timeSlots.map(timeSlot => { + const entry = getOrderForTimeSlot(timeSlot); + const order = entry ? getOrderInfo(entry.orderId, orders) : undefined; + return ( + + + + + + ); + })} + +
ВремяЗаказДействия
{timeSlot} + {order ? ( +
+ {order.clientName} +
+ {order.address} +
+ ) : ( + Свободно + )} +
+ {order && entry ? ( + + ) : ( + + )} +
+
+ + {/* Список доступных заказов */} +
+
Доступные заказы:
+
+ {availableOrders.map(order => ( +
+
handleOrderSelect(order)} + > +
+
{order.clientName}
+

+ {order.address} +

+

+ + {order.orderCost.toLocaleString()} руб. + +

+
+
+
+ ))} +
+
+
+
+
+ ); +}; + +export default WaybillWidget; \ No newline at end of file diff --git a/src/pages/OrdersPage.tsx b/src/pages/OrdersPage.tsx new file mode 100644 index 0000000..829805d --- /dev/null +++ b/src/pages/OrdersPage.tsx @@ -0,0 +1,185 @@ +import React, { useState, useEffect } from 'react'; +import { Order } from '../types'; +import { ordersApi } from '../services/api'; +import OrderFilters from '../components/OrderFilters'; +import Loading from '../components/Loading'; +import ErrorAlert from '../components/ErrorAlert'; + +const OrdersPage: React.FC = () => { + const [orders, setOrders] = useState([]); + const [filteredOrders, setFilteredOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const [filters, setFilters] = useState({ + clientName: '', + minCost: '', + maxCost: '', + orderDate: '', + status: '' + }); + + // Загрузка данных + const loadOrders = async () => { + try { + setLoading(true); + setError(null); + const response = await ordersApi.getOrders(); + setOrders(response.data); + setFilteredOrders(response.data); + } catch (err) { + setError('Не удалось загрузить список заказов'); + console.error('Error loading orders:', err); + } finally { + setLoading(false); + } + }; + + // Функция изменения статуса заказа + const updateOrderStatus = async (orderId: number, newStatus: Order['status']) => { + try { + // Находим заказ для обновления + const orderToUpdate = orders.find(order => order.id === orderId); + if (!orderToUpdate) return; + + // Создаем обновленный заказ + const updatedOrder = { + ...orderToUpdate, + status: newStatus + }; + + // Отправляем запрос на сервер + await ordersApi.updateOrder(orderId, updatedOrder); + + // Обновляем локальное состояние + setOrders(prevOrders => + prevOrders.map(order => + order.id === orderId ? updatedOrder : order + ) + ); + + console.log(`Статус заказа ${orderId} изменен на: ${newStatus}`); + } catch (err) { + setError('Не удалось обновить статус заказа'); + console.error('Error updating order status:', err); + } + }; + + useEffect(() => { + loadOrders(); + }, []); + + // Применение фильтров + useEffect(() => { + let result = orders; + + if (filters.clientName) { + result = result.filter(order => + order.clientName.toLowerCase().includes(filters.clientName.toLowerCase()) + ); + } + + if (filters.minCost) { + result = result.filter(order => + order.orderCost >= parseFloat(filters.minCost) + ); + } + + if (filters.maxCost) { + result = result.filter(order => + order.orderCost <= parseFloat(filters.maxCost) + ); + } + + if (filters.orderDate) { + result = result.filter(order => + order.orderDate === filters.orderDate + ); + } + + if (filters.status) { + result = result.filter(order => + order.status === filters.status + ); + } + + setFilteredOrders(result); + }, [filters, orders]); + + if (loading) return ; + if (error) return ; + + return ( +
+

Список заказов

+ + + +
+
+
+ Найдено заказов: {filteredOrders.length} +
+
+
+ {filteredOrders.length === 0 ? ( +

Заказы не найдены

+ ) : ( +
+ + + + + + + + + + + + + + {filteredOrders.map(order => ( + + + + + + + + + + ))} + +
IDКлиентСтоимостьДата заказаСтатусАдресДействия
{order.id}{order.clientName}{order.orderCost.toLocaleString()} руб.{new Date(order.orderDate).toLocaleDateString()} + + {order.status === 'pending' ? 'Ожидает' : + order.status === 'in_progress' ? 'В процессе' : + order.status === 'completed' ? 'Завершен' : 'Отменен'} + + {order.address} + +
+
+ )} +
+
+
+ ); +}; + +export default OrdersPage; \ No newline at end of file diff --git a/src/pages/VehicleDetailPage.tsx b/src/pages/VehicleDetailPage.tsx new file mode 100644 index 0000000..cd9112e --- /dev/null +++ b/src/pages/VehicleDetailPage.tsx @@ -0,0 +1,132 @@ +import React, { useState, useEffect } from 'react'; +import { useParams, Link } from 'react-router-dom'; +import { Vehicle, Order, WaybillEntry } from '../types'; +import { vehiclesApi, ordersApi, waybillApi } from '../services/api'; +import WaybillWidget from '../components/WaybillWidget'; +import Loading from '../components/Loading'; +import ErrorAlert from '../components/ErrorAlert'; + +const VehicleDetailPage: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const [vehicle, setVehicle] = useState(null); + const [completedOrders, setCompletedOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [selectedDate, setSelectedDate] = useState( + new Date().toISOString().split('T')[0] + ); + + const loadVehicleData = async () => { + if (!id) return; + + try { + setLoading(true); + setError(null); + + // Загружаем данные машины + const vehicleResponse = await vehiclesApi.getVehicle(parseInt(id)); + setVehicle(vehicleResponse.data); + + // Загружаем историю выполненных заказов + const ordersResponse = await ordersApi.getOrders(); + const completed = ordersResponse.data.filter( + order => order.status === 'completed' + ); + setCompletedOrders(completed); + + } catch (err) { + setError('Не удалось загрузить данные машины'); + console.error('Error loading vehicle data:', err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadVehicleData(); + }, [id]); + + if (loading) return ; + if (error) return ; + if (!vehicle) return
Машина не найдена
; + + return ( +
+ + +
+
+
+
+
Информация о машине
+
+
+

Тип машины: {vehicle.vehicleType}

+

Водитель: {vehicle.driverName}

+

Гос. номер: {vehicle.licensePlate}

+
+
+ +
+
+
История выполненных заказов
+
+
+ {completedOrders.length === 0 ? ( +

Нет выполненных заказов

+ ) : ( +
+ {completedOrders.map(order => ( +
+
{order.clientName}
+

{order.address}

+ + {new Date(order.orderDate).toLocaleDateString()} - {' '} + {order.orderCost.toLocaleString()} руб. + +
+ ))} +
+ )} +
+
+
+ +
+
+
+
Путевой лист
+
+ + setSelectedDate(e.target.value)} + /> +
+
+
+ +
+
+
+
+
+ ); +}; + +export default VehicleDetailPage; \ No newline at end of file diff --git a/src/pages/VehiclesPage.tsx b/src/pages/VehiclesPage.tsx new file mode 100644 index 0000000..b6ab0fe --- /dev/null +++ b/src/pages/VehiclesPage.tsx @@ -0,0 +1,111 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { Vehicle } from '../types'; +import { vehiclesApi } from '../services/api'; +import VehicleFilters from '../components/VehicleFilters'; +import Loading from '../components/Loading'; +import ErrorAlert from '../components/ErrorAlert'; + +const VehiclesPage: React.FC = () => { + const [vehicles, setVehicles] = useState([]); + const [filteredVehicles, setFilteredVehicles] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const [filters, setFilters] = useState({ + driverName: '', + vehicleType: '', + licensePlate: '' + }); + + const loadVehicles = async () => { + try { + setLoading(true); + setError(null); + const response = await vehiclesApi.getVehicles(); + setVehicles(response.data); + setFilteredVehicles(response.data); + } catch (err) { + setError('Не удалось загрузить список машин'); + console.error('Error loading vehicles:', err); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadVehicles(); + }, []); + + useEffect(() => { + let result = vehicles; + + if (filters.driverName) { + result = result.filter(vehicle => + vehicle.driverName.toLowerCase().includes(filters.driverName.toLowerCase()) + ); + } + + if (filters.vehicleType) { + result = result.filter(vehicle => + vehicle.vehicleType.toLowerCase().includes(filters.vehicleType.toLowerCase()) + ); + } + + if (filters.licensePlate) { + result = result.filter(vehicle => + vehicle.licensePlate.toLowerCase().includes(filters.licensePlate.toLowerCase()) + ); + } + + setFilteredVehicles(result); + }, [filters, vehicles]); + + if (loading) return ; + if (error) return ; + + return ( +
+

Список машин

+ + + +
+
+
+ Найдено машин: {filteredVehicles.length} +
+
+
+ {filteredVehicles.length === 0 ? ( +

Машины не найдены

+ ) : ( +
+ {filteredVehicles.map(vehicle => ( +
+
+
+
{vehicle.vehicleType}
+

+ Водитель: {vehicle.driverName}
+ Гос. номер: {vehicle.licensePlate} +

+ + Подробнее + +
+
+
+ ))} +
+ )} +
+
+
+ ); +}; + +export default VehiclesPage; \ No newline at end of file diff --git a/src/services/api.ts b/src/services/api.ts new file mode 100644 index 0000000..4da3102 --- /dev/null +++ b/src/services/api.ts @@ -0,0 +1,34 @@ +import axios from 'axios'; +import { Order, Vehicle, WaybillEntry } from '../types'; + +const API_BASE = 'http://localhost:3001'; + +const api = axios.create({ + baseURL: API_BASE, +}); + +// Сервис для работы с заказами +// Сервис для работы с заказами +export const ordersApi = { + getOrders: () => api.get('/orders'), + getOrder: (id: number) => api.get(`/orders/${id}`), + updateOrder: (id: number, order: Order) => api.put(`/orders/${id}`, order), +}; + +// Сервис для работы с машинами +export const vehiclesApi = { + getVehicles: () => api.get('/vehicles'), + getVehicle: (id: number) => api.get(`/vehicles/${id}`), +}; + +// Сервис для работы с путевыми листами +export const waybillApi = { + getEntries: () => api.get('/waybillEntries'), + createEntry: (entry: Omit) => + api.post('/waybillEntries', entry), + updateEntry: (id: number, entry: Partial) => + api.put(`/waybillEntries/${id}`, entry), + deleteEntry: (id: number) => api.delete(`/waybillEntries/${id}`), +}; + +export default api; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..9876871 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,32 @@ +// Основные типы данных +export interface Order { + id: number; + clientName: string; + orderCost: number; + orderDate: string; + status: 'pending' | 'in_progress' | 'completed' | 'cancelled'; + address: string; + description: string; +} + +export interface Vehicle { + id: number; + driverName: string; + vehicleType: string; + licensePlate: string; +} + +export interface WaybillEntry { + id: number; + vehicleId: number; + orderId: number; + startTime: string; + endTime: string; + date: string; +} + +export interface Waybill { + vehicleId: number; + date: Date; + entries: WaybillEntry[]; +} \ No newline at end of file