From c5aee9e2ece88ea94858b86cb48c0ca3839cae1f Mon Sep 17 00:00:00 2001 From: Possible Date: Thu, 3 Apr 2025 17:05:59 +0100 Subject: [PATCH] Update | Project Ready --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 1 + .prettierrc | 0 README.md | 130 + boat_lifts.sql | 52 + index.html | 21 + jsconfig.json | 23 + package-lock.json | 5235 +++++++++++++++++ package.json | 52 + pg.js | 281 + postcss.config.js | 6 + src/.DS_Store | Bin 0 -> 6148 bytes src/App.css | 0 src/App.jsx | 41 + src/assets/images/BrandLogo.png | Bin 0 -> 10848 bytes src/assets/images/gray.png | Bin 0 -> 14977 bytes src/assets/images/index.js | 4 + src/assets/images/perforated.png | Bin 0 -> 29054 bytes src/assets/images/woodgrain.png | Bin 0 -> 23999 bytes src/assets/svgs/ArrowLeftIcon.tsx | 9 + src/assets/svgs/ArrowRrightIcon.tsx | 9 + src/assets/svgs/Chevron.jsx | 9 + src/assets/svgs/CloseIcon.tsx | 9 + src/assets/svgs/CopyIcon.jsx | 9 + src/assets/svgs/DeleteIcon.svg | 3 + src/assets/svgs/DownloadIcon.tsx | 9 + src/assets/svgs/GreenTickIcon.tsx | 9 + src/assets/svgs/HandIcon.jsx | 9 + src/assets/svgs/Loader.jsx | 16 + src/assets/svgs/MinusSquareIcon.jsx | 9 + src/assets/svgs/PasteIcon.jsx | 15 + src/assets/svgs/PlusSquareIcon.jsx | 9 + src/assets/svgs/PrinterIcon.jsx | 9 + src/assets/svgs/ReverseLeftIcon.jsx | 9 + src/assets/svgs/ReverseRightIcon.jsx | 9 + src/assets/svgs/RotateIcon.svg | 3 + src/assets/svgs/TrashIcon.jsx | 9 + src/assets/svgs/UploadIcon.tsx | 9 + src/assets/svgs/index.js | 19 + src/authContext.jsx | 104 + .../ActionButtons/ActionButtons.jsx | 85 + src/components/ActionButtons/index.js | 1 + src/components/AddButton.jsx | 16 + src/components/AdminHeader.jsx | 205 + .../BuildCanvasFromLocalModal.jsx | 82 + .../BuildCanvasFromLocalModal/index.js | 1 + src/components/Builder/Builder.jsx | 758 +++ src/components/Builder/index.js | 1 + src/components/Comments/Comments.jsx | 80 + src/components/Comments/index.js | 1 + .../ContactInformation/ContactInformation.jsx | 324 + src/components/ContactInformation/index.js | 1 + src/components/DockBuilder/DockBuilder.jsx | 823 +++ src/components/DockBuilder/index.js | 1 + src/components/DockSidebar/DockSidebar.jsx | 73 + src/components/DockSidebar/index.js | 1 + src/components/DynamicContentType.jsx | 464 ++ .../EstimateModal/EstimateModal.jsx | 168 + src/components/EstimateModal/index.js | 1 + src/components/FabricObjects/Rect/Rect.jsx | 63 + .../InteractiveButton/InteractiveButton.jsx | 48 + src/components/InteractiveButton/index.js | 1 + .../LakeSurroundings/LakeSurroundings.jsx | 141 + src/components/LakeSurroundings/index.js | 1 + src/components/Loader.jsx | 22 + src/components/LoadingIndicator.jsx | 58 + src/components/Modal/Modal.jsx | 38 + src/components/Modal/index.js | 1 + src/components/PaginationBar.jsx | 41 + src/components/PublicHeader.jsx | 15 + src/components/SelectedItems/Field.jsx | 18 + src/components/SelectedItems/SelectedItem.jsx | 48 + .../SelectedItems/SelectedItems.jsx | 15 + src/components/SelectedItems/index.js | 3 + .../SidebarBuilder/SidebarBuilder.jsx | 51 + src/components/SidebarBuilder/index.js | 1 + src/components/SidebarFoot/SidebarFoot.jsx | 38 + src/components/SidebarFoot/index.js | 1 + src/components/SidebarLogo/SidebarLogo.jsx | 15 + src/components/SidebarLogo/index.js | 1 + src/components/SnackBar.jsx | 32 + src/components/Tab/Tab.jsx | 10 + src/components/Tab/index.js | 1 + src/components/Table/Table.jsx | 95 + src/components/Table/index.js | 1 + src/components/Tabs/Tabs.jsx | 25 + src/components/Tabs/index.js | 1 + src/components/TopHeader.jsx | 52 + .../ViewDockImageModal/ViewDockImageModal.jsx | 77 + src/components/ViewDockImageModal/index.js | 1 + src/components/index.js | 6 + src/data.js | 1963 ++++++ src/favicon.svg | 15 + src/globalContext.jsx | 103 + src/index.css | 143 + src/index.jsx | 14 + src/logo.svg | 7 + src/main.jsx | 310 + src/output.css | 5097 ++++++++++++++++ src/pages/404/NotFoundPage.jsx | 27 + src/pages/404/index.js | 1 + .../admin/Add/AddAdminAccessoriesPage.jsx | 248 + .../admin/Add/AddAdminAnalyticLogPage.jsx | 269 + src/pages/admin/Add/AddAdminBoatLiftsPage.jsx | 318 + src/pages/admin/Add/AddAdminCmsPage.jsx | 158 + src/pages/admin/Add/AddAdminDealersPage.jsx | 150 + src/pages/admin/Add/AddAdminDocksPage.jsx | 266 + src/pages/admin/Add/AddAdminEmailPage.jsx | 162 + .../admin/Add/AddAdminInstructionsPage.jsx | 131 + src/pages/admin/Add/AddAdminPhotoPage.jsx | 74 + .../Add/AddAdminQuotesMailRecipientsPage.jsx | 132 + src/pages/admin/Add/AddAdminQuotesPage.jsx | 440 ++ src/pages/admin/Add/AddAdminRampsPage.jsx | 275 + .../admin/Add/AddAdminReferenceItemsPage.jsx | 202 + src/pages/admin/Add/AddAdminUserPage.jsx | 151 + src/pages/admin/Add/AddAdminWedgesPage.jsx | 265 + src/pages/admin/Add/pg.js | 96 + src/pages/admin/AdminDashboardPage.jsx | 82 + src/pages/admin/Auth/AdminForgotPage.jsx | 107 + src/pages/admin/Auth/AdminLoginPage.jsx | 126 + src/pages/admin/Auth/AdminProfilePage.jsx | 154 + src/pages/admin/Auth/AdminResetPage.jsx | 156 + .../admin/Edit/EditAdminAccessoriesPage.jsx | 278 + .../admin/Edit/EditAdminAnalyticLogPage.jsx | 315 + .../admin/Edit/EditAdminBoatLiftsPage.jsx | 348 ++ src/pages/admin/Edit/EditAdminCmsPage.jsx | 182 + src/pages/admin/Edit/EditAdminDealersPage.jsx | 173 + src/pages/admin/Edit/EditAdminDocksPage.jsx | 297 + src/pages/admin/Edit/EditAdminEmailPage.jsx | 176 + .../admin/Edit/EditAdminInstructionsPage.jsx | 151 + .../EditAdminQuotesMailRecipientsPage.jsx | 152 + src/pages/admin/Edit/EditAdminQuotesPage.jsx | 489 ++ src/pages/admin/Edit/EditAdminRampsPage.jsx | 317 + src/pages/admin/Edit/EditAdminUserPage.jsx | 228 + src/pages/admin/Edit/EditAdminWedgesPage.jsx | 298 + .../admin/List/AdminAccessoriesListPage.jsx | 349 ++ .../admin/List/AdminAnalyticLogListPage.jsx | 534 ++ .../admin/List/AdminBoatLiftsListPage.jsx | 433 ++ src/pages/admin/List/AdminCmsListPage.jsx | 257 + src/pages/admin/List/AdminDealersListPage.jsx | 341 ++ src/pages/admin/List/AdminDocksListPage.jsx | 423 ++ src/pages/admin/List/AdminEmailListPage.jsx | 162 + .../admin/List/AdminInstructionsListPage.jsx | 333 ++ src/pages/admin/List/AdminPhotoListPage.jsx | 295 + src/pages/admin/List/AdminQuotesListPage.jsx | 500 ++ .../AdminQuotesMailRecipientsListPage.jsx | 378 ++ src/pages/admin/List/AdminRampsListPage.jsx | 474 ++ .../List/AdminReferenceItemsListPage.jsx | 443 ++ src/pages/admin/List/AdminUserListPage.jsx | 326 + src/pages/admin/List/AdminWedgesListPage.jsx | 379 ++ .../admin/View/ViewAdminAccessoriesPage.jsx | 78 + .../admin/View/ViewAdminAnalyticLogPage.jsx | 104 + .../admin/View/ViewAdminBoatLiftsPage.jsx | 109 + src/pages/admin/View/ViewAdminDealersPage.jsx | 63 + src/pages/admin/View/ViewAdminDocksPage.jsx | 94 + .../admin/View/ViewAdminInstructionsPage.jsx | 56 + .../ViewAdminQuotesMailRecipientsPage.jsx | 58 + src/pages/admin/View/ViewAdminQuotesPage.jsx | 239 + src/pages/admin/View/ViewAdminRampsPage.jsx | 97 + .../View/ViewAdminReferenceItemsPage.jsx | 82 + src/pages/admin/View/ViewAdminWedgesPage.jsx | 98 + src/pages/admin/index.js | 60 + src/pages/dock/DockBuilderPage.jsx | 23 + src/pages/dock/index.js | 1 + src/utils/DockBuilderUtils/clone.jsx | 130 + src/utils/DockBuilderUtils/edgeDetection.jsx | 170 + src/utils/DockBuilderUtils/index.js | 6 + src/utils/MkdSDK.jsx | 1763 ++++++ src/utils/constants/constants.jsx | 110 + src/utils/constants/index.js | 31 + src/utils/constants/tableColumns.jsx | 113 + src/utils/helper/helper.js | 8 + src/utils/helper/index.js | 1 + src/utils/utils.jsx | 143 + tailwind.config.js | 8 + vite.config.js | 20 + 176 files changed, 34486 insertions(+) create mode 100644 .DS_Store create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 README.md create mode 100644 boat_lifts.sql create mode 100644 index.html create mode 100644 jsconfig.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pg.js create mode 100644 postcss.config.js create mode 100644 src/.DS_Store create mode 100644 src/App.css create mode 100644 src/App.jsx create mode 100644 src/assets/images/BrandLogo.png create mode 100644 src/assets/images/gray.png create mode 100644 src/assets/images/index.js create mode 100644 src/assets/images/perforated.png create mode 100644 src/assets/images/woodgrain.png create mode 100644 src/assets/svgs/ArrowLeftIcon.tsx create mode 100644 src/assets/svgs/ArrowRrightIcon.tsx create mode 100644 src/assets/svgs/Chevron.jsx create mode 100644 src/assets/svgs/CloseIcon.tsx create mode 100644 src/assets/svgs/CopyIcon.jsx create mode 100644 src/assets/svgs/DeleteIcon.svg create mode 100644 src/assets/svgs/DownloadIcon.tsx create mode 100644 src/assets/svgs/GreenTickIcon.tsx create mode 100644 src/assets/svgs/HandIcon.jsx create mode 100644 src/assets/svgs/Loader.jsx create mode 100644 src/assets/svgs/MinusSquareIcon.jsx create mode 100644 src/assets/svgs/PasteIcon.jsx create mode 100644 src/assets/svgs/PlusSquareIcon.jsx create mode 100644 src/assets/svgs/PrinterIcon.jsx create mode 100644 src/assets/svgs/ReverseLeftIcon.jsx create mode 100644 src/assets/svgs/ReverseRightIcon.jsx create mode 100644 src/assets/svgs/RotateIcon.svg create mode 100644 src/assets/svgs/TrashIcon.jsx create mode 100644 src/assets/svgs/UploadIcon.tsx create mode 100644 src/assets/svgs/index.js create mode 100644 src/authContext.jsx create mode 100644 src/components/ActionButtons/ActionButtons.jsx create mode 100644 src/components/ActionButtons/index.js create mode 100644 src/components/AddButton.jsx create mode 100644 src/components/AdminHeader.jsx create mode 100644 src/components/BuildCanvasFromLocalModal/BuildCanvasFromLocalModal.jsx create mode 100644 src/components/BuildCanvasFromLocalModal/index.js create mode 100644 src/components/Builder/Builder.jsx create mode 100644 src/components/Builder/index.js create mode 100644 src/components/Comments/Comments.jsx create mode 100644 src/components/Comments/index.js create mode 100644 src/components/ContactInformation/ContactInformation.jsx create mode 100644 src/components/ContactInformation/index.js create mode 100644 src/components/DockBuilder/DockBuilder.jsx create mode 100644 src/components/DockBuilder/index.js create mode 100644 src/components/DockSidebar/DockSidebar.jsx create mode 100644 src/components/DockSidebar/index.js create mode 100644 src/components/DynamicContentType.jsx create mode 100644 src/components/EstimateModal/EstimateModal.jsx create mode 100644 src/components/EstimateModal/index.js create mode 100644 src/components/FabricObjects/Rect/Rect.jsx create mode 100644 src/components/InteractiveButton/InteractiveButton.jsx create mode 100644 src/components/InteractiveButton/index.js create mode 100644 src/components/LakeSurroundings/LakeSurroundings.jsx create mode 100644 src/components/LakeSurroundings/index.js create mode 100644 src/components/Loader.jsx create mode 100644 src/components/LoadingIndicator.jsx create mode 100644 src/components/Modal/Modal.jsx create mode 100644 src/components/Modal/index.js create mode 100644 src/components/PaginationBar.jsx create mode 100644 src/components/PublicHeader.jsx create mode 100644 src/components/SelectedItems/Field.jsx create mode 100644 src/components/SelectedItems/SelectedItem.jsx create mode 100644 src/components/SelectedItems/SelectedItems.jsx create mode 100644 src/components/SelectedItems/index.js create mode 100644 src/components/SidebarBuilder/SidebarBuilder.jsx create mode 100644 src/components/SidebarBuilder/index.js create mode 100644 src/components/SidebarFoot/SidebarFoot.jsx create mode 100644 src/components/SidebarFoot/index.js create mode 100644 src/components/SidebarLogo/SidebarLogo.jsx create mode 100644 src/components/SidebarLogo/index.js create mode 100644 src/components/SnackBar.jsx create mode 100644 src/components/Tab/Tab.jsx create mode 100644 src/components/Tab/index.js create mode 100644 src/components/Table/Table.jsx create mode 100644 src/components/Table/index.js create mode 100644 src/components/Tabs/Tabs.jsx create mode 100644 src/components/Tabs/index.js create mode 100644 src/components/TopHeader.jsx create mode 100644 src/components/ViewDockImageModal/ViewDockImageModal.jsx create mode 100644 src/components/ViewDockImageModal/index.js create mode 100644 src/components/index.js create mode 100644 src/data.js create mode 100644 src/favicon.svg create mode 100644 src/globalContext.jsx create mode 100644 src/index.css create mode 100644 src/index.jsx create mode 100644 src/logo.svg create mode 100644 src/main.jsx create mode 100644 src/output.css create mode 100644 src/pages/404/NotFoundPage.jsx create mode 100644 src/pages/404/index.js create mode 100644 src/pages/admin/Add/AddAdminAccessoriesPage.jsx create mode 100644 src/pages/admin/Add/AddAdminAnalyticLogPage.jsx create mode 100644 src/pages/admin/Add/AddAdminBoatLiftsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminCmsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminDealersPage.jsx create mode 100644 src/pages/admin/Add/AddAdminDocksPage.jsx create mode 100644 src/pages/admin/Add/AddAdminEmailPage.jsx create mode 100644 src/pages/admin/Add/AddAdminInstructionsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminPhotoPage.jsx create mode 100644 src/pages/admin/Add/AddAdminQuotesMailRecipientsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminQuotesPage.jsx create mode 100644 src/pages/admin/Add/AddAdminRampsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminReferenceItemsPage.jsx create mode 100644 src/pages/admin/Add/AddAdminUserPage.jsx create mode 100644 src/pages/admin/Add/AddAdminWedgesPage.jsx create mode 100644 src/pages/admin/Add/pg.js create mode 100644 src/pages/admin/AdminDashboardPage.jsx create mode 100644 src/pages/admin/Auth/AdminForgotPage.jsx create mode 100644 src/pages/admin/Auth/AdminLoginPage.jsx create mode 100644 src/pages/admin/Auth/AdminProfilePage.jsx create mode 100644 src/pages/admin/Auth/AdminResetPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminAccessoriesPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminAnalyticLogPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminBoatLiftsPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminCmsPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminDealersPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminDocksPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminEmailPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminInstructionsPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminQuotesMailRecipientsPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminQuotesPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminRampsPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminUserPage.jsx create mode 100644 src/pages/admin/Edit/EditAdminWedgesPage.jsx create mode 100644 src/pages/admin/List/AdminAccessoriesListPage.jsx create mode 100644 src/pages/admin/List/AdminAnalyticLogListPage.jsx create mode 100644 src/pages/admin/List/AdminBoatLiftsListPage.jsx create mode 100644 src/pages/admin/List/AdminCmsListPage.jsx create mode 100644 src/pages/admin/List/AdminDealersListPage.jsx create mode 100644 src/pages/admin/List/AdminDocksListPage.jsx create mode 100644 src/pages/admin/List/AdminEmailListPage.jsx create mode 100644 src/pages/admin/List/AdminInstructionsListPage.jsx create mode 100644 src/pages/admin/List/AdminPhotoListPage.jsx create mode 100644 src/pages/admin/List/AdminQuotesListPage.jsx create mode 100644 src/pages/admin/List/AdminQuotesMailRecipientsListPage.jsx create mode 100644 src/pages/admin/List/AdminRampsListPage.jsx create mode 100644 src/pages/admin/List/AdminReferenceItemsListPage.jsx create mode 100644 src/pages/admin/List/AdminUserListPage.jsx create mode 100644 src/pages/admin/List/AdminWedgesListPage.jsx create mode 100644 src/pages/admin/View/ViewAdminAccessoriesPage.jsx create mode 100644 src/pages/admin/View/ViewAdminAnalyticLogPage.jsx create mode 100644 src/pages/admin/View/ViewAdminBoatLiftsPage.jsx create mode 100644 src/pages/admin/View/ViewAdminDealersPage.jsx create mode 100644 src/pages/admin/View/ViewAdminDocksPage.jsx create mode 100644 src/pages/admin/View/ViewAdminInstructionsPage.jsx create mode 100644 src/pages/admin/View/ViewAdminQuotesMailRecipientsPage.jsx create mode 100644 src/pages/admin/View/ViewAdminQuotesPage.jsx create mode 100644 src/pages/admin/View/ViewAdminRampsPage.jsx create mode 100644 src/pages/admin/View/ViewAdminReferenceItemsPage.jsx create mode 100644 src/pages/admin/View/ViewAdminWedgesPage.jsx create mode 100644 src/pages/admin/index.js create mode 100644 src/pages/dock/DockBuilderPage.jsx create mode 100644 src/pages/dock/index.js create mode 100644 src/utils/DockBuilderUtils/clone.jsx create mode 100644 src/utils/DockBuilderUtils/edgeDetection.jsx create mode 100644 src/utils/DockBuilderUtils/index.js create mode 100644 src/utils/MkdSDK.jsx create mode 100644 src/utils/constants/constants.jsx create mode 100644 src/utils/constants/index.js create mode 100644 src/utils/constants/tableColumns.jsx create mode 100644 src/utils/helper/helper.js create mode 100644 src/utils/helper/index.js create mode 100644 src/utils/utils.jsx create mode 100644 tailwind.config.js create mode 100644 vite.config.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1cf0c6cee29b602956d5d87879198a06860fb3d3 GIT binary patch literal 6148 zcmeHKJ5Iwu5S>k27$riXK`ytTM#>FLWKNKh29ih#QY=S&dS3#Gf`WpEOHgqD1b3jM zL$tiv9c(WSlqfdDq1Q-sBc3)CK1mO@rO`7H@T=$@%(m_bhvr zy1b|v+tGk;hw0Q*0aZX1*f<4nX0ydTk2m`DZt+ci@}%*mLA=w1Cwh3fDzoz zaLv60*I2<+u=I!s%=l8EFE!F)7+(&*mAq82^ytgUNb_NY*+_?C96Q#xE}cy3QD;>^ z6$lmR`r84Y|A#;K|6!5dsRF9Nzf!_JI2m+O1^!flPt8VS1^@s6 literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..615c01e --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# PARADISE DOCK BUILDER + +## SETUP Process + +- You are expected to clone this repository and set up a new repo remotely on ur github with the code (Preferably name the repo with your name. E.g john_doe_paradise_dock_builder_solution) + To set up locally + + - **a.** clone the repo to your local machine + - **a.** update remote origin + - **b.** install dependencies (using -- "npm install") + - **b.** run locally on your computer using this command - "npm run dev" + +- Make sure to commit each fix with a valid message entailing what issue was fixed in the commit. This will help when grading your solution + +- When you are done, cross check all fixes and then submit the repo that has your solutions. + +- Any commit after your submmission will be discarded and won't be graded. + +- PLEASE make sure to deploy the app to a live server, to allow full test by the Development team and the Quality Assurance Team (preferably netlify or vercel) + +## Table of Contents + +1. [Dock Category](#dock-category) +2. [Dock Data](#dock-data) +3. [Issues](#issues) + +## Dock Category + +- **Dock Category** is a category of the docks. + - **Dock Category** can be one of the following: + - **ROLL-IN** + - **FLOATING** + - **SECTIONAL** + - **WEDGES** + - **RAMPS** + - **BOAT LIFE LIFTS (2 CYLINDER)** + - **BOAT LIFE LIFTS (4 CYLINDER)** + - **ACCESSORIES** + +## Dock Data + +- **Dock Data** is the collection of all the docks information + - docks is as follows: { + itemName: activeDockCategory (ROLL-IN, FLOATING, SECTIONAL, WEDGES, RAMPS, BOAT LIFTS (2 CYLINDER), BOAT LIFTS (4 CYLINDER), ACCESSORIES) + image: dock?.image, + category: category, + length: dock?.length, + materials: materials, + top_view: dock?.top_view, + width: dock?.width, + lift_range: dock?.lift_range, + model: dock?.model, + no_of_cylinders: dock?.no_of_cylinders, + name: dock?.name, + thumbnail: dock?.thumbnail, + weight_capacity: dock?.weight_capacity + }; + - this information is to be attached to every dock that is added into the canvas editor as `dockData` + +#### The Issues Are Listed Below + +1. On the sidebar, each dock is listed as tablets with names and dimensions or size, and when you click on a dock, it will is added to the canvas editor + + Now the issue here is that is doesn't work + + Please fix this issue. (Tip - An onDockSelect handles this in Builder.jsx file) + +2. CopySelection Functionality is not working + Expected Behavior: When you click on the copy button, the selected object in the canvas editor should be copied to the clipboard + Actual Behavior: The selected object is not copied to the clipboard + + Please fix this issue. + +3. PasteSelection Functionality is not working + Expected Behavior: When you click on the paste button, the selected object in the clipboard should be pasted to the canvas editor + Actual Behavior: The selected object is not pasted to the canvas editor + + Please fix this issue. + +4. onRedoClick Functionality is not working + Expected Behavior: When you click on the redo button, the last undone action should be redone + Actual Behavior: The last undone action is not redone + + Please fix this issue. + +5. onUndoClick Functionality is not working + Expected Behavior: When you click on the undo button, the last action should be undone + Actual Behavior: The last action is not undone + + Please fix this issue. + +6. onPrintScreen Functionality is not working + Expected Behavior: When you click on the print screen button, the canvas editor should be printed + Actual Behavior: The canvas editor is not printed + + Please fix this issue. + +7. onDeleteSelection Functionality is not working + Expected Behavior: When you click on the delete button, the selected object in the canvas editor should be deleted + Actual Behavior: The selected object is not deleted + + Please fix this issue. + +8. on the sidebar the downloadImage function is not working + + Expected Behavior: When you click on the download button, the selected dock should be downloaded as a png file and also trigger the download of the dockData you extracted in excel format + Actual Behavior: The selected dock is not downloaded as a png file + + Please fix this issue. + +9. on the sidebar onDownloadFile triggers toJSON function + + Expected Behavior: When you click on the download button, the canvas editor content is downloaded as a json file + Actual Behavior: Nothing happens when you click on the download button + + Please fix this issue. + +10. on the sidebar, the onUploadFile triigers uploadFile function + + Expected Behavior: When you click on the upload button, the json file we had previously downloaded should be uploaded, its data extracted and loaded into the editor + Actual Behavior: Nothing happens when you click on the upload button + + Please fix this issue. + +11. Finally, objects in the canvas editor of the category DockPanelCategories.Accessories, DockPanelCategories.BoatLift2, DockPanelCategories.BoatLift4, when being moved, should snap to the nearest snap point of other objects of any the above listed categories + + Expected Behavior: When you move an object in the canvas editor of the category DockPanelCategories.Accessories, DockPanelCategories.BoatLift2, DockPanelCategories.BoatLift4, it should snap to the nearest snap point of other objects of any the above listed categories + Actual Behavior: Objects in the canvas editor of the category DockPanelCategories.Accessories, DockPanelCategories.BoatLift2, DockPanelCategories.BoatLift4 are not snapping to the nearest snap point of other objects of any the above listed categories + + Please fix this issue. diff --git a/boat_lifts.sql b/boat_lifts.sql new file mode 100644 index 0000000..0731ab9 --- /dev/null +++ b/boat_lifts.sql @@ -0,0 +1,52 @@ +-- Adminer 4.6.2 MySQL dump + +SET NAMES utf8; +SET time_zone = '+00:00'; +SET foreign_key_checks = 0; +SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; + +DROP TABLE IF EXISTS `paralift_boat_lifts`; +CREATE TABLE `paralift_boat_lifts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `create_at` date NOT NULL, + `update_at` datetime NOT NULL, + `model` varchar(255) NOT NULL, + `weight_capacity` float NOT NULL, + `lift_range` int(11) NOT NULL, + `no_of_cylinders` int(11) NOT NULL, + `length` float DEFAULT NULL, + `width` float DEFAULT NULL, + `image` longtext NOT NULL, + `top_view` longtext NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + +INSERT INTO `paralift_boat_lifts` (`id`, `create_at`, `update_at`, `model`, `weight_capacity`, `lift_range`, `no_of_cylinders`, `length`, `width`, `image`, `top_view`) VALUES +(8, '2022-11-17', '2022-11-17 18:25:37', 'PH-2K-4', 2000, 4, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(9, '2022-11-17', '2022-11-17 18:28:51', 'PH-2K-5', 2000, 5, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(10, '2022-11-17', '2022-11-17 18:27:02', 'PH-2K-6', 2000, 6, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(11, '2022-11-17', '2022-11-17 18:27:29', 'PH-6.5K-4', 6500, 4, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(12, '2022-11-17', '2022-11-17 18:27:57', 'PH-6K-5', 6000, 5, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(13, '2022-11-17', '2022-11-17 18:28:32', 'PH-6K-6', 6000, 6, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(14, '2022-11-17', '2022-11-17 18:29:25', 'PH-8.5K-4', 8500, 4, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(15, '2022-11-17', '2022-11-17 18:29:53', 'PH-8K-5', 8000, 5, 2, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/2%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(16, '2022-11-17', '2022-11-17 18:33:12', 'PH-8K-6', 8000, 6, 4, 14.84, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-6%2C8K-4%2C5%2C6.png'), +(17, '2022-11-17', '2022-11-17 18:37:36', 'PH-11K-4S', 1100, 4, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(20, '2022-11-17', '2022-11-17 18:55:35', 'PH-11K-4', 11000, 4, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(21, '2022-11-17', '2022-11-17 18:56:18', 'PH-10K-5', 10000, 5, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(22, '2022-11-17', '2022-11-17 18:57:25', 'PH-10K-6', 10000, 6, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(23, '2022-11-17', '2022-11-17 18:59:12', 'PH-13K-4', 13000, 4, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(24, '2022-11-17', '2022-11-17 18:59:39', 'PH-12K-5', 12000, 5, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(25, '2022-11-17', '2022-11-17 19:00:36', 'PH-12K-6', 12000, 6, 4, 16.85, 11.3, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-10%2C12K-4%2C5%2C6.png'), +(26, '2022-11-17', '2022-11-17 19:05:57', 'PH-15K-4', 15000, 4, 4, 20.95, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-15K-4%2C5%2C6.png'), +(27, '2022-11-17', '2022-11-17 19:06:47', 'PH-15K-5', 15000, 5, 4, 20.95, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-15K-4%2C5%2C6.png'), +(28, '2022-11-17', '2022-11-17 19:07:28', 'PH-15K-6', 15000, 6, 4, 20.95, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-15K-4%2C5%2C6.png'), +(29, '2022-11-17', '2022-11-17 19:08:30', 'PH-20K-4', 20000, 4, 4, 22.86, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-20K-4%2C5%2C6.png'), +(30, '2022-11-17', '2022-11-17 19:09:09', 'PH-20K-5', 20000, 5, 4, 22.86, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-20K-4%2C5%2C6.png'), +(31, '2022-11-17', '2022-11-17 19:10:21', 'PH-20K-6', 20000, 6, 4, 22.86, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-20K-4%2C5%2C6.png'), +(32, '2022-11-17', '2022-11-17 19:11:09', 'PH-24K-4', 24000, 4, 4, 25.03, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-24K-4%2C5%2C6.png'), +(33, '2022-11-17', '2022-11-17 19:12:07', 'PH-24K-5', 24000, 5, 4, 25.03, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-24K-4%2C5%2C6.png'), +(34, '2022-11-17', '2022-11-17 19:12:37', 'PH-24K-6', 24000, 6, 4, 25.03, 12.41, 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/4%20Cylinder%20Boat%20Lift-min.png', 'https://s3.us-west-1.amazonaws.com/com.mkdlab.images/PH-24K-4%2C5%2C6.png') +ON DUPLICATE KEY UPDATE `id` = VALUES(`id`), `create_at` = VALUES(`create_at`), `update_at` = VALUES(`update_at`), `model` = VALUES(`model`), `weight_capacity` = VALUES(`weight_capacity`), `lift_range` = VALUES(`lift_range`), `no_of_cylinders` = VALUES(`no_of_cylinders`), `length` = VALUES(`length`), `width` = VALUES(`width`), `image` = VALUES(`image`), `top_view` = VALUES(`top_view`); + +-- 2025-04-02 18:13:38 diff --git a/index.html b/index.html new file mode 100644 index 0000000..9d950f5 --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + + + + + + Paradise Dock + + + + +
+ + + + \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..243270c --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "jsx": "react", + "baseUrl": ".", + "paths": { + "Components/*": [ + "src/components/*" + ], + "Pages/*": [ + "src/pages/*" + ], + "Utils/*": [ + "src/utils/*" + ], + "Assets/*": [ + "src/assets/*" + ], + "Src/*": [ + "src/*" + ], + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..33e21f3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5235 @@ +{ + "name": "adminportal", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "adminportal", + "version": "0.0.0", + "dependencies": { + "@fontsource/rajdhani": "^4.5.10", + "@heroicons/react": "^2.0.12", + "@hookform/resolvers": "^2.8.10", + "@stripe/react-stripe-js": "^1.9.0", + "@stripe/stripe-js": "^1.32.0", + "@uppy/aws-s3": "^2.1.0", + "@uppy/core": "^2.2.0", + "@uppy/dashboard": "^2.1.4", + "@uppy/drag-drop": "^2.1.0", + "@uppy/dropbox": "^2.0.5", + "@uppy/google-drive": "^2.0.5", + "@uppy/onedrive": "^2.0.6", + "@uppy/react": "^2.2.0", + "@uppy/tus": "^2.3.0", + "@uppy/xhr-upload": "^2.1.0", + "bootstrap": "^5.2.2", + "fabric": "5.2.4", + "fabricjs-react": "1.0.8", + "framer-motion": "^10.12.4", + "moment": "^2.29.3", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-hook-form": "^7.34.2", + "react-loading-skeleton": "^3.1.0", + "react-router": "^6.2.2", + "react-router-dom": "^6.2.2", + "tw-elements": "^1.0.0-alpha12", + "uppy": "^2.9.1", + "xlsx": "^0.18.5", + "yup": "^0.32.11" + }, + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "@vitejs/plugin-react": "^1.3.0", + "autoprefixer": "^10.4.7", + "postcss": "^8.4.14", + "tailwindcss": "^3.0.24", + "vite": "^2.9.9" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@fontsource/rajdhani": { + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@fontsource/rajdhani/-/rajdhani-4.5.10.tgz", + "integrity": "sha512-4qsQsC4BELF8FC6UEBs5QBkek4v+/t0PYmQYtw1WDlNlq0V5mjUldMxjXzZano9/5HBihL7ztfQRPOf8WYexIQ==" + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, + "node_modules/@hookform/resolvers": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.11.tgz", + "integrity": "sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "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==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/@stripe/react-stripe-js": { + "version": "1.16.5", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-1.16.5.tgz", + "integrity": "sha512-lVPW3IfwdacyS22pP+nBB6/GNFRRhT/4jfgAK6T2guQmtzPwJV1DogiGGaBNhiKtSY18+yS8KlHSu+PvZNclvQ==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": "^1.44.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-1.54.2.tgz", + "integrity": "sha512-R1PwtDvUfs99cAjfuQ/WpwJ3c92+DAMy9xGApjqlWQMj0FKQabUAys2swfTRNzuYAYJh7NqK2dzcYVNkKLEKUg==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", + "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==" + }, + "node_modules/@types/lodash": { + "version": "4.17.16", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", + "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.19", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.19.tgz", + "integrity": "sha512-fcdJqaHOMDbiAwJnXv6XCzX0jDW77yI3tJqYh1Byn8EL5/S628WRx9b/y3DnNe55zTukUQKrfYxiZls2dHcUMw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "dev": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@uppy/audio": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@uppy/audio/-/audio-0.3.3.tgz", + "integrity": "sha512-HmIE3berOiHZko0G8cZyIx6B0xY3EphjlLqUM3Q5xPJk6iU4ELIQWXjdgfYs7AFCSOPLcOf5IxldoSxAFCr+Cw==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/aws-s3": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@uppy/aws-s3/-/aws-s3-2.2.4.tgz", + "integrity": "sha512-kdyrm79fWm1uMUvza4LwD76zh7gmV41VlB5S5WojtT5JjT/hHlfsYZIuXNqqt7oWSzHUNPwAKxPvp9fHmEJ/tw==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.3", + "@uppy/xhr-upload": "^2.1.3", + "nanoid": "^3.1.25" + }, + "peerDependencies": { + "@uppy/core": "^2.3.4" + } + }, + "node_modules/@uppy/aws-s3-multipart": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@uppy/aws-s3-multipart/-/aws-s3-multipart-2.4.3.tgz", + "integrity": "sha512-2z/mTmDceQimsHGEXuhdUL6v7Twsj1TKLDTxp+YPEtf9cuSBhzwkUd/YltHHa8tH/ocdDXs4rwLuMZBXNIo0Qw==", + "deprecated": "Use @uppy/aws-s3 instead.", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/box": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@uppy/box/-/box-1.0.8.tgz", + "integrity": "sha512-ACgfD1o+f+Tu+K99605p5HhziYyHqa8B5wx3Lf/e5XqqdaHBi99qyJ902I8UVQQpdr/KCvkF1hbxtsPep5+Geg==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/companion-client": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.2.tgz", + "integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "namespace-emitter": "^2.0.1" + } + }, + "node_modules/@uppy/compressor": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@uppy/compressor/-/compressor-0.3.3.tgz", + "integrity": "sha512-EsaKXOksToMDjTmrJhWJ8xKJsZyJCQysHwo5aoLQ/7lq+wVifF8TqvglT1Z8c8nSRkgcDum39vw8oQqvlmdyNQ==", + "dependencies": { + "@transloadit/prettier-bytes": "^0.0.9", + "@uppy/utils": "^4.1.2", + "compressorjs": "^1.1.1", + "preact": "^10.5.13", + "promise-queue": "^2.2.5" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/compressor/node_modules/@transloadit/prettier-bytes": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.9.tgz", + "integrity": "sha512-pCvdmea/F3Tn4hAtHqNXmjcixSaroJJ+L3STXlYJdir1g1m2mRQpWbN8a4SvgQtaw2930Ckhdx8qXdXBFMKbAA==" + }, + "node_modules/@uppy/core": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.4.tgz", + "integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/store-default": "^2.1.1", + "@uppy/utils": "^4.1.3", + "lodash.throttle": "^4.1.1", + "mime-match": "^1.0.2", + "namespace-emitter": "^2.0.1", + "nanoid": "^3.1.25", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/dashboard": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@uppy/dashboard/-/dashboard-2.4.3.tgz", + "integrity": "sha512-OPpvosiRaZXN873mraDmiM8T8c+2rIl86Ho7lQPsq+aQfjLUiPML+Y2rjmwDPE6eo7EiBszV5dQkO6vPjGO8/g==", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/informer": "^2.1.1", + "@uppy/provider-views": "^2.1.3", + "@uppy/status-bar": "^2.2.2", + "@uppy/thumbnail-generator": "^2.2.2", + "@uppy/utils": "^4.1.3", + "classnames": "^2.2.6", + "is-shallow-equal": "^1.0.1", + "lodash.debounce": "^4.0.8", + "memoize-one": "^5.0.4", + "nanoid": "^3.1.25", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.4" + } + }, + "node_modules/@uppy/drag-drop": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/drag-drop/-/drag-drop-2.1.2.tgz", + "integrity": "sha512-J6hBiYcBc8p6U9PylqtZ+eMJ48yT1qP1Xzon2Pou5AQxQ4D7UAL97OvcjnONpOfp8P7uGmaqXFUubBNgEUCfQg==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/drop-target": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@uppy/drop-target/-/drop-target-1.1.4.tgz", + "integrity": "sha512-TCFTLqBnHGDJTV0DOjiT8HYO/YKm39Sg3tXNdubhidWAy/S4UVAj73X634UVOQascK5Fo4iHSwWaj8052xAETw==", + "dependencies": { + "@uppy/utils": "^4.1.2" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/dropbox": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@uppy/dropbox/-/dropbox-2.0.8.tgz", + "integrity": "sha512-nvzRTW38sEsj0jhtMizLq0aEQbkY1fd5rDQy6phobt07aiJ8T/KT2NrefuIUaT1ECkz929l0yqDbrdSk7iynZw==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/facebook": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@uppy/facebook/-/facebook-2.0.8.tgz", + "integrity": "sha512-2sz8IFowl/7nOH0Sx4OK6Uo/Wps7ARUYmJs5vjeYUNzODdnQM6WZPmDpQ0vfJW/5cgj8ilx87kUaCLZul1Ey1A==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/file-input": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/file-input/-/file-input-2.1.2.tgz", + "integrity": "sha512-tdn6HNMnLOC2xpdZYbdXNSjTS9EpLvY97vOfq9SZWkoX/cmZiOf6JfFc3Qm8pS3RRnVmwsdi5usEPbzMQ5RAEg==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/form": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@uppy/form/-/form-2.0.7.tgz", + "integrity": "sha512-FsZ97NRUeXCi5iAJB2VAxxCa+WOizrP8y42g2dD36S783e81a4iZqSm2N7WSC3PQHNu7OKkQnPYN+9xU0w5emw==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "get-form-data": "^2.0.0" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/golden-retriever": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@uppy/golden-retriever/-/golden-retriever-2.1.3.tgz", + "integrity": "sha512-G1zBjgsfzMjebQ9KMLTE5H6AX/fuMAe6izFx6B6FE0M4mJ9ZZ+ClbHQeFzomepagGq8dJoIB4M85CzRyq5Igbw==", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/utils": "^4.1.3", + "lodash.throttle": "^4.1.1" + }, + "peerDependencies": { + "@uppy/core": "^2.3.4" + } + }, + "node_modules/@uppy/google-drive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/google-drive/-/google-drive-2.1.2.tgz", + "integrity": "sha512-78JAaoV3MNeaNDCzJpym5nIY0NynWivyjcbJ6Ool2xyAKqS5nBUXnAG+ciEl8ZMAhFdv94kI9pLtrRNezYI7vg==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/image-editor": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@uppy/image-editor/-/image-editor-1.4.2.tgz", + "integrity": "sha512-oqcvIphTQVhRJiMpi7aRe5gx2O+GqVOp9G+/kNPDjFZTZoS6vTrGWdmlr27dt0JkZarckjOK7+2iboCSIe9Qqg==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "cropperjs": "1.5.7", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/informer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/informer/-/informer-2.1.1.tgz", + "integrity": "sha512-aSdtJO0QvDGzcWHQ1Kd1hOFLyn+0e8LY82708WGkt8BwYwjmKhCJUuxdPDsCu3I2wpFUSUzpvQ9pik7AajBFjw==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/instagram": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/instagram/-/instagram-2.1.2.tgz", + "integrity": "sha512-dvQJ6PeX9hFN5f0+hi7qx2zDtZz0AyNYqB/O41VUulO0eDgfh8c4r4w4EUp676o9aY/0Ia71il7D5y3vtysqIA==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/onedrive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/onedrive/-/onedrive-2.1.2.tgz", + "integrity": "sha512-rlRNPdOT+AWmeDkiGqQODjATQw6w3Xn/Uod6Ct2JvJhLhxImbTsxDedQotwE18m3AnQ9wLdLM9cCGAcvKeEsHg==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/progress-bar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/progress-bar/-/progress-bar-2.1.2.tgz", + "integrity": "sha512-BzO+LSMDj+daT93yoUhNdkQ1Bq79lSm+hTUcuFpUt397B0ETzUeHUg3wUj39Zu3r7BlO/JmQLbH4NkejK4rYGg==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/provider-views": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@uppy/provider-views/-/provider-views-2.1.3.tgz", + "integrity": "sha512-IXk8j+0nXxsTLV1KwUJbholiwMYXJ9H2r7pJlBRiu/lB/hgd5t7ENqt2susnepBFQJ+XlaIsuM5YVLgppBwc5w==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "classnames": "^2.2.6", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/react": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@uppy/react/-/react-2.2.3.tgz", + "integrity": "sha512-Zr8KRHATAPeYGI10Xty+KS4rSh3mvoE5l++9Ondo0Qu7TbFPIrmLlq52d9yMI1dV4hd98kshv6kRa394Ex4UbQ==", + "dependencies": { + "@uppy/dashboard": "^2.4.2", + "@uppy/drag-drop": "^2.1.2", + "@uppy/file-input": "^2.1.2", + "@uppy/progress-bar": "^2.1.2", + "@uppy/status-bar": "^2.2.2", + "@uppy/utils": "^4.1.2", + "prop-types": "^15.6.1" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@uppy/redux-dev-tools": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/redux-dev-tools/-/redux-dev-tools-2.1.1.tgz", + "integrity": "sha512-dLJv/gofRIkyw6RIUFQxCOhtvplRjQNcp7BlbSfThH0AkaRtm+dH/0hjCpl6wED5r746u+ZUoZyMi7P4STGC0g==", + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/remote-sources": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@uppy/remote-sources/-/remote-sources-0.1.2.tgz", + "integrity": "sha512-MBsFAvXH8tEMzl3E+5s0F27K3iwewV/7QaXeHrgOcToxlAuk7f+3xmYCKIt9vK3pp7iSebJ0SQ/qaHSHZizL2g==", + "dependencies": { + "@uppy/box": "^1.0.8", + "@uppy/dashboard": "^2.4.2", + "@uppy/dropbox": "^2.0.8", + "@uppy/facebook": "^2.0.8", + "@uppy/google-drive": "^2.1.2", + "@uppy/instagram": "^2.1.2", + "@uppy/onedrive": "^2.1.2", + "@uppy/unsplash": "^2.1.1", + "@uppy/url": "^2.2.1", + "@uppy/zoom": "^1.1.2" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/screen-capture": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@uppy/screen-capture/-/screen-capture-2.1.2.tgz", + "integrity": "sha512-gsVtosu/3rHe2W2oJQUwcNFdBZuJ6DQmILrtdMicBN4SuQck0ClmztOTjQXGuCurjSErS5HHvxz3G98I/F5Epw==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/status-bar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/status-bar/-/status-bar-2.2.2.tgz", + "integrity": "sha512-XV4/3RyNF42enqPc4wWZupqI1KuGtfdt49waux7kebxaGqNzV+T72o/C+QDDqY/h4mKadrp6p98/BnMefC5QtQ==", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/utils": "^4.1.2", + "classnames": "^2.2.6", + "lodash.throttle": "^4.1.1", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/store-default": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.1.tgz", + "integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==" + }, + "node_modules/@uppy/store-redux": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/store-redux/-/store-redux-2.1.1.tgz", + "integrity": "sha512-RFb/fi4BBlC+l1TC/Z76Wo1ZsssAr4Su23C0ZgB1KCA2RrXRIu7dkwQuFHvy+HokJhhyDPm3Sm474ulPr5wpzA==", + "dependencies": { + "nanoid": "^3.1.25" + } + }, + "node_modules/@uppy/thumbnail-generator": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/thumbnail-generator/-/thumbnail-generator-2.2.2.tgz", + "integrity": "sha512-5VwwzzvKRqXJNz28U/VwXu9K9dHY5vXQvzljxqkeCJrKIMgu/8vzKEFndAPY6sJZkUcF0jtAb3gUU2q5TGRlJg==", + "dependencies": { + "@uppy/utils": "^4.1.3", + "exifr": "^7.0.0" + }, + "peerDependencies": { + "@uppy/core": "^2.3.4" + } + }, + "node_modules/@uppy/transloadit": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@uppy/transloadit/-/transloadit-2.3.7.tgz", + "integrity": "sha512-LETmEvMX/Am3Gu/thxttHGdIy7oOjU2+G5+VHozzyBF8Z1kqW0p+LUS0HwWM9w7cz4S/Yi+ZI+rP0aR5J2txhg==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/tus": "^2.4.3", + "@uppy/utils": "^4.1.2", + "component-emitter": "^1.2.1", + "socket.io-client": "^4.1.3" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/tus": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-2.4.6.tgz", + "integrity": "sha512-0R8D65YKZRyvx+SKNKPkgVYrgAcZiz6vtzc+ZmcCePFXNZj945kFIoeqNUuQ9aQlkIasihKB4kut6X2F4G93IA==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.3", + "tus-js-client": "^2.1.1" + }, + "peerDependencies": { + "@uppy/core": "^2.3.4" + } + }, + "node_modules/@uppy/unsplash": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/unsplash/-/unsplash-2.1.1.tgz", + "integrity": "sha512-S6pX12ierlx9iBkTXvPlljmPI32CYdp61WoLrGXrhmZmCrFsk64ngZK7PVmkJ4avsokNq5vDjkZvgjxZ5wmtzQ==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/url": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@uppy/url/-/url-2.2.1.tgz", + "integrity": "sha512-OY85TLyaQG0ELd3+fX9KTJ41EOmsWvVYMYQvNXSs/+K+xe52mzEqV8PK85dxlQwEIuPAGtQQGz+6IICuwsWDdA==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/utils": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==", + "dependencies": { + "lodash.throttle": "^4.1.1" + } + }, + "node_modules/@uppy/webcam": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/webcam/-/webcam-2.2.2.tgz", + "integrity": "sha512-7pYFhzYRj7AcwXg0onrcDMUjW5uNC8ImF9PhUzaZBtp9pnXMKpxfbiHv3MobqH8XtCZWYAvPizPc4dqDMY7iJA==", + "dependencies": { + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz", + "integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2", + "nanoid": "^3.1.25" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@uppy/zoom": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@uppy/zoom/-/zoom-1.1.2.tgz", + "integrity": "sha512-b19x6jnEqCnm7UNyM/vO+AVq4xaaPuVD3wzpkzh47aROtLm/DGVKhTFSJAFVfae7iE4JmeA80JHXYYfb7dgUFA==", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/utils": "^4.1.2", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", + "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.10", + "@babel/plugin-transform-react-jsx": "^7.17.3", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-jsx-self": "^7.16.7", + "@babel/plugin-transform-react-jsx-source": "^7.16.7", + "@rollup/pluginutils": "^4.2.1", + "react-refresh": "^0.13.0", + "resolve": "^1.22.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "optional": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "optional": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "optional": true + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blueimp-canvas-to-blob": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz", + "integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==" + }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "optional": true + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001706", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001706.tgz", + "integrity": "sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + }, + "node_modules/chartjs-plugin-datalabels": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz", + "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==", + "peerDependencies": { + "chart.js": ">=3.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combine-errors": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", + "integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==", + "dependencies": { + "custom-error-instance": "2.1.1", + "lodash.uniqby": "4.5.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressorjs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/compressorjs/-/compressorjs-1.2.1.tgz", + "integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==", + "dependencies": { + "blueimp-canvas-to-blob": "^3.29.0", + "is-blob": "^2.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "optional": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cropperjs": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.7.tgz", + "integrity": "sha512-sGj+G/ofKh+f6A4BtXLJwtcKJgMUsXYVUubfTo9grERiDGXncttefmue/fyQFvn8wfdyoD1KhDRYLfjkJFl0yw==" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "optional": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "optional": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "optional": true + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/custom-error-instance": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", + "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==" + }, + "node_modules/custom-event-polyfill": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz", + "integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "optional": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "optional": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "devOptional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "optional": true + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "optional": true + }, + "node_modules/detect-autofill": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/detect-autofill/-/detect-autofill-1.1.4.tgz", + "integrity": "sha512-utCBQwCR/beSnADQmBC7C4tTueBBkYCl6WSpfGUkYKO/+MzPxqYGj6G4MvHzcKmH1gCTK+VunX2vaagvkRXPvA==", + "dependencies": { + "custom-event-polyfill": "^1.0.7" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "optional": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.122", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.122.tgz", + "integrity": "sha512-EML1wnwkY5MFh/xUnCvY8FrhUuKzdYhowuZExZOfwJo+Zu9OsNCI23Cgl5y7awy7HrUHSwB1Z8pZX5TI34lsUg==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exifr": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz", + "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==" + }, + "node_modules/fabric": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-5.2.4.tgz", + "integrity": "sha512-3+oLKvsbSJ76/nvPPrQVuUJDp0kOh8i867PpdrOPIHUyN+eLc+9nY3rmzMmDw6ndRm20f/uULv55G8sN01j9+Q==", + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "canvas": "^2.8.0", + "jsdom": "^19.0.0" + } + }, + "node_modules/fabricjs-react": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/fabricjs-react/-/fabricjs-react-1.0.8.tgz", + "integrity": "sha512-IHsxpEAAxXAUToSsQQw6OMjJNkHYwURqdf+RPjeSilPn1iVpDfBpSyYBX+hqCTg3x7b7WplYZOi/bhmrxMVvYQ==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "fabric": "^4.3.1", + "react": "^17.0.1", + "react-dom": "^17.0.1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", + "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "optional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-form-data": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-form-data/-/get-form-data-2.0.0.tgz", + "integrity": "sha512-YUpw0aTWeGliifqMYrTohe/YdqVmKLmaNwuscd2WlRNGfba57JHGuuvvv2c6LiZdFys285POVWANTh6SqcwFag==" + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "optional": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "optional": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "optional": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "optional": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "optional": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-blob": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz", + "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "optional": true + }, + "node_modules/is-shallow-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz", + "integrity": "sha512-lq5RvK+85Hs5J3p4oA4256M1FEffzmI533ikeDHvJd42nouRRx5wBzt36JuviiGe5dIPyHON/d0/Up+PBo6XkQ==" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "optional": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash._baseiteratee": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", + "integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==", + "dependencies": { + "lodash._stringtopath": "~4.8.0" + } + }, + "node_modules/lodash._basetostring": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz", + "integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw==" + }, + "node_modules/lodash._baseuniq": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==", + "dependencies": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "node_modules/lodash._createset": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA==" + }, + "node_modules/lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==" + }, + "node_modules/lodash._stringtopath": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz", + "integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==", + "dependencies": { + "lodash._basetostring": "~4.12.0" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + }, + "node_modules/lodash.uniqby": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz", + "integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==", + "dependencies": { + "lodash._baseiteratee": "~4.7.0", + "lodash._baseuniq": "~4.6.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "dependencies": { + "wildcard": "^1.1.0" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==" + }, + "node_modules/nan": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", + "optional": true + }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "optional": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.19", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.19.tgz", + "integrity": "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==", + "optional": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "optional": true + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/perfect-scrollbar": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", + "integrity": "sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/preact": { + "version": "10.26.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz", + "integrity": "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/promise-queue": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha512-p/iXrPSVfnqPft24ZdNNLECw/UrtLTpT3jpAAMzl/o5/rDsGCPo3/CQS2611flL6LkoEJ3oQZw7C8Q80ZISXRQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proper-lockfile": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", + "integrity": "sha512-rjaeGbsmhNDcDInmwi4MuI6mRwJu6zq8GjYCLuSuE7GF+4UjgzkL69sVKKJ2T2xH61kK7rXvGYpvaTu909oXaQ==", + "dependencies": { + "graceful-fs": "^4.1.2", + "retry": "^0.10.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "optional": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-loading-skeleton": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.5.0.tgz", + "integrity": "sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-refresh": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", + "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", + "engines": { + "node": "*" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.77.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz", + "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "optional": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "devOptional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "optional": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "optional": true + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "optional": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "optional": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "node_modules/tus-js-client": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-2.3.2.tgz", + "integrity": "sha512-5a2rm7gp+G7Z+ZB0AO4PzD/dwczB3n1fZeWO5W8AWLJ12RRk1rY4Aeb2VAYX9oKGE+/rGPrdxoFPA/vDSVKnpg==", + "dependencies": { + "buffer-from": "^1.1.2", + "combine-errors": "^3.0.3", + "is-stream": "^2.0.0", + "js-base64": "^2.6.1", + "lodash.throttle": "^4.1.1", + "proper-lockfile": "^2.0.1", + "url-parse": "^1.5.7" + } + }, + "node_modules/tw-elements": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tw-elements/-/tw-elements-1.1.0.tgz", + "integrity": "sha512-IUr9YW2l99oTVZxuVjQg6rpuCpo6VZqbaKGYmZUNVVsoSVU/ljPpkJAY2Pn/morlXwKPhP9MTPNQMlWosqHL4w==", + "dependencies": { + "@popperjs/core": "^2.6.0", + "chart.js": "^3.7.1", + "chartjs-plugin-datalabels": "^2.0.0", + "deepmerge": "^4.2.2", + "detect-autofill": "^1.1.3", + "perfect-scrollbar": "^1.5.5", + "tailwindcss": "3.3.0" + } + }, + "node_modules/tw-elements/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/tw-elements/node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/tw-elements/node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/tw-elements/node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/tw-elements/node_modules/tailwindcss": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.0.tgz", + "integrity": "sha512-hOXlFx+YcklJ8kXiCAfk/FMyr4Pm9ck477G0m/us2344Vuj355IpoEDB5UmGAsSpTBmr+4ZhjzW04JuFXkb/fw==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.17.2", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.0.9", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1", + "sucrase": "^3.29.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tw-elements/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uppy": { + "version": "2.13.8", + "resolved": "https://registry.npmjs.org/uppy/-/uppy-2.13.8.tgz", + "integrity": "sha512-qsEvSil1ebnBj+WgkCKn3GLNCKN4xyGKjtrjz6BUlGWyaw6kv47Kb8q1VmBl85z17tkedA/OLfPd0pXZ8RfQNg==", + "dependencies": { + "@uppy/audio": "^0.3.3", + "@uppy/aws-s3": "^2.2.4", + "@uppy/aws-s3-multipart": "^2.4.3", + "@uppy/box": "^1.0.8", + "@uppy/companion-client": "^2.2.2", + "@uppy/compressor": "^0.3.3", + "@uppy/core": "^2.3.4", + "@uppy/dashboard": "^2.4.3", + "@uppy/drag-drop": "^2.1.2", + "@uppy/drop-target": "^1.1.4", + "@uppy/dropbox": "^2.0.8", + "@uppy/facebook": "^2.0.8", + "@uppy/file-input": "^2.1.2", + "@uppy/form": "^2.0.7", + "@uppy/golden-retriever": "^2.1.3", + "@uppy/google-drive": "^2.1.2", + "@uppy/image-editor": "^1.4.2", + "@uppy/informer": "^2.1.1", + "@uppy/instagram": "^2.1.2", + "@uppy/onedrive": "^2.1.2", + "@uppy/progress-bar": "^2.1.2", + "@uppy/provider-views": "^2.1.3", + "@uppy/redux-dev-tools": "^2.1.1", + "@uppy/remote-sources": "^0.1.2", + "@uppy/screen-capture": "^2.1.2", + "@uppy/status-bar": "^2.2.2", + "@uppy/store-default": "^2.1.1", + "@uppy/store-redux": "^2.1.1", + "@uppy/thumbnail-generator": "^2.2.2", + "@uppy/transloadit": "^2.3.7", + "@uppy/tus": "^2.4.6", + "@uppy/unsplash": "^2.1.1", + "@uppy/url": "^2.2.1", + "@uppy/webcam": "^2.2.2", + "@uppy/xhr-upload": "^2.1.3", + "@uppy/zoom": "^1.1.2" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/vite": { + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.18.tgz", + "integrity": "sha512-sAOqI5wNM9QvSEE70W3UGMdT8cyEn0+PmJMTFvTB8wB0YbYUWw3gUbY62AOyrXosGieF2htmeLATvNxpv/zNyQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.14.27", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": ">=2.59.0 <2.78.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": ">=12.2.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + } + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "optional": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "optional": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "optional": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "optional": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==" + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "optional": true + }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "optional": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "optional": true + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b72c6a3 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "adminportal", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "tw": "npx tailwindcss -i ./src/index.css -o ./src/output.css --watch", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@fontsource/rajdhani": "^4.5.10", + "@heroicons/react": "^2.0.12", + "@hookform/resolvers": "^2.8.10", + "@stripe/react-stripe-js": "^1.9.0", + "@stripe/stripe-js": "^1.32.0", + "@uppy/aws-s3": "^2.1.0", + "@uppy/core": "^2.2.0", + "@uppy/dashboard": "^2.1.4", + "@uppy/drag-drop": "^2.1.0", + "@uppy/dropbox": "^2.0.5", + "@uppy/google-drive": "^2.0.5", + "@uppy/onedrive": "^2.0.6", + "@uppy/react": "^2.2.0", + "@uppy/tus": "^2.3.0", + "@uppy/xhr-upload": "^2.1.0", + "bootstrap": "^5.2.2", + "fabric": "5.2.4", + "fabricjs-react": "1.0.8", + "framer-motion": "^10.12.4", + "moment": "^2.29.3", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-hook-form": "^7.34.2", + "react-loading-skeleton": "^3.1.0", + "react-router": "^6.2.2", + "react-router-dom": "^6.2.2", + "tw-elements": "^1.0.0-alpha12", + "uppy": "^2.9.1", + "xlsx": "^0.18.5", + "yup": "^0.32.11" + }, + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "@vitejs/plugin-react": "^1.3.0", + "autoprefixer": "^10.4.7", + "postcss": "^8.4.14", + "tailwindcss": "^3.0.24", + "vite": "^2.9.9" + } +} diff --git a/pg.js b/pg.js new file mode 100644 index 0000000..acb9be0 --- /dev/null +++ b/pg.js @@ -0,0 +1,281 @@ +const a = { + authentication: { + login: true, + register: false, + forgot: true, + reset: true, + lambdaLogin: true, + lambdaRegister: true, + lambdatwofa: true, + lambdaFacebook: true, + lambdaGoogle: true, + lambdaPreference: true, + lambdaProfile: true, + lambdaRealtime: true, + lambdaReset: true, + lambdaSend_Mail: true, + lambdaUpdate_Email: false, + lambdaUpdate_Password: true, + lambdaUpload: true, + lambdaForgot: true, + lambdaEmail: true, + "lambdaVerify-email": true, + lambdaVerify_user: true, + lambdaApple: true, + lambdaCaptcha: true + }, + models: { + user: { + POST: true, + PUT: true, + GET: true, + DELETE: true, + EXPORT: true, + AUTOCOMPLETE: true, + IMPORT: false, + GETALL: true, + PAGINATE: true + }, + token: { + POST: false, + PUT: false, + GET: false, + DELETE: false, + EXPORT: false, + AUTOCOMPLETE: false, + IMPORT: false, + GETALL: false + }, + photo: { + POST: true, + PUT: true, + GET: true, + DELETE: true, + EXPORT: true, + AUTOCOMPLETE: true, + IMPORT: true, + GETALL: true, + PUTWHERE: true, + PAGINATE: true + }, + email: { + POST: true, + PUT: true, + GET: true, + DELETE: true, + EXPORT: true, + AUTOCOMPLETE: true, + IMPORT: true, + GETALL: true, + PUTWHERE: true, + PAGINATE: true + }, + profile: { + POST: true, + PUT: true, + GET: true, + DELETE: true, + EXPORT: true, + AUTOCOMPLETE: true, + IMPORT: true, + GETALL: true, + PUTWHERE: true, + PAGINATE: true + }, + permission: { + POST: true, + PUT: true, + GET: true, + DELETE: true, + EXPORT: false, + AUTOCOMPLETE: false, + IMPORT: false, + GETALL: true + }, + room: { + POST: true, + PUT: true, + GET: true, + DELETE: false, + EXPORT: false, + AUTOCOMPLETE: false, + IMPORT: false, + GETALL: true + }, + chat: { + POST: true, + PUT: true, + GET: true, + DELETE: false, + EXPORT: false, + AUTOCOMPLETE: false, + IMPORT: false, + GETALL: true + }, + accessories: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + trigger_rules: { + AUTOCOMPLETE: false, + DELETE: false, + EXPORT: false, + GET: false, + GETALL: false, + IMPORT: false, + POST: false, + PUT: false, + PUTWHERE: false + }, + boat_lifts: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + dealers: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + docks: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + analytic_log: { + AUTOCOMPLETE: false, + DELETE: false, + EXPORT: false, + GET: false, + GETALL: false, + IMPORT: false, + POST: false, + PUT: false, + PUTWHERE: false + }, + cms: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + trigger_type: { + AUTOCOMPLETE: false, + DELETE: false, + EXPORT: false, + GET: false, + GETALL: false, + IMPORT: false, + POST: false, + PUT: false, + PUTWHERE: false + }, + instructions: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + quotes: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + wedges: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + ramps: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + quotes_mail_recipients: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + }, + reference_items: { + AUTOCOMPLETE: true, + DELETE: true, + EXPORT: true, + GET: true, + GETALL: true, + IMPORT: true, + POST: true, + PUT: true, + PUTWHERE: true, + PAGINATE: true + } + } +}; diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6c4cc91e714bfd08ce5a96af6482d1ce1bc5a705 GIT binary patch literal 6148 zcmeHKOHRW;4E407stSurY*^+By+NqL9;pkqv?*$lnx<0eK38DJ88`$h794>cSKtUd zkF6+^kJuqp+0pn-X6)E`QtS*7x!G|wC7KY?2!nAjhH4Ss=h2X!uCTN7&v>H4c0HY^ zdA4s@+k!D*4E#3+_}d-Qf)2PIsQ&%B^tryD&K=1Z(|QMh<}dB1MjtbVoGZz^@7 z=gY;IQpzc#J@gLQ?3(%9y&_htT}NIWwypWK+6H;{V}z$OWVoO;G7PKT@bV+v?Z~O( zpXIHYR4jt{qg#o81T&Act;MI$FCV|$?TlZs=!joh1o7#c;-fFrC^^(F;*-Du>*193KUnTM+_(KSdY{$RxAX? zotz#%oJMwfLUBGio*#L0a { + const handleTabReload = ( e ) => { + e.preventDefault() + localStorage.removeItem( "token" ) + } + + window.addEventListener( 'beforeunload', handleTabReload ) + + return () => { + window.removeEventListener( 'beforeunload', handleTabReload ) + } + }, [] ) + return ( + + + + +
+ + + + + ); +} + +export default App; diff --git a/src/assets/images/BrandLogo.png b/src/assets/images/BrandLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b5bd3f955febcf75de2b1376bae1e7f1af58fb GIT binary patch literal 10848 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 aebw{Tax%-h3e1aK_`st=#oh`l}ohflDS& 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 qb`+;@QPQP6)ZlW4R6 literal 0 HcmV?d00001 diff --git a/src/assets/images/index.js b/src/assets/images/index.js new file mode 100644 index 0000000..db3b2d0 --- /dev/null +++ b/src/assets/images/index.js @@ -0,0 +1,4 @@ +export { default as BrandLogo } from './BrandLogo.png' +export { default as GrayMaterial } from './gray.png' +export { default as WoodgrainMaterial } from './woodgrain.png' +export { default as PerforatedMaterial } from './perforated.png' \ No newline at end of file diff --git a/src/assets/images/perforated.png b/src/assets/images/perforated.png new file mode 100644 index 0000000000000000000000000000000000000000..9acf2f7efb78d07121cbe98dc326bdb582ace0e6 GIT binary patch literal 29054 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 literal 0 HcmV?d00001 diff --git a/src/assets/images/woodgrain.png b/src/assets/images/woodgrain.png new file mode 100644 index 0000000000000000000000000000000000000000..50c90a0b97b6ed8ca571f670b9f3e21214a42e27 GIT binary patch literal 23999 zcmV)eK&HQmP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>DT~tX#K~#8NO}%NC zBYAe-2OzNT`<9u=y>h9&x~r>~>DijIa>yaYktm50Da*1>H{L&x6 zpTKXV5haeqAvrzM(`&6+mHUpwzDFPs2n68Y4UEq*rWykTBI1Al_dfTz&y6jepa0+g zTQZrPOeUJ9>AK$U_tWXLWlaOYfY0YEm&@UB*c^`?PG>L>3-rt8DeXq-%^`7FDdPJJFFn~Hw;!Q)D``|QPjrO`AU&TuU5 z^SYdt$?P37#&eBEIM>O)kzs%d%YLx~#xhNFG#aInNsq@f(sk?$g+jzhRSB5}hK$jO zmGP<9=O5}LmrDUU;J`3V7Z#XCE|q-m{U;8m=hM$$o?q3gwN_yvn@9!+YBLxNIxWRH zRs3GB;!^bCkRV}*J{ry(0vG&-JH-8t86)CNoaJ+Su!=#kTz;C(ST1pMI9SMBtcmGX z6w90dTGLeDesurIlgBTfz5dzHetvLtlt`s>`6BZhn-fLxI2D)2=N%b3`;5jSOp`Ql zITeq`J06dwQ!5sav1X&uWC@d;Sd(tA7Y>DivNjrdeO^X4t;uMlEC1?Geuq`q#dY}_ zQ>K=MH;D=}Aql#jPB;?b!(cFAi8Y-{+GR4N2=n7T7K=^Cqn)*4C>s9c*?y(dQ%B=S zz!&y;+%8heRIp1SfyQIgBn;+w0=n5D;FwOQxC4i>b228GOb{O;I+kn;ViKTdbmon-e11Rs0e|++HIK(9S)(XeDB*NE8PUeLi*-C6HUZN?7`u{DiXsS z>5-84$veA?OZmV2tEXq@t!~#0hoa+AFP;cNH~?@wHh_gd#F{!4H_SFBH*M^b>g1E6 z5F&9afnhjYMhLM7GgwH#SO}yrSeSDQ@4uPO zTq~<9j0jN3S|Wtc$OYIU6bzFbFvM^;GRG!WfW6^_SS((x)-+Y)17p?dwYj4J6ERc% z;KMtZ((QJGA$F=SE-XL=*viIp`<9oNo6RO4BGD)e#>Tw(m05;@A<17-5ztO2-r4Bk z_G&ob`>!8AFL(Mr0%%Pl(YOQTxD=lk+O+1l=6k!{CIEpz0Mq%(SYVvbQQI%|~%$2S-FHmJX)_00jdf+lUFJAQB3b8P?dCjCIGjTgb$}^U-(PUG>j zbLMjSP&lgT5DrZ4hicdaJ`{#@x)>C{08qvyJPb#`@CGB`CM@IwevC$=_-CLFSTZ-5 zAN7Ihc6zOu1-v~TXl4`)`F`uW-%-`^fB5N7U%z=3i$(65DJCs^#%xt#v{FcZ#J7sVTZy0<=_8pIL+o01H9-X$w4f`8t8~9 zf*{_h{5TwQiOWo)=$TUlh1%l}n3lusoP<3N%TSBO!p6?_v%Ry!!(&bB&!mvLQ>hgB z7Z9p-k0&OPNF)-yZjTSloOeoyF*abf;Yc5wW84N3v7Y+R1TbmPAFwBq39b;SBIVS} zPyCOSL&%5e%rX!OmT9TGJ4;(TYi|zD_7BV5{%D}#3s+%b(HN6{BP@rzkPN+Ee_~q6 zWQv{Hnd%@I&^0e6hOq>R5vkz32q!k<89+vi@wML}b|S}5*hC;Pk=$}Aj&wQ&-%Tba&Sgm4IvE@BaA;;t zh)Oh(rv6Z-9zWPJCa#Y^eW_}FVhsD_vKiv<_P7uT;$e#-Axy$lnK|>M!b>*e8i|2W z99EII)DLVW+c2Fe2_e9lUN2B}K=p19B>{4alD>);sJWa|=VTlW`=301m`SI;{A%yw zrrPOt`r2@5X$g0CS}pd^aCihXP{tV%#1KM|0YQL8_Kz_v#0+4e4@DR7nRP&YtJMU2 z2+>Zr9SHeh)K0s{ya_676OTlOYHu_e-n)0_-o3lWM^}ILpMLi8)&A!C=2EHD>vR;? z4A&v|gc_z6nazwycRt``=!dZgp~nX^Pe6CCKOpmzfBKV;;2Er$^ElSn7{_Z6s|E8Q zouNm5f<0K1h3QP1^+5pz0Ac4LawFt`g48lBwsUx#mfIRFl$N%4w_iMeb9Q=8kpxpH zlwdfFj)h?;JTvIrsrUmvf&rF61A31*zy`zvbFqXCgZ=e_lDm81qv5Xk`_pv8XF?>u)U*i*r)4DKXH z(KRkGVNS-%-~av-fHvoAEaQcDcqbTGOjaQr`ApD3G%V5Yqt=jcU{Ll#E;22kjlN|A z^2FviG(2QIggN`|~{k8|Rj5(^8froMoeJfpx8Evy9= zfPj+5xa^AZz)Bc^xaViA>U6q%z)luXdCZYP)ERv`nW7#Ep+6WvCF67=%71sa)>leP zm0J7F!O7K4b!3_8LOuhL8U}3-O$+!XRKpR(E8Gt(criS^6q8{m;1{6SNgz1pSL~so zfR=Fuu_T*gF=>^=drsOQg|JdzydI_3X{O?#58iz{m0A4!%U3`D#jiR8ZSB@pBpRbh z1uN{CV7q2c$|s5=o=C76v6-Wjh{15Ip!~DH^<OFbH07jc_!Q-xMv2HP{5*k?hzF zv{`{ywOXZ3W-EM$kJuei#KrS!NfSc|S(1o_S%FS!^VWJY9skv@zv^^a{ocTu;^1I1 z8~1o&Il?t_D*%JAVkD%YB6`VWw#P$ zr<1(_7rX|1amakK{A#0GG!CXRG$z0eECIoUjjiZvdB-7SEyj~%`9k{1yASCFe*Tvq zzkIppbhuZRH)&V~+7NiprVbG{s0>ZT=MnTL$bf<*VCXwt`3FCIj~WXXE-jVdI%o~v zCo1!qi}Z>{E0szl9*3P+2cSiYXA8B;<8#@UEho+ z6F9;|8b~)qZ0OY_pZGT4Au*E{p2on|?(CKt%jo!rX|gih%U3KRY=p-yy|NuPun;4& z*&JqQ!#3LX-re2z-+wYxji3I<|N8mo&okNl#;seu?!YkMz7hkhA);)F`;dspmD0*el~}UA4ZV4K zu`<0k8ye$bFyNWaVDQn#`Udsj%NKjic5^V$BB8kA_Jxqe6S_Rcm3$X!b~s5P{7i~q z4~Aloz}#jbUSNWStVY%`7Vf~XcB?gS$nYJ}WjUU;-Rk#w=y{D`fOI#Vj_K{~oqKoh zeDdYXt8(?^^a#IiY;00sSQ)wGQV>lO(XDpdOTyCVNIXVzFa;Z!AV{}CApI#H=8}XD zI1TTCS~LP5*aPn@91Qw6EgYnE^K{x>yn65cz4t$O@2Y(B)1Us?;o<(R-Q5yRQLDkY z!V&g=I0Vw7c;gdc;6MKU6D+_K24;G^lPDw;bC%?dMp+w9U`R{>AS}c+_#KH4HBn=+ zA{Yv>N4BE20dP#G&a(+a&Zd*0rcz|+Do`bbLT3BU^2zbl(cxvYsZJ+Q^fa4~BU`;5 zANwc93;_e+1%*_jBbHIDdBEnTvIaw7CpKWPG=)L*N|>*#Feh_>g0tDsF;jfLsLvO` z&xS!Nr%LsD^Uj^c($eL{CDA8Tx~8)+1rKLZ*pz?tyB{(nvI=p5D;bz~fI#=a zsC>o+3^s4P@K-XC><{`FVS{CWa%FSi1C)ppR9P$*c_*&AVc)IZ8k$%} zIOutBcgODw{^H{=>&?1B>#De8k!UhT!(+BOe(p{JyZL=Oe*?~ufKnOR{rA4=X-DV zeSyg0(n`Nwk4A<6k*Umrfq(*#1u=YP31I*V44O(MCEHM*Za12OB`_0KF(NC|;A0gZ z@Dllf3XdgI0BA7i`h0FOjLHqyp`V|;{np0D>RUXrtr+HAor^h{v3(#gEu$M~iTxqwpQSPMy}&Nn=dx z#(r-ArHYL3_R_R7cchSTwGkFVsO`(>BDkJL>~&z@7MJaZ3$?$ zG2>zxmZ0(Q;QU}15yHG|HVdE$42cEG`IXe12aJ5}4SF-jBp#1UX}l+VNcB4Ecr=bj zg2`0kdq4c1+Z*}p^RIsTAO4K2e(UjD($mWUKsQ3S*=#BAuf+=s3!|Ck4TbCNCTct2 zp(_~Yb<5O^Q9hfY^Jgcli3FPqblPmM&x0Ofif+0` zNsftqIKzX?K^l%mI^acd4CQcxVwht-5JJX<{i%SE32GmXViTGTG!xVspo)pd2c+ch z|M&;a>Ew&go*tc-jhSnJf=wr4iFmWtbXe@c4b%XT-%Gtw*x2EoxqZ@YBiV@rE+SgI z=*-gT4B=rl_>)$0PTi?wN*H)N<}1n@ydWH;FgqO@#e@#mt!!6$tKF0pt-j+i3dq|S@4DF*<>smMWFc${twUjTdwhSLf6q7J}6E|cCCspWuAf;m>}PZI6_z_GUoOQySRf*s)MfAePHS^*>CwZxjh^<|7tgON^=7L}jbC41 zM}?1!q2h6ZZZN?+a6#Tptf||j-~;BxXtp!lb3c$R2=C)_ZsVCCC1qlq0zmu z5I3<2@nWHUbl1TLVhGXbmt~Bkg(Q- z6mz)Hieu6aX)6G?C7%pzH&~c~#-V5AGb2KbERi7DWrolAly&7Cqj*XVDq0f>B-`n_ zLC)O02OX{-ytz6%tDT)*(tz(ix*bbIbwi&zCZNGCpALsbgB0<4r1c~38G?W@YmkbI z048`PY`o?cVidmO14sp7Q9JD&t%e=g_&p@Ooy7m zH8$B+x}Zva751~Eg=^+M#lI{E*ual=6K~7W9hMT{*=%#U1be(XJUhF*8IBD$5}(Ua zStyRK6A(z(rMM_GXhkxYDN6v@KT*L>ky~gG`$+CQFvI}fBflj!BDJk(G{$Ot#^G$j zYQcaUL}$v$?tT3&%1$Yw~3d5=dELpQyCIq-+t zflwJraeJt0j7uKl8Hi)gya}VuK7N@frSvTi>_Q;fo zQ@hnprjm>YE|@mNST2{Pe)|0M9|VT=K?;z>4tz=m<@0%rgVP}Wfi}QEh|x|y^22gW zN3Pi@zmR|Y?mO*{_W2jD>ea@es|A97*fkWP9CiRKA|)IO%ZZ{0WFm(Wm>A3D@RXf8 zq%)W&a&yq&BR=3AcpJ;X+>oY6;)lW(l*pOw5dxZ;>y@^bitF&G14O@3D8}D@=K&qX zS5Nn^F6!4ejobGg*gmjeX`oE%lISA4?3Yw-pAUDL#&{~`$cZj&zys`_(OC!2Ff<83 z3FlYLV|ThfralpE!bN#w!$2sgYhBYEI;Z2sh5TD@J>0o{Tlqi#hu>RWT{%3vzHanX zl3W|a!>~71Dn_?KsMr^SL4fRt(X&^*EMs9rk+~#^LL`*@SB&1tyZVq>nOC+(O4px zY&IHfGMieVa0qfHLO6m=P!n?(Ul_S`wY{lDS#sUpxwBl_c>2}eb@}?`%a<#wYq@-$Y6s->kxGK&e#W8_`zg}n zF)cU$W1lP}JXpn)u>rtAZeRLS%`Nqm=A!=4ucu7 zuR^h*FFh&i+YypdC)lu$^$|sfQx2Pv1-Kdc;}KhCrZLPU!*4&iHEHSmo<>mYY_8JX?;W02`lD{IA96c*UoI`y z8+CBOtdKH9Mb!p0xxH?WfvJ<}bP9x`YDj808iSy|tE(&AMD=1}i{!KWFkppK=(1?H5bthfRr%tZ!=&su9%}6jDxDpFOBU z&E=VS0np5Aj;3CzdJbK0I2ChaYwf-Fp1gQ*`fBgwaQ|R5GSg`oB!lXa@WVZdBH)v2 zH~geNJRA;%h#y#|Sjn*{pI5cd;^HOgh+{E6fD+^=f`Wv=LY%=)sA{zBiqkWl%!pIS zA4DoEfBFaCrpJPULV@7gN~w@99PPhuwEE?aL6b7n2b&u!gFdpv>+n23kBCSLlT7gPX`rq=ghpkEf@q#l=P4Fi89-Aa{ zfo+)wcx$Imh*?OI9n=%2&P?}o5kCKmP zm4EfeKa$Qa;H4PyLpqh**}8RhT`4y@Bg15OQ){+;YjZj=#=`;i7>xw;4OJCu#3Qqr zrVpuzb0V5k%AD>p*=(g+Effkwh0NncKg3pRE32JOyVYufsrB_W@*h6*`vTAfdyr!g za*$rmb0QMFQv|x}?%f^a)>qH=C{eXmHx^I$J<8hZ>ebZ+Oo--ndVIr?NhIc1s1YR4 zN}^Du=I2e=8)joLFHjHU@ev%(YFLYHvVwim2kt`dGYi5g?X!IaqZhuWsaF)@4RO8y z!Mm{5%a?nVW~bXznO3Q^OahtXkvUX>9V$`n4`?fKJ(3C`!?wTyd*)u^1C10UiKFy<&66D4CRrdiT#uT-7t|wM#)L5vH{k_#PAka#`ONx62=DODa%={> ze8%<6guW0u=U1)$gVWas2lZw*lgkyeS+BzyY6EjNBRR&> zAxP1(6GqN^iK9>)P+T~aNF*E7UAbIE4g@1Ka0shO+aoC@ECoI^xqMO1E-C-yC*LIB z*%zUj07eJ1nuL6=t&Np%F#K}wNFPn>?GExfo6ln?81ehO)`WrOLdKCnQHhaQa3GZ# z6yR&v4=Tg&%x&%$ekv9V%pdP05(#MMx?IL7Bv5}afb}3=agcp*7BK+0Y#viAr<+AI zL~hqC8V)?Te@7jTj!&+xDmC=#aIB|uDY;~4G70;9x-r7*#EBqb2K<_ffTesrLG#uL>NVefILoTrj$O#(941h|8xn)v#FB*^8VvS6eGh3hv!$2+H zv5Ls?t6an(9pO~@*Z<(R0U)OEg;`0q0_Vvv6Y(z;3LanZvRv+}db2lZb_eljEEHgp z(B6y=V`NNV;oe{XaM&G)uyvF~ixt2E{|~X)V5Gt|z!8{kx7*l}$z=MyzJP7aR&vmu zguoKpY;mg;f0XxB@diRT8_Df*z~GbZwNf^h|MdCc)lIdp4umbe-dNBVP@I4QcZ!a( zk1VrqhGuGzK2Bq|m^4oy_K!CqZTy9`Eax-p&tI&A%kbcw6>;KR<+FsSqYp__#Jb1r z@;R(bI?1mS`Md(JMn%1ugX=h_R&kaag>*hBsrr5owAUx z;F5gORLw;}v&SM%IEU$A0rOG*@SD5R5_LM!cmfR4pE46^O3_1>xwx1CI4@ql>~^Vs zhES?6Kx%v4auFocGbUrQhmy#gW6@}%*_amw=$=t+o8+`QHrWPQC?87b0QkxELnUOc zst(b(y-so=F=Akjb^)6Z_$-w zHbsk+ipApWoJjy+@Cz2$1HTfRd64iv@8r7le~N;^d}TO#41Ss)3IgC{I0Qh7Q`O`! zzXv_3lK1u~F8oerz|6RcO8@Y|-LHT6{<9ZHuMbX-j*iOZ%I*6PbIU9BYIQ7a`UvLl zt13apX%ZyM5{=_l5Oh?^WID}SeY+oxClV~dRO$Vr(O$O;VN%dQp!Yjn zsKJW`6LaeFwbgMX7IHd9$+-W~gZnq-YWcbjL)BZ&STy38PGXTT&UAa-5G|m?x!{HN zu-<4e7+4_Z@kQ9a_K_Dj#S8gD{^tN77U~hHIe~gPKjkW(9GN=>FuVMU|=EM!yZ}- zSPDWQ%rIxMAcX_gQU2tI?_fc{KVVhJ4QYq1sG3}U;q2@rl@K+Ie7U{7m5N6{`|`D> z8|Rnj?tp)2=*f5@>JP*B;drRi?nWc=TCKTITx>RK(FiU2G#rjVarlL?DF%S_rhK!y zx{Ac7`;+r^K5w;BrFw^KtP#IvyA_E(+o8+-d_@F5~L5DNEtouy)4*9NE+O^*ChySyYlv4IPT0j?oeunN^e^$vxC znyL~EH~CqMqt zP@8=Gt1nJ3u72_HuXZ0jzVp^Ywcpk?+BP-6P#EhP?R7epVXU#7m1iJ1`cCR#Lnf7G zIqiM6P@ry=udhk>g@py>pZ?8{$aF9j@cF8hn>1wDY?YQ4NupaDn>Xcpz)!31@q69r zM0j<5^YHk%*6Fr-noIFRartB-m5f(g^_9{}quwm!7V5QnX>nnw_NnT2VV4&fjS(c- z;^Jbr(`9<_54=DI(?nAv31mE;RJCC|9)m@tc!WYIjW*wg46(7 zK>`K=4z;1rD~(61*^I{IvX)w{##br?@Shj0a`_sPfZ^rjiZ(3d@}^-@OOY8fNC<{y zlBjSw_=++!Hr7i&`rRM>{I5T~X>|8q?R6ThNAJDQzPhcZP?1LoN21h%e4z-thQk8W zR5As0;8war`U+O-*eBvko0}JBXQZ0)KmYL$sR{5k0R-=$9(E$U?1fq3GWdZFu*B#0 zqMIH*yjv;vsufxz4f!`RAu+X(TOo(y%^Vd$~thI2NhJ!f2L0cVa#EQTeRgb#5(`|CEF z7#2&XmEU{+7QDp#^Z9(aTt;HxI|zr!VIQO8R({3Zf*6OXIA{0nJur;v;nA@+8meR6 zoJ<{K$I{Z`$k2R#mt~>oMxk&Ncc)Y7ZihM)KnuYe{hoN%=MMm!STx3J>=UWXY;P(R zhQkd|U(~)(0)j@Zt}L%qDpgw+%76GPoz5UXDeoYHW)pXhMexd?~aBEBWN_tqr3C~MH*&P@OVUBf&Wm>~wIv)Mu@BFZQ(|q=7|Kj@Ui%&nl zwY#%WDyc&i%{?5diFB&drA9_^1$jom!qEr{XnvX$d^2kZg^H^D%fI)7PP-$wfedx( zCn6-1&7#3@A1uK;lf+igCnq5M0p|>PXm0JScznv`)n%(a?Dq|8JTWFlzL=GB2o7g3 z5Yjb~!qsY3GQ++Ybe=u9gUR3su!HckF+N6h&Wjm3UUV?pS`Gxu>Hl7*i&gA_uWSpd z@FT#tRjQmBo}X>9k4IZBbGVpGzVrCO@p&*B-BKl-|5Puhkk~eDP(w+u7XR4uvC-rP>=H&ZjenVNU$P0NM&a zV6c-VhCz}cDC8@K{lg!<%WUyuAQ*s}01@xljp`k!D&FBUe2uv{Xf&KsEv62AJnpWo zFO^Cw`)@8fy@5U&LO4_!I;Uhj1)hL}9H2zY*+(#?v$L<_=S7<0ZW)_yi4I0?U={s? zgUED924n2#dguWHz91in7KlbG%l3rsl#bWa{FeEDLF()CQ~^K&rD|$aycc%XY6FwFdjD3zu_5{ z01E;`{HQTZ0WnN~S;C-0U5DI(RKOpam=2H21DHmmq2i^Pp_7fi`}W&GZ}82*@#)F& z$mZ5&!WW0Gj#~EFa-rcr-8TtL78lt*$xJMP(meOCZfScV+_M17!w3~_BCiU zXI_5*0v(Q|t0N(JjVGo~`pzbstEJo9oBIc+XXn>_4PmR1RS`Ly6Nfx!PC3GcV;P#A zGi3||+9(kUJ2_3XpdIES7?q1erCG#9$z&RgBjWJ}0pmk75{0aUkimd0m+TuCW+)ne z5LgpR1gGVmS>EXkt2Q^*bz}PM>C5U({qW5ZO1oHE^mskJX45a#OOTBpe8E7w(S+f! z0T6>-fX}bD70DEj6D{)Hn5cArvx#DjXX$v_&=G9j55E3x zTepsmPW!!~uF(^lhED&KmZQ}6B`XLjAue^me!wIMg4swTWm?{$GKIkdF6SbEULZrT4Zu%r*W*;dYNjE2l)9B-d@nk|b%=sa5k4v#=27I*d7VLHB?wzfj+rR$x zvudUG^2PJZ)8p;k-Do1&YqvttFv2gDPN9=isZ6WgCN<^4m2g=3*Z<&0s2vCjci;tH zU+wk;5s+eyCwYC>#p6+ARiUpHQ4#AYp(g-F`flbj)1&V$L7%B~pp; zWDLxGUJnUJ0$DSM*XMz&`gFv8Z%{7y6^)D6U=bz>=>ZF$+qJroeQWpj^OuKb<#u~C z?F~&|K#9i#Aup0d?$wn{SL7aW$HYJjI2DgULyBB5POUfP##c!wR+t zM39hG&fv|k7?aBp*z99+;<4T@uL%|=rW=X*W4a%zo}iSYYhdnXWz{@I`Z^>8>i zJw0?a;8(Ef{IWkUD&6MQR&U$bNoPN(@roc$}K*bPOL~Q zO5{jTmSpoe7_d>R(QV33<-Z9*#SE|C=cLfmQ}^1Tuv|nHjfB_MmVfuhzq7Z0vbT3| zc6RdFuYP_1(W3~+E|=FfHq@S)&Sd)CelnTpX{z#X{+r(gCc?1xjVriMq-vP*-m_Ut z4qm`TR4+&zsKVdULdE08*Z@uCo10W7>2OinCh=J0Ywx{tcz9B)_1ir;3q=iw780?T z+pYA~VK|yPK09AsS|ps18v~(AK?bQnD|2#kBFa!MJB{NVUVJz?J&_Zb_SF;Q4XDCF zd}hztOt#zW+QLnaHDi1z6rRro#V^-x*p42XBf}cd?X9j9A3wbF$;V%T?aQmGI_RfT zakOMO8s zxpCinQSO!* zBUlg-R0ue49wD?wAP|HJNpN|WOFu120g$CKQMV+h_613p2mRv7VzCHlFc?gOGvGzM z5g_eeHy954+yV~!0ArZuWilFs1HPa9-9H3V&z`W?Q%@`f#ubDFZ_L382zaNE z%rCOUY&gK@^$}oZH#X&o9;6*b8ma)z;D)xz=cPlEQcZfu>AG&G({6?%a`pgGU0huF z-fw^Z#mm_i3Xp&| z3p(WBy#?JvUqooG^qBcPQ>j2pOG}hVa7Q2+g{VMrm(LN$YHyMt_Sh3S;L3FH7?Z6@OfT7`#HHjAZ zCe?^KAE>)Tnm7#zr(p!5z(d%ZY~?GWp1r>R-sA1vTgQjzZ}txM_xD|Hk32po98>@FKe*dl8IO4k2>1f7yGGlY*j}RvSq)xF=tkvrPAE)yoPV?M_cqO0Y9x1OV zF~#kZ$C!A+;gCEMK~5Q9OjKCKnvgiGiANy6;b;hkgu9T}a-Lc^mhW+dcibtckg#;zWYzssGIKS*|%}j%+#&0w7EoAHwp=t^8yL5uzmrD;UO$c#v~w4(DtpS*W-Q#R8Foe!Beu zJ*C`fL#dm{^{kCX6AY0FE{6bUepN+(Ko{gwwIRGgA3Yvwy-qtC35~RY$K|0>dF%08 z=({hTzPP%+RQ}ih{%^uIR6oEn?^2tsRxY0>RZ)ZF6U%Yd`NahTU_&yM#87|?Ytf3) z9%BZJONI2Sz5TvE>WwDdHtk)9(y?4xq<92F zL3s7z;v$nyBN}tr3S2?mHUZi~d@bvZl@mCRgIGZgmk>~8Pg+5P1&zfgYi z^*gk>1ev*!xsVSOMBO72AO$AjD^6KiSs|b3MtP^jp^~#nE|+V!TG9h)L)s&X4ePTL zTr4$nsHf8@BniGP6bf5gTc_t2&30EC&$OXB>@__zWBb;IY0b{dwTTUc zxttCdh@B5X$54ZNC`kea0k@paAV}<)mK4V0g-5W5DhrzN4?rXMh`=}2)QNt5|} zvtCDeLR2BU0l?Z;vy~|pRl)(jyzXy*>l@0y`G>!UQ`jT3q9F5uf&rp=$2&}zoWs$0 zgw}@~2gRrsV>F^;y}G)J#bZzk$c4WAaw~)z2}>%#NT||QtC@($-^%9{U=jtjLGKn6xFqj&Fh<+WHM!62rfqr zDmD9XS6|vbDrh_|hf_~ZPhr$>C)d%>WN7By>}jN?cVv%|K;P+Wb$hN;Oe@(Tq*^99&;qmts;-;UJ1K>Et5r% z3WYoiK?A{%3vn?~p3@Zo@eBj^Br+L%g25PB$meOR@m?aCvTqu5b$Y#gAzwbbSYBIg zHX6bZih^hac_<|m5Xeg<6BE=U>;L`-yYpi_1c^!Vfxr+a;!k)Pk9R-~h=>`Ggg}@- z%P%i4OC@=J0;Ep0A%*QytQU#}LI|{J^cZiXOKJ6cz1>bb8H;UhtoVKIgM(w$uxdTM z(H(^RK8K;*zPaH8Kg+12es*R zhTt(utx~~syJ>Ty4Uu7)eEtfy&t@s85&O{va#+n$)|M*8gsMTsrgDvqj1!> z2BmaUqx|vr-@)mG2d9G<5X#=+ zP|}FO_`ohmHR8l)3OfUj0^Ubk0TUMABKLR3`#Q7ynMIfoF3h#4Nq0NeEX17OH} z2xO<-3i`b1Yz8tsJ2~#l2@bQ~>P@NMBmLISu25*bzPPw_#`L05Pk<=OjjAMBp;)Zf z>!jL1Qyne`ctS(+-fYPe9^H6wWUQ{O-P}}x2xWhczo6Xo!2e6fO0)xM=tb)8-gJU1B%DhZMA5#e(T#`d-43R(dpOgwUe{+Y%W_W$!#psX~{*d3QlLw zj1G~I0dlb4zQ;||b?PoP6*S;RoJo~I%>WIW0@zvCG+Mu2vjNW&Y}5-Wi;ic{;4OjSIc9Kq|l|T8x69yw5IGr#sy?N#m9zJ6Y-dSH?=bg{?gPLX}KG?xZ(vgs$ zR=_x$OfAVjqJ?FoT_hBsgifrf-ya;y$v01*vKfxjVo|Ab5%1e~Z^2WCm-VXVfVG{b zPQk!a3wB6y`Rx2DbaBLbr;SX911X}sPiOKRQA8txNKWY4c@IvLnQWt4168mGtMz*F z7z_45>X6$YM*8k!&DOs`5uf!TJ?G&l|&qqb{J35wRI9Y$fpA+bE^FP?>&aQA#tKb3KJ*$ z#(Ir+Bo^UeR@8>I^>rp`3n!XMxsad0uZSUaeekb-7AQ1v%mpMhQmL(I}C@ z#iGlhSufqN+f}XAmseH^1~|aQl}a_jn)I;I=y0S{@G_aK!zK4ZQ~X5;*w4|#=9QJz zL9cJw=f;rEa@51V9f3WIkef|W_f&qyh=f9l%c%}JbQ%xeesFS8IXk;n2a`8%&em4f z(`h=BQP3NnpF^~$9+LlSc`k(1gmFt0#3h?+n=qoLUCZ0f-A$Q(?Azzp4R zyX1inXi+p5W!)$3yj-em8fG*SMOkM*Mt<_=PRTJxy=hQ zY)kRz_Xqq+zSFy~kch5sY|xW;x;^-lZ7FrEj~9d|O#|{r#e#CGF0n_0;yWyptBAdx z;b=&7*Va~-iuv=)>t+Wq^hnTVyTQ(ji$&bWeCB!wQ+&Yygeg}VjmNn{5y@bm9I|K? zR#w+QF_D;9a&;7Z3oEiY(It4ObIW2xc@`ZtNi1&i3EiKbqB4@IcI5UiD)e>EQqS9)uB-&0DFXUGuaGPl&|m}(IWHkEY?_4 z3)B03B<5W4t~pR!d5Y#-QC)(S8C;}YaP9y4lA{~OYso?VlLBa z*0BL|aR;TI6sHI3bb1U&6GOAb5S5!su~?W*t$FSe5|kJfYko2%mJmXw#v~H*I03Xk zC@gpB5?urZT0+7+ozZ9$W{43kVeYtsl|{QL?r6ZH_gm>i^qt2KK7aP2T$3vl_D`-; zxkNgbMjD`F#~9Nern8IvZr`bpLQ*Y~=@edNBEl8~5(y?x^l(p%aWtK-TwNCz7Fa<% zsq0{sxe!Ze31;Sn@fd^gn4$cwZ{7!1f+_o~C1GSKE8SEotE;Qbf%VAK`GE;E0cIVU z;ut)Mm;e8AgGk^Vw#A7Q4~8UK5F9x=-w;EYc_Nu0Fu2Jd42;dOHvm`2IdYe4HqwWw zXn1#f4LaXDI2~J4!?YUB7F{9TP%e|T&wv<(V&UlMaCvp@{Op`KF$59@EJJtMOt#ZO z^+t2~LYr#j^|sqx+7~%*IgwkbNK@1kB@iKs-oaV$Hev{Dp$TPbjRxjXJb*v5BdbKd z+TAP2gLUa${eDkx(D}(9{J7TW?!7)U$J67p6Lx-kdmDx&7txSZamtM1^-y)ZJ}-K0 zv9#2mU!(?-dVLJ0D-A`Xw7rWfs~0CHavhocIz>j6udlHaOfxz_!M!+$;E8S^t8kx< zxCAsMN8#kr0R~Q|bE3kNF{nm#7h|V0>3J{Az-S)S7qg1R;=qR(^YydOJ|pvpr40lx zghtJ1w>tE&_!_zsDLB$2(wWL5X@q=eQ=shOAQ}p+uB@)CZ+!LiWp6NmsV^?C)A8tZ z=166-iEQ@Z&6_)q9`3zct+(5srptj)_;eZ3ePdZr$p%n(%?Hfiw*;)$0vdp<1c<0|CmI zP=&}bC6P%V9v9xTtydzQ-KRDU}&go${&5_Ey#*IAPtEDCem3FRp^!O z`sPO3DHPGA-PO)d1M2oQvxF8=&O4KVl*vnSe0g~pVMZYN3NEC@>-Sj)3KfEu+c6Bt z5LnTafOmrGupIRJP$B>UD7j2>cW3kB`~nU{LtkB$krv>QpO&|_o__wt_U+r}7nj9s zh8&WV&1B>>D}eI(E44~op4O#ib9o?1!6!&k9X*~%Gy>^2n=KrKd8k&*z$)m4W=I&@ z>x;%Bh-1a0^wj~WABu!gDDzW@@pQJ)k?U=}iYvq(Tr<~n{No>er`_xx9Gn>w>)`lA zADhdoE6GFz5#jZPZmuqK1yJjRGlGF2X+R4si2_WuA%Fm*DE?$TK2Qh6#YNH@x8NcY znr4PXWP-xmsvHr;X61kSdq2cb)_|Jf2RcwNK&Z%lzLHynhoqr}J~%jl5`i9H*%BHh zFefLcG}SnYuYj>~bCa~6ihvgi_NCNt6yb*nVGZ-ZR&lv|U5GcK4GofF zJXEK~a3Pc0+1^4oUzV%d$ZEDb2pnI)Pq`&*5FPYzdU}d&;E7~#I%#1|+7e76M#_)h z+aeywQ}R8Tk~6R{7!$B@XTw$wcx&t~Unm?L9xkt}(2dIJEBo?)NRNn;5P+1~F0Za$ zUS5&Qc!x=DtZ%fMtz<$jj}<=g`&CU%(+{>g!QVV^eufG{%LSyUm>xvu)Y5g0Heh9S z1L`|CI)=Eqg8?Fwkd!YkzV*HDT%4Tx{h@RwyTA8lb!{C0q9bzo93w6)E=VLM6S?eQ zY^-fr#rCL}h_Iq8ATyy5Sxh^j*EdXxN#w;jQlc`m$R^*aPk^CEK_9l%A>AxIDl#EHxRq?m|aZ?szc3fs0i;%?enKmJ*lq;ph z66)D9r}1d)?BsO0R1(z4b<`&6ne^{>d*IYG$8u=LzO@K;YImTf0IJXF@p=3~IScME z@9(VL-CEmwyT3KccUcM|;Z=ns9(t!4(b$Ev@(UC-)xR-TLgySJ&-9y|2A| z@d{yl`~JOZ<%WE5y1gz)!|(0%x&ix;rZt;UqDU|DoSZ{uQg`r)9IbH7l8HFrrc@18 zjf8`Wq@hasT#*<`Q7aYm7Pe01QK%3r;)^6;Uyv0+B8Q-L#8ozzBaJ`=QXAEt%gQ4G z6LS7$!paK^OT33e5em%6&~v$*T+)=w%FV)Xs@G$fa+!Fm-Dc-dKbgdS>}%P=l6m&g zVVq_YCOqInUN0?`YV`)WG9K%VdVMyVR&Q!s+jkdBimB9;I*kdJ9!2pkHA zDI|!Isa(A;7rS8_wGo>2`U7%}z{I3}yFJSHzW32}rEzw0X$*~vi!!;g^Y8)t@3iWX zNWeZ_?zf+ar=z6g&`~5Ne+FhG_YlBj%o;SKm2f8Um7+L5EoYyIS0etPrj4TEpgB@A zsU*ci)%0LEMDF8FUQnXwa_TKojpn!AhEM1g*#*nt9OA(YLqSFl9v&Xq$BgAE3b91I zkIR;pF0ZeUe2_A5mhMeXhEWovgi5yAH{Q;}gUF!plEi$)7sOI=JClj%*81vLSFbOx zI=%j2G#Q#s8mUAgy1TtOXt$=;B%aE)x_!W?!ak~&Or_hc7ED{O$@5V_gW%V`rG>7U zSF_phdBqEKe)Q>B0@4{bkx1e!(g@c;#?(L1V~)qsXbe}1P7!pr6SJ z+;K1E3P0m>@r=uUf|Py3GD}DvWo>z#8q;YuSC-&0`C-m=b^zpx06LhnvomBap9h*o z=|bZXB>HD)YGq{wO;0BQIXGPkmF(5tUN%>t&!Gs>>Uj*H@PmQlcoA$|O^y zW6D0dL9O!J&w|7n_D|#xqtumoCk{8#M`Cm=>|bA9!Q+P~=e?n+>IU_>dUG8M2e)>2 z@K>$gTv*+x*Q@f!6+E8Hi;Jz>cgi=FOfKj2css4u;vzD=#}E`?sk^z{)#U}gAZ%b( zine{}3xguS$TAWRCqRZ|BnwF|+)CJh;37?Hug5}ul{+fvUWdcDTvVhdW3gVR4RwG2 z2OnMC+-_<%=`CiUHsWZ!@mILh6%T za{zW3i=w53-Ohkt{&v(&Zf-@`?4eY3@L*?A25VZ zc6N5o&n_9s=abtp*ETj!j*h)vUpNvvJv%F{Z#1NkE;!L+H|5e&sZmD(%K2HzS^FaO zL?WdjJMF8Hu#ercA9hGZ)HT&(|D~@`IG8WyOUo;#r{}Fs8^ED;syCHEZ?L*?E0IdS ze)D>LbA3Q3KTtPr-6G!6REma_z^tyVz1n+~&*vx?0LwlMp+efs3)M4o$d$#c$=JjL zt{WS`*#u1JH)2C3(SA|wX}|DqyWPrWGsKD1M7zoXRXE@8?zacw2Q}>-fREh@Ybzc zZ8<^bqSd{=y570HTd7uY`@-Vl{@$zYd-rOUGL1_rk$CfFf9uv3EGmb; zLJ71F{mipp`M>_%pH%CeO0(Vs{u zl>!C?h&H8nWo7l`^mM7TjEl+WTs}{#l}gKKLYQ}bb@k=br}=z=@(WbS_lvW0)}bU2 z2{^6QYOk!WwAw8wnM{Wji53k$u%JWKRXrMucT^2ZqPv)yqnKa0fA5ZJOfIgPJxvql@i?XqLV|)PV06+AHbZnu|-b-TDb@>o4@m`Uq8LNh72ZOeEC%@m0Vt3b|@Bc%0$x+%OR%) zOu3~l7LSZgxs~7HaDfo=USK`7lz;w*-$5@fEfzZMHeL>d!dW$ zqoJ-2^_7*iv$Ingc;bv7X`JT=mQeL*v)jA75^S$8nND9`oFhYO)hgXM@i^E&xPALJ zzfLDsHea|rKSx8;x8j|6QWBSZg*N57aQji^d<75yjV=c(p4JQ^iVBrXs($41cY zL_y^8g=(b+uuLlUVA!r#g+5^-xmm6onMr0Z6VkunwH6BPmhlmR@NwZ z{1l7H4a+Gxu-OXB=?oF#G?(OhE}NtB2wdE5NErUW#WaPKYPn?5PA@c3HkU{G;}AIz zDt|*vt{}R;x+)fmvaUYG7A!q*J0P((*D1 zP9?zZR;#hTzJ7W}E0d)3;Zw8_=wr8JMx|0)SSZqX2|pC_<#J_laRJYQi$bw*bb3-+ zUnebky&f&|#n~CXwrErP$^c+nEEambu1j%ebD4{)OWHVx)4`q*ggAwIiFa%vH2+g^ zepCJujzZFJu9Tck&*|v}F7B(N^UE@tsM~HXt(1K7*!gZen;Z1{NQOuz(`mGpx3`Dd zIG@dVJPKM|&Nt$6$QYe4b57_i?I$e_^&#G(a>!9#uLn3vdt~1hHorhaXw#IV1~%~h z^tL#3b#)bo@Dpu5g&1r348>KeHIi~{_6xb#leh2fA6(pA*E{Xr!NH|DcA$nl0TKGR zLaLxP_9?>5;fUP3PHJKj^+@@5fBJXTUN`7d!U1nO5vM6UIz9LJ0zJ)`%p9F=pXz4} zhnZB$?^SMUwbJr(rBa1Gsr;C=w6q9c!K}dQ{QMlT4#4~BV5w9(Iyz#Vu{G&*d&?`! zXD271(od_~?=P;b+?2~yY+jUNg4gSGDfMV5y5mx52_Zv}h@~--6JSuBoVT|B0v}(y z{Xr-acH!VL^31)xwH}Qn_ud>1My95ZJFTYI?UHMc(&_BN;>E>PBAtP4)&8Kkx>~)i z`Xe#L>xWVV^^GQ71Yu~@8`L)9g^`24I<;mJ21_oBPzUnh#fYS6fHF*m1p_UO3GvS^65bWV-=yh8jx#x5Ct#5uf(yhazi{Z$u)*2U= z*ZGCTY_V{8d5$TOKv?T*@&Ex#{@@rGLR^@JseJTseR-*931ZSCyT?1P7hNoq+hm*-b{EL^kyhRVMprv;61Dwzb= zID+}Z31k4C5~kT6D*!_2(Xq^s? zQFdwR?C>nTuxOdno9lAAm~T{S$yDm%;*ypY35u4XaV9ya+30f7enM_)u~0x@EaeFO{3YU()DZlaO#-39MWBp7_gf1zx=mx%gZY_Hh{X*{VB2!UtMpS}IgJ5?Cc=|nP}o}N;K@Hmam`u6s#=g(Ky*Ga)93T=HIg_bK8 zY32;mT)VYNMp6Z-;H`RF&WiO0_Ol%=5{ZAPF~l9^or*y($W(M)OC@8scegIjFY5Is zBEC|qdwd=$;#Akx@7^`6N%iV_d272-t^0z(cp_Q3xLm)zgE6_%lG^W9Yt=#_9}ERY zau@d$#-rb2L`q|$*^)bcB=_WSIJu7dAnoPlWjN!eQjrHZ8*-Bfmc$bBxJC-(g9rC( zwJOw2PIP-+;*I6OK+rTMG>frV2*Uo>Hy`7Wr%#UvXQ$JDb9kID7B|*c%9Ts6&l?U$ z2YvZ_B2+B74dGA!;3E?0rdnHAEVa5lXea3NxUBK^*5=2bd>)L(4RdBpCe=!n9BQ}f zaPIu16#0kg;Fp?2n3)Gn*zw89ty^1?F1o(Cz5U|F^Tnm5nNw<#T;W!4FcXK%RlT{n zwYz(HcIs4={K5hPdTo8Pep5w*Ih~HmO?hK`yD2Bpr@2fP5lpHvVIVC1jcL*pJ7$*4 zLH)=!w=r;4D|i8>+#glMv;#+kK$~W%W!NGMRR(-x^-*V?M}^c zda-zRdg2X-(Aj{A8VOwD3Hd8{7!PN_>f{Hs#!S)0v|$7Zu|u3FX26cH0W2yp-GZ!Z zUt^09VuesJ^yc7=(I*=;zfM);LK{@!D^$hPd>!6D6%G1C1WXC|HKv>We#@cv)? z;!|HJV2mclc+zRLOH0LeORm=~7V@uNy-a6P^q7zvjzB>Sb$wxBaqrdMox68YYd4jf z`;Q*Jc=o)I$&#$d_l>m;xE{ZzvzfEw{rdg}Ot3a3@2w!MJ`5T{;i6NI=`TQ=A-|h9wg?SDu5e<%q(6aH-_kY_Lj6Qp@ zHy)3MgKo8aO+`eNAz9b&-M>0M%@!9tK7XZrwX(6Hp)U|bSYKUU zmzGzMlvIVKrPBH7*+OX<-CI7tT;JI_Jv)xr>`|}Qb{;&~d-0O)Cz?#5VzPxI<$>%8 zMS60@m z&8A$6<8dRS65;ru)qe8e&fT5WFTQ-y>#O6bviEAg-|akl>t27*>~(8_a8P+~GmXTg zFrq18!pqAm@*NQhOz5lV&3AToKK}TbKO_$e>8X9B( z39S14<m_2A*7x}81CYwM`QOunGF+_Yj_+q+limk8gOoFsfj8d2xb z>%@<(cJAMAR_lX)zgQ}j?fV!gQA8PzBGReU{AjQJ@Iel3$llazUm_0oqWusK;b;^bAbQu=*5Q1Tk9>m??%cV9L%7i19H0EAf$+#9a^%QLD($czhD7TQW#7KD^VQQmh;^oThlbhi_2CDhc$agr zn5QA_4K!M=v$Hd3I+M#)ZfL3&5ozeXe7<;ka=LE2~cqAfE(T_yF`SlNtfm$us+QVsMFq%w;LD#5yd7LTaR&H(IoL$l}jdX(s zD?vf(s}B3x=JHjUCKRe83lOEW#`Fn(zZX~V*-QB`<&JUQA-&bLHBybyaUEa5E7Lbd zqm`9q8hOBkK!p-fCEc!^{3CLJjP(f{pP2}lMwj<{-Do(d_51hl?JO2DM^}}bW@l=- zo_@9Gm@2pL+*AJWTaWTk)75o0lZC$Erp4kyqu!K18)ZL1CX-GNwZXkR+egRet)Axb z%Yh$bY`UEC7tN*foJyNx7)z^bhp+ePrisugB}yJM zc#U4l6bsjvm#KV?1g(_Ix3+i4fM_}?&&q4G3hQf?vkSjJkj!LT4N(@jiGGkJ_H9CP zz>o*L>hzE7=L#!M7rBy5B`H;Ct#5wg8~$MEx^mr>n@D@@cGu_e^eWY4GPi#J zJ`O`c&`VXWD*+li2UTyDTUvnkq?;L=?RGPnmTUUu0#>{2g}@0RKm{m3oSBjzbX})s zXS5{*hGxYwC%6Lb8l`n>wJ?jSX#TnKU8q qy!%e0)jd8ucT8PZSM`hQ%KrofqXLWo0000 { + return ( + + + + ) +} diff --git a/src/assets/svgs/ArrowRrightIcon.tsx b/src/assets/svgs/ArrowRrightIcon.tsx new file mode 100644 index 0000000..6caad51 --- /dev/null +++ b/src/assets/svgs/ArrowRrightIcon.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const ArrowRrightIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/Chevron.jsx b/src/assets/svgs/Chevron.jsx new file mode 100644 index 0000000..dd299ad --- /dev/null +++ b/src/assets/svgs/Chevron.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const Chevron = ( { active } ) => { + return ( + + + + ) +} diff --git a/src/assets/svgs/CloseIcon.tsx b/src/assets/svgs/CloseIcon.tsx new file mode 100644 index 0000000..c48bd56 --- /dev/null +++ b/src/assets/svgs/CloseIcon.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const CloseIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/CopyIcon.jsx b/src/assets/svgs/CopyIcon.jsx new file mode 100644 index 0000000..77cd19f --- /dev/null +++ b/src/assets/svgs/CopyIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const CopyIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/DeleteIcon.svg b/src/assets/svgs/DeleteIcon.svg new file mode 100644 index 0000000..350ce01 --- /dev/null +++ b/src/assets/svgs/DeleteIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/DownloadIcon.tsx b/src/assets/svgs/DownloadIcon.tsx new file mode 100644 index 0000000..a037645 --- /dev/null +++ b/src/assets/svgs/DownloadIcon.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const DownloadIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/GreenTickIcon.tsx b/src/assets/svgs/GreenTickIcon.tsx new file mode 100644 index 0000000..a85d46a --- /dev/null +++ b/src/assets/svgs/GreenTickIcon.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const GreenTickIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/HandIcon.jsx b/src/assets/svgs/HandIcon.jsx new file mode 100644 index 0000000..9da0349 --- /dev/null +++ b/src/assets/svgs/HandIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const HandIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/Loader.jsx b/src/assets/svgs/Loader.jsx new file mode 100644 index 0000000..27ff8ab --- /dev/null +++ b/src/assets/svgs/Loader.jsx @@ -0,0 +1,16 @@ +import React from 'react' + +export const Loader = ( { fill, stroke, className } ) => { + return ( + + + + + ) +} + +Loader.defaultProps = { + className: "h-5 w-5", + fill: "none", + stroke: "currentColor", +} \ No newline at end of file diff --git a/src/assets/svgs/MinusSquareIcon.jsx b/src/assets/svgs/MinusSquareIcon.jsx new file mode 100644 index 0000000..9dd1dff --- /dev/null +++ b/src/assets/svgs/MinusSquareIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const MinusSquareIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/PasteIcon.jsx b/src/assets/svgs/PasteIcon.jsx new file mode 100644 index 0000000..0340174 --- /dev/null +++ b/src/assets/svgs/PasteIcon.jsx @@ -0,0 +1,15 @@ + +import React from 'react' + +export const PasteIcon = () => { + return ( + + + + ) +} + + + // + // + // \ No newline at end of file diff --git a/src/assets/svgs/PlusSquareIcon.jsx b/src/assets/svgs/PlusSquareIcon.jsx new file mode 100644 index 0000000..f380659 --- /dev/null +++ b/src/assets/svgs/PlusSquareIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const PlusSquareIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/PrinterIcon.jsx b/src/assets/svgs/PrinterIcon.jsx new file mode 100644 index 0000000..ea24e8f --- /dev/null +++ b/src/assets/svgs/PrinterIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const PrinterIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/ReverseLeftIcon.jsx b/src/assets/svgs/ReverseLeftIcon.jsx new file mode 100644 index 0000000..50a35eb --- /dev/null +++ b/src/assets/svgs/ReverseLeftIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const ReverseLeftIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/ReverseRightIcon.jsx b/src/assets/svgs/ReverseRightIcon.jsx new file mode 100644 index 0000000..5a74b70 --- /dev/null +++ b/src/assets/svgs/ReverseRightIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const ReverseRightIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/RotateIcon.svg b/src/assets/svgs/RotateIcon.svg new file mode 100644 index 0000000..eae13be --- /dev/null +++ b/src/assets/svgs/RotateIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svgs/TrashIcon.jsx b/src/assets/svgs/TrashIcon.jsx new file mode 100644 index 0000000..94283a5 --- /dev/null +++ b/src/assets/svgs/TrashIcon.jsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const TrashIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/UploadIcon.tsx b/src/assets/svgs/UploadIcon.tsx new file mode 100644 index 0000000..7f07813 --- /dev/null +++ b/src/assets/svgs/UploadIcon.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +export const UploadIcon = () => { + return ( + + + + ) +} diff --git a/src/assets/svgs/index.js b/src/assets/svgs/index.js new file mode 100644 index 0000000..889ae9f --- /dev/null +++ b/src/assets/svgs/index.js @@ -0,0 +1,19 @@ +export { UploadIcon } from './UploadIcon' +export { DownloadIcon } from './DownloadIcon' +export { Chevron } from './Chevron' +export { HandIcon } from './HandIcon' +export { PlusSquareIcon } from './PlusSquareIcon' +export { MinusSquareIcon } from './MinusSquareIcon' +export { ReverseLeftIcon } from './ReverseLeftIcon' +export { ReverseRightIcon } from './ReverseRightIcon' +export { CopyIcon } from './CopyIcon' +export { TrashIcon } from './TrashIcon' +export { PasteIcon } from './PasteIcon' +export { Loader } from './Loader' +export { CloseIcon } from './CloseIcon' +export { ArrowRrightIcon } from './ArrowRrightIcon' +export { ArrowLeftIcon } from './ArrowLeftIcon' +export { GreenTickIcon } from './GreenTickIcon' +export { PrinterIcon } from './PrinterIcon' +export { default as DeleteIcon } from './DeleteIcon.svg' +export { default as RotateIcon } from './RotateIcon.svg' \ No newline at end of file diff --git a/src/authContext.jsx b/src/authContext.jsx new file mode 100644 index 0000000..f6d6784 --- /dev/null +++ b/src/authContext.jsx @@ -0,0 +1,104 @@ +import React, { useReducer } from "react"; +import MkdSDK from "./utils/MkdSDK"; + +export const AuthContext = React.createContext(); + +const initialState = { + isAuthenticated: false, + user: null, + token: null, + role: null, +}; + +const reducer = (state, action) => { + switch (action.type) { + case "LOGIN": + localStorage.setItem("user", Number(action.payload.user_id)); + localStorage.setItem("token", action.payload.token); + localStorage.setItem("role", action.payload.role); + return { + ...state, + isAuthenticated: true, + user: Number(localStorage.getItem("user")), + token: localStorage.getItem("token"), + role: localStorage.getItem("role"), + }; + case "LOGOUT": + localStorage.removeItem("user"); + localStorage.removeItem("token"); + return { + ...state, + isAuthenticated: false, + user: null, + }; + default: + return state; + } +}; + +let sdk = new MkdSDK(); + +export const tokenExpireError = (dispatch, errorMessage) => { + /** + * either this or we pass the role as a parameter + */ + const role = localStorage.getItem("role"); + if (errorMessage === "TOKEN_EXPIRED") { + dispatch({ + type: "LOGOUT", + }); + + location.href = "/" + role + "/login"; + } +}; + +const AuthProvider = ({ children }) => { + const [state, dispatch] = useReducer(reducer, initialState); + + React.useEffect(() => { + const user = localStorage.getItem("user"); + const token = localStorage.getItem("token"); + const role = localStorage.getItem("role"); + + if (token) { + (async function () { + try { + const result = await sdk.check(role); + dispatch({ + type: "LOGIN", + payload: { + user_id: user, + token, + role: role, + }, + }); + } catch (error) { + if (role) { + dispatch({ + type: "LOGOUT", + }); + window.location.href = "/" + role + "/login"; + } else { + dispatch({ + type: "LOGOUT", + }); + window.location.href = "/"; + } + } + })(); + } + }, []); + + return ( + + {children} + + ); +}; + +export default AuthProvider; diff --git a/src/components/ActionButtons/ActionButtons.jsx b/src/components/ActionButtons/ActionButtons.jsx new file mode 100644 index 0000000..b414569 --- /dev/null +++ b/src/components/ActionButtons/ActionButtons.jsx @@ -0,0 +1,85 @@ + +import React, { useCallback, useState } from 'react' +// import { GrayMaterial, PerforatedMaterial, WoodgrainMaterial } from 'Assets/images' +// import { MaterialType } from 'Utils/constants' +import { CopyIcon, HandIcon, PasteIcon, PrinterIcon, ReverseLeftIcon, ReverseRightIcon, TrashIcon } from 'Assets/svgs' +import { CanvasModes } from 'Utils/constants' + + + +export const ActionButtons = ( { className, onRedoClick, onUndoClick, onCopy, onPaste, onDeleteSelection, onPrintScreen } ) => { + const [ mode, setMode ] = useState( CanvasModes.Still ) + // const [ activeMaterial, setActiveMaterial ] = useState( MaterialType.Gray ) + + // const changeMode = useCallback( () => { + // if ( mode === CanvasModes.Pan ) { + // setMode( CanvasModes.Still ) + // onCanvasModeChange( CanvasModes.Still ) + // } else if ( mode === CanvasModes.Still ) { + // setMode( CanvasModes.Pan ) + // onCanvasModeChange( CanvasModes.Pan ) + // } + // }, [ mode ] ) + + return ( +
+ + + + {/* */} + + + + + + + + + + + +
+ ) +} \ No newline at end of file diff --git a/src/components/ActionButtons/index.js b/src/components/ActionButtons/index.js new file mode 100644 index 0000000..d6114b8 --- /dev/null +++ b/src/components/ActionButtons/index.js @@ -0,0 +1 @@ +export { ActionButtons } from './ActionButtons' \ No newline at end of file diff --git a/src/components/AddButton.jsx b/src/components/AddButton.jsx new file mode 100644 index 0000000..7a5df95 --- /dev/null +++ b/src/components/AddButton.jsx @@ -0,0 +1,16 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; +const AddButton = ({ link }) => { + return ( + <> + + + {" "} + {" "} + + + + ); +}; + +export default AddButton; diff --git a/src/components/AdminHeader.jsx b/src/components/AdminHeader.jsx new file mode 100644 index 0000000..e3d75ed --- /dev/null +++ b/src/components/AdminHeader.jsx @@ -0,0 +1,205 @@ +import React from "react"; +import { AuthContext } from "../authContext"; +import { NavLink } from "react-router-dom"; +import { GlobalContext } from "../globalContext"; +export const AdminHeader = () => { + const { dispatch } = React.useContext(AuthContext); + const { state } = React.useContext(GlobalContext); + + return ( + <> +
+
+
+
+ Admin +
+
+
+
    +
  • + + Dashboard + +
  • + +
  • + + Accessories + +
  • + +
  • + + Email + +
  • + +
  • + + Photo + +
  • + +
  • + + Boat Lifts + +
  • + +
  • + + Dealers + +
  • + +
  • + + Docks + +
  • + +
  • + + Cms + +
  • + +
  • + + Instructions + +
  • + +
  • + + User + +
  • + +
  • + + Quotes + +
  • + +
  • + + Wedges + +
  • +
  • + + Ramps + +
  • + +
  • + + Quotes Mail Recipients + +
  • + +
  • + + Profile + +
  • +
  • + + dispatch({ + type: "LOGOUT", + }) + } + > + Logout + +
  • +
+
+
+
+ + ); +}; + +export default AdminHeader; diff --git a/src/components/BuildCanvasFromLocalModal/BuildCanvasFromLocalModal.jsx b/src/components/BuildCanvasFromLocalModal/BuildCanvasFromLocalModal.jsx new file mode 100644 index 0000000..a80ec40 --- /dev/null +++ b/src/components/BuildCanvasFromLocalModal/BuildCanvasFromLocalModal.jsx @@ -0,0 +1,82 @@ +import React, { memo, useState, useCallback } from 'react'; +import { Modal } from 'Components/Modal'; +import { InteractiveButton } from 'Components/InteractiveButton'; +// import { ArrowLeftIcon, ArrowRrightIcon, GreenTickIcon } from 'Assets/svgs'; +// import { dock_image, EstimateSteps, Tables, Truthy } from 'Utils/constants'; +// import { ContactInformation } from 'Components/ContactInformation'; +// import { LakeSurroundings } from 'Components/LakeSurroundings'; +// import { Comments } from 'Components/Comments'; +import MkdSDK from 'Utils/MkdSDK'; + +const sdk = new MkdSDK() +const classes = { + modal: 'string', + modalDialog: 'relative bg-white w-[500px]', + modalContent: 'string', + modalHeader: 'string', + modalTitle: 'string', + modalBody: 'string', + modalFooter: 'string', + closeButtonClass: 'string', + saveButtonClass: 'string', +} + +const BuildCanvasFromLocalModal = ( { + showBuildCanvasFromLocalModal, + modalCloseClick, + editor +} ) => { + // const [ step, setStep ] = useState( EstimateSteps.ContactInformation ) + // const [ submitLoading, setSubmitLoading ] = useState( false ) + // const [ errorMessage, setErrorMessage ] = useState( null ) + // const [ hasDealer, setHasDealer ] = useState( Truthy.False ) + + + const onConfirm = useCallback( () => { + const json = JSON.parse( localStorage.getItem( "dock" ) ) + editor.loadFromJSON( json, () => { + editor.renderAll() + }, ( o ) => { + // console.log( o ) + } ) + modalCloseClick() + }, [ editor ] ) + + return ( + +
+
+ Would You Like to Continue Building Dock From Last Save? +
+ +
+ + + +
+
+ + + +
+ ); +} + + +const BuildCanvasFromLocalModalMemo = memo( BuildCanvasFromLocalModal ); + +export { BuildCanvasFromLocalModalMemo as BuildCanvasFromLocalModal }; diff --git a/src/components/BuildCanvasFromLocalModal/index.js b/src/components/BuildCanvasFromLocalModal/index.js new file mode 100644 index 0000000..74b74de --- /dev/null +++ b/src/components/BuildCanvasFromLocalModal/index.js @@ -0,0 +1 @@ +export { BuildCanvasFromLocalModal } from './BuildCanvasFromLocalModal' \ No newline at end of file diff --git a/src/components/Builder/Builder.jsx b/src/components/Builder/Builder.jsx new file mode 100644 index 0000000..67d72e6 --- /dev/null +++ b/src/components/Builder/Builder.jsx @@ -0,0 +1,758 @@ +import React, { useCallback, useEffect, useState } from "react"; +// import { Tabs } from 'Components/Tabs' +// import { TabNames } from 'Utils' +import { fabric } from "fabric"; +import { + GrayMaterial, + PerforatedMaterial, + WoodgrainMaterial +} from "Assets/images"; +import { + DockPanelCategories, + MaterialType, + DockPanelCategoryMap, + CylinderType +} from "Utils/constants"; +import { Chevron } from "Assets/svgs"; +import MkdSDK from "Utils/MkdSDK"; +import { oneFeet, scaleFactor, Tables } from "Utils/constants"; +import { + getCategory, + getMaterial, + getRampsCategory, + getWedgesAndRampsMaterial, + getWedgesCategory +} from "Utils/utils"; + +const sdk = new MkdSDK(); +export const Builder = ({ editor }) => { + // let scaleFactor = 0.2; + const [activeMaterial, setActiveMaterial] = useState(MaterialType.Gray); + const [activeDockCategory, setActiveDockCategory] = useState( + DockPanelCategories.RollIn + ); + const rampsInitialState = { + data: [] + }; + // const [ramps, setRamps] = useState(null); + const [activeLiftRange, setActiveLiftRange] = useState(null); + const [dock, setDock] = useState([]); + const [docks, setDocks] = useState([]); + const [accessories, setAccessories] = useState([]); + const [boatlifts, setBoatlifts] = useState([]); + const [wedgesAndRamps, setWedgesAndRamps] = useState({ + wedges: [], + ramps: [], + selectedRamps: [], + selectedWedges: [] + }); + const [boatLift, setBoatLift] = useState([]); + const [liftRanges, setLiftRanges] = useState([]); + const [left, setLeft] = useState(300); + // const [ rollinDock, setRollinDock ] = useState( null ) + // const [ floatingDock, setFloatingDock ] = useState( null ) + // const [ sectionalDock, setSectionalDock ] = useState( null ) + + const onMaterialClick = useCallback( + (material) => { + setActiveMaterial(material); + }, + [activeMaterial] + ); + + const onDockSelect = useCallback((dock) => { + if (!editor) { + return; + } + const editorHeight = editor.getHeight(); + const division = editorHeight / oneFeet - 4; + + let imageTopViewURL; + let materials; + let category; + if (["wedges", "ramps"].includes(dock?.type)) { + imageTopViewURL = (dock?.top_view).replace("%20", "+"); + + materials = getWedgesAndRampsMaterial(dock?.material); + } else { + imageTopViewURL = dock?.top_view; + materials = getMaterial(dock?.materials); + } + + if (["ramps"].includes(dock?.type)) { + category = getRampsCategory(dock?.category); + } else if (["wedges"].includes(dock?.type)) { + category = getWedgesCategory(dock?.category); + } else { + category = getCategory(dock?.category); + } + + const dockData = { + itemName: activeDockCategory, + image: dock?.image, + category: category, + length: dock?.length, + materials: materials, + top_view: dock?.top_view, + width: dock?.width, + lift_range: dock?.lift_range, + model: dock?.model, + no_of_cylinders: dock?.no_of_cylinders, + name: dock?.name, + thumbnail: dock?.thumbnail, + weight_capacity: dock?.weight_capacity + }; + + // TODO: Add dock to editor + // TODO: object which is the image should have the dockData, snapAngle of 45, snapThreshold of 5 + // TODO: image should be scaled down by scaleFactor + // TODO: image should be positioned at the top left of the editor + // TODO: image should be added to the editor + // TODO: render the editor + }, []); + + const getItems = useCallback((table) => { + // console.log( category, materials ); + (async () => { + try { + // sdk.setTable( table ); + const result = await sdk.getItems(table); + // console.log( result ) + switch (table) { + case Tables.Docks: + return setDocks(() => + result?.model + ? [...result?.model.map((item) => ({ ...item, type: "docks" }))] + : [] + ); + + case Tables.Boat_lifts: + // return console.log( result ) + const liftRanges = result?.model + .filter((boatLift, index, self) => { + return ( + index === + self.findIndex( + (selfItem) => selfItem.lift_range === boatLift.lift_range + ) + ); + }) + .map((item) => item.lift_range); + setLiftRanges(liftRanges.sort()); + setActiveLiftRange(liftRanges.sort()[0]); + return setBoatlifts(() => + result?.model + ? [ + ...result?.model.map((item) => ({ + ...item, + type: "boatlifts" + })) + ] + : [] + ); + + case Tables.Accessories: + // return console.log( result ) + return setAccessories(() => + result?.model + ? [ + ...result?.model.map((item) => ({ + ...item, + type: "accessories" + })) + ] + : [] + ); + case Tables.Wedges: + // return console.log( result ) + return setWedgesAndRamps((prev) => ({ + ...prev, + wedges: result?.model + ? [ + ...result?.model.map((item) => ({ + ...item, + type: "wedges" + })) + ] + : [] + })); + case Tables.Ramps: + // return console.log( result ) + return setWedgesAndRamps((prev) => ({ + ...prev, + ramps: result?.model + ? [...result?.model.map((item) => ({ ...item, type: "ramps" }))] + : [] + })); + // return setRamps(() => [...result?.model]); + } + } catch (error) { + console.log(error.message); + } + })(); + }, []); + + const onDockPanelClick = useCallback( + (dockCategory) => { + if (dockCategory !== activeDockCategory) { + setActiveDockCategory(dockCategory); + } else { + setActiveDockCategory(""); + } + }, + [activeDockCategory] + ); + + useEffect(() => { + if (!docks.length) { + // console.log( "I ran Docks" ) + getItems(Tables.Docks); + } + if (!accessories.length) { + // console.log( "I ran Accessories" ) + getItems(Tables.Accessories); + } + if (!wedgesAndRamps?.wedges?.length) { + // console.log( "I ran Wedges" ) + getItems(Tables.Wedges); + } + if (!wedgesAndRamps?.ramps?.length) { + // console.log( "I ran Ramps" ) + getItems(Tables.Ramps); + } + if (!boatlifts.length) { + // console.log( "I ran Boat_lifts" ) + getItems(Tables.Boat_lifts); + } + }, [docks, accessories, boatlifts]); + + useEffect(() => { + if ( + [ + DockPanelCategories.RollIn, + DockPanelCategories.Floating, + DockPanelCategories.Sectional + ].includes(activeDockCategory) + ) { + // console.log( activeDockCategory, activeMaterial ) + const category = + activeDockCategory === DockPanelCategories.RollIn + ? DockPanelCategoryMap.RollIn + : activeDockCategory === DockPanelCategories.Floating + ? DockPanelCategoryMap.Floating + : activeDockCategory === DockPanelCategories.Sectional + ? DockPanelCategoryMap.Sectional + : null; + + const dockObj = docks + .map((dock) => { + if (dock.category === category && dock.materials === activeMaterial) { + return dock; + } + }) + .filter(Boolean); + // console.log( dockObj ) + setDock(() => [...dockObj]); + } + if (DockPanelCategories.Wedges === activeDockCategory) { + } + }, [activeMaterial, activeDockCategory, docks, wedgesAndRamps]); + + useEffect(() => { + if ( + [DockPanelCategories.BoatLift2, DockPanelCategories.BoatLift4].includes( + activeDockCategory + ) + ) { + // console.log( activeDockCategory, activeLiftRange ) + const cylinder = CylinderType[activeDockCategory]; + + const boatliftsObj = boatlifts + .map((boatlift) => { + if (boatlift.no_of_cylinders === cylinder) { + return boatlift; + } + }) + .filter(Boolean); + // console.log( boatliftsObj ) + setBoatLift(() => [...boatliftsObj]); + } + }, [activeLiftRange, boatlifts, activeDockCategory]); + // useEffect(() => { + // (async () => { + // await sdk.projectHealth(); + // })(); + // }, []); + + return ( +
+
+
onMaterialClick(MaterialType.Gray)} + className={`grow h-full px-[12px] py-[5px] flex justify-center items-center cursor-pointer + ${ + activeMaterial === MaterialType.Gray + ? " bg-white border-transparent" + : "bg-gray-200" + }`} + > + GreyMaterial +
+ +
onMaterialClick(MaterialType.Perforated)} + className={`grow h-full px-[12px] py-[5px] flex justify-center items-center cursor-pointer + ${ + activeMaterial === MaterialType.Perforated + ? " bg-white border-transparent" + : "bg-gray-200" + }`} + > + PerforatedMaterial +
+ +
onMaterialClick(MaterialType.Woodgrain)} + className={`grow h-full px-[12px] py-[5px] flex justify-center items-center cursor-pointer + ${ + activeMaterial === MaterialType.Woodgrain + ? " bg-white border-transparent" + : "bg-gray-200" + }`} + > + WoodgrainMaterial +
+
+
+
+
onDockPanelClick(DockPanelCategories.RollIn)} + className={`w-full h-[67px] z-30 flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.RollIn} +
+
+ {dock.length ? ( + <> + +
+ {dock?.map((dockItem, index) => ( + + ))} +
+ + ) : null} +
+ +
onDockPanelClick(DockPanelCategories.Floating)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.Floating} +
+
+ {dock.length ? ( + <> + +
+ {dock?.map((dockItem, index) => ( + + ))} +
+ + ) : null} +
+ +
onDockPanelClick(DockPanelCategories.Sectional)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.Sectional} +
+
+ {dock.length ? ( + <> + +
+ {dock?.map((dockItem, index) => ( + + ))} +
+ + ) : null} +
+
onDockPanelClick(DockPanelCategories.Wedges)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.Wedges} +
+
+ {wedgesAndRamps?.wedges.length ? ( + <> + +
+ {wedgesAndRamps?.wedges?.map((dockItem, index) => ( + + ))} +
+ + ) : null} +
+
onDockPanelClick(DockPanelCategories.Ramps)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.Ramps} +
+
+ {wedgesAndRamps.ramps.length ? ( + <> + +
+ {wedgesAndRamps.ramps?.map((dockItem, index) => ( + + ))} +
+ + ) : null} +
+
onDockPanelClick(DockPanelCategories.BoatLift2)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.BoatLift2} +
+
+ {boatLift.length ? ( + <> +
+
+ Lift Range +
+
+ {liftRanges.map((liftRange) => ( + + ))} +
+
+ +
+ {boatLift?.map((dockItem, index) => { + if (dockItem?.lift_range === activeLiftRange) { + return ( +
+ +
+ ); + } + })} +
+ + ) : null} +
+ +
onDockPanelClick(DockPanelCategories.BoatLift4)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.BoatLift4} +
+
+ {boatLift.length ? ( + <> +
+
+ Lift Range +
+
+ {liftRanges.map((liftRange) => ( + + ))} +
+
+ +
+ {boatLift?.map((dockItem, index) => { + if (dockItem?.lift_range === activeLiftRange) { + return ( +
+ +
+ ); + } + })} +
+ + ) : null} +
+ +
onDockPanelClick(DockPanelCategories.Accessories)} + className={`w-full h-[67px] z-30 border-b-2 border-b-[#B9C0D4] flex items-center pl-3 gap-x-1 font-bold text-[18px] leading-[150%] tracking-tight text-[#111322] cursor-pointer`} + > + + {DockPanelCategories.Accessories} +
+
+ {accessories.length ? ( + <> +
+ {accessories?.map((dockItem, index) => ( +
onDockSelect(dockItem)} + className={`h-fit rounded mx-1 mt-1 border border-[#B9C0D4] cursor-pointer`} + > + {/*
+ { dockItem.width }' + x + { dockItem.length }' */} + + {/*
*/} +
+ ))} +
+ + ) : null} +
+
+
+
+ ); +}; + +{ + /*
+ +
*/ +} diff --git a/src/components/Builder/index.js b/src/components/Builder/index.js new file mode 100644 index 0000000..e0effd9 --- /dev/null +++ b/src/components/Builder/index.js @@ -0,0 +1 @@ +export { Builder } from './Builder' \ No newline at end of file diff --git a/src/components/Comments/Comments.jsx b/src/components/Comments/Comments.jsx new file mode 100644 index 0000000..17f1b7d --- /dev/null +++ b/src/components/Comments/Comments.jsx @@ -0,0 +1,80 @@ +import React, { memo } from 'react'; +import { ArrowLeftIcon, ArrowRrightIcon, } from 'Assets/svgs'; +import { EstimateSteps, } from 'Utils/constants'; + +const Comments = ( { step, onStepChange, onUpdateComment, onSubmit, errorMessage, submitLoading, comments } ) => { + + {/* Comments */ } + + return ( + +
+ +
+ +
+

Comments

+ (3 of 3) +
+
+
+
+ +
+
+ {/* */} +
+ + +
+
+
+
+ +
+

{ errorMessage }

+
+ ); +} + + +const CommentsMemo = memo( Comments ); + +export { CommentsMemo as Comments }; diff --git a/src/components/Comments/index.js b/src/components/Comments/index.js new file mode 100644 index 0000000..25cf78d --- /dev/null +++ b/src/components/Comments/index.js @@ -0,0 +1 @@ +export { Comments } from './Comments' \ No newline at end of file diff --git a/src/components/ContactInformation/ContactInformation.jsx b/src/components/ContactInformation/ContactInformation.jsx new file mode 100644 index 0000000..cf9dcce --- /dev/null +++ b/src/components/ContactInformation/ContactInformation.jsx @@ -0,0 +1,324 @@ +import React, { memo, useState, useCallback, useEffect } from 'react'; +import { ArrowRrightIcon } from 'Assets/svgs'; +import { EstimateSteps, Tables, Truthy } from 'Utils/constants'; +import MkdSDK from 'Utils/MkdSDK'; + +const sdk = new MkdSDK() +const ContactInformation = ( { + step, + onStepChange, + hasDealer, + onHasDealerChange, + onDealerChange, + setFirstName, + setLastName, + setEmail, + setPhone, + setLake, + setCity, + setCountry, +} ) => { + + const [ TC, setTC ] = useState( false ) + const [ dealers, setDealers ] = useState( [] ) + + const getDealers = useCallback( ( table ) => { + ( async () => { + try { + const result = await sdk.getItems( table ) + setDealers( result?.model ) + } catch ( error ) { + console.log( error.message ) + } + } )() + }, [ dealers ] ) + + useEffect( () => { + if ( !dealers.length ) { + getDealers( Tables.Dealers ) + } + }, [ dealers ] ) + + {/* ContactInformation */ } + return ( +
+ +
+
+

Contact information

+ (1 of 3) +
+
+
+
+
+ +
+ {/*
+ +
*/} + setFirstName( e.target.value ) } + /> +
+
+
+ +
+
+ {/* */} +
+ setLastName( e.target.value ) } + /> +
+
+
+
+ +
+
+ {/* */} +
+ setEmail( e.target.value ) } + /> +
+
+
+ +
+
+ {/* */} +
+ setPhone( e.target.value ) } + /> +
+
+
+
+ +
+
+ {/* */} +
+ setLake( e.target.value ) } + /> +
+
+
+ +
+
+ {/* */} +
+ setCity( e.target.value ) } + /> +
+
+
+ +
+ {/*
+ +
*/} + +
+
+
+
+ +
+ onHasDealerChange( Truthy.True ) } + /> + + onHasDealerChange( Truthy.False ) } + /> + +
+
+ { hasDealer ? +
+ +
+ {/*
+ +
*/} + +
+
+ : null + } +
+
+ + setTC( e.target.checked ) } + /> + +
+
+
+
+ +
+
+ ); +} + + +const ContactInformationMemo = memo( ContactInformation ); + +export { ContactInformationMemo as ContactInformation }; diff --git a/src/components/ContactInformation/index.js b/src/components/ContactInformation/index.js new file mode 100644 index 0000000..9fb2f0f --- /dev/null +++ b/src/components/ContactInformation/index.js @@ -0,0 +1 @@ +export { ContactInformation } from './ContactInformation' \ No newline at end of file diff --git a/src/components/DockBuilder/DockBuilder.jsx b/src/components/DockBuilder/DockBuilder.jsx new file mode 100644 index 0000000..2392660 --- /dev/null +++ b/src/components/DockBuilder/DockBuilder.jsx @@ -0,0 +1,823 @@ +import React, { + useEffect, + useMemo, + useCallback, + useRef, + useState, + useContext +} from "react"; +import { FabricJSCanvas, useFabricJSEditor } from "fabricjs-react"; +import { fabric } from "fabric"; +import { DockSidebar } from "Components/DockSidebar"; +import { ActionButtons } from "Components/ActionButtons"; +import MkdSDK from "Utils/MkdSDK"; +import { Stack } from "Utils/utils"; +import { EstimateModal } from "Components/EstimateModal"; +import * as XLSX from "xlsx"; +import { BuildCanvasFromLocalModal } from "Components/BuildCanvasFromLocalModal"; +import { GlobalContext } from "../../globalContext"; +import { clearClone, clone } from "Utils/DockBuilderUtils/clone"; +import { + edgeDetection, + handleEdgeDetection, + handleIntersection +} from "Utils/DockBuilderUtils"; +import { + CanvasModes, + deleteIcon, + DockPanelCategories, + edgeSnapThreshold, + nintyDeg, + oneEightyDeg, + oneFeet, + rotateIcon, + scaleFactor, + twoSeventyDeg +} from "Utils/constants"; +import { + reScaleXY, + resolveHeight, + resolveWidth +} from "Utils/DockBuilderUtils/edgeDetection"; +import { DeleteIcon, RotateIcon } from "Assets/svgs"; +import { capitalize } from "Utils/helper"; + +const sdk = new MkdSDK(); +const stack = new Stack(); + +export const DockBuilder = () => { + let clipboard = null; + let mouseDownPoint = null; + let shiftKeyDown = false; + window.counter = 0; + let prevPointer = null; + let isDragging = false; + let isZoomed = false; + let startX, startY; + + const { dispatch } = useContext(GlobalContext); + const { editor, onReady } = useFabricJSEditor({ selection: true }); + + const canvasModeRef = useRef(null); + const fileRef = useRef(); + const imageRef = useRef(); + + const editorMemo = useMemo(() => editor?.canvas, [editor?.canvas]); + // const { dispatch } = React.useContext( AuthContext ); + const [objHovered, setObjHovered] = useState(false); + const [selectedItems, setSelectedItems] = useState([]); + const [estimateLoading, setEstimateLoading] = useState(false); + const [showEstimateModal, setShowEstimateModal] = useState(false); + const [showBuildCanvasFromLocalModal, setShowBuildCanvasFromLocalModal] = + useState(false); + const [initialLoad, setInitialLoad] = useState(true); + const [linesAdded, setLinesAdded] = useState(false); + const [dockImage, setDockImage] = useState(null); + const [editorReady, setEditorReady] = useState(false); + const [dockLabel, setDockLabel] = useState(null); + const [dockDimensions, setDockDimensions] = useState(null); + + const deleteImg = document.createElement("img"); + deleteImg.src = deleteIcon; + const rotateImg = document.createElement("img"); + rotateImg.src = rotateIcon; + + const onEstimateModalClose = useCallback(() => { + setShowEstimateModal(false); + }, [showEstimateModal]); + + const onEstimateModalOpen = useCallback(() => { + const ext = "png"; + const base64 = editorMemo.toDataURL({ + format: ext, + enableRetinaScaling: true + }); + setDockImage(base64); + setShowEstimateModal(true); + }, [showEstimateModal, editorMemo, dockImage]); + + // const onAddRect = useCallback( () => { + // editor?.canvas?.add( rect ); + // window.counter++; + // // newleft += 200; + // }, [ editor ] ); + + const toJSON = () => { + // TODO: download the json file + // TODO: get json of editor content + // TODO: Ensure dockData is included in the json + // TODO: save the json to the local storage as dock + // TODO: name the file as paradise_dock_.dock + }; + + const uploadFile = (e) => { + // TODO: Our own upload the file we must have downloaded previously + // TODO: extract the json from the file + // TODO: load the json to the editor + // TODO: render all + }; + + const downloadImage = useCallback(() => { + // // console.log( 'Download' ) + const ext = "png"; + + // TODO: download the image + // TODO: get the json of the editor content + // TODO: extract the dockData from the json + // TODO: filter the dockData to ensure it does not contain snapClone + // TODO: generate the base64 image + // TODO: download the image and name it as paradise_dock_snapshot_.${ext} + // TODO: download the excel file of the dockData you extracted + + // TODO: check if dockData is empty + // TODO: generate the base64 image + + // TODO: trigger download of the dockData you extracted in excel format + }, [editorMemo]); + + const renderBg = useCallback(() => { + const imageURL = + "https://s3.us-east-2.amazonaws.com/com.nds.images/download.png"; + + new fabric.Image.fromURL( + imageURL, + function (img) { + img.set({ + scaleX: editorMemo.width / img.width, + scaleY: editorMemo.height / img.height + }); + editorMemo.setBackgroundImage(img); + editorMemo.renderAll(); + // updateModifications( true, ) + }, + { + crossOrigin: "anonymous" + } + ); + }, [editorMemo]); + + const downloadExcel = useCallback((data) => { + const worksheet = XLSX.utils.json_to_sheet(data); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, "Selected Items"); + XLSX.writeFile(workbook, "Paradise_dock_selected_items.xlsx"); + }, []); + + function updateZoom(opt) { + let delta = opt.e.deltaY; + let pointer = editorMemo.getPointer(opt.e); + let zoom = editorMemo.getZoom(); + + if (delta > 0) { + zoom *= 1.1; + } else { + zoom /= 1.1; + } + if (zoom > 5) zoom = 5; + if (zoom < 1) zoom = 1; + // console.log( zoom ) + // editorMemo.setBackgroundImageStretch=false + var vpt = editorMemo.viewportTransform; + if (zoom === 1) { + vpt[4] = 0; + vpt[5] = 0; + editorMemo.selection = true; + this.isZoomed = false; + } + if (zoom > 1) { + this.isZoomed = true; + } + + fabric.util.animate({ + startValue: editorMemo.getZoom(), + endValue: zoom, + // duration: 0, + onChange: function (value) { + editorMemo.zoomToPoint({ x: pointer.x, y: pointer.y }, value); + }, + fps: 1080 + }); + opt.e.preventDefault(); + opt.e.stopPropagation(); + } + + function handleZoomMouseDown(options) { + var pointer = editorMemo.getPointer(options.e, true); + mouseDownPoint = new fabric.Point(pointer.x, pointer.y); + if (options.target === null && this.isZoomed) { + editorMemo.upperCanvasEl.style.cursor = "grabbing"; + editorMemo.selection = false; + // this.lastPosX = options.e.movementX; + // this.lastPosY = options.e.movementY; + this.lastPosX = options.e.clientX; + this.lastPosY = options.e.clientY; + + // + } + // else if ( options.target !== null && this.isZoomed ) { + // var activeObject = editorMemo.getActiveObject(); + // if ( activeObject ) { + // activeObject.lockMovementX = true; + // activeObject.lockMovementY = true; + // activeObject.lockScalingX = true; + // activeObject.lockScalingY = true; + // } + // } + } + + const handleZoomMouseUp = useCallback(() => { + editorMemo.defaultCursor = "default"; + mouseDownPoint = null; + editorMemo.selection = true; + }, [editor]); + + function handleZoomMouseMove(options) { + if (options.target === null && mouseDownPoint && this.isZoomed) { + editorMemo.selection = false; + editorMemo.upperCanvasEl.style.cursor = "grabbing"; + + var delta = new fabric.Point(options.e.movementX, options.e.movementY); + editorMemo.relativePan(delta); + editorMemo.viewportTransform[4] = Math.max( + Math.min(editorMemo.viewportTransform[4], 0), + editorMemo.getWidth() - editorMemo.getWidth() * editorMemo.getZoom() + ); + editorMemo.viewportTransform[5] = Math.max( + Math.min(editorMemo.viewportTransform[5], 0), + editorMemo.getHeight() - editorMemo.getHeight() * editorMemo.getZoom() + ); + + // var vpt = this.viewportTransform; + // vpt[ 4 ] += options.e.clientX - this.lastPosX; + // vpt[ 5 ] += options.e.clientY - this.lastPosY; + requestAnimationFrame(function () { + editorMemo.renderAll(); + this.requestRenderAll(); + }); + // this.lastPosX = options.e.movementX; + // this.lastPosY = options.e.movementY; + // this.lastPosX = options.e.clientX; + // this.lastPosY = options.e.clientY; + } + } + + const addEdgeDetectionClone = useCallback( + (options) => { + objectMoving = true; + if (options) { + const selectedObj = options.target.setCoords(); + editorMemo.defaultCursor = "grabbing"; + if ( + selectedObj.dockData && + selectedObj.dockData.itemName === DockPanelCategories.Accessories + ) { + return; + } + + editorMemo.forEachObject((obj) => { + const pointer = { x: options.e.clientX, y: options.e.clientY }; + + if (obj === selectedObj) return; + // canvas.setCursor('grabbing'); + // // console.log(pointer.x, pointer.y); + if (!obj.snapClone && obj.type === "image") { + // // console.log( pointer, prevPointer ) + if ( + prevPointer && + (prevPointer.x !== pointer.x || prevPointer.y !== pointer.y) + ) { + switch (true) { + case edgeDetection(selectedObj, obj, "left"): + clone(selectedObj, editorMemo, obj, "left"); + break; + case edgeDetection(selectedObj, obj, "right"): + clone(selectedObj, editorMemo, obj, "right"); + break; + case edgeDetection(selectedObj, obj, "top"): + clone(selectedObj, editorMemo, obj, "top"); + break; + case edgeDetection(selectedObj, obj, "bottom"): + clone(selectedObj, editorMemo, obj, "bottom"); + break; + case edgeDetection(selectedObj, obj, "neutral"): + clearClone(editorMemo); + break; + default: + // console.log( "edge detection switch default" ) + } + // prevPointer = null; + } + prevPointer = pointer; + // // console.log( pointer, prevPointer ) + } + // if ( edgeDetection( selectedObj, obj, "left" ) ) { + // clone( selectedObj, editorMemo, obj, "left" ) + // } else { + // // clearClone( editorMemo ) + // } + + // if ( edgeDetection( selectedObj, obj, 'right' ) ) { + // clone( selectedObj, editorMemo, obj, "right" ) + + // } else { + // // clearClone( editorMemo ) + // } + + // if ( edgeDetection( selectedObj, obj, 'top' ) ) { + // clone( selectedObj, editorMemo, obj, "top" ) + + // } else { + // // clearClone( editorMemo ) + // } + + // if ( edgeDetection( selectedObj, obj, 'bottom' ) ) { + // clone( selectedObj, editorMemo, obj, "bottom" ) + // } else { + // // clearClone( editorMemo ) + // } + }); + } + }, + [editor, editorMemo] + ); + + const horizontalIndicatorLines = (newTop, objTop) => { + editorMemo.getObjects("line").forEach((obj) => { + if (obj.testLine) { + editorMemo.remove(obj); + } + }); + + let line = new fabric.Line([0, newTop, editorMemo.getWidth(), newTop], { + stroke: "#AAAAAA", + testLine: true + // strokeDashArray: [ 5 ], + }); + let line2 = new fabric.Line([0, objTop, editorMemo.getWidth(), objTop], { + stroke: "#AAAAAA", + testLine: true + // strokeDashArray: [ 5 ], + }); + editorMemo.add(line); + editorMemo.add(line2); + editorMemo.renderAll(); + }; + const verticleIndicatorLines = (newLeft, objLeft) => { + editorMemo.getObjects("line").forEach((obj) => { + if (obj.testLine) { + editorMemo.remove(obj); + } + }); + + let line = new fabric.Line([newLeft, 0, newLeft, editorMemo.getHeight()], { + stroke: "#AAAAAA", + testLine: true + // strokeDashArray: [ 5 ], + }); + let line2 = new fabric.Line([objLeft, 0, objLeft, editorMemo.getWidth()], { + stroke: "#AAAAAA", + testLine: true + // strokeDashArray: [ 5 ], + }); + editorMemo.add(line); + editorMemo.add(line2); + editorMemo.renderAll(); + }; + + function edgeDetectionAndSnap(options) { + if (this.isZoomed) { + return; + } + + // TODO: Edge detection and snap to object within snap range + // TODO: Detect if the object is within the snap range of the selected object + // TODO: detect if the selected object contains dockData and dockData contains itemName of DockPanelCategories.Accessories || DockPanelCategories.BoatLift2 || DockPanelCategories.BoatLift4 + // TODO: handle rotation of 0, 90, 180, 270 degrees + + editorMemo.defaultCursor = "default"; + prevPointer = null; + clearClone(editorMemo); + } + + const showHighlight = useCallback( + (event) => { + if (event.target) { + if (event.target.type === "image") { + setObjHovered(true); + // console.log(event.target.dockData) + if ( + event.target && + event.target.dockData && + event.target.dockData.image + ) { + imageRef.current.src = event.target.dockData.image; + setDockLabel( + `${ + event.target.dockData.materials + ? capitalize(event.target.dockData.itemName) + : event.target.dockData.itemName + } (${ + event.target.dockData.materials + ? event.target.dockData.materials + : event.target.dockData.model + })` + ); + setDockDimensions( + `${event.target.dockData.width}' x ${event.target.dockData.length}'` + ); + } else if ( + event.target && + event.target.dockData && + event.target.dockData.thumbnail + ) { + setDockLabel( + `${capitalize(event.target.dockData.itemName)} (${ + event.target.dockData.name + })` + ); + setDockDimensions( + `${event.target.dockData.width}' x ${event.target.dockData.length}'` + ); + imageRef.current.src = event.target.dockData.thumbnail; + } + } + } + if (!event.target && objHovered) { + setObjHovered(false); + setDockLabel(null); + setDockDimensions(null); + setDockDimensions(null); + imageRef.current.src = ""; + } + }, + [objHovered] + ); + + const hideHighlight = useCallback(() => { + if (objHovered) { + setObjHovered(false); + setDockLabel(null); + setDockDimensions(null); + imageRef.current.src = ""; + } + }, [objHovered]); + + const updateModifications = useCallback( + (savehistory, event) => { + if (savehistory === true) { + if ((event && event === "update") || event.target.dockData) { + const json = editor?.canvas?.toJSON(["dockData", "snapClone"]); + const data = JSON.stringify(json); + stack.updateStack(data); + + updateLocalstore(); + } + } + }, + [selectedItems, editor] + ); + + const updateLocalstore = useCallback(() => { + const json = editor?.canvas?.toJSON(["dockData", "snapClone"]); + const newObjects = json.objects + .filter((object) => !object.snapClone) + .filter(Boolean); + json.objects = newObjects; + const items = json.objects + .map((object) => { + if (object.dockData && !object.snapClone) { + return object.dockData; + } + }) + .filter(Boolean); + setSelectedItems(() => [...items]); + + const data = JSON.stringify(json); + localStorage.setItem("dock", data); + }, [editor, selectedItems]); + + const onUndoClick = useCallback(() => { + // TODO: Undo + }, [editorMemo]); + + const onRedoClick = useCallback(() => { + // TODO: Redo + }, [editorMemo]); + + const onPrintScreen = useCallback(() => { + // convert the canvas to a data url and download it. + // TODO: Print screen + // TODO: print screen without background image and background color, the background image should be white + // TODO: after printing, the background image and background color should be restored + }, [editorMemo]); + + const CopySelection = () => { + // TODO: Copy selection + }; + + const PasteSelection = () => { + // TODO: Paste selection + }; + + const onDeleteSelection = () => { + // TODO: Delete selection + }; + + const addLines = useCallback(() => { + // Initiate a line instance + const editorHeight = editorMemo.getHeight(); + const division = editorHeight / oneFeet; + + if (!linesAdded) { + let initialFt = 8; + let multiplier = 1; + for (let i = division - 3; i > 0; i--) { + if (multiplier > division - 4) { + break; + } + let line = new fabric.Line( + [0, oneFeet * i, editorMemo.getWidth(), oneFeet * i], + { + stroke: "#AAAAAA", + strokeDashArray: [5] + } + ); + + let text = new fabric.Text(`${initialFt * multiplier}ft`, { + // stroke: '#000000', + fill: "#AAAAAA", + fontSize: 18, + left: editorMemo.getWidth() - 40, + top: oneFeet * i + }); + + multiplier++; + // Render the rectangle in canvas + editorMemo.add(line).sendToBack(line); + editorMemo.add(text).sendToBack(text); + editorMemo.renderAll(); + } + setLinesAdded(true); + } + }, [editorMemo, linesAdded]); + + const removeLines = useCallback(() => { + editorMemo.getObjects("line").forEach((line) => { + editorMemo.remove(line); + }); + editorMemo.getObjects("text").forEach((text) => { + editorMemo.remove(text); + }); + setLinesAdded(false); + }, [editorMemo, linesAdded]); + + const onCloseBuildFromLocalModal = useCallback(() => { + setShowBuildCanvasFromLocalModal(false); + + dispatch({ + type: "DOCK_LOADING", + payload: false + }); + setInitialLoad(false); + }, [editorMemo, showBuildCanvasFromLocalModal, dispatch, initialLoad]); + function renderIcon(icon) { + return function renderIcon(ctx, left, top, styleOverride, fabricObject) { + var size = this.cornerSize; + ctx.save(); + ctx.translate(left, top); + ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle)); + ctx.drawImage(icon, -size / 2, -size / 2, size, size); + ctx.restore(); + }; + } + function deleteObject(eventData, transform) { + var target = transform.target; + var canvas = target.canvas; + canvas.remove(target); + canvas.requestRenderAll(); + } + + useEffect(() => { + if (editor) { + editorMemo.backgroundColor = "#011e23"; + editorMemo.enableRetinaScaling = false; + + // Use hardware acceleration + // editorMemo.renderOnAddRemove = false; + editorMemo.skipOffscreen = true; + // Lower render quality setting + fabric.devicePixelRatio = 1; + fabric.Group.prototype.hasControls = true; + fabric.Group.prototype.snapAngle = 45; + fabric.Group.prototype.snapThreshold = 5; + fabric.Group.prototype.stroke = "#0f75bc"; + fabric.Object.prototype.snapAngle = 45; + // Optimize object rendering + fabric.Object.prototype.objectCaching = true; + + fabric.Object.prototype.snapThreshold = 5; + fabric.Object.prototype.setControlsVisibility({ + tl: false, //top-left + mt: false, // middle-top + tr: true, //top-right + ml: false, //middle-left + mr: false, //middle-right + bl: false, // bottom-left + mb: false, //middle-bottom + br: false, //bottom-right + mtr: false // rotate icon + }); + // fabric.Object.prototype.controls.deleteControl = new fabric.Control( { + // x: -0.8, + // y: -1, + // offsetY: 16, + // offsetX: 16, + // cursorStyle: 'pointer', + // mouseUpHandler: deleteObject, + // render: renderIcon( deleteImg ), + // cornerPadding: 0, + // cornerSize: 24 + // } ); + fabric.Object.prototype.controls.tr = new fabric.Control({ + x: 0.5, + y: -0.5, + cursorStyle: "rotate", + offsetY: -16, + offsetX: 16, + cornerPadding: 0, + actionHandler: fabric.controlsUtils.rotationWithSnapping, + cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler, + withConnection: true, + render: renderIcon(rotateImg), + cornerSize: 20, + actionName: "rotate", + sizeX: 40, + sizeY: 40, + touchSizeX: 40, + touchSizeY: 40 + }); + fabric.Object.prototype.hasControls = true; + fabric.Object.prototype.lockScalingX = true; + fabric.Object.prototype.lockScalingY = true; + fabric.Object.prototype.cornerSize = 5; + fabric.Line.prototype.hasBorders = false; + fabric.Line.prototype.cornerSize = 0; + fabric.Line.prototype.borderColor = "transparent"; + fabric.Line.prototype.hasRotatingPoint = false; + fabric.Line.prototype.hasControls = false; + fabric.Line.prototype.lockRotation = true; + fabric.Line.prototype.hoverCursor = "default"; + fabric.Line.prototype.selectable = false; + fabric.Line.prototype.lockMovementX = true; + fabric.Line.prototype.lockMovementY = true; + fabric.Text.prototype.hasBorders = false; + fabric.Text.prototype.cornerSize = 0; + fabric.Text.prototype.borderColor = "transparent"; + fabric.Text.prototype.hasRotatingPoint = false; + fabric.Text.prototype.hoverCursor = "default"; + fabric.Text.prototype.selectable = false; + fabric.Text.prototype.hasControls = false; + fabric.Text.prototype.lockRotation = true; + fabric.Text.prototype.lockMovementX = true; + fabric.Text.prototype.lockMovementY = true; + + // fabric.Text.prototype.viewportCenter = true + setEditorReady(true); + editorMemo.setHeight(window.innerHeight); + editorMemo.setWidth(window.innerWidth); + fabric.Object.prototype.cornerColor = "#B9C0D4"; + + renderBg(); + addLines(); + + // Intersection + // TODO: Edge detection and snap to object within snap range + editorMemo.on("object:moving", edgeDetectionAndSnap); + + editorMemo.on("mouse:over", showHighlight); + editorMemo.on("mouse:out", hideHighlight); + editorMemo.on("object:modified", (e) => updateModifications(true, e)); + editorMemo.on("object:added", (e) => updateModifications(true, e)); + + // Listen for the delete key event + document.onkeydown = (event) => { + if (event.keyCode === 46) { + // 46 is the keyCode for the delete key + onDeleteSelection(); + } + }; + + const savedDock = localStorage.getItem("dock") + ? JSON.parse(localStorage.getItem("dock")) + : null; + if (savedDock) { + if (initialLoad) { + setShowBuildCanvasFromLocalModal(true); + } + } else if (initialLoad) { + setInitialLoad(false); + dispatch({ + type: "DOCK_LOADING", + payload: false + }); + } + } + }, [editor]); + + useEffect(() => { + if (editor) { + const handleResize = () => { + // let canvas = canvasRef.current; + editorMemo.setWidth(window.innerWidth); + editorMemo.setHeight(window.innerHeight); + editorMemo.setBackgroundImage(editorMemo.backgroundImage, (bgImage) => { + bgImage.set({ + scaleX: editorMemo.width / bgImage.width, + scaleY: editorMemo.height / bgImage.height + }); + }); + removeLines(); + addLines(); + editorMemo.renderAll(); + }; + window.addEventListener("resize", handleResize); + handleResize(); + return () => { + window.removeEventListener("resize", handleResize); + }; + } + }, []); + + return ( +
+ <> + + + + + + +
+ {dockLabel ? ( +
+ {dockLabel} +
+ ) : null} + + + + {dockDimensions ? ( +
+ {dockDimensions} +
+ ) : null} +
+ + + + +
+ ); +}; diff --git a/src/components/DockBuilder/index.js b/src/components/DockBuilder/index.js new file mode 100644 index 0000000..879ff23 --- /dev/null +++ b/src/components/DockBuilder/index.js @@ -0,0 +1 @@ +export { DockBuilder } from './DockBuilder' \ No newline at end of file diff --git a/src/components/DockSidebar/DockSidebar.jsx b/src/components/DockSidebar/DockSidebar.jsx new file mode 100644 index 0000000..e447940 --- /dev/null +++ b/src/components/DockSidebar/DockSidebar.jsx @@ -0,0 +1,73 @@ +import React, { useCallback, useContext } from 'react' +import { SidebarLogo } from 'Components/SidebarLogo' +import { GlobalContext } from '../../globalContext' +import { SidebarFoot } from 'Components/SidebarFoot' +import { SidebarBuilder } from 'Components/SidebarBuilder' + +export const DockSidebar = ( { onAddItem, onDownloadImage, onDownloadFile, onUploadFile, fileRef, editor, updateModifications, selectedItems, onEstimateModalOpen } ) => { + const { state, dispatch } = useContext( GlobalContext ) + const { dockSideBarOpen } = state + let toggleOpen = ( open ) => + dispatch( { + type: "TOGGLE_DOCK_SIDEBAR", + payload: { dockSideBarOpen: open }, + } ); + + const onWheel = useCallback( ( e ) => { + if ( e.ctrlKey ) { + return e.preventDefault();//prevent zoom + } + + }, [] ); + + return ( +
+
+ + + + +
+ toggleOpen( !dockSideBarOpen ) }> + { !dockSideBarOpen ? ( + + ) : ( + + ) } + +
+ ) +} diff --git a/src/components/DockSidebar/index.js b/src/components/DockSidebar/index.js new file mode 100644 index 0000000..e46adcf --- /dev/null +++ b/src/components/DockSidebar/index.js @@ -0,0 +1 @@ +export { DockSidebar } from './DockSidebar' \ No newline at end of file diff --git a/src/components/DynamicContentType.jsx b/src/components/DynamicContentType.jsx new file mode 100644 index 0000000..e8cb43f --- /dev/null +++ b/src/components/DynamicContentType.jsx @@ -0,0 +1,464 @@ +import React from "react"; +import MkdSDK from "../utils/MkdSDK"; +import {empty} from "../utils/utils"; + +const sdk = new MkdSDK(); +const defaultImage = 'https://via.placeholder.com/150?text=%20'; +const DynamicContentType = ({contentType, contentValue, setContentValue}) => { + const [previewUrl, setPreviewUrl] = React.useState(defaultImage); + const handleImageChange = async (e) => { + const formData = new FormData(); + formData.append('file', e.target.files[0]); + try { + const result = await sdk.uploadImage(formData); + setPreviewUrl(result.url); + setContentValue(result.url); + }catch(err) { + console.error(err); + } + + } + switch (contentType) { + case "text": + return ( + <> + + + ) + + case "image": + return ( + <> + preview + + + ) + + case "number": + return ( + setContentValue(e.target.value)} + defaultValue={contentValue} + /> + ) + + case "team-list": + return ( + + ) + + case "image-list": + return ( + + ) + + case "captioned-image-list": + return ( + + ) + + case "kvp": + return ( + + ) + + default: + break; + } +} + +export default DynamicContentType; + +const ImageList = ({contentValue, setContentValue}) => { + let itemsObj = [ + {key: '', value_type: 'image', value: null} + ]; + if (!empty(contentValue)) { + itemsObj = JSON.parse(contentValue); + } + const [items, setItems] = React.useState(itemsObj); + + + const handleImageChange = async (e) => { + const listKey = e.target.getAttribute('listkey'); + const formData = new FormData(); + formData.append('file', e.target.files[0]); + try { + const result = await sdk.uploadImage(formData); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.value = result.url; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + }catch(err) { + console.error(err); + } + } + + const handleKeyChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.key = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + + } + + return ( +
+ {items.map( (item, index) =>
+ preview +
+ + +
+ +
) + } + +
+ + ) +} + +const CaptionedImageList = ({setContentValue, contentValue}) => { + let itemsObj = [ + {key: '', value_type: 'image', value: null, caption: ''} + ] + + if (!empty(contentValue)) { + itemsObj = JSON.parse(contentValue); + } + const [items, setItems] = React.useState(itemsObj); + + const handleImageChange = async (e) => { + const listKey = e.target.getAttribute('listkey'); + const formData = new FormData(); + formData.append('file', e.target.files[0]); + try { + const result = await sdk.uploadImage(formData); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.value = result.url; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + }catch(err) { + console.error(err); + } + } + + const handleKeyChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.key = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + const handleCaptionChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.caption = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + return ( +
+ {items.map( (item, index) =>
+ preview +
+ + +
+ + +
) + } + +
+ + ) +} +const TeamList = ({setContentValue, contentValue}) => { + let itemsObj = [ + {name: '', image: null, title: ''} + ] + + if (!empty(contentValue)) { + itemsObj = JSON.parse(contentValue); + } + const [items, setItems] = React.useState(itemsObj); + + const handleImageChange = async (e) => { + const listKey = e.target.getAttribute('listkey'); + const formData = new FormData(); + formData.append('file', e.target.files[0]); + try { + const result = await sdk.uploadImage(formData); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.image = result.url; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + }catch(err) { + console.error(err); + } + } + + const handleNameChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.name = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + const handleTitleChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.title = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + return ( +
+ {items.map( (item, index) =>
+ + {/*
*/} + + + preview + + {/*
*/} + + +
) + } + +
+ + ) +} +const KeyValuePair = ({setContentValue, contentValue}) => { + let itemsObj = [ + {key: '', value_type: 'text', value: ''} + ] + + if (!empty(contentValue)) { + itemsObj = JSON.parse(contentValue); + } + + const [items, setItems] = React.useState(itemsObj); + const valueTypeMap = [ + { + key: "text", + value: "Text" + }, + { + key: "number", + value: "Number" + }, + { + key: "json", + value: "JSON Object" + }, + { + key: "url", + value: "URL" + } + ] + + + const handleKeyChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.key = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + const handleValueChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.value = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + const handleValueTypeChange = (e) => { + const listKey = e.target.getAttribute('listkey'); + setItems(oldItems => { + let updatedItems = oldItems.map((item, index) => { + if (index == listKey) { + item.value_type = e.target.value; + return item; + } + return item; + }); + return updatedItems; + }) + setContentValue(JSON.stringify(items)) + } + + return ( +
+ {items.map( (item, index) =>
+ + + + + +
) + } + +
+ + ) +} + \ No newline at end of file diff --git a/src/components/EstimateModal/EstimateModal.jsx b/src/components/EstimateModal/EstimateModal.jsx new file mode 100644 index 0000000..d5a8958 --- /dev/null +++ b/src/components/EstimateModal/EstimateModal.jsx @@ -0,0 +1,168 @@ +import React, { memo, useState, useCallback } from 'react'; +import { Modal } from 'Components/Modal'; +import { InteractiveButton } from 'Components/InteractiveButton'; +import { ArrowLeftIcon, ArrowRrightIcon, GreenTickIcon } from 'Assets/svgs'; +import { dock_image, EstimateSteps, Tables, Truthy } from 'Utils/constants'; +import { ContactInformation } from 'Components/ContactInformation'; +import { LakeSurroundings } from 'Components/LakeSurroundings'; +import { Comments } from 'Components/Comments'; +import MkdSDK from 'Utils/MkdSDK'; + +const sdk = new MkdSDK() +const classes = { + modal: 'string', + modalDialog: 'relative bg-white w-[500px]', + modalContent: 'string', + modalHeader: 'string', + modalTitle: 'string', + modalBody: 'string', + modalFooter: 'string', + closeButtonClass: 'string', + saveButtonClass: 'string', +} + +const EstimateModal = ( { + showEstimateModal, + modalCloseClick, + selectedItems, + dockImage, +} ) => { + const [ step, setStep ] = useState( EstimateSteps.ContactInformation ) + const [ submitLoading, setSubmitLoading ] = useState( false ) + const [ errorMessage, setErrorMessage ] = useState( null ) + const [ hasDealer, setHasDealer ] = useState( Truthy.False ) + + // Contact Information + const [ dealer, setDealer ] = useState( 0 ) + const [ firstName, setFirstName ] = useState( '' ) + const [ lastName, setLastName ] = useState( '' ) + const [ email, setEmail ] = useState( '' ) + const [ phone, setPhone ] = useState( '' ) + const [ lake, setLake ] = useState( '' ) + const [ city, setCity ] = useState( '' ) + const [ country, setCountry ] = useState( '' ) + + // Lake Surroundings + const [ dockConnection, setDockConnection ] = useState( '' ) + const [ lakeBottom, setLakeBottom ] = useState( '' ) + const [ willDockBoat, setWillDockBoat ] = useState( Truthy.False ) + + // Comments + const [ comments, setComments ] = useState( "" ) + + const onStepChange = useCallback( ( next ) => { + setStep( next ) + }, [ step ] ) + + const onUpdateComment = useCallback( ( comment ) => { + setComments( comment ) + }, [ comments ] ) + + const onSubmit = useCallback( () => { + ( async () => { + try { + setSubmitLoading( true ) + const quoteBody = { + first_name: firstName, + last_name: lastName, + email: email, + phone: phone, + lake: lake, + city: city, + dock_connection: dockConnection, + lake_bottom: lakeBottom, + country: country, + dock_image: dockImage, + has_dealer: hasDealer, + dealer_id: dealer, + will_dock_boat: willDockBoat, + comments: comments, + selected_items: JSON.stringify( selectedItems ) + } + const result = await sdk.requestQuote( quoteBody ) + if ( !result?.error ) { + setSubmitLoading( false ) + setStep( EstimateSteps.InquirySubmitted ) + } + } catch ( error ) { + setErrorMessage( error.message ) + setSubmitLoading( false ) + } + } )() + }, [ step, submitLoading, errorMessage, comments, hasDealer, dealer, firstName, lastName, email, phone, lake, city, country, dockConnection, lakeBottom, willDockBoat, selectedItems, dockImage ] ) + + const onClose = useCallback( () => { + setStep( EstimateSteps.ContactInformation ) + modalCloseClick() + }, [ step ] ) + + return ( + + {/* ContactInformation */ } + + + {/* LakeSurroundings */ } + + + {/* Comments */ } + + + {/* InquirySubmitted */ } +
+ +
+
+ +

Inquiry Submitted

+
+
+
+ +
+
+
+ ); +} + + +const EstimateModalMemo = memo( EstimateModal ); + +export { EstimateModalMemo as EstimateModal }; diff --git a/src/components/EstimateModal/index.js b/src/components/EstimateModal/index.js new file mode 100644 index 0000000..9d43703 --- /dev/null +++ b/src/components/EstimateModal/index.js @@ -0,0 +1 @@ +export { EstimateModal } from './EstimateModal' \ No newline at end of file diff --git a/src/components/FabricObjects/Rect/Rect.jsx b/src/components/FabricObjects/Rect/Rect.jsx new file mode 100644 index 0000000..dd96bfe --- /dev/null +++ b/src/components/FabricObjects/Rect/Rect.jsx @@ -0,0 +1,63 @@ +import React from "react"; +import { fabric } from "fabric"; + +function Rect({ left, top, width, height, fill }) { + const rectRef = React.useRef(null); + + React.useEffect(() => { + const rect = new fabric.Rect({ + left, + top, + width, + height, + fill, + }); + + rectRef.current = rect; + }, [left, top, width, height, fill]); + + return <>; +} + +export default Rect; + +// const addTestRect = () => { +// if (editor) { +// const rect = new fabric.Rect({ +// left: 200, +// top: 50, +// width: 500, +// height: 50, +// fill: "red", +// }); + +// editorMemo.add(rect); +// const rect2 = new fabric.Rect({ +// left: 250, +// top: 100, +// width: 200, +// height: 50, +// fill: "red", +// }); + +// editorMemo.add(rect2); +// const rect3 = new fabric.Rect({ +// left: 300, +// top: 150, +// width: 200, +// height: 50, +// fill: "red", +// }); + +// editorMemo.add(rect3); +// const rect4 = new fabric.Rect({ +// left: 350, +// top: 200, +// width: 200, +// height: 50, +// fill: "red", +// }); + +// editorMemo.add(rect4); +// } +// } diff --git a/src/components/InteractiveButton/InteractiveButton.jsx b/src/components/InteractiveButton/InteractiveButton.jsx new file mode 100644 index 0000000..9bea1eb --- /dev/null +++ b/src/components/InteractiveButton/InteractiveButton.jsx @@ -0,0 +1,48 @@ +import React from "react"; + +export const InteractiveButton = ({ + loading, + children, + type = "button", + className, + backgroundColor, + onClick, +}) => { + return ( + + ); +}; diff --git a/src/components/InteractiveButton/index.js b/src/components/InteractiveButton/index.js new file mode 100644 index 0000000..a0a4dd1 --- /dev/null +++ b/src/components/InteractiveButton/index.js @@ -0,0 +1 @@ +export { InteractiveButton } from './InteractiveButton' \ No newline at end of file diff --git a/src/components/LakeSurroundings/LakeSurroundings.jsx b/src/components/LakeSurroundings/LakeSurroundings.jsx new file mode 100644 index 0000000..082b1dc --- /dev/null +++ b/src/components/LakeSurroundings/LakeSurroundings.jsx @@ -0,0 +1,141 @@ +import React, { memo } from 'react'; +import { ArrowLeftIcon, ArrowRrightIcon } from 'Assets/svgs'; +import { EstimateSteps, Truthy } from 'Utils/constants'; + +const LakeSurroundings = ( { step, onStepChange, setWillDockBoat, setDockConnection, setLakeBottom } ) => { + + const dockConnectionMap = [ 'Resting', 'Bolted', 'Other' ] + const lakeBottomMap = [ 'Rocky', 'Silty', 'Sandy' ] + // const dock_connection = { 0: 'Resting', 1: 'Bolted', 2: 'Other' } + // const lake_bottom = { 0: 'Rocky', 1: 'Silty', 2: 'Sandy' } + // const has_dealer = { 0: 'No', 1: 'Yes' } + // const will_dock_boat = { 0: 'No', 1: 'Yes' } + {/* LakeSurroundings */ } + + return ( + +
+ +
+ +
+

Lake Surroundings

+ (2 of 3) +
+
+
+
+ +
+
+ {/* */} +
+ +
+
+
+ +
+
+ {/* */} +
+ +
+
+
+ +
+ setWillDockBoat( Truthy.True ) } + /> + + setWillDockBoat( Truthy.False ) } + /> + +
+
+ + +
+
+ +
+
+ ); +} + + +const LakeSurroundingsMemo = memo( LakeSurroundings ); + +export { LakeSurroundingsMemo as LakeSurroundings }; diff --git a/src/components/LakeSurroundings/index.js b/src/components/LakeSurroundings/index.js new file mode 100644 index 0000000..53b7ed3 --- /dev/null +++ b/src/components/LakeSurroundings/index.js @@ -0,0 +1 @@ +export { LakeSurroundings } from './LakeSurroundings' \ No newline at end of file diff --git a/src/components/Loader.jsx b/src/components/Loader.jsx new file mode 100644 index 0000000..6270200 --- /dev/null +++ b/src/components/Loader.jsx @@ -0,0 +1,22 @@ + +import Indicator from "./LoadingIndicator"; + +const Loader = ({ style }) => { + return ( +
+ +
+ ); +}; + +export default Loader; + diff --git a/src/components/LoadingIndicator.jsx b/src/components/LoadingIndicator.jsx new file mode 100644 index 0000000..8990358 --- /dev/null +++ b/src/components/LoadingIndicator.jsx @@ -0,0 +1,58 @@ + + +import React from "react"; + +import { motion } from "framer-motion"; + +const containerVariant = { + start: { + transition: { staggerChildren: 0.2 } + }, + end: { + transition: { staggerChildren: 0.2 } + } +}; + +const dotsVariants = { + start: { + y: "0%" + }, + end: { + y: "100%" + } +}; + +const loadingTransition = { + duration: 0.4, + yoyo: Infinity, + ease: "easeIn" +}; + +export default function Indicator({ dotsClasses, size, style }) { + const dotsStyles = "block w-[9px] h-[9px] bg-slate-900 rounded-md shrink-0 " + dotsClasses; + return ( + + + + + + ); +} diff --git a/src/components/Modal/Modal.jsx b/src/components/Modal/Modal.jsx new file mode 100644 index 0000000..e1a1527 --- /dev/null +++ b/src/components/Modal/Modal.jsx @@ -0,0 +1,38 @@ +import React, { useEffect, useRef, useState, memo } from 'react'; +import { CloseIcon } from 'Assets/svgs'; + +const Modal = ( { + children, + title, + isOpen = false, + modalCloseClick, + modalHeader, + classes +} ) => { + + + return ( +
+
+ { modalHeader && +
+
{ title }
+
+ +
+
+ } + +
+ { children } +
+
+
+ ); +}; + + +const ModalMemo = memo( Modal ); +export { ModalMemo as Modal }; diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js new file mode 100644 index 0000000..3d80065 --- /dev/null +++ b/src/components/Modal/index.js @@ -0,0 +1 @@ +export { Modal } from './Modal' \ No newline at end of file diff --git a/src/components/PaginationBar.jsx b/src/components/PaginationBar.jsx new file mode 100644 index 0000000..bd3d0a3 --- /dev/null +++ b/src/components/PaginationBar.jsx @@ -0,0 +1,41 @@ +import React from "react"; +const PaginationBar = ({ currentPage, pageCount, pageSize, canPreviousPage, canNextPage, updatePageSize, previousPage, nextPage }) => { + return ( + <> +
+
+ + Page{" "} + + {+currentPage} of {pageCount} + {" "} + + +
+ {/* */} +
+ {" "} + {" "} +
+
+ + ); +}; + +export default PaginationBar; diff --git a/src/components/PublicHeader.jsx b/src/components/PublicHeader.jsx new file mode 100644 index 0000000..47f83e2 --- /dev/null +++ b/src/components/PublicHeader.jsx @@ -0,0 +1,15 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router"; +import { AuthContext } from "../authContext"; + +export const PublicHeader = () => { + const { state, dispatch } = React.useContext(AuthContext); + const [isOpen, setIsOpen] = useState(false); + const navigate = useNavigate(); + + return
+ +
; +}; + +export default PublicHeader; diff --git a/src/components/SelectedItems/Field.jsx b/src/components/SelectedItems/Field.jsx new file mode 100644 index 0000000..3c39caa --- /dev/null +++ b/src/components/SelectedItems/Field.jsx @@ -0,0 +1,18 @@ +import React from 'react' +import { capitalize } from 'Utils/helper' + +export const Field = ( { item, property } ) => { + + return ( + <> + { item[ property ] ? ( +
+
{ capitalize( property ) }:
+
{ item[ property ] }{ " " }{ [ "length", "width" ].includes( property ) ? 'ft' : '' }
+
) + + : null + } + + ) +} diff --git a/src/components/SelectedItems/SelectedItem.jsx b/src/components/SelectedItems/SelectedItem.jsx new file mode 100644 index 0000000..059ec0a --- /dev/null +++ b/src/components/SelectedItems/SelectedItem.jsx @@ -0,0 +1,48 @@ +import { Chevron } from 'Assets/svgs' +import React from 'react' +import { Field } from './Field' + +export const SelectedItem = ( { selectedItem } ) => { + const itemRef = React.useRef( null ) + + const [ open, setOpen ] = React.useState( false ) + + const onOpen = React.useCallback( ( isTrue ) => { + if ( isTrue ) { + itemRef.current.style.maxHeight = null + } else { + itemRef.current.style.maxHeight = `${ itemRef.current.scrollHeight }px` + } + setOpen( !isTrue ) + }, [ open, itemRef ] ) + + return ( +
+ + + +
+ + + + + + + + + +
+ +
+ ) +} diff --git a/src/components/SelectedItems/SelectedItems.jsx b/src/components/SelectedItems/SelectedItems.jsx new file mode 100644 index 0000000..23adfde --- /dev/null +++ b/src/components/SelectedItems/SelectedItems.jsx @@ -0,0 +1,15 @@ +import React from 'react' +import { SelectedItem } from './SelectedItem' + +export const SelectedItems = ( { selectedItems } ) => { + + return ( +
+ { selectedItems?.length ? selectedItems?.map( ( item, index ) => ( + + ) ) + + : null } +
+ ) +} diff --git a/src/components/SelectedItems/index.js b/src/components/SelectedItems/index.js new file mode 100644 index 0000000..d4761d6 --- /dev/null +++ b/src/components/SelectedItems/index.js @@ -0,0 +1,3 @@ +export { SelectedItems } from './SelectedItems' +export { SelectedItem } from './SelectedItem' +export { Field } from './Field' \ No newline at end of file diff --git a/src/components/SidebarBuilder/SidebarBuilder.jsx b/src/components/SidebarBuilder/SidebarBuilder.jsx new file mode 100644 index 0000000..77c7e70 --- /dev/null +++ b/src/components/SidebarBuilder/SidebarBuilder.jsx @@ -0,0 +1,51 @@ +import React, { useCallback, useState } from "react"; +import { Tabs } from "Components/Tabs"; +import { Builder } from "Components/Builder"; +import { SelectedItems } from "Components/SelectedItems"; +import { TabNames } from "Utils/constants"; + +export const SidebarBuilder = ({ + editor, + updateModifications, + selectedItems, +}) => { + const [activeTab, setActiveTab] = useState(TabNames.Builder); + // const [ramps, setRamps] = useState([]); + + const onTabClick = useCallback( + (tab) => { + setActiveTab(tab); + }, + [activeTab] + ); + + return ( +
+ + + {activeTab === TabNames.Builder ? ( + + ) : null} + {activeTab === TabNames.SelectedItems ? ( + + ) : null} +
+ ); +}; + +{ + /*
+ +
*/ +} diff --git a/src/components/SidebarBuilder/index.js b/src/components/SidebarBuilder/index.js new file mode 100644 index 0000000..830750c --- /dev/null +++ b/src/components/SidebarBuilder/index.js @@ -0,0 +1 @@ +export { SidebarBuilder } from './SidebarBuilder' \ No newline at end of file diff --git a/src/components/SidebarFoot/SidebarFoot.jsx b/src/components/SidebarFoot/SidebarFoot.jsx new file mode 100644 index 0000000..8a15148 --- /dev/null +++ b/src/components/SidebarFoot/SidebarFoot.jsx @@ -0,0 +1,38 @@ +import React from 'react' +import { DownloadIcon, UploadIcon } from 'Assets/svgs' + +export const SidebarFoot = ( { onDownloadImage, onDownloadFile, fileRef, onUploadFile, onEstimateModalOpen } ) => { + + return ( +
+ + + + + + + +
+ ) +} \ No newline at end of file diff --git a/src/components/SidebarFoot/index.js b/src/components/SidebarFoot/index.js new file mode 100644 index 0000000..c40b167 --- /dev/null +++ b/src/components/SidebarFoot/index.js @@ -0,0 +1 @@ +export { SidebarFoot } from './SidebarFoot' \ No newline at end of file diff --git a/src/components/SidebarLogo/SidebarLogo.jsx b/src/components/SidebarLogo/SidebarLogo.jsx new file mode 100644 index 0000000..7aeb9e1 --- /dev/null +++ b/src/components/SidebarLogo/SidebarLogo.jsx @@ -0,0 +1,15 @@ +import { BrandLogo } from 'Assets/images' +import React from 'react' + +export const SidebarLogo = () => +{ + return ( +
+ Brand Logo +
+
Paradise Dock & Lift Inc.
+
Dock builder tool
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/SidebarLogo/index.js b/src/components/SidebarLogo/index.js new file mode 100644 index 0000000..5cd5197 --- /dev/null +++ b/src/components/SidebarLogo/index.js @@ -0,0 +1 @@ +export { SidebarLogo } from './SidebarLogo' \ No newline at end of file diff --git a/src/components/SnackBar.jsx b/src/components/SnackBar.jsx new file mode 100644 index 0000000..26aa222 --- /dev/null +++ b/src/components/SnackBar.jsx @@ -0,0 +1,32 @@ +import React from "react"; +import { GlobalContext } from "../globalContext"; +const SnackBar = () => { + const { state, dispatch } = React.useContext(GlobalContext); + const show = state.globalMessage.length > 0; + return show ? ( + + ) : null; +}; + +export default SnackBar; diff --git a/src/components/Tab/Tab.jsx b/src/components/Tab/Tab.jsx new file mode 100644 index 0000000..7ab518f --- /dev/null +++ b/src/components/Tab/Tab.jsx @@ -0,0 +1,10 @@ + +import React from 'react' + +export const Tab = ( { name, className, active, onTabClick } ) => { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/Tab/index.js b/src/components/Tab/index.js new file mode 100644 index 0000000..ddacd77 --- /dev/null +++ b/src/components/Tab/index.js @@ -0,0 +1 @@ +export { Tab } from './Tab' \ No newline at end of file diff --git a/src/components/Table/Table.jsx b/src/components/Table/Table.jsx new file mode 100644 index 0000000..20d25be --- /dev/null +++ b/src/components/Table/Table.jsx @@ -0,0 +1,95 @@ +import React from 'react' + +export const Table = ( { className, tableColumns, tableData } ) => { + return ( +
+ + + + { tableColumns.map( ( column, i ) => ( + + ) ) } + + + + { tableData.map( ( row, i ) => { + return ( + + { tableColumns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ {/* */} + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { [ "image", "top_view", "thumbnail" ].includes( cell.accessor ) ? : row[ cell.accessor ] } +
+
+ + ) +} \ No newline at end of file diff --git a/src/components/Table/index.js b/src/components/Table/index.js new file mode 100644 index 0000000..0eb36c2 --- /dev/null +++ b/src/components/Table/index.js @@ -0,0 +1 @@ +export { Table } from './Table' \ No newline at end of file diff --git a/src/components/Tabs/Tabs.jsx b/src/components/Tabs/Tabs.jsx new file mode 100644 index 0000000..28b0cc0 --- /dev/null +++ b/src/components/Tabs/Tabs.jsx @@ -0,0 +1,25 @@ +import React from 'react' +import { Tab } from 'Components/Tab' +import { TabNames } from 'Utils/constants' + + + +export const Tabs = ( { activeTab, className, onTabClick } ) => { + return ( +
+ + + +
+ ) +} \ No newline at end of file diff --git a/src/components/Tabs/index.js b/src/components/Tabs/index.js new file mode 100644 index 0000000..d82601c --- /dev/null +++ b/src/components/Tabs/index.js @@ -0,0 +1 @@ +export { Tabs } from './Tabs' \ No newline at end of file diff --git a/src/components/TopHeader.jsx b/src/components/TopHeader.jsx new file mode 100644 index 0000000..3f6b264 --- /dev/null +++ b/src/components/TopHeader.jsx @@ -0,0 +1,52 @@ +import React from "react"; +import { GlobalContext } from "../globalContext"; +const TopHeader = () => { + const { state, dispatch } = React.useContext(GlobalContext); + let { isOpen } = state; + let toggleOpen = (open) => + dispatch({ + type: "OPEN_SIDEBAR", + payload: { isOpen: open }, + }); + return ( +
+ toggleOpen(!isOpen)}> + {!isOpen ? ( + + ) : ( + + )} + +
+ ); +}; + +export default TopHeader; diff --git a/src/components/ViewDockImageModal/ViewDockImageModal.jsx b/src/components/ViewDockImageModal/ViewDockImageModal.jsx new file mode 100644 index 0000000..77c80de --- /dev/null +++ b/src/components/ViewDockImageModal/ViewDockImageModal.jsx @@ -0,0 +1,77 @@ +import React, { memo, useState, useCallback } from 'react'; +import { Modal } from 'Components/Modal'; +import { InteractiveButton } from 'Components/InteractiveButton'; +// import { ArrowLeftIcon, ArrowRrightIcon, GreenTickIcon } from 'Assets/svgs'; +// import { dock_image, EstimateSteps, Tables, Truthy } from 'Utils/constants'; +// import { ContactInformation } from 'Components/ContactInformation'; +// import { LakeSurroundings } from 'Components/LakeSurroundings'; +// import { Comments } from 'Components/Comments'; +import MkdSDK from 'Utils/MkdSDK'; + +const sdk = new MkdSDK() +const classes = { + modal: 'string', + modalDialog: 'relative bg-white w-[800px]', + modalContent: 'string', + modalHeader: 'string', + modalTitle: 'string', + modalBody: 'string', + modalFooter: 'string', + closeButtonClass: 'string', + saveButtonClass: 'string', +} + +const ViewDockImageModal = ( { + showViewDockImageModal, + modalCloseClick, + dockImage +} ) => { + // const [ step, setStep ] = useState( EstimateSteps.ContactInformation ) + // const [ submitLoading, setSubmitLoading ] = useState( false ) + // const [ errorMessage, setErrorMessage ] = useState( null ) + // const [ hasDealer, setHasDealer ] = useState( Truthy.False ) + + + const onDownloadClick = useCallback( () => { + const Anchor = document.createElement( "a" ) + // console.log( "viewDockImageModal", dockImage ) + Anchor.href = dockImage; + Anchor.download = `dock_image_snapshot.png`; + Anchor.click(); + // modalCloseClick() + }, [ dockImage ] ) + + return ( + +
+
+ dock_image +
+ +
+ + Download + +
+
+ + + +
+ ); +} + + +const ViewDockImageModalMemo = memo( ViewDockImageModal ); + +export { ViewDockImageModalMemo as ViewDockImageModal }; diff --git a/src/components/ViewDockImageModal/index.js b/src/components/ViewDockImageModal/index.js new file mode 100644 index 0000000..b169e63 --- /dev/null +++ b/src/components/ViewDockImageModal/index.js @@ -0,0 +1 @@ +export { ViewDockImageModal } from './ViewDockImageModal' \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..74ee0cc --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,6 @@ +export { default as AddButton } from './AddButton' +export { default as AdminHeader } from './AdminHeader' +export { default as PaginationBar } from './PaginationBar' +export { default as PublicHeader } from './PublicHeader' +export { default as SnackBar } from './SnackBar' +export { default as TopHeader } from './TopHeader' \ No newline at end of file diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..37d3570 --- /dev/null +++ b/src/data.js @@ -0,0 +1,1963 @@ +export const dealers = { + error: false, + model: [ + { + id: 11, + create_at: "2022-11-01", + update_at: "2022-11-01T20:08:25.000Z", + name: "Rumman", + address: "street 1" + }, + { + id: 10, + create_at: "2022-11-01", + update_at: "2022-11-01T19:41:36.000Z", + name: "Rumman Mohsin", + address: "street 3" + } + ], + mapping: null +}; +export const wedges = { + error: false, + model: [ + { + id: 12, + create_at: "2023-04-21", + update_at: "2024-02-29T23:02:47.000Z", + category: 1, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20SS.png", + material: 2 + }, + { + id: 11, + create_at: "2023-04-21", + update_at: "2024-02-29T23:02:28.000Z", + category: 1, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20Grey.png", + material: 1 + }, + { + id: 10, + create_at: "2023-04-21", + update_at: "2024-02-29T23:01:59.000Z", + category: 1, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20Wood.png", + material: 0 + }, + { + id: 9, + create_at: "2023-04-21", + update_at: "2024-02-29T23:01:42.000Z", + category: 0, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Floating-Sectional%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20SS.png", + material: 2 + }, + { + id: 8, + create_at: "2023-04-21", + update_at: "2024-02-29T23:01:10.000Z", + category: 0, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Floating-Sectional%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20Grey.png", + material: 1 + }, + { + id: 7, + create_at: "2023-04-21", + update_at: "2024-02-29T23:00:40.000Z", + category: 0, + length: 4, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wedge%20Floating-Sectional%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x4%20Wedge%20Wood.png", + material: 0 + } + ], + mapping: { + category: { + 0: "Floating Wedge", + 1: "Roll-In Wedge" + }, + material: { + 0: "Wood Grain", + 1: "Grey Aluminium", + 2: "Perforated" + } + } +}; + +export const docks = { + error: false, + model: [ + { + id: 92, + create_at: "2022-11-08", + update_at: "2024-03-05T20:06:21.000Z", + category: 2, + materials: 1, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x16%20RI%20SS.png" + }, + { + id: 91, + create_at: "2022-11-08", + update_at: "2024-03-05T20:05:56.000Z", + category: 2, + materials: 1, + length: 12, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x12%20RI%20SS.png" + }, + { + id: 90, + create_at: "2022-11-08", + update_at: "2024-03-05T20:05:37.000Z", + category: 2, + materials: 1, + length: 8, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x8%20RI%20SS.png" + }, + { + id: 89, + create_at: "2022-11-08", + update_at: "2024-03-05T20:05:16.000Z", + category: 2, + materials: 1, + length: 12, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x12%20RI%20SS.png" + }, + { + id: 88, + create_at: "2022-11-08", + update_at: "2024-03-05T20:04:52.000Z", + category: 2, + materials: 1, + length: 16, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20RI%20SS.png" + }, + { + id: 87, + create_at: "2022-11-08", + update_at: "2024-03-05T20:04:14.000Z", + category: 2, + materials: 1, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20RI%20SS.png" + }, + { + id: 86, + create_at: "2022-11-08", + update_at: "2024-03-05T20:03:23.000Z", + category: 2, + materials: 1, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20RI%20SS.png" + }, + { + id: 85, + create_at: "2022-11-08", + update_at: "2024-03-05T19:57:24.000Z", + category: 2, + materials: 2, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x16%20RI%20Grey.png" + }, + { + id: 84, + create_at: "2022-11-08", + update_at: "2024-03-05T19:57:05.000Z", + category: 2, + materials: 2, + length: 12, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x12%20RI%20Grey.png" + }, + { + id: 83, + create_at: "2022-11-08", + update_at: "2024-03-05T19:56:42.000Z", + category: 2, + materials: 2, + length: 8, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x8%20RI%20Grey.png" + }, + { + id: 82, + create_at: "2022-11-08", + update_at: "2024-03-05T19:56:04.000Z", + category: 2, + materials: 2, + length: 12, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x12%20RI%20Grey.png" + }, + { + id: 81, + create_at: "2022-11-08", + update_at: "2024-03-05T19:55:00.000Z", + category: 2, + materials: 2, + length: 16, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20RI%20Grey.png" + }, + { + id: 80, + create_at: "2022-11-08", + update_at: "2024-03-05T19:54:42.000Z", + category: 2, + materials: 2, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20RI%20Grey.png" + }, + { + id: 79, + create_at: "2022-11-08", + update_at: "2024-03-05T19:54:25.000Z", + category: 2, + materials: 2, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20RI%20Grey.png" + }, + { + id: 78, + create_at: "2022-11-08", + update_at: "2024-03-05T19:50:50.000Z", + category: 2, + materials: 0, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x16%20RI%20Wood.png" + }, + { + id: 77, + create_at: "2022-11-08", + update_at: "2024-03-05T19:50:34.000Z", + category: 2, + materials: 0, + length: 12, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x12%20RI%20Wood.png" + }, + { + id: 76, + create_at: "2022-11-08", + update_at: "2024-03-05T19:50:16.000Z", + category: 2, + materials: 0, + length: 8, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x8%20RI%20Wood.png" + }, + { + id: 75, + create_at: "2022-11-08", + update_at: "2024-03-05T19:49:59.000Z", + category: 2, + materials: 0, + length: 12, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x12%20RI%20Wood.png" + }, + { + id: 74, + create_at: "2022-11-08", + update_at: "2024-03-05T19:49:41.000Z", + category: 2, + materials: 0, + length: 16, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20RI%20Wood.png" + }, + { + id: 73, + create_at: "2022-11-08", + update_at: "2024-03-05T19:49:26.000Z", + category: 2, + materials: 0, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20RI%20Wood.png" + }, + { + id: 72, + create_at: "2022-11-08", + update_at: "2024-03-05T19:48:00.000Z", + category: 2, + materials: 0, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Roll-in%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20RI%20Wood.png" + }, + { + id: 71, + create_at: "2022-11-04", + update_at: "2024-03-05T19:46:49.000Z", + category: 1, + materials: 1, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20S%20SS.png" + }, + { + id: 70, + create_at: "2022-11-04", + update_at: "2024-03-05T19:46:22.000Z", + category: 1, + materials: 1, + length: 10, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x10%20S%20SS.png" + }, + { + id: 69, + create_at: "2022-11-04", + update_at: "2024-03-05T19:45:55.000Z", + category: 1, + materials: 1, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20RI%20SS.png" + }, + { + id: 68, + create_at: "2022-11-04", + update_at: "2024-03-05T19:45:34.000Z", + category: 1, + materials: 2, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20S%20Grey.png" + }, + { + id: 67, + create_at: "2022-11-04", + update_at: "2024-03-05T19:45:11.000Z", + category: 1, + materials: 2, + length: 10, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x10%20S%20Grey.png" + }, + { + id: 66, + create_at: "2022-11-04", + update_at: "2024-03-05T19:44:49.000Z", + category: 1, + materials: 2, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20S%20Grey.png" + }, + { + id: 65, + create_at: "2022-11-04", + update_at: "2024-03-05T19:44:28.000Z", + category: 1, + materials: 0, + length: 12, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20S%20Wood.png" + }, + { + id: 64, + create_at: "2022-11-04", + update_at: "2024-03-05T19:43:23.000Z", + category: 1, + materials: 0, + length: 10, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x10%20S%20Wood.png" + }, + { + id: 63, + create_at: "2022-11-04", + update_at: "2024-03-05T19:43:00.000Z", + category: 1, + materials: 0, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Sectional%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20S%20Wood.png" + }, + { + id: 62, + create_at: "2022-11-04", + update_at: "2024-03-05T19:42:20.000Z", + category: 0, + materials: 1, + length: 24, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x24%20F%20SS.png" + }, + { + id: 61, + create_at: "2022-11-04", + update_at: "2024-03-05T19:42:01.000Z", + category: 0, + materials: 1, + length: 20, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x20%20F%20SS.png" + }, + { + id: 60, + create_at: "2022-11-04", + update_at: "2024-03-05T19:41:39.000Z", + category: 0, + materials: 1, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x16%20F%20SS.png" + }, + { + id: 59, + create_at: "2022-11-04", + update_at: "2024-03-05T19:40:39.000Z", + category: 0, + materials: 1, + length: 8, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x8%20F%20SS.png" + }, + { + id: 58, + create_at: "2022-11-04", + update_at: "2024-03-05T19:40:15.000Z", + category: 0, + materials: 1, + length: 24, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x24%20F%20Grey.png" + }, + { + id: 57, + create_at: "2022-11-04", + update_at: "2024-03-05T19:39:23.000Z", + category: 0, + materials: 1, + length: 20, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x20%20F%20SS.png" + }, + { + id: 56, + create_at: "2022-11-04", + update_at: "2024-03-05T19:39:00.000Z", + category: 0, + materials: 1, + length: 16, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x16%20F%20SS.png" + }, + { + id: 55, + create_at: "2022-11-04", + update_at: "2024-03-05T19:38:32.000Z", + category: 0, + materials: 1, + length: 24, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x24%20F%20SS.png" + }, + { + id: 54, + create_at: "2022-11-04", + update_at: "2024-03-05T19:38:09.000Z", + category: 0, + materials: 1, + length: 20, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x20%20F%20SS.png" + }, + { + id: 53, + create_at: "2022-11-04", + update_at: "2024-03-05T19:37:42.000Z", + category: 0, + materials: 1, + length: 16, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x16%20F%20SS.png" + }, + { + id: 52, + create_at: "2022-11-04", + update_at: "2024-03-05T19:36:17.000Z", + category: 0, + materials: 1, + length: 24, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x24%20F%20SS.png" + }, + { + id: 51, + create_at: "2022-11-04", + update_at: "2024-03-05T19:36:00.000Z", + category: 0, + materials: 1, + length: 20, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x20%20F%20SS.png" + }, + { + id: 50, + create_at: "2022-11-04", + update_at: "2024-03-05T19:35:42.000Z", + category: 0, + materials: 1, + length: 16, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20F%20SS.png" + }, + { + id: 49, + create_at: "2022-11-04", + update_at: "2024-03-05T19:35:20.000Z", + category: 0, + materials: 1, + length: 24, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x24%20F%20SS.png" + }, + { + id: 48, + create_at: "2022-11-04", + update_at: "2024-03-05T19:34:58.000Z", + category: 0, + materials: 1, + length: 20, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x20%20F%20SS.png" + }, + { + id: 47, + create_at: "2022-11-04", + update_at: "2024-03-05T19:34:38.000Z", + category: 0, + materials: 1, + length: 16, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Perforated.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x16%20F%20SS.png" + }, + { + id: 46, + create_at: "2022-11-04", + update_at: "2024-03-05T19:34:15.000Z", + category: 0, + materials: 2, + length: 24, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x24%20F%20Grey.png" + }, + { + id: 45, + create_at: "2022-11-04", + update_at: "2024-03-05T19:33:54.000Z", + category: 0, + materials: 2, + length: 20, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x20%20F%20Grey.png" + }, + { + id: 44, + create_at: "2022-11-04", + update_at: "2024-03-05T19:33:30.000Z", + category: 0, + materials: 2, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x20%20F%20Grey.png" + }, + { + id: 43, + create_at: "2022-11-04", + update_at: "2024-03-05T19:32:32.000Z", + category: 0, + materials: 2, + length: 8, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x8%20F%20Grey.png" + }, + { + id: 42, + create_at: "2022-11-04", + update_at: "2024-03-05T19:31:50.000Z", + category: 0, + materials: 2, + length: 24, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x24%20F%20Grey.png" + }, + { + id: 41, + create_at: "2022-11-04", + update_at: "2024-03-05T19:31:35.000Z", + category: 0, + materials: 2, + length: 20, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x20%20F%20Grey.png" + }, + { + id: 40, + create_at: "2022-11-04", + update_at: "2024-03-05T19:31:19.000Z", + category: 0, + materials: 2, + length: 16, + width: 6, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426x16%20F%20Grey.png" + }, + { + id: 39, + create_at: "2022-11-04", + update_at: "2024-03-05T19:31:01.000Z", + category: 0, + materials: 2, + length: 24, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x24%20F%20Grey.png" + }, + { + id: 38, + create_at: "2022-11-04", + update_at: "2024-03-05T19:30:43.000Z", + category: 0, + materials: 2, + length: 20, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x20%20F%20Grey.png" + }, + { + id: 37, + create_at: "2022-11-04", + update_at: "2024-03-05T19:30:14.000Z", + category: 0, + materials: 2, + length: 16, + width: 5, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887425x16%20F%20Grey.png" + }, + { + id: 36, + create_at: "2022-11-04", + update_at: "2024-03-05T19:29:24.000Z", + category: 0, + materials: 2, + length: 24, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x20%20F%20Grey.png" + }, + { + id: 35, + create_at: "2022-11-04", + update_at: "2024-03-05T19:25:26.000Z", + category: 0, + materials: 2, + length: 20, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x20%20F%20Grey.png" + }, + { + id: 34, + create_at: "2022-11-04", + update_at: "2024-03-05T19:25:08.000Z", + category: 0, + materials: 2, + length: 16, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20F%20Grey.png" + }, + { + id: 33, + create_at: "2022-11-04", + update_at: "2024-03-05T19:24:27.000Z", + category: 0, + materials: 2, + length: 8, + width: 4, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x8%20F%20Grey.png" + }, + { + id: 32, + create_at: "2022-11-04", + update_at: "2024-03-05T19:23:38.000Z", + category: 0, + materials: 2, + length: 24, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x24%20F%20Grey.png" + }, + { + id: 31, + create_at: "2022-11-04", + update_at: "2024-03-05T19:23:12.000Z", + category: 0, + materials: 2, + length: 20, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x20%20F%20Grey.png" + }, + { + id: 30, + create_at: "2022-11-04", + update_at: "2024-03-05T19:22:46.000Z", + category: 0, + materials: 2, + length: 16, + width: 3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423x16%20F%20Grey.png" + }, + { + id: 29, + create_at: "2022-11-04", + update_at: "2024-03-05T19:21:31.000Z", + category: 0, + materials: 0, + length: 24, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x24%20F%20Wood.png" + }, + { + id: 28, + create_at: "2022-11-04", + update_at: "2024-03-04T21:44:39.000Z", + category: 0, + materials: 0, + length: 20, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x20%20F%20Wood.png" + }, + { + id: 27, + create_at: "2022-11-04", + update_at: "2024-03-04T21:43:40.000Z", + category: 0, + materials: 0, + length: 16, + width: 8, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428x16%20F%20Wood.png" + }, + { + id: 26, + create_at: "2022-11-04", + update_at: "2022-11-04T16:28:13.000Z", + category: 0, + materials: 0, + length: 8, + width: 8, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/8x8%20F%20Wood.png" + }, + { + id: 25, + create_at: "2022-11-04", + update_at: "2022-11-04T16:27:58.000Z", + category: 0, + materials: 0, + length: 24, + width: 6, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/6x24%20F%20Wood.png" + }, + { + id: 24, + create_at: "2022-11-04", + update_at: "2022-11-04T16:27:44.000Z", + category: 0, + materials: 0, + length: 20, + width: 6, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/6x20%20F%20Wood.png" + }, + { + id: 23, + create_at: "2022-11-04", + update_at: "2022-11-04T16:27:25.000Z", + category: 0, + materials: 0, + length: 16, + width: 6, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/6x16%20F%20Wood.png" + }, + { + id: 22, + create_at: "2022-11-04", + update_at: "2022-11-04T16:27:03.000Z", + category: 0, + materials: 0, + length: 24, + width: 5, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/5x24%20F%20Wood.png" + }, + { + id: 21, + create_at: "2022-11-04", + update_at: "2022-11-04T16:26:41.000Z", + category: 0, + materials: 0, + length: 20, + width: 5, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/5x20%20F%20Wood.png" + }, + { + id: 20, + create_at: "2022-11-04", + update_at: "2022-11-04T16:26:27.000Z", + category: 0, + materials: 0, + length: 16, + width: 5, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/5x16%20F%20Wood.png" + }, + { + id: 19, + create_at: "2022-11-04", + update_at: "2022-11-04T16:26:04.000Z", + category: 0, + materials: 0, + length: 24, + width: 4, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/4x24%20F%20Wood.png" + }, + { + id: 18, + create_at: "2022-11-04", + update_at: "2022-11-04T16:25:43.000Z", + category: 0, + materials: 0, + length: 20, + width: 4, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/4x20%20F%20Wood.png" + }, + { + id: 17, + create_at: "2022-11-04", + update_at: "2022-11-04T16:25:23.000Z", + category: 0, + materials: 0, + length: 16, + width: 4, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/4x16%20F%20Wood.png" + }, + { + id: 16, + create_at: "2022-11-04", + update_at: "2022-11-04T16:24:55.000Z", + category: 0, + materials: 0, + length: 8, + width: 4, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/4x8%20F%20Wood.png" + }, + { + id: 15, + create_at: "2022-11-04", + update_at: "2022-11-04T16:24:34.000Z", + category: 0, + materials: 0, + length: 24, + width: 3, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/3x24%20F%20Wood.png" + }, + { + id: 14, + create_at: "2022-11-04", + update_at: "2022-11-04T16:23:40.000Z", + category: 0, + materials: 0, + length: 20, + width: 3, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/3x20%20F%20Wood.png" + }, + { + id: 11, + create_at: "2022-11-04", + update_at: "2022-11-04T16:22:47.000Z", + category: 0, + materials: 0, + length: 16, + width: 3, + image: + "https://s3.us-east-2.amazonaws.com/com.nds.images/Floating%20Woodgrain-min.png", + top_view: + "https://s3.us-east-2.amazonaws.com/com.nds.images/3x16%20F%20Wood.png" + } + ], + mapping: { + category: { + 0: "Floating Dock", + 1: "Sectional Dock", + 2: "Roll-In Dock" + }, + materials: { + 0: "Wood Grain", + 1: "Perforated", + 2: "Grey Aluminium" + } + } +}; + +export const accessories = { + error: false, + model: [ + { + id: 38, + create_at: "2022-11-14", + update_at: "2024-03-04T21:40:12.000Z", + name: "8 Step Stairs -Woodgrain", + length: 6.91, + width: 3.15, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20WG%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428%20Step%20Stairs%20-Woodgrain.png" + }, + { + id: 37, + create_at: "2022-11-14", + update_at: "2024-03-04T21:39:38.000Z", + name: "8 Step Stairs -Grey", + length: 6.91, + width: 3.15, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20Grey%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887428%20Step%20Stairs%20-Grey.png" + }, + { + id: 36, + create_at: "2022-11-14", + update_at: "2024-03-04T21:38:55.000Z", + name: "6 Step Stairs -Woodgrain", + length: 5.42, + width: 3.15, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20WG%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426%20Step%20Stairs%20-Woodgrain.png" + }, + { + id: 35, + create_at: "2022-11-14", + update_at: "2024-03-04T21:38:19.000Z", + name: "6 Step Stairs -Grey", + length: 5.42, + width: 3.15, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20Grey%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887426%20Step%20Stairs%20-Grey.png" + }, + { + id: 34, + create_at: "2022-11-14", + update_at: "2024-03-04T21:37:46.000Z", + name: "4 Step Stairs -Woodgrain", + length: 4.02, + width: 3.2, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20WG%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Step%20Stairs%20-Woodgrain.png" + }, + { + id: 33, + create_at: "2022-11-14", + update_at: "2024-03-04T21:37:23.000Z", + name: "4 Step Stairs -Grey", + length: 4.02, + width: 3.2, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Accesories%20Stairs%20Grey%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Step%20Stairs%20-Grey.png" + }, + { + id: 32, + create_at: "2022-11-14", + update_at: "2024-03-04T21:36:16.000Z", + name: "Wheel Kit-8ft Wide Dock", + length: 9.63, + width: 1.67, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Wheel%20Kit%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wheel%20Kit-8ft%20Wide%20Dock.png" + }, + { + id: 31, + create_at: "2022-11-14", + update_at: "2024-03-04T21:35:57.000Z", + name: "Wheel Kit-6ft Wide Dock", + length: 7.57, + width: 1.69, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Wheel%20Kit%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wheel%20Kit-6ft%20Wide%20Dock.png" + }, + { + id: 30, + create_at: "2022-11-14", + update_at: "2024-03-04T21:35:40.000Z", + name: "Wheel Kit-4ft Wide Dock", + length: 5.61, + width: 1.64, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Wheel%20Kit%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Wheel%20Kit-4ft%20Wide%20Dock.png" + }, + { + id: 29, + create_at: "2022-11-14", + update_at: "2024-03-04T21:35:08.000Z", + name: "Vertical Bumper", + length: 0.29, + width: 0.34, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Vertical%20Bumper%20Render%202.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Vertical%20Bumper.png" + }, + { + id: 28, + create_at: "2022-11-14", + update_at: "2024-03-04T21:34:47.000Z", + name: "Tie Down Cleat", + length: 0.21, + width: 0.71, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Cleat%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Tie%20Down%20Cleat.png" + }, + { + id: 27, + create_at: "2022-11-14", + update_at: "2024-03-04T21:34:17.000Z", + name: "Table and Chair Set", + length: 2.25, + width: 5.95, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Table%20and%20Chair%20Set.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Table%20and%20Chair%20Set.png" + }, + { + id: 26, + create_at: "2022-11-14", + update_at: "2024-03-04T21:33:51.000Z", + name: "PWC Catwalk-Woodgrain", + length: 1.32, + width: 16.01, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742WD2223.jpg", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PWC.png" + }, + { + id: 25, + create_at: "2022-11-14", + update_at: "2024-03-04T21:32:15.000Z", + name: "PWC Catwalk-Grey", + length: 1.32, + width: 16.01, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742snapport-1-768x768.jpg", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PWC.png" + }, + { + id: 24, + create_at: "2022-11-14", + update_at: "2024-03-04T20:53:42.000Z", + name: "Power Pack Box-With Solar", + length: 1.88, + width: 2.73, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Basta-Dock-Box-with-Solar-Panel.jpg", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Power%20Pack%20Box-With%20Solar.png" + }, + { + id: 23, + create_at: "2022-11-14", + update_at: "2024-03-04T20:53:18.000Z", + name: "Power Pack Box-No Solar", + length: 1.88, + width: 2.73, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887421-titanstor_35-15000-WW_opened_lowres.jpg", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Power%20Pack%20Box-No%20Solar.png" + }, + { + id: 22, + create_at: "2022-11-14", + update_at: "2024-03-04T20:42:01.000Z", + name: "Ladder", + length: 1.72, + width: 0.21, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%204%20Step%20Ladder%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Ladder.png" + }, + { + id: 20, + create_at: "2022-11-14", + update_at: "2024-03-04T20:41:29.000Z", + name: "Kayak Rack", + length: 2.94, + width: 2.86, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Kayak%20Rack%20Render%201.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Kayak%20Rack.png" + }, + { + id: 19, + create_at: "2022-11-14", + update_at: "2024-03-04T20:40:52.000Z", + name: "Fishing Rod Holder", + length: 0.77, + width: 0.5, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Fishing%20Rod%20Holder%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Fishing%20Rod%20Holder.png" + }, + { + id: 18, + create_at: "2022-11-14", + update_at: "2024-03-04T20:33:47.000Z", + name: "Box Mount", + length: 1.53, + width: 1.83, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Dock-Sides-Dock-Box-Bracket-08000011-2-Giant.jpg", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Box%20Mount.png" + }, + { + id: 16, + create_at: "2022-11-14", + update_at: "2024-03-04T20:39:34.000Z", + name: "Boarding Step -Woodgrain", + length: 2.04, + width: 1.53, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Woodgrain.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Boarding%20Step%20-Woodgrain.png" + }, + { + id: 15, + create_at: "2022-11-14", + update_at: "2024-03-04T20:39:15.000Z", + name: "Boarding Step -Grey", + length: 2.04, + width: 1.53, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Floating%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Boarding%20Step%20-Grey.png" + }, + { + id: 14, + create_at: "2022-11-14", + update_at: "2024-03-04T20:07:57.000Z", + name: "Bench-Woodgrain", + length: 1.98, + width: 4.37, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20WG%20Bench%20render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Bench-Woodgrain.png" + }, + { + id: 13, + create_at: "2022-11-14", + update_at: "2024-03-04T20:07:20.000Z", + name: "Bench-Grey", + length: 1.98, + width: 4.37, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Grey%20Bench%20Render.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Bench-Grey.png" + }, + { + id: 12, + create_at: "2022-11-14", + update_at: "2024-03-04T20:06:46.000Z", + name: "4ft Horizontal Bumper", + length: 0.32, + width: 4.02, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Horizontal%20Bumper%20Render%2048.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424ft%20Horizontal%20Bumper.png" + }, + { + id: 11, + create_at: "2022-11-14", + update_at: "2024-03-04T20:06:13.000Z", + name: "3ft Horizontal Bumper", + length: 0.32, + width: 3.02, + thumbnail: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742Paradise%20Roll%20In%20Dock%20Horizontal%20Bumper%20Render%2048.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887423ft%20Horizontal%20Bumper.png" + } + ], + mapping: null +}; + +export const ramps = { + error: false, + model: [ + { + id: 8, + create_at: "2023-05-01", + update_at: "2024-02-29T23:21:04.000Z", + category: 1, + length: 4, + width: 16, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20Wood.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20Wood.png", + material: 0 + }, + { + id: 7, + create_at: "2023-05-01", + update_at: "2024-02-29T23:20:51.000Z", + category: 1, + length: 4, + width: 16, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20SS.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20SS.png", + material: 2 + }, + { + id: 6, + create_at: "2023-05-01", + update_at: "2024-02-29T23:20:37.000Z", + category: 1, + length: 4, + width: 16, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x16%20TP%20Grey.png", + material: 1 + }, + { + id: 5, + create_at: "2023-05-01", + update_at: "2024-02-29T23:20:03.000Z", + category: 0, + length: 4, + width: 12, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20Wood.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20Wood.png", + material: 0 + }, + { + id: 4, + create_at: "2023-05-01", + update_at: "2024-02-29T23:19:52.000Z", + category: 0, + length: 4, + width: 12, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20SS.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20SS.png", + material: 2 + }, + { + id: 3, + create_at: "2023-05-01", + update_at: "2024-02-29T23:19:39.000Z", + category: 0, + length: 4, + width: 12, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20Grey.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424x12%20PR%20Grey.png", + material: 1 + } + ], + mapping: null +}; + +export const boat_lifts = { + error: false, + model: [ + { + id: 34, + create_at: "2022-11-17", + update_at: "2024-02-29T22:57:59.000Z", + model: "PH-24K-6", + weight_capacity: 24000, + lift_range: 6, + no_of_cylinders: 4, + length: 25.03, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-24K-4%2C5%2C6.png" + }, + { + id: 33, + create_at: "2022-11-17", + update_at: "2024-02-29T22:56:30.000Z", + model: "PH-24K-5", + weight_capacity: 24000, + lift_range: 5, + no_of_cylinders: 4, + length: 25.03, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-24K-4%2C5%2C6.png" + }, + { + id: 32, + create_at: "2022-11-17", + update_at: "2024-02-29T22:56:03.000Z", + model: "PH-24K-4", + weight_capacity: 24000, + lift_range: 4, + no_of_cylinders: 4, + length: 25.03, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-24K-4%2C5%2C6.png" + }, + { + id: 31, + create_at: "2022-11-17", + update_at: "2024-02-29T22:55:48.000Z", + model: "PH-20K-6", + weight_capacity: 20000, + lift_range: 6, + no_of_cylinders: 4, + length: 22.86, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-20K-4%2C5%2C6.png" + }, + { + id: 30, + create_at: "2022-11-17", + update_at: "2024-02-29T22:55:16.000Z", + model: "PH-20K-5", + weight_capacity: 20000, + lift_range: 5, + no_of_cylinders: 4, + length: 22.86, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-20K-4%2C5%2C6.png" + }, + { + id: 29, + create_at: "2022-11-17", + update_at: "2024-02-29T22:54:59.000Z", + model: "PH-20K-4", + weight_capacity: 20000, + lift_range: 4, + no_of_cylinders: 4, + length: 22.86, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-20K-4%2C5%2C6.png" + }, + { + id: 28, + create_at: "2022-11-17", + update_at: "2024-02-29T22:54:36.000Z", + model: "PH-15K-6", + weight_capacity: 15000, + lift_range: 6, + no_of_cylinders: 4, + length: 20.95, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-15K-4%2C5%2C6.png" + }, + { + id: 27, + create_at: "2022-11-17", + update_at: "2024-02-29T22:53:56.000Z", + model: "PH-15K-5", + weight_capacity: 15000, + lift_range: 5, + no_of_cylinders: 4, + length: 20.95, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-15K-4%2C5%2C6.png" + }, + { + id: 26, + create_at: "2022-11-17", + update_at: "2024-02-29T22:54:18.000Z", + model: "PH-15K-4", + weight_capacity: 15000, + lift_range: 4, + no_of_cylinders: 4, + length: 20.95, + width: 12.41, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-15K-4%2C5%2C6.png" + }, + { + id: 25, + create_at: "2022-11-17", + update_at: "2024-02-29T22:52:44.000Z", + model: "PH-12K-6", + weight_capacity: 12000, + lift_range: 6, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 24, + create_at: "2022-11-17", + update_at: "2024-02-29T22:52:30.000Z", + model: "PH-12K-5", + weight_capacity: 12000, + lift_range: 5, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 23, + create_at: "2022-11-17", + update_at: "2024-02-29T22:52:16.000Z", + model: "PH-13K-4", + weight_capacity: 13000, + lift_range: 4, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 22, + create_at: "2022-11-17", + update_at: "2024-02-29T22:52:00.000Z", + model: "PH-10K-6", + weight_capacity: 10000, + lift_range: 6, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 21, + create_at: "2022-11-17", + update_at: "2024-02-29T22:47:24.000Z", + model: "PH-10K-5", + weight_capacity: 10000, + lift_range: 5, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 20, + create_at: "2022-11-17", + update_at: "2024-02-29T22:47:08.000Z", + model: "PH-11K-4", + weight_capacity: 11000, + lift_range: 4, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 17, + create_at: "2022-11-17", + update_at: "2024-02-29T22:45:55.000Z", + model: "PH-11K-4S", + weight_capacity: 1100, + lift_range: 4, + no_of_cylinders: 4, + length: 16.85, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 16, + create_at: "2022-11-17", + update_at: "2024-02-29T22:45:38.000Z", + model: "PH-8K-6", + weight_capacity: 8000, + lift_range: 6, + no_of_cylinders: 4, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887424%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-10%2C12K-4%2C5%2C6.png" + }, + { + id: 15, + create_at: "2022-11-17", + update_at: "2024-02-28T20:45:35.000Z", + model: "PH-8K-5", + weight_capacity: 8000, + lift_range: 5, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 14, + create_at: "2022-11-17", + update_at: "2024-02-28T20:45:21.000Z", + model: "PH-8.5K-4", + weight_capacity: 8500, + lift_range: 4, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 13, + create_at: "2022-11-17", + update_at: "2024-02-28T20:45:03.000Z", + model: "PH-6K-6", + weight_capacity: 6000, + lift_range: 6, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 12, + create_at: "2022-11-17", + update_at: "2024-02-28T20:44:49.000Z", + model: "PH-6K-5", + weight_capacity: 6000, + lift_range: 5, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 11, + create_at: "2022-11-17", + update_at: "2024-02-28T20:44:33.000Z", + model: "PH-6.5K-4", + weight_capacity: 6500, + lift_range: 4, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 10, + create_at: "2022-11-17", + update_at: "2024-02-28T20:44:20.000Z", + model: "PH-2K-6", + weight_capacity: 2000, + lift_range: 6, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 9, + create_at: "2022-11-17", + update_at: "2024-02-28T20:41:32.000Z", + model: "PH-2K-5", + weight_capacity: 2000, + lift_range: 5, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + }, + { + id: 8, + create_at: "2022-11-17", + update_at: "2024-02-28T20:13:22.000Z", + model: "PH-2K-4", + weight_capacity: 2000, + lift_range: 4, + no_of_cylinders: 2, + length: 14.84, + width: 11.3, + image: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/0112854887422%20Cylinder%20Boat%20Lift.png", + top_view: + "https://dockbuilderapp.s3.us-west-2.amazonaws.com/baas/paralift/011285488742PH-6%2C8K-4%2C5%2C6.png" + } + ], + mapping: { + no_of_cylinders: { + 2: "2", + 4: "4" + } + } +}; diff --git a/src/favicon.svg b/src/favicon.svg new file mode 100644 index 0000000..de4aedd --- /dev/null +++ b/src/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/globalContext.jsx b/src/globalContext.jsx new file mode 100644 index 0000000..86c8b55 --- /dev/null +++ b/src/globalContext.jsx @@ -0,0 +1,103 @@ +import React, { useReducer } from "react"; +export const GlobalContext = React.createContext(); + +const initialState = { + globalMessage: "", + isOpen: true, + dockSideBarOpen: true, + path: "", + dockLoading: true, + dockLeft: null, + dockTop: null, +}; + +const reducer = ( state, action ) => { + switch ( action.type ) { + case "SNACKBAR": + return { + ...state, + globalMessage: action.payload.message, + }; + case "SETPATH": + return { + ...state, + path: action.payload.path, + }; + case "OPEN_SIDEBAR": + return { + ...state, + isOpen: action.payload.isOpen, + }; + case "TOGGLE_DOCK_SIDEBAR": + return { + ...state, + dockSideBarOpen: action.payload.dockSideBarOpen, + }; + case "DOCK_LOADING": + return { + ...state, + dockLoading: action.payload, + }; + case "UPDATE_DOCK_TOP": + return { + ...state, + dockTop: action.payload, + }; + case "UPDATE_DOCK_LEFT": + return { + ...state, + dockLeft: action.payload, + }; + + default: + return state; + } +}; + +export const showToast = ( dispatch, message, timeout = 3000 ) => { + dispatch( { + type: "SNACKBAR", + payload: { + message, + }, + } ); + + setTimeout( () => { + dispatch( { + type: "SNACKBAR", + payload: { + message: "", + }, + } ); + }, timeout ); +}; + +export const toggleDockSideBar = ( dispatch, dockSideBarOpen ) => { + dispatch( { + type: "TOGGLE_DOCK_SIDEBAR", + payload: { + dockSideBarOpen + }, + } ); +}; + +const GlobalProvider = ( { children } ) => { + const [ state, dispatch ] = useReducer( reducer, initialState ); + + // React.useEffect(() => { + + // }, []); + + return ( + + { children } + + ); +}; + +export default GlobalProvider; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..2dbfdfb --- /dev/null +++ b/src/index.css @@ -0,0 +1,143 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + transition: all .5s ease; +} +*::-webkit-scrollbar { + width: 0; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* color: #ffffff65; */ +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; +} + +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.sidebar-holder { + width: 100%; + min-width: 240px; + max-width: 240px; + position: relative; + background: #151515; + z-index: 2; + transition: all 0.3s; + min-height: 100vh; + overflow: hidden; + transition: 0.2s; +} + +.open-nav { + min-width: 0px !important; + max-width: 0px !important; + width: 0 !important; + transition: 0.2s; + opacity: 0; +} + +.close-dock-panel { + min-height: 0px !important; + max-height: 0px !important; + height: 0 !important; + transition: 0.2s; + opacity: 0; + /* overflow: hidden; */ +} + +.sidebar-list ul li a { + padding: 10px; + display: block; + width: 100%; + font-size: 18px; + font-weight: 600; + transition: 0.2s ease-in; + text-transform: capitalize; +} + +.sidebar-list ul li a:hover { + color: #151515; + background: white; +} + +.page-header { + width: 100%; + padding: 20px; + background: white; +} + +.page-header span { + cursor: pointer; + display: block; + width: fit-content; + font-size: 20px; +} + +.center-svg { + aspect-ratio: 1/1; + align-items: center; + justify-content: center; + line-height: 1.2em !important; +} + +/* Custom BEN */ + +.modal-holder { + position: fixed; + left: 0; + top: 0; + background: rgba(255, 255, 255, 0.782); + backdrop-filter: blur(3px); + min-height: 100vh; + width: 100%; + z-index: 99999999; +} +.cus-m-close { + position: absolute; + right: 40px; + top: 40px; + cursor: pointer; +} + +.filter-close{ + position: absolute; + right: 20px; + top: 20px; + cursor: pointer; +} +.uppy-Dashboard-inner { + width: 100% !important; +} + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@media screen and (max-width: 767px) { + .sidebar-holder { + width: 100%; + min-width: 200px; + max-width: 200px; + position: fixed; + top: 0; + left: 0; + } + + .page-header span { + margin-left: auto; + } +} \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 0000000..8cf4fe2 --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import "@fontsource/rajdhani"; +import "./index.css"; +import "./output.css"; +import App from "./App"; + +const root = ReactDOM.createRoot(document.getElementById("root")); + +root.render( + + + +); diff --git a/src/logo.svg b/src/logo.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/src/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..579b427 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,310 @@ +import React from "react"; +import { AuthContext } from "./authContext"; +import { Routes, Route } from "react-router-dom"; + +import { AdminHeader, TopHeader, PublicHeader, SnackBar } from "./components"; + +import { NotFoundPage } from "Pages/404"; + +import { + AddAdminAccessoriesPage, + AddAdminBoatLiftsPage, + AddAdminCmsPage, + AddAdminDealersPage, + AddAdminDocksPage, + AddAdminEmailPage, + AddAdminInstructionsPage, + AddAdminPhotoPage, + AddAdminQuotesPage, + AddAdminUserPage, + AddAdminWedgesPage, + AddAdminQuotesMailRecipientsPage, + AdminQuotesMailRecipientsListPage, + AdminAccessoriesListPage, + AdminBoatLiftsListPage, + AdminCmsListPage, + AdminDashboardPage, + AdminDealersListPage, + AdminDocksListPage, + AdminEmailListPage, + AdminForgotPage, + AdminInstructionsListPage, + AdminLoginPage, + AdminPhotoListPage, + AdminProfilePage, + AdminQuotesListPage, + AdminResetPage, + AdminUserListPage, + AdminWedgesListPage, + EditAdminAccessoriesPage, + EditAdminBoatLiftsPage, + EditAdminCmsPage, + EditAdminDealersPage, + EditAdminDocksPage, + EditAdminEmailPage, + EditAdminInstructionsPage, + EditAdminQuotesPage, + EditAdminUserPage, + EditAdminWedgesPage, + EditAdminQuotesMailRecipientsPage, + ViewAdminAccessoriesPage, + ViewAdminBoatLiftsPage, + ViewAdminDealersPage, + ViewAdminDocksPage, + ViewAdminInstructionsPage, + ViewAdminQuotesPage, + ViewAdminWedgesPage, + ViewAdminQuotesMailRecipientsPage, + AdminRampsListPage, + AddAdminRampsPage, + ViewAdminRampsPage, + EditAdminRampsPage, +} from "Pages/admin"; + +import { DockBuilderPage } from "Pages/dock"; + +function renderHeader(role) { + switch (role) { + case "admin": + return ; + + default: + return ; + } +} + +function renderRoutes(role) { + switch (role) { + case "admin": + return ( + + }> + } + > + } + > + + } + > + } + > + } + > + } + > + + } + > + } + > + } + > + } + > + + }> + } + > + } + > + + }> + } + > + + } + > + } + > + } + > + } + > + + } + > + } + > + } + > + } + > + + }> + } + > + } + > + } + > + + }> + }> + } + > + + } + > + } + > + } + > + } + > + + }> + }> + } + > + + }> + } + > + } + > + } + > + + }> + } + > + } + > + } + > + + }> + } + > + } + > + } + > + + {/* || } + > */} + + ); + + default: + return ( + + }> + }> + } + > + }> + + }> + + ); + } +} + +function Main() { + const { state } = React.useContext(AuthContext); + + return ( +
+
+ {!state.isAuthenticated ? : renderHeader(state?.role)} +
+ {state.isAuthenticated ? : null} +
+ {!state.isAuthenticated + ? renderRoutes("none") + : renderRoutes(state?.role)} +
+
+
+ +
+ ); +} + +export default Main; diff --git a/src/output.css b/src/output.css new file mode 100644 index 0000000..676d34b --- /dev/null +++ b/src/output.css @@ -0,0 +1,5097 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; +} + +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.sidebar-holder { + width: 100%; + min-width: 240px; + max-width: 240px; + position: relative; + background: #151515; + color: #fff; + z-index: 2; + transition: all 0.3s; + min-height: 100vh; + overflow: hidden; + transition: 0.2s; +} + +.open-nav { + min-width: 0px !important; + max-width: 0px !important; + width: 0 !important; + transition: 0.2s; + opacity: 0; +} + +.sidebar-list ul li a { + padding: 10px; + display: block; + width: 100%; + font-size: 18px; + font-weight: 600; + transition: 0.2s ease-in; + text-transform: capitalize; +} + +.sidebar-list ul li a:hover { + color: #151515; + background: white; +} + +.page-header { + width: 100%; + padding: 20px; + background: white; +} + +.page-header span { + cursor: pointer; + display: block; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + font-size: 20px; +} + +.center-svg { + aspect-ratio: 1/1; + align-items: center; + justify-content: center; + line-height: 1.2em !important; +} + +.uppy-Dashboard-inner { + width: 100% !important; +} + +/* +! tailwindcss v3.0.24 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Inter, sans-serif; + /* 4 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input:-ms-input-placeholder, textarea:-ms-input-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* +Ensure the default browser behavior of the `hidden` attribute. +*/ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +:root { + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #0d6efd; + --bs-secondary: #6c757d; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-primary-rgb: 13, 110, 253; + --bs-secondary-rgb: 108, 117, 125; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg-rgb: 255, 255, 255; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-body-font-family: var(--bs-font-sans-serif); + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-bg: #fff; +} + +.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.is-invalid ~ .invalid-feedback { + display: block; +} + +.is-invalid ~ .invalid-tooltip { + display: block; +} + +.form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); +} + +textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +} + +.form-select.is-invalid { + border-color: #dc3545; +} + +.form-select.is-invalid:not([multiple]):not([size]) { + padding-right: 4.125rem; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.form-select.is-invalid:not([multiple])[size="1"] { + padding-right: 4.125rem; + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.form-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); +} + +.form-check-input.is-invalid { + border-color: #dc3545; +} + +.form-check-input.is-invalid:checked { + background-color: #dc3545; +} + +.form-check-input.is-invalid:focus { + box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); +} + +.form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; +} + +.input-group .form-control.is-invalid { + z-index: 2; +} + +.input-group .form-select.is-invalid { + z-index: 2; +} + +.input-group .form-control.is-invalid:focus { + z-index: 3; +} + +.input-group .form-select.is-invalid:focus { + z-index: 3; +} + +.btn.active { + box-shadow: none; +} + +.btn.active:focus { + box-shadow: none; +} + +.fade { + transition: opacity 0.15s linear; +} + +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} + +.collapsing.collapse-horizontal { + width: 0; + height: auto; + transition: width 0.35s ease; +} + +.dropdown-menu { + z-index: 1000; +} + +.dropdown-item.active { + color: #1f2937; + -webkit-text-decoration: none; + text-decoration: none; + background-color: #0d6efd; +} + +.dropdown-item:active { + color: #1f2937; + -webkit-text-decoration: none; + text-decoration: none; + background-color: #0d6efd; +} + +.dropdown-item:disabled { + color: #adb5bd; + pointer-events: none; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-menu-dark .dropdown-item.active { + color: #fff; + background-color: #0d6efd; +} + +.dropdown-menu-dark .dropdown-item:active { + color: #fff; + background-color: #0d6efd; +} + +.dropdown-menu-dark .dropdown-item.disabled { + color: #adb5bd; +} + +.dropdown-menu-dark .dropdown-item:disabled { + color: #adb5bd; +} + +.nav-tabs .nav-link { + color: #4b5563; +} + +.nav-tabs .nav-link:hover { + isolation: isolate; +} + +.nav-tabs .nav-link:focus { + isolation: isolate; +} + +.nav-tabs .nav-link.disabled { + color: #9ca3af; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active { + color: #2563eb; + border-color: #2563eb; +} + +.nav-tabs .nav-item.show .nav-link { + color: #2563eb; + border-color: #2563eb; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + background: #f3f4f6; + color: #4b5563; + box-shadow: none; +} + +.nav-pills .nav-link.active { + background: #2563eb; + color: #fff; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.nav-pills .show > .nav-link { + background: #2563eb; + color: #fff; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.nav-pills .disabled { + color: #9ca3af; + background-color: rgba(243, 244, 246, 0.5); +} + +.nav-pills.menu-sidebar .nav-link { + background-color: transparent; + box-shadow: none; + padding: 0 5px; + border-radius: 0; +} + +.nav-pills.menu-sidebar .nav-link.active { + color: #1266f1; + font-weight: 600; + border-left: 0.125rem solid #1266f1; +} + +.nav-justified > .nav-link { + -ms-flex-basis: 0; + flex-basis: 0; +} + +.nav-justified .nav-item { + -ms-flex-basis: 0; + flex-basis: 0; +} + +.tab-content > .active { + display: block; +} + +.navbar-expand .navbar-nav { + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +.navbar-expand .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} + +.navbar-light .navbar-nav .show > .nav-link { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show > .nav-link { + color: #fff; +} + +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} + +.accordion-item:last-of-type .accordion-button.collapsed { + border-bottom-right-radius: calc(0.5rem - 1px); + border-bottom-left-radius: calc(0.5rem - 1px); +} + +.btn-close.disabled { + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + opacity: 0.25; +} + +.modal { + z-index: 1055; +} + +.modal-dialog { + margin: 0.5rem; +} + +.modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -50px); +} + +.modal.show .modal-dialog { + transform: none; +} + +.modal.modal-static .modal-dialog { + transform: scale(1.02); +} + +.modal-dialog-scrollable .modal-body { + overflow-y: auto; +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + width: 100vw; + height: 100vh; + background-color: #000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.5; +} + +.modal-body { + flex: 1 1 auto; +} + +.modal-fullscreen .modal-body { + overflow-y: auto; +} + +.tooltip { + position: absolute; + z-index: 1080; + display: block; + margin: 0; + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + -webkit-text-align: start; + text-align: start; + -webkit-text-decoration: none; + text-decoration: none; + -webkit-text-shadow: none; + text-shadow: none; + -webkit-text-transform: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 1; +} + +.bs-tooltip-top .tooltip-arrow { + bottom: 0; +} + +.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { + bottom: 0; +} + +.bs-tooltip-top .tooltip-arrow::before { + top: -1px; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { + top: -1px; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-end .tooltip-arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-end .tooltip-arrow::before { + right: -1px; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { + right: -1px; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom .tooltip-arrow { + top: 0; +} + +.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { + top: 0; +} + +.bs-tooltip-bottom .tooltip-arrow::before { + bottom: -1px; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { + bottom: -1px; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-start .tooltip-arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-start .tooltip-arrow::before { + left: -1px; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { + left: -1px; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + font-size: 14px; + padding: 6px 16px; + color: #fff; + -webkit-text-align: center; + text-align: center; + background-color: #6d6d6d; + border-radius: 0.25rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1070; + display: block; + max-width: 276px; + font-family: var(--bs-font-sans-serif); + font-style: normal; + font-weight: 400; + line-height: 1.5; + -webkit-text-align: start; + text-align: start; + -webkit-text-decoration: none; + text-decoration: none; + -webkit-text-shadow: none; + text-shadow: none; + -webkit-text-transform: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 0; + border-radius: 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +.bs-popover-top > .popover-arrow { + bottom: calc(-0.5rem - 1px); +} + +.bs-popover-auto[data-popper-placement^=top] > .popover-arrow { + bottom: calc(-0.5rem - 1px); +} + +.bs-popover-top > .popover-arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-top > .popover-arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; +} + +.bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; +} + +.bs-popover-end > .popover-arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; +} + +.bs-popover-auto[data-popper-placement^=right] > .popover-arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; +} + +.bs-popover-end > .popover-arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-end > .popover-arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; +} + +.bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; +} + +.bs-popover-bottom > .popover-arrow { + top: calc(-0.5rem - 1px); +} + +.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { + top: calc(-0.5rem - 1px); +} + +.bs-popover-bottom > .popover-arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-bottom > .popover-arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; +} + +.bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; +} + +.bs-popover-bottom .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f0f0f0; +} + +.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f0f0f0; +} + +.bs-popover-start > .popover-arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; +} + +.bs-popover-auto[data-popper-placement^=left] > .popover-arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; +} + +.bs-popover-start > .popover-arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-start > .popover-arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; +} + +.bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 1rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #fff; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; + font-weight: 500; +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 1rem 1rem; + color: #212529; +} + +.carousel.pointer-event { + touch-action: pan-y; +} + +.carousel-item { + display: none; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; +} + +.carousel-item.active { + display: block; +} + +.carousel-item-next { + display: block; +} + +.carousel-item-prev { + display: block; +} + +.carousel-item-next:not(.carousel-item-start) { + transform: translateX(100%); +} + +.active.carousel-item-end { + transform: translateX(100%); +} + +.carousel-item-prev:not(.carousel-item-end) { + transform: translateX(-100%); +} + +.active.carousel-item-start { + transform: translateX(-100%); +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; +} + +.carousel-fade .carousel-item.active { + z-index: 1; + opacity: 1; +} + +.carousel-fade .carousel-item-next.carousel-item-start { + z-index: 1; + opacity: 1; +} + +.carousel-fade .carousel-item-prev.carousel-item-end { + z-index: 1; + opacity: 1; +} + +.carousel-fade .active.carousel-item-start { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; +} + +.carousel-fade .active.carousel-item-end { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; +} + +.carousel-indicators { + z-index: 2; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators [data-bs-target] { + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + padding: 0; + margin-right: 3px; + margin-left: 3px; + -webkit-text-indent: -999px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: 0.5; + transition: opacity 0.6s ease; +} + +.carousel-indicators .active { + opacity: 1; +} + +.carousel-dark .carousel-indicators [data-bs-target] { + background-color: #000; +} + +.offcanvas { + z-index: 1045; +} + +.offcanvas-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; +} + +.offcanvas-backdrop.fade { + opacity: 0; +} + +.offcanvas-backdrop.show { + opacity: 0.5; +} + +.offcanvas.show { + transform: none; +} + +.sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; +} + +.vr { + display: inline-block; + align-self: stretch; + width: 1px; + min-height: 1em; + background-color: currentColor; + opacity: 0.25; +} + +.animation { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + padding: auto; +} + +.fade-in { + -webkit-animation-name: _fade-in; + animation-name: _fade-in; +} + +.fade-out { + -webkit-animation-name: _fade-out; + animation-name: _fade-out; +} + +.animation.infinite { + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; +} + +.animation.delay-1s { + -webkit-animation-delay: 1s; + animation-delay: 1s; +} + +.animation.delay-2s { + -webkit-animation-delay: 2s; + animation-delay: 2s; +} + +.animation.delay-3s { + -webkit-animation-delay: 3s; + animation-delay: 3s; +} + +.animation.delay-4s { + -webkit-animation-delay: 4s; + animation-delay: 4s; +} + +.animation.delay-5s { + -webkit-animation-delay: 5s; + animation-delay: 5s; +} + +.animation.fast { + -webkit-animation-duration: 800ms; + animation-duration: 800ms; +} + +.animation.faster { + -webkit-animation-duration: 500ms; + animation-duration: 500ms; +} + +.animation.slow { + -webkit-animation-duration: 2s; + animation-duration: 2s; +} + +.animation.slower { + -webkit-animation-duration: 3s; + animation-duration: 3s; +} + +.slide-in-left { + -webkit-animation-name: _slide-in-left; + animation-name: _slide-in-left; +} + +.slide-in-right { + -webkit-animation-name: _slide-in-right; + animation-name: _slide-in-right; +} + +.slide-out-left { + -webkit-animation-name: _slide-out-left; + animation-name: _slide-out-left; +} + +.slide-out-right { + -webkit-animation-name: _slide-out-right; + animation-name: _slide-out-right; +} + +.ripple-surface { + position: relative; + overflow: hidden; + display: inline-block; + vertical-align: bottom; +} + +.ripple-surface-unbound { + overflow: visible; +} + +.ripple-wave { + background-image: radial-gradient(circle, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, transparent 70%); + border-radius: 50%; + opacity: 0.5; + pointer-events: none; + position: absolute; + touch-action: none; + transform: scale(0); + transition-property: transform, opacity; + transition-timing-function: cubic-bezier(0, 0, 0.15, 1), cubic-bezier(0, 0, 0.15, 1); + z-index: 999; +} + +.ripple-wave.active { + transform: scale(1); + opacity: 0; +} + +.btn .ripple-wave { + background-image: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); +} + +.ripple-surface-primary .ripple-wave { + background-image: radial-gradient(circle, rgba(18, 102, 241, 0.2) 0, rgba(18, 102, 241, 0.3) 40%, rgba(18, 102, 241, 0.4) 50%, rgba(18, 102, 241, 0.5) 60%, rgba(18, 102, 241, 0) 70%); +} + +.ripple-surface-secondary .ripple-wave { + background-image: radial-gradient(circle, rgba(178, 60, 253, 0.2) 0, rgba(178, 60, 253, 0.3) 40%, rgba(178, 60, 253, 0.4) 50%, rgba(178, 60, 253, 0.5) 60%, rgba(178, 60, 253, 0) 70%); +} + +.ripple-surface-success .ripple-wave { + background-image: radial-gradient(circle, rgba(0, 183, 74, 0.2) 0, rgba(0, 183, 74, 0.3) 40%, rgba(0, 183, 74, 0.4) 50%, rgba(0, 183, 74, 0.5) 60%, rgba(0, 183, 74, 0) 70%); +} + +.ripple-surface-info .ripple-wave { + background-image: radial-gradient(circle, rgba(57, 192, 237, 0.2) 0, rgba(57, 192, 237, 0.3) 40%, rgba(57, 192, 237, 0.4) 50%, rgba(57, 192, 237, 0.5) 60%, rgba(57, 192, 237, 0) 70%); +} + +.ripple-surface-warning .ripple-wave { + background-image: radial-gradient(circle, rgba(255, 169, 0, 0.2) 0, rgba(255, 169, 0, 0.3) 40%, rgba(255, 169, 0, 0.4) 50%, rgba(255, 169, 0, 0.5) 60%, rgba(255, 169, 0, 0) 70%); +} + +.ripple-surface-danger .ripple-wave { + background-image: radial-gradient(circle, rgba(249, 49, 84, 0.2) 0, rgba(249, 49, 84, 0.3) 40%, rgba(249, 49, 84, 0.4) 50%, rgba(249, 49, 84, 0.5) 60%, rgba(249, 49, 84, 0) 70%); +} + +.ripple-surface-light .ripple-wave { + background-image: radial-gradient(circle, rgba(251, 251, 251, 0.2) 0, rgba(251, 251, 251, 0.3) 40%, rgba(251, 251, 251, 0.4) 50%, rgba(251, 251, 251, 0.5) 60%, rgba(251, 251, 251, 0) 70%); +} + +.ripple-surface-dark .ripple-wave { + background-image: radial-gradient(circle, rgba(38, 38, 38, 0.2) 0, rgba(38, 38, 38, 0.3) 40%, rgba(38, 38, 38, 0.4) 50%, rgba(38, 38, 38, 0.5) 60%, rgba(38, 38, 38, 0) 70%); +} + +.ripple-surface-white .ripple-wave { + background-image: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%); +} + +.ripple-surface-black .ripple-wave { + background-image: radial-gradient(circle, rgba(0, 0, 0, 0.2) 0, rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.5) 60%, transparent 70%); +} + +.datepicker-toggle-button { + position: absolute; + outline: none; + border: none; + background-color: transparent; + right: 10px; + top: 50%; + transform: translate(-50%, -50%); +} + +.datepicker-toggle-button:focus { + color: #2979ff; +} + +.datepicker-toggle-button:hover { + color: #2979ff; +} + +.datepicker-backdrop { + width: 100%; + height: 100%; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.4); + z-index: 1065; +} + +.datepicker-dropdown-container { + width: 328px; + height: 380px; + background-color: #fff; + border-radius: 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + z-index: 1066; +} + +.datepicker-modal-container { + display: flex; + flex-direction: column; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 328px; + height: 512px; + background-color: #fff; + border-radius: 0.6rem 0.6rem 0.5rem 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + z-index: 1066; +} + +.datepicker-header { + height: 120px; + padding-right: 24px; + padding-left: 24px; + background-color: #2979ff; + display: flex; + flex-direction: column; + border-radius: 0.5rem 0.5rem 0 0; +} + +.datepicker-title { + height: 32px; + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +.datepicker-title-text { + font-size: 10px; + font-weight: 400; + -webkit-text-transform: uppercase; + text-transform: uppercase; + letter-spacing: 1.7px; + color: #fff; +} + +.datepicker-date { + height: 72px; + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +.datepicker-date-text { + font-size: 34px; + font-weight: 400; + color: #fff; +} + +.datepicker-main { + position: relative; + height: 100%; +} + +.datepicker-date-controls { + padding: 10px 12px 0 12px; + display: flex; + justify-content: space-between; + color: rgba(0, 0, 0, 0.64); +} + +.datepicker-view-change-button { + padding: 10px; + color: #666; + font-weight: 500; + font-size: 0.9rem; + border-radius: 10px; + box-shadow: none; + background-color: transparent; + margin: 0; + border: none; +} + +.datepicker-view-change-button:hover { + background-color: #eee; +} + +.datepicker-view-change-button:focus { + background-color: #eee; +} + +.datepicker-view-change-button:after { + content: ""; + display: inline-block; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top-width: 5px; + border-top-style: solid; + margin: 0 0 0 5px; + vertical-align: middle; +} + +.datepicker-arrow-controls { + margin-top: 10px; +} + +.datepicker-previous-button { + position: relative; + padding: 0; + width: 40px; + height: 40px; + line-height: 40px; + border: none; + outline: none; + margin: 0; + color: rgba(0, 0, 0, 0.64); + background-color: transparent; + margin-right: 24px; +} + +.datepicker-previous-button:hover { + background-color: #eee; + border-radius: 50%; +} + +.datepicker-previous-button:focus { + background-color: #eee; + border-radius: 50%; +} + +.datepicker-previous-button::after { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + content: ""; + margin: 15.5px; + border: 0 solid currentColor; + border-top-width: 2px; + border-left-width: 2px; + transform: translateX(2px) rotate(-45deg); +} + +.datepicker-next-button { + position: relative; + padding: 0; + width: 40px; + height: 40px; + line-height: 40px; + border: none; + outline: none; + margin: 0; + color: rgba(0, 0, 0, 0.64); + background-color: transparent; +} + +.datepicker-next-button:hover { + background-color: #eee; + border-radius: 50%; +} + +.datepicker-next-button:focus { + background-color: #eee; + border-radius: 50%; +} + +.datepicker-next-button::after { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + content: ""; + margin: 15.5px; + border: 0 solid currentColor; + border-top-width: 2px; + border-right-width: 2px; + transform: translateX(-2px) rotate(45deg); +} + +.datepicker-view { + padding-left: 12px; + padding-right: 12px; + outline: none; +} + +.datepicker-table { + margin-right: auto; + margin-left: auto; + width: 304px; +} + +.datepicker-day-heading { + width: 40px; + height: 40px; + -webkit-text-align: center; + text-align: center; + font-size: 12px; + font-weight: 400; +} + +.datepicker-cell { + -webkit-text-align: center; + text-align: center; +} + +.datepicker-cell.disabled { + color: #ccc; + cursor: default; + pointer-events: none; +} + +.datepicker-cell.disabled:hover { + cursor: default; +} + +.datepicker-cell:hover { + cursor: pointer; +} + +.datepicker-cell:not(.disabled):not(.selected):hover .datepicker-cell-content { + background-color: #d3d3d3; +} + +.datepicker-cell.selected .datepicker-cell-content { + background-color: #2979ff; + color: #fff; +} + +.datepicker-cell:not(.selected).focused .datepicker-cell-content { + background-color: #eee; +} + +.datepicker-cell.focused .datepicker-cell-content.selected { + background-color: #2979ff; +} + +.datepicker-cell.current .datepicker-cell-content { + border: 1px solid #000; +} + +.datepicker-small-cell { + width: 40px; + height: 40px; +} + +.datepicker-small-cell-content { + width: 36px; + height: 36px; + line-height: 36px; + border-radius: 50%; + font-size: 13px; +} + +.datepicker-large-cell { + width: 76px; + height: 42px; +} + +.datepicker-large-cell-content { + width: 72px; + height: 40px; + line-height: 40px; + padding: 1px 2px; + border-radius: 999px; +} + +.datepicker-footer { + height: 56px; + display: flex; + position: absolute; + width: 100%; + bottom: 0; + justify-content: flex-end; + align-items: center; + padding-left: 12px; + padding-right: 12px; +} + +.datepicker-footer-btn { + background-color: #fff; + color: #2979ff; + border: none; + cursor: pointer; + padding: 0 10px; + -webkit-text-transform: uppercase; + text-transform: uppercase; + font-size: 0.8rem; + font-weight: 500; + height: 40px; + line-height: 40px; + letter-spacing: 0.1rem; + border-radius: 10px; + margin-bottom: 10px; +} + +.datepicker-footer-btn:hover { + background-color: #eee; +} + +.datepicker-footer-btn:focus { + background-color: #eee; +} + +.datepicker-clear-btn { + margin-right: auto; +} + +.timepicker-wrapper { + touch-action: none; + z-index: 1065; + opacity: 0; + right: 0; + bottom: 0; + top: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.4); +} + +.timepicker-elements { + min-width: 310px; + min-height: 325px; + background: #fff; + border-top-right-radius: 0.6rem; + border-top-left-radius: 0.6rem; +} + +.timepicker-head { + background-color: #2979ff; + height: 100px; + border-top-right-radius: 0.5rem; + border-top-left-radius: 0.5rem; + padding: 10px 24px 10px 50px; +} + +.timepicker-button { + font-size: 0.8rem; + min-width: 64px; + box-sizing: border-box; + font-weight: 500; + line-height: 40px; + border-radius: 10px; + letter-spacing: 0.1rem; + -webkit-text-transform: uppercase; + text-transform: uppercase; + color: #2979ff; + border: none; + background-color: transparent; + transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + outline: none; + padding: 0 10px; + height: 40px; + margin-bottom: 10px; +} + +.timepicker-button:hover { + background-color: rgba(0, 0, 0, 0.08); +} + +.timepicker-button:focus { + outline: none; + background-color: rgba(0, 0, 0, 0.08); +} + +.timepicker-current { + font-size: 3.75rem; + font-weight: 300; + line-height: 1.2; + letter-spacing: -0.00833em; + color: #fff; + opacity: 0.54; + border: none; + background: transparent; + padding: 0; +} + +.timepicker-current.active { + opacity: 1; +} + +.timepicker-current-wrapper { + direction: ltr; +} + +.timepicker-mode-wrapper { + margin-left: 20px; + font-size: 18px; + color: rgba(255, 255, 255, 0.54); +} + +.timepicker-mode-wrapper.active { + opacity: 1; +} + +.timepicker-clock-wrapper { + min-width: 310px; + max-width: 325px; + min-height: 305px; + overflow-x: hidden; + height: 100%; +} + +.timepicker-clock { + position: relative; + border-radius: 100%; + width: 260px; + height: 260px; + cursor: default; + margin: 0 auto; + background-color: rgba(0, 0, 0, 0.07); +} + +.timepicker-time-tips-minutes.active { + color: #fff; + background-color: #2979ff; + font-weight: 400; +} + +.timepicker-time-tips-inner.active { + color: #fff; + background-color: #2979ff; + font-weight: 400; +} + +.timepicker-time-tips-hours.active { + color: #fff; + background-color: #2979ff; + font-weight: 400; +} + +.timepicker-time-tips-minutes.disabled { + color: #b3afaf; + pointer-events: none; + background-color: transparent; +} + +.timepicker-time-tips-inner.disabled { + color: #b3afaf; + pointer-events: none; + background-color: transparent; +} + +.timepicker-time-tips-hours.disabled { + color: #b3afaf; + pointer-events: none; + background-color: transparent; +} + +.timepicker-dot { + font-weight: 300; + line-height: 1.2; + letter-spacing: -0.00833em; + color: #fff; + font-size: 3.75rem; + opacity: 0.54; + border: none; + background: transparent; + padding: 0; +} + +.timepicker-middle-dot { + top: 50%; + left: 50%; + width: 6px; + height: 6px; + transform: translate(-50%, -50%); + border-radius: 50%; + background-color: #2979ff; +} + +.timepicker-hand-pointer { + background-color: #2979ff; + bottom: 50%; + height: 40%; + left: calc(50% - 1px); + transform-origin: center bottom 0; + width: 2px; +} + +.timepicker-time-tips.active { + color: #fff; +} + +.timepicker-circle { + top: -21px; + left: -15px; + width: 4px; + border: 14px solid #2979ff; + height: 4px; + box-sizing: content-box; + border-radius: 100%; +} + +.timepicker-hour-mode { + padding: 0; + background-color: transparent; + border: none; + color: #fff; + opacity: 0.54; + cursor: pointer; +} + +.timepicker-hour { + cursor: pointer; +} + +.timepicker-minute { + cursor: pointer; +} + +.timepicker-hour-mode:hover { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-hour-mode:focus { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-hour:hover { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-hour:focus { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-minute:hover { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-minute:focus { + background-color: rgba(0, 0, 0, 0.15); + outline: none; +} + +.timepicker-hour-mode.active { + color: #fff; + opacity: 1; +} + +.timepicker-hour.active { + color: #fff; + opacity: 1; +} + +.timepicker-minute.active { + color: #fff; + opacity: 1; +} + +.timepicker-footer { + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + height: 56px; + padding-left: 12px; + padding-right: 12px; + background-color: #fff; +} + +.timepicker-container { + max-height: calc(100% - 64px); + overflow-y: auto; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.07), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +.timepicker-icon-up.active { + opacity: 1; +} + +.timepicker-icon-down.active { + opacity: 1; +} + +.timepicker-toggle-button { + position: absolute; + outline: none; + border: none; + background-color: transparent; + right: 10px; + top: 50%; + transform: translate(-50%, -50%); + transition: all 0.3s ease; + cursor: pointer; +} + +.timepicker-toggle-button:hover { + color: #2979ff; +} + +.timepicker-toggle-button:focus { + color: #2979ff; +} + +.timepicker-input:focus + .timepicker-toggle-button { + color: #2979ff; +} + +.timepicker-input:focus + .timepicker-toggle-button i { + color: #2979ff; +} + +.timepicker a.timepicker-toggle-button { + right: 1px; +} + +.timepicker-toggle-button.timepicker-icon { + right: 1px; +} + +.timepicker-modal .fade.show { + opacity: 1; +} + +.stepper { + position: relative; + padding: 0; + margin: 0; + width: 100%; + list-style: none; + overflow: hidden; + transition: height 0.2s ease-in-out; +} + +.stepper:not(.stepper-vertical) { + display: flex; + justify-content: space-between; +} + +.stepper:not(.stepper-vertical) .stepper-content { + position: absolute; + width: 100%; + padding: 1rem; +} + +.stepper:not(.stepper-vertical) .stepper-step { + flex: auto; + height: 4.5rem; +} + +.stepper:not(.stepper-vertical) .stepper-step:first-child .stepper-head { + padding-left: 1.5rem; +} + +.stepper:not(.stepper-vertical) .stepper-step:last-child .stepper-head { + padding-right: 1.5rem; +} + +.stepper:not(.stepper-vertical) .stepper-step:not(:first-child) .stepper-head:before { + flex: 1; + height: 1px; + width: 100%; + margin-right: 0.5rem; + content: ""; + background-color: rgba(0, 0, 0, 0.1); +} + +.stepper:not(.stepper-vertical) .stepper-step:not(:last-child) .stepper-head:after { + flex: 1; + height: 1px; + width: 100%; + margin-left: 0.5rem; + content: ""; + background-color: rgba(0, 0, 0, 0.1); +} + +.stepper:not(.stepper-vertical) .stepper-head-icon { + margin: 1.5rem 0.5rem 1.5rem 0; +} + +.stepper.stepper-mobile { + justify-content: center; + align-items: flex-end; +} + +.stepper.stepper-mobile.stepper-progress-bar .stepper-head-icon { + display: none; +} + +.stepper.stepper-mobile .stepper-step { + flex: unset; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + margin: 1rem 0 1rem 0; +} + +.stepper.stepper-mobile .stepper-step:not(:last-child) .stepper-head:after { + margin-left: 0; +} + +.stepper.stepper-mobile .stepper-step:not(:first-child) .stepper-head:before { + margin-right: 0; +} + +.stepper.stepper-mobile .stepper-step:not(:last-child):not(:first-child) .stepper-head { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.stepper.stepper-mobile .stepper-head-icon { + font-size: 0; + margin: 0; + height: 0.5rem; + width: 0.5rem; + z-index: 1; +} + +.stepper.stepper-mobile .stepper-head-text { + display: none; +} + +.stepper.stepper-mobile .stepper-content { + top: 2.56rem; +} + +@media (prefers-reduced-motion: reduce) { + .form-control::-webkit-file-upload-button { + -webkit-transition: none; + transition: none; + } + .form-control::file-selector-button { + transition: none; + } + + .form-control::-webkit-file-upload-button { + -webkit-transition: none; + transition: none; + } + + .form-switch .form-check-input { + transition: none; + } + + .form-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none; + } + + .form-range::-moz-range-thumb { + -moz-transition: none; + transition: none; + } + + .form-floating > label { + transition: none; + } + + .fade { + transition: none; + } + + .collapsing { + transition: none; + } + + .collapsing.collapse-horizontal { + transition: none; + } + + .accordion-button::after { + transition: none; + } + + .modal.fade .modal-dialog { + transition: none; + } + + .carousel-item { + transition: none; + } + + .carousel-fade .active.carousel-item-start { + transition: none; + } + + .carousel-fade .active.carousel-item-end { + transition: none; + } + + .carousel-control-prev { + transition: none; + } + + .carousel-control-next { + transition: none; + } + + .carousel-indicators [data-bs-target] { + transition: none; + } + + .spinner-border { + -webkit-animation-duration: 1.5s; + animation-duration: 1.5s; + } + + .spinner-grow { + -webkit-animation-duration: 1.5s; + animation-duration: 1.5s; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-sm .navbar-collapse { + display: flex !important; + -ms-flex-basis: auto; + flex-basis: auto; + } + + .navbar-expand-sm .navbar-toggler { + display: none; + } + + .navbar-expand-sm .offcanvas-header { + display: none; + } + + .navbar-expand-sm .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; + } + + .navbar-expand-sm .offcanvas-top { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-sm .offcanvas-bottom { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-sm .offcanvas-body { + display: flex; + -ms-flex-grow: 0; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } + + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + + .modal-dialog-scrollable { + height: calc(100% - 3.5rem); + } + + .modal-dialog-centered { + min-height: calc(100% - 3.5rem); + } + + .modal-sm { + max-width: 300px; + } + + .sticky-sm-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-md .navbar-collapse { + display: flex !important; + -ms-flex-basis: auto; + flex-basis: auto; + } + + .navbar-expand-md .navbar-toggler { + display: none; + } + + .navbar-expand-md .offcanvas-header { + display: none; + } + + .navbar-expand-md .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; + } + + .navbar-expand-md .offcanvas-top { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-md .offcanvas-bottom { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-md .offcanvas-body { + display: flex; + -ms-flex-grow: 0; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } + + .sticky-md-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-lg .navbar-collapse { + display: flex !important; + -ms-flex-basis: auto; + flex-basis: auto; + } + + .navbar-expand-lg .navbar-toggler { + display: none; + } + + .navbar-expand-lg .offcanvas-header { + display: none; + } + + .navbar-expand-lg .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; + } + + .navbar-expand-lg .offcanvas-top { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-lg .offcanvas-bottom { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-lg .offcanvas-body { + display: flex; + -ms-flex-grow: 0; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } + + .modal-lg { + max-width: 800px; + } + + .modal-xl { + max-width: 800px; + } + + .sticky-lg-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xl .navbar-collapse { + display: flex !important; + -ms-flex-basis: auto; + flex-basis: auto; + } + + .navbar-expand-xl .navbar-toggler { + display: none; + } + + .navbar-expand-xl .offcanvas-header { + display: none; + } + + .navbar-expand-xl .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; + } + + .navbar-expand-xl .offcanvas-top { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-xl .offcanvas-bottom { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-xl .offcanvas-body { + display: flex; + -ms-flex-grow: 0; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } + + .modal-xl { + max-width: 1140px; + } + + .sticky-xl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +@media (min-width: 1400px) { + .navbar-expand-xxl { + flex-wrap: nowrap; + justify-content: flex-start; + } + + .navbar-expand-xxl .navbar-nav { + flex-direction: row; + } + + .navbar-expand-xxl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xxl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-xxl .navbar-nav-scroll { + overflow: visible; + } + + .navbar-expand-xxl .navbar-collapse { + display: flex !important; + -ms-flex-basis: auto; + flex-basis: auto; + } + + .navbar-expand-xxl .navbar-toggler { + display: none; + } + + .navbar-expand-xxl .offcanvas-header { + display: none; + } + + .navbar-expand-xxl .offcanvas { + position: inherit; + bottom: 0; + z-index: 1000; + -ms-flex-grow: 1; + flex-grow: 1; + visibility: visible !important; + background-color: transparent; + border-right: 0; + border-left: 0; + transition: none; + transform: none; + } + + .navbar-expand-xxl .offcanvas-top { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-xxl .offcanvas-bottom { + height: auto; + border-top: 0; + border-bottom: 0; + } + + .navbar-expand-xxl .offcanvas-body { + display: flex; + -ms-flex-grow: 0; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } + + .sticky-xxl-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +@media (max-width: 575.98px) { + .modal-fullscreen-sm-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-sm-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-header { + border-radius: 0; + } + + .modal-fullscreen-sm-down .modal-body { + overflow-y: auto; + } + + .modal-fullscreen-sm-down .modal-footer { + border-radius: 0; + } +} + +@media (max-width: 767.98px) { + .modal-fullscreen-md-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-md-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-header { + border-radius: 0; + } + + .modal-fullscreen-md-down .modal-body { + overflow-y: auto; + } + + .modal-fullscreen-md-down .modal-footer { + border-radius: 0; + } +} + +@media (max-width: 991.98px) { + .modal-fullscreen-lg-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-lg-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-header { + border-radius: 0; + } + + .modal-fullscreen-lg-down .modal-body { + overflow-y: auto; + } + + .modal-fullscreen-lg-down .modal-footer { + border-radius: 0; + } +} + +@media (max-width: 1199.98px) { + .modal-fullscreen-xl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-header { + border-radius: 0; + } + + .modal-fullscreen-xl-down .modal-body { + overflow-y: auto; + } + + .modal-fullscreen-xl-down .modal-footer { + border-radius: 0; + } +} + +@media (max-width: 1399.98px) { + .modal-fullscreen-xxl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + + .modal-fullscreen-xxl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-header { + border-radius: 0; + } + + .modal-fullscreen-xxl-down .modal-body { + overflow-y: auto; + } + + .modal-fullscreen-xxl-down .modal-footer { + border-radius: 0; + } +} + +@media (prefers-reduced-motion) { + .animation { + transition: none !important; + -webkit-animation: unset !important; + animation: unset !important; + } +} + +@media screen and (min-width: 320px) and (max-width: 820px) and (orientation: landscape) { + .datepicker-modal-container .datepicker-header { + height: 100%; + } + + .datepicker-modal-container .datepicker-date { + margin-top: 100px; + } + + .datepicker-modal-container .datepicker-day-cell { + width: 32x; + height: 32x; + } + + .datepicker-modal-container { + flex-direction: row; + width: 475px; + height: 360px; + } + + .datepicker-modal-container.datepicker-day-cell { + width: 36px; + height: 36px; + } +} + +@media screen and (min-width: 320px) and (max-width: 825px) and (orientation: landscape) { + .timepicker-elements { + flex-direction: row !important; + border-bottom-left-radius: 0.5rem; + min-width: auto; + min-height: auto; + overflow-y: auto; + } + + .timepicker-head { + border-top-right-radius: 0; + border-bottom-left-radius: 0; + padding: 10px; + padding-right: 10px !important; + height: auto; + min-height: 305px; + } + + .timepicker-head-content { + flex-direction: column; + } + + .timepicker-mode-wrapper { + justify-content: space-around !important; + flex-direction: row !important; + } + + .timepicker-current { + font-size: 3rem; + font-weight: 400; + } + + .timepicker-dot { + font-size: 3rem; + font-weight: 400; + } +} + +@-webkit-keyframes _spinner-grow { + 0% { + transform: scale(0); + } + + 50% { + opacity: 1; + transform: none; + } +} + +@keyframes _spinner-grow { + 0% { + transform: scale(0); + } + + 50% { + opacity: 1; + transform: none; + } +} + +@-webkit-keyframes _fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes _fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@-webkit-keyframes _fade-out { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + +@keyframes _fade-out { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} + +@-webkit-keyframes _fade-in-down { + from { + opacity: 0; + transform: translate3d(0, -100%, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes _fade-in-down { + from { + opacity: 0; + transform: translate3d(0, -100%, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _fade-in-left { + from { + opacity: 0; + transform: translate3d(-100%, 0, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes _fade-in-left { + from { + opacity: 0; + transform: translate3d(-100%, 0, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _fade-in-right { + from { + opacity: 0; + transform: translate3d(100%, 0, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes _fade-in-right { + from { + opacity: 0; + transform: translate3d(100%, 0, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _fade-in-up { + from { + opacity: 0; + transform: translate3d(0, 100%, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes _fade-in-up { + from { + opacity: 0; + transform: translate3d(0, 100%, 0); + } + + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _fade-out-down { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(0, 100%, 0); + } +} + +@keyframes _fade-out-down { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(0, 100%, 0); + } +} + +@-webkit-keyframes _fade-out-left { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes _fade-out-left { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(-100%, 0, 0); + } +} + +@-webkit-keyframes _fade-out-right { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(100%, 0, 0); + } +} + +@keyframes _fade-out-right { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(100%, 0, 0); + } +} + +@-webkit-keyframes _fade-out-up { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(0, -100%, 0); + } +} + +@keyframes _fade-out-up { + from { + opacity: 1; + } + + to { + opacity: 0; + transform: translate3d(0, -100%, 0); + } +} + +@-webkit-keyframes _slide-in-down { + from { + visibility: visible; + transform: translate3d(0, -100%, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes _slide-in-down { + from { + visibility: visible; + transform: translate3d(0, -100%, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _slide-in-left { + from { + visibility: visible; + transform: translate3d(-100%, 0, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes _slide-in-left { + from { + visibility: visible; + transform: translate3d(-100%, 0, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _slide-in-right { + from { + visibility: visible; + transform: translate3d(100%, 0, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes _slide-in-right { + from { + visibility: visible; + transform: translate3d(100%, 0, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _slide-in-up { + from { + visibility: visible; + transform: translate3d(0, 100%, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes _slide-in-up { + from { + visibility: visible; + transform: translate3d(0, 100%, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@-webkit-keyframes _slide-out-down { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(0, 100%, 0); + } +} + +@keyframes _slide-out-down { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(0, 100%, 0); + } +} + +@-webkit-keyframes _slide-out-left { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes _slide-out-left { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(-100%, 0, 0); + } +} + +@-webkit-keyframes _slide-out-right { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(100%, 0, 0); + } +} + +@keyframes _slide-out-right { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(100%, 0, 0); + } +} + +@-webkit-keyframes _slide-out-up { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(0, -100%, 0); + } +} + +@keyframes _slide-out-up { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(0, -100%, 0); + } +} + +@-webkit-keyframes _slide-down { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(0, 100%, 0); + } +} + +@keyframes _slide-down { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(0, 100%, 0); + } +} + +@-webkit-keyframes _slide-left { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(-100%, 0, 0); + } +} + +@keyframes _slide-left { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(-100%, 0, 0); + } +} + +@-webkit-keyframes _slide-right { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(100%, 0, 0); + } +} + +@keyframes _slide-right { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(100%, 0, 0); + } +} + +@-webkit-keyframes _slide-up { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(0, -100%, 0); + } +} + +@keyframes _slide-up { + from { + transform: translate3d(0, 0, 0); + } + + to { + transform: translate3d(0, -100%, 0); + } +} + +@-webkit-keyframes _zoom-in { + from { + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); + } + + 50% { + opacity: 1; + } +} + +@keyframes _zoom-in { + from { + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); + } + + 50% { + opacity: 1; + } +} + +@-webkit-keyframes _zoom-out { + from { + opacity: 1; + } + + 50% { + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); + } + + to { + opacity: 0; + } +} + +@keyframes _zoom-out { + from { + opacity: 1; + } + + 50% { + opacity: 0; + transform: scale3d(0.3, 0.3, 0.3); + } + + to { + opacity: 0; + } +} + +@-webkit-keyframes _tada { + from { + transform: scale3d(1, 1, 1); + } + + 10% { + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 20% { + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 30% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 50% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 70% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 90% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 60% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 80% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + to { + transform: scale3d(1, 1, 1); + } +} + +@keyframes _tada { + from { + transform: scale3d(1, 1, 1); + } + + 10% { + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 20% { + transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); + } + + 30% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 50% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 70% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 90% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); + } + + 40% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 60% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + 80% { + transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); + } + + to { + transform: scale3d(1, 1, 1); + } +} + +@-webkit-keyframes _pulse { + from { + transform: scale3d(1, 1, 1); + } + + 50% { + transform: scale3d(1.05, 1.05, 1.05); + } + + to { + transform: scale3d(1, 1, 1); + } +} + +@keyframes _pulse { + from { + transform: scale3d(1, 1, 1); + } + + 50% { + transform: scale3d(1.05, 1.05, 1.05); + } + + to { + transform: scale3d(1, 1, 1); + } +} + +@-webkit-keyframes _show-up-clock { + 0% { + opacity: 0; + transform: scale(0.7); + } + + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes _show-up-clock { + 0% { + opacity: 0; + transform: scale(0.7); + } + + to { + opacity: 1; + transform: scale(1); + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.visible { + visibility: visible; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.sticky { + position: -webkit-sticky; + position: sticky; +} + +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; +} + +.top-0 { + top: 0px; +} + +.right-0 { + right: 0px; +} + +.bottom-0 { + bottom: 0px; +} + +.top-5 { + top: 1.25rem; +} + +.right-5 { + right: 1.25rem; +} + +.z-10 { + z-index: 10; +} + +.m-auto { + margin: auto; +} + +.m-2 { + margin: 0.5rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-auto { + margin-top: auto; + margin-bottom: auto; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.ml-auto { + margin-left: auto; +} + +.mt-0 { + margin-top: 0px; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-5 { + margin-bottom: 1.25rem; +} + +.mb-10 { + margin-bottom: 2.5rem; +} + +.mt-10 { + margin-top: 2.5rem; +} + +.mt-8 { + margin-top: 2rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.ml-5 { + margin-left: 1.25rem; +} + +.ml-6 { + margin-left: 1.5rem; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.hidden { + display: none; +} + +.h-full { + height: 100%; +} + +.h-7 { + height: 1.75rem; +} + +.h-fit { + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; +} + +.h-10 { + height: 2.5rem; +} + +.h-12 { + height: 3rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-14 { + height: 3.5rem; +} + +.h-4 { + height: 1rem; +} + +.h-40 { + height: 10rem; +} + +.h-\[70vh\] { + height: 70vh; +} + +.h-\[50vh\] { + height: 50vh; +} + +.h-8 { + height: 2rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-screen { + height: 100vh; +} + +.h-16 { + height: 4rem; +} + +.max-h-\[70vh\] { + max-height: 70vh; +} + +.max-h-\[80vh\] { + max-height: 80vh; +} + +.max-h-60 { + max-height: 15rem; +} + +.min-h-\[60vh\] { + min-height: 60vh; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-full { + width: 100%; +} + +.w-7 { + width: 1.75rem; +} + +.w-11\/12 { + width: 91.666667%; +} + +.w-10 { + width: 2.5rem; +} + +.w-12 { + width: 3rem; +} + +.w-32 { + width: 8rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-14 { + width: 3.5rem; +} + +.w-4 { + width: 1rem; +} + +.w-8 { + width: 2rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-16 { + width: 4rem; +} + +.min-w-full { + min-width: 100%; +} + +.max-w-lg { + max-width: 32rem; +} + +.max-w-xs { + max-width: 20rem; +} + +.max-w-sm { + max-width: 24rem; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-auto { + flex: 1 1 auto; +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.cursor-pointer { + cursor: pointer; +} + +.resize { + resize: both; +} + +.list-none { + list-style-type: none; +} + +.appearance-none { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.content-center { + align-content: center; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-around { + justify-content: space-around; +} + +.justify-evenly { + justify-content: space-evenly; +} + +.gap-2 { + gap: 0.5rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-divide-opacity)); +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-x-auto { + overflow-x: auto; +} + +.overflow-y-auto { + overflow-y: auto; +} + +.overflow-x-hidden { + overflow-x: hidden; +} + +.overflow-y-scroll { + overflow-y: scroll; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.whitespace-pre-line { + white-space: pre-line; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-t-lg { + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} + +.border-2 { + border-width: 2px; +} + +.border { + border-width: 1px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} + +.border-white { + --tw-border-opacity: 1; + border-color: rgb(255 255 255 / var(--tw-border-opacity)); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.border-red-500 { + --tw-border-opacity: 1; + border-color: rgb(239 68 68 / var(--tw-border-opacity)); +} + +.bg-blue-500 { + --tw-bg-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); +} + +.bg-blue-900 { + --tw-bg-opacity: 1; + background-color: rgb(30 58 138 / var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.bg-blue-400 { + --tw-bg-opacity: 1; + background-color: rgb(96 165 250 / var(--tw-bg-opacity)); +} + +.bg-transparent { + background-color: transparent; +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(34 197 94 / var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)); +} + +.bg-gray-300 { + --tw-bg-opacity: 1; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)); +} + +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} + +.bg-slate-100 { + --tw-bg-opacity: 1; + background-color: rgb(241 245 249 / var(--tw-bg-opacity)); +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.bg-red-600 { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} + +.bg-slate-200 { + --tw-bg-opacity: 1; + background-color: rgb(226 232 240 / var(--tw-bg-opacity)); +} + +.bg-gray-800 { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-4 { + padding: 1rem; +} + +.p-2 { + padding: 0.5rem; +} + +.p-2\.5 { + padding: 0.625rem; +} + +.p-1\.5 { + padding: 0.375rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-5 { + padding: 1.25rem; +} + +.p-6 { + padding: 1.5rem; +} + +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.pr-6 { + padding-right: 1.5rem; +} + +.pb-6 { + padding-bottom: 1.5rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.pb-10 { + padding-bottom: 2.5rem; +} + +.pr-0 { + padding-right: 0px; +} + +.pr-2 { + padding-right: 0.5rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pt-6 { + padding-top: 1.5rem; +} + +.pb-8 { + padding-bottom: 2rem; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.align-baseline { + vertical-align: baseline; +} + +.align-text-bottom { + vertical-align: text-bottom; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-7xl { + font-size: 4.5rem; + line-height: 1; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-normal { + font-weight: 400; +} + +.uppercase { + text-transform: uppercase; +} + +.italic { + font-style: italic; +} + +.leading-6 { + line-height: 1.5rem; +} + +.leading-tight { + line-height: 1.25; +} + +.tracking-wider { + letter-spacing: 0.05em; +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-slate-500 { + --tw-text-opacity: 1; + color: rgb(100 116 139 / var(--tw-text-opacity)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.text-red-500 { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity)); +} + +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +.text-red-400 { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); +} + +.opacity-40 { + opacity: 0.4; +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-inner { + --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.outline { + outline-style: solid; +} + +.ring-blue-600 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity)); +} + +.ring-indigo-600 { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(79 70 229 / var(--tw-ring-opacity)); +} + +.ring-offset-2 { + --tw-ring-offset-width: 2px; +} + +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition { + transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-300 { + transition-duration: 300ms; +} + +.duration-150 { + transition-duration: 150ms; +} + +.ease-in-out { + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +@media screen and (max-width: 767px) { + .sidebar-holder { + width: 100%; + min-width: 200px; + max-width: 200px; + position: fixed; + top: 0; + left: 0; + } + + .page-header span { + margin-left: auto; + } +} + +.hover\:scale-105:hover { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:bg-blue-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.hover\:bg-green-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(22 163 74 / var(--tw-bg-opacity)); +} + +.hover\:bg-red-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-900:hover { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.hover\:text-gray-800:hover { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.hover\:text-gray-900:hover { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.hover\:text-blue-800:hover { + --tw-text-opacity: 1; + color: rgb(30 64 175 / var(--tw-text-opacity)); +} + +.hover\:shadow-lg:hover { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.focus\:bg-blue-700:focus { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); +} + +.focus\:bg-green-600:focus { + --tw-bg-opacity: 1; + background-color: rgb(22 163 74 / var(--tw-bg-opacity)); +} + +.focus\:bg-red-700:focus { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} + +.focus\:bg-gray-900:focus { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.focus\:shadow-lg:focus { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-0:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-gray-300:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); +} + +.active\:bg-blue-800:active { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity)); +} + +.active\:bg-green-700:active { + --tw-bg-opacity: 1; + background-color: rgb(21 128 61 / var(--tw-bg-opacity)); +} + +.active\:bg-red-800:active { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} + +.active\:bg-gray-900:active { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + +.active\:shadow-lg:active { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); + } + + .dark\:text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); + } + + .dark\:hover\:bg-gray-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); + } + + .dark\:hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + } +} + +@media (min-width: 640px) { + .sm\:ml-4 { + margin-left: 1rem; + } + + .sm\:flex { + display: flex; + } + + .sm\:w-1\/2 { + width: 50%; + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + + .sm\:text-left { + text-align: left; + } +} + +@media (min-width: 768px) { + .md\:h-52 { + height: 13rem; + } + + .md\:w-1\/2 { + width: 50%; + } +} + +@media (min-width: 1024px) { + .lg\:block { + display: block; + } + + .lg\:flex { + display: flex; + } + + .lg\:hidden { + display: none; + } + + .lg\:h-80 { + height: 20rem; + } + + .lg\:max-h-\[60vh\] { + max-height: 60vh; + } + + .lg\:w-1\/3 { + width: 33.333333%; + } + + .lg\:w-1\/2 { + width: 50%; + } + + .lg\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 1280px) { + .xl\:w-1\/4 { + width: 25%; + } + + .xl\:w-1\/2 { + width: 50%; + } +} diff --git a/src/pages/404/NotFoundPage.jsx b/src/pages/404/NotFoundPage.jsx new file mode 100644 index 0000000..7473bde --- /dev/null +++ b/src/pages/404/NotFoundPage.jsx @@ -0,0 +1,27 @@ +import Loader from "Components/Loader"; +import React from "react"; + +const NotFoundPage = () => { + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const interval = setTimeout(() => { + setLoading(false); + }, 5000); + // return () => clearInterval(interval); + }, []); + + return ( + <> + {loading ? ( + + ) : ( +
+ Not Found +
+ )} + + ); +}; + +export default NotFoundPage; diff --git a/src/pages/404/index.js b/src/pages/404/index.js new file mode 100644 index 0000000..3cb343e --- /dev/null +++ b/src/pages/404/index.js @@ -0,0 +1 @@ +export { default as NotFoundPage } from './NotFoundPage' \ No newline at end of file diff --git a/src/pages/admin/Add/AddAdminAccessoriesPage.jsx b/src/pages/admin/Add/AddAdminAccessoriesPage.jsx new file mode 100644 index 0000000..8fd9c45 --- /dev/null +++ b/src/pages/admin/Add/AddAdminAccessoriesPage.jsx @@ -0,0 +1,248 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "Utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; + +const AddAdminAccessoriesPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const schema = yup + .object({ + name: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + thumbnail: yup.string().required(), + top_view: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(AuthContext); + const [fileObj, setFileObj] = React.useState({}); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for (let item in fileObj) { + let formData = new FormData(); + formData.append("file", fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + + sdk.setTable("accessories"); + + const result = await sdk.callRestAPI( + { + name: data.name, + length: data.length, + width: data.width, + thumbnail: data.thumbnail, + top_view: data.top_view, + }, + "POST" + ); + if (!result.error) { + showToast(globalDispatch, "Added"); + navigate("/admin/accessories"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("name", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "accessories", + }, + }); + }, []); + + return ( +
+

Add Accessories

+
+
+ + +

{errors.name?.message}

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

{errors.width?.message}

+
+ +
+ + {fileObj["thumbnail"] ? ( + isImage(fileObj["thumbnail"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("thumbnail", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.thumbnail?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.thumbnail?.message} +

+
+ +
+ + {fileObj["top_view"] ? ( + isImage(fileObj["top_view"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("top_view", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.top_view?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.top_view?.message} +

+
+ + +
+
+ ); +}; + +export default AddAdminAccessoriesPage; diff --git a/src/pages/admin/Add/AddAdminAnalyticLogPage.jsx b/src/pages/admin/Add/AddAdminAnalyticLogPage.jsx new file mode 100644 index 0000000..094cbe0 --- /dev/null +++ b/src/pages/admin/Add/AddAdminAnalyticLogPage.jsx @@ -0,0 +1,269 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "Utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminAnalyticLogPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const schema = yup + .object({ + user_id: yup.number().required().positive().integer(), + url: yup.string().required(), + path: yup.string().required(), + hostname: yup.string().required(), + ip: yup.string().required(), + role: yup.string().required(), + browser: yup.string().required(), + country: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + + try { + for (let item in fileObj) { + let uploadResult = await sdk.upload(fileObj[item].file); + data[item] = uploadResult.url; + } + + sdk.setTable("analytic_log"); + + const result = await sdk.callRestAPI( + { + user_id: data.user_id, + url: data.url, + path: data.path, + hostname: data.hostname, + ip: data.ip, + role: data.role, + browser: data.browser, + country: data.country, + }, + "POST" + ); + if (!result.error) { + showToast(globalDispatch, "Added"); + navigate("/admin/analytic_log"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("user_id", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "analytic_log", + }, + }); + }, []); + + return ( +
+

Add AnalyticLog

+
+
+ + +

+ {errors.user_id?.message} +

+
+ +
+ + +

{errors.url?.message}

+
+ +
+ + +

{errors.path?.message}

+
+ +
+ + +

+ {errors.hostname?.message} +

+
+ +
+ + +

{errors.ip?.message}

+
+ +
+ + +

{errors.role?.message}

+
+ +
+ + +

+ {errors.browser?.message} +

+
+ +
+ + +

+ {errors.country?.message} +

+
+ + +
+
+ ); +}; + +export default AddAdminAnalyticLogPage; diff --git a/src/pages/admin/Add/AddAdminBoatLiftsPage.jsx b/src/pages/admin/Add/AddAdminBoatLiftsPage.jsx new file mode 100644 index 0000000..ee38c52 --- /dev/null +++ b/src/pages/admin/Add/AddAdminBoatLiftsPage.jsx @@ -0,0 +1,318 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +import { BoatLiftRange } from "Utils/constants"; + +const AddAdminBoatLiftsPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const schema = yup + .object({ + model: yup.string().required(), + weight_capacity: yup.number().required(), + lift_range: yup.number().required().positive().integer(), + no_of_cylinders: yup.number().required().positive().integer(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for (let item in fileObj) { + let formData = new FormData(); + formData.append("file", fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + + sdk.setTable("boat_lifts"); + + const result = await sdk.callRestAPI( + { + model: data.model, + weight_capacity: data.weight_capacity, + lift_range: data.lift_range, + no_of_cylinders: data.no_of_cylinders, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + }, + "POST" + ); + if (!result.error) { + showToast(globalDispatch, "Added"); + navigate("/admin/boat_lifts"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("model", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "boat_lifts", + }, + }); + }, []); + + return ( +
+

Add BoatLifts

+
+
+ + +

{errors.model?.message}

+
+ +
+ + +

+ {errors.weight_capacity?.message} +

+
+ +
+ + +

+ {errors.lift_range?.message} +

+
+ +
+ + +

+ {errors.no_of_cylinders?.message} +

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

{errors.width?.message}

+
+ +
+ + {fileObj["image"] ? ( + isImage(fileObj["image"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("image", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.image?.message ? "border-red-500" : "" + }`} + /> +

{errors.image?.message}

+
+ +
+ + {fileObj["top_view"] ? ( + isImage(fileObj["top_view"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("top_view", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.top_view?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.top_view?.message} +

+
+ + +
+
+ ); +}; + +export default AddAdminBoatLiftsPage; diff --git a/src/pages/admin/Add/AddAdminCmsPage.jsx b/src/pages/admin/Add/AddAdminCmsPage.jsx new file mode 100644 index 0000000..7430349 --- /dev/null +++ b/src/pages/admin/Add/AddAdminCmsPage.jsx @@ -0,0 +1,158 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "Utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import DynamicContentType from "Components/DynamicContentType"; + +const AddAdminCmsPage = () => { + const schema = yup + .object({ + page: yup.string().required(), + key: yup.string().required(), + type: yup.string().required(), + value: yup.string(), + }) + .required(); + + const selectType = [ + { key: "text", value: "Text" }, + { key: "image", value: "Image" }, + { key: "number", value: "Number" }, + { key: "kvp", value: "Key-Value Pair" }, + { key: "image-list", value: "Image List" }, + { key: "captioned-image-list", value: "Captioned Image List" }, + { key: "team-list", value: "Team List" }, + ]; + + const { dispatch } = React.useContext(GlobalContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [contentType, setContentType] = React.useState(selectType[0]?.key); + const [contentValue, setContentValue] = React.useState(''); + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + console.log(data); + try { + sdk.setTable("cms"); + + const result = await sdk.cmsAdd( + data.page, + data.key, + data.type, + contentValue + ); + if (!result.error) { + navigate("/admin/cms"); + showToast(globalDispatch, "Added"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("page", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "cms", + }, + }); + }, []); + + return ( +
+

Add CMS Content

+
+
+ + +
+
+ + +

{errors.key?.message}

+
+
+ + +
+ + + + +
+ ); +}; + +export default AddAdminCmsPage; + diff --git a/src/pages/admin/Add/AddAdminDealersPage.jsx b/src/pages/admin/Add/AddAdminDealersPage.jsx new file mode 100644 index 0000000..284a527 --- /dev/null +++ b/src/pages/admin/Add/AddAdminDealersPage.jsx @@ -0,0 +1,150 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +const AddAdminDealersPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + name: yup.string().required(), + address: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + + sdk.setTable( "dealers" ); + + const result = await sdk.callRestAPI( + { + + name: data.name, + address: data.address, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/dealers" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "name", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "dealers", + }, + } ); + }, [] ); + + return ( +
+

Add Dealers

+
+ + +
+ + +

+ { errors.name?.message } +

+
+ + +
+ + +

+ { errors.address?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminDealersPage; diff --git a/src/pages/admin/Add/AddAdminDocksPage.jsx b/src/pages/admin/Add/AddAdminDocksPage.jsx new file mode 100644 index 0000000..a63d030 --- /dev/null +++ b/src/pages/admin/Add/AddAdminDocksPage.jsx @@ -0,0 +1,266 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminDocksPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + category: yup.string().required(), + materials: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + + sdk.setTable( "docks" ); + + const result = await sdk.callRestAPI( + { + + category: data.category, + materials: data.materials, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/docks" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "category", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "docks", + }, + } ); + }, [] ); + + return ( +
+

Add Docks

+
+ + +
+ + +

+ { errors.category?.message } +

+
+ + +
+ + +

+ { errors.materials?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : ( + <> + ) } + + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+ + { fileObj[ "top_view" ] ? ( + isImage( fileObj[ "top_view" ] ) ? ( + ) : ( + <> + ) + ) : ( + <> + ) } + + previewImage( "top_view", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.top_view?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.top_view?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminDocksPage; diff --git a/src/pages/admin/Add/AddAdminEmailPage.jsx b/src/pages/admin/Add/AddAdminEmailPage.jsx new file mode 100644 index 0000000..30a8b50 --- /dev/null +++ b/src/pages/admin/Add/AddAdminEmailPage.jsx @@ -0,0 +1,162 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +const AddAdminEmailPage = () => { + const schema = yup + .object( { + slug: yup.string().required(), + subject: yup.string().required(), + html: yup.string().required(), + tag: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + sdk.setTable( "email" ); + + const result = await sdk.callRestAPI( + { + slug: data.slug, + subject: data.subject, + html: data.html, + tag: data.tag, + }, + "POST" + ); + if ( !result.error ) { + navigate( "/admin/email" ); + showToast( globalDispatch, "Added" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "subject", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "email", + }, + } ); + }, [] ); + + return ( +
+

Add Email

+
+
+ + +
+
+ + +

+ { errors.subject?.message } +

+
+
+ + +

{ errors.tag?.message }

+
+
+ + +

{ errors.html?.message }

+
+ +
+
+ ); +}; + +export default AddAdminEmailPage; diff --git a/src/pages/admin/Add/AddAdminInstructionsPage.jsx b/src/pages/admin/Add/AddAdminInstructionsPage.jsx new file mode 100644 index 0000000..c0227e0 --- /dev/null +++ b/src/pages/admin/Add/AddAdminInstructionsPage.jsx @@ -0,0 +1,131 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminInstructionsPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + content: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + + sdk.setTable( "instructions" ); + + const result = await sdk.callRestAPI( + { + + content: data.content, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/instructions" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "content", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "instructions", + }, + } ); + }, [] ); + + return ( +
+

Add Instructions

+
+ + +
+ + +

+ { errors.content?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminInstructionsPage; diff --git a/src/pages/admin/Add/AddAdminPhotoPage.jsx b/src/pages/admin/Add/AddAdminPhotoPage.jsx new file mode 100644 index 0000000..8c8c29a --- /dev/null +++ b/src/pages/admin/Add/AddAdminPhotoPage.jsx @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import Uppy from "@uppy/core"; +import XHRUpload from "@uppy/xhr-upload"; +import { Dashboard, useUppy } from "@uppy/react"; +import "@uppy/core/dist/style.css"; +import "@uppy/drag-drop/dist/style.css"; + +import { useNavigate } from "react-router"; +// import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const AddAdminPhotoPage = () => { + const { dispatch } = React.useContext( GlobalContext ); + const navigate = useNavigate(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const uppy = useUppy( () => { + let model = new Uppy(); + model.use( XHRUpload, { + id: "XHRUpload", + method: "post", + formData: true, + limit: 0, + fieldName: "file", + metaFields: [ "caption", "size" ], + headers: sdk.getHeader(), + endpoint: sdk.uploadUrl(), + } ); + + model.on( "file-added", ( file ) => { + model.setFileMeta( file.id, { + size: file.size, + caption: "", + } ); + } ); + + model.on( "upload-success", async ( file, response ) => { + const httpStatus = response.status; // HTTP status code + const responseBody = response.body; + console.log( "response", response ); + showToast( globalDispatch, "Uploaded" ); + navigate( "/admin/photos" ); + } ); + + model.on( "upload-error", ( file, error, response ) => { + const httpStatus = response.status; // HTTP status code + if ( httpStatus == 401 ) { + tokenExpireError( dispatch, "TOKEN_EXPIRED" ); + } + } ); + return model; + } ); + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "photos", + }, + } ); + }, [] ); + + return ( +
+

Add Photo

+ +
+ ); +}; + +export default AddAdminPhotoPage; diff --git a/src/pages/admin/Add/AddAdminQuotesMailRecipientsPage.jsx b/src/pages/admin/Add/AddAdminQuotesMailRecipientsPage.jsx new file mode 100644 index 0000000..7d458b7 --- /dev/null +++ b/src/pages/admin/Add/AddAdminQuotesMailRecipientsPage.jsx @@ -0,0 +1,132 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "Utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { isImage } from "Utils/utils"; + +const AddAdminQuotesMailRecipientsPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const schema = yup + .object({ + + email: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + + try { + for (let item in fileObj) { + let formData = new FormData(); + formData.append('file', fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + + sdk.setTable("quotes_mail_recipients"); + + const result = await sdk.callRestAPI( + { + + email: data.email, + }, + "POST" + ); + if (!result.error) { + showToast(globalDispatch, "Added"); + navigate("/admin/quotes_mail_recipients"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("email", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "quotes_mail_recipients", + }, + }); + }, []); + + return ( +
+

Add Quotes Mail Recipients

+
+ + +
+ + +

+ {errors.email?.message} +

+
+ + +
+
+ ); +}; + +export default AddAdminQuotesMailRecipientsPage; diff --git a/src/pages/admin/Add/AddAdminQuotesPage.jsx b/src/pages/admin/Add/AddAdminQuotesPage.jsx new file mode 100644 index 0000000..41455f7 --- /dev/null +++ b/src/pages/admin/Add/AddAdminQuotesPage.jsx @@ -0,0 +1,440 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminQuotesPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + first_name: yup.string().required(), + last_name: yup.string().required(), + email: yup.string().required(), + phone: yup.string().required(), + lake: yup.string().required(), + city: yup.string().required(), + has_dealer: yup.string().required(), + dealer_id: yup.string().required(), + dock_connection: yup.string().required(), + lake_bottom: yup.string().required(), + will_dock_boat: yup.string().required(), + comments: yup.string().required(), + dock_image: yup.string().required(), + selected_items: yup.string().required(), + country: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + + sdk.setTable( "quotes" ); + + const result = await sdk.callRestAPI( + { + + first_name: data.first_name, + last_name: data.last_name, + email: data.email, + phone: data.phone, + lake: data.lake, + city: data.city, + has_dealer: data.has_dealer, + dealer_id: data.dealer_id, + dock_connection: data.dock_connection, + lake_bottom: data.lake_bottom, + will_dock_boat: data.will_dock_boat, + comments: data.comments, + dock_image: data.dock_image, + selected_items: data.selected_items, + country: data.country, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/quotes" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "first_name", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "quotes", + }, + } ); + }, [] ); + + return ( +
+

Add Quotes

+
+ + +
+ + +

+ { errors.first_name?.message } +

+
+ + +
+ + +

+ { errors.last_name?.message } +

+
+ + +
+ + +

+ { errors.email?.message } +

+
+ + +
+ + +

+ { errors.phone?.message } +

+
+ + +
+ + +

+ { errors.lake?.message } +

+
+ + +
+ + +

+ { errors.city?.message } +

+
+ + +
+ + +

+ { errors.has_dealer?.message } +

+
+ + +
+ + +

+ { errors.dealer_id?.message } +

+
+ + +
+ + +

+ { errors.dock_connection?.message } +

+
+ + +
+ + +

+ { errors.lake_bottom?.message } +

+
+ + +
+ + +

+ { errors.will_dock_boat?.message } +

+
+ + +
+ + +

+ { errors.comments?.message } +

+
+ + +
+ + +

+ { errors.dock_image?.message } +

+
+ + +
+ + +

+ { errors.selected_items?.message } +

+
+ + +
+ + +

+ { errors.country?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminQuotesPage; diff --git a/src/pages/admin/Add/AddAdminRampsPage.jsx b/src/pages/admin/Add/AddAdminRampsPage.jsx new file mode 100644 index 0000000..e6575b6 --- /dev/null +++ b/src/pages/admin/Add/AddAdminRampsPage.jsx @@ -0,0 +1,275 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddRampsPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const schema = yup + .object({ + category: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + material: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for (let item in fileObj) { + let formData = new FormData(); + formData.append("file", fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + sdk.setTable("ramps"); + + const result = await sdk.callRestAPI( + { + category: data.category, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + material: data.material, + }, + "POST" + ); + if (!result.error) { + showToast(globalDispatch, "Added"); + navigate("/admin/ramps"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("category", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "ramps", + }, + }); + }, []); + + return ( +
+

Add Ramps

+
+
+ + +

+ {errors.category?.message} +

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

{errors.width?.message}

+
+ +
+ + {fileObj["image"] ? ( + isImage(fileObj["image"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("image", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.image?.message ? "border-red-500" : "" + }`} + /> +

{errors.image?.message}

+
+ +
+ + {fileObj["top_view"] ? ( + isImage(fileObj["top_view"]) ? ( + + ) : ( + <> + ) + ) : ( + <> + )} + + previewImage("top_view", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.top_view?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.top_view?.message} +

+
+ +
+ + +

+ {errors.material?.message} +

+
+ + +
+
+ ); +}; + +export default AddRampsPage; diff --git a/src/pages/admin/Add/AddAdminReferenceItemsPage.jsx b/src/pages/admin/Add/AddAdminReferenceItemsPage.jsx new file mode 100644 index 0000000..d4d19db --- /dev/null +++ b/src/pages/admin/Add/AddAdminReferenceItemsPage.jsx @@ -0,0 +1,202 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "../authContext"; +import { GlobalContext, showToast } from "../globalContext"; +import { isImage } from "Utils/utils"; + +const AddAdminReferenceItemsPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + name: yup.string().required(), + length: yup.string(), + width: yup.string(), + image: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + + sdk.setTable( "reference_items" ); + + const result = await sdk.callRestAPI( + { + + name: data.name, + length: data.length, + width: data.width, + image: data.image, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/reference_items" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "name", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "reference_items", + }, + } ); + }, [] ); + + return ( +
+

Add ReferenceItems

+
+ + +
+ + +

+ { errors.name?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : ( + <> + ) } + + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminReferenceItemsPage; diff --git a/src/pages/admin/Add/AddAdminUserPage.jsx b/src/pages/admin/Add/AddAdminUserPage.jsx new file mode 100644 index 0000000..df8e407 --- /dev/null +++ b/src/pages/admin/Add/AddAdminUserPage.jsx @@ -0,0 +1,151 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminUserPage = () => { + const schema = yup + .object( { + email: yup.string().email().required(), + password: yup.string().required(), + role: yup.string(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + // const selectRole = [ + // { name: "role", value: "admin" }, + // { name: "role", value: "employee" }, + // ]; + + /** + * + * + * Use /user/put to update user remaining info after registration success + * use /profile/put to update profile info + */ + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + const result = await sdk.createUser( data.email, data.password, 'admin' ); + if ( !result.error ) { + showToast( dispatch, "Added" ); + navigate( "/admin/user" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "email", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "users", + }, + } ); + }, [] ); + return ( +
+

Add User

+
+
+ + +

{ errors.email?.message }

+
+ {/*
+ + +
*/} +
+ + +

{ errors.password?.message }

+
+ +
+
+ ); +}; + +export default AddAdminUserPage; diff --git a/src/pages/admin/Add/AddAdminWedgesPage.jsx b/src/pages/admin/Add/AddAdminWedgesPage.jsx new file mode 100644 index 0000000..ee16822 --- /dev/null +++ b/src/pages/admin/Add/AddAdminWedgesPage.jsx @@ -0,0 +1,265 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import MkdSDK from "../utils/MkdSDK"; +import { useNavigate } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +const AddAdminWedgesPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const schema = yup + .object( { + + category: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + material: yup.string().required(), + } ) + .required(); + + const { dispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + const onSubmit = async ( data ) => { + let sdk = new MkdSDK(); + + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + sdk.setTable( "wedges" ); + + const result = await sdk.callRestAPI( + { + + category: data.category, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + material: data.material, + }, + "POST" + ); + if ( !result.error ) { + showToast( globalDispatch, "Added" ); + navigate( "/admin/wedges" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "category", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "wedges", + }, + } ); + }, [] ); + + return ( +
+

Add Wedges

+
+ + +
+ + +

+ { errors.category?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : ( + <> + ) } + + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+ + { fileObj[ "top_view" ] ? ( + isImage( fileObj[ "top_view" ] ) ? ( + ) : ( + <> + ) + ) : ( + <> + ) } + + previewImage( "top_view", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.top_view?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.top_view?.message } +

+
+ + +
+ + +

+ { errors.material?.message } +

+
+ + +
+
+ ); +}; + +export default AddAdminWedgesPage; diff --git a/src/pages/admin/Add/pg.js b/src/pages/admin/Add/pg.js new file mode 100644 index 0000000..6416cf3 --- /dev/null +++ b/src/pages/admin/Add/pg.js @@ -0,0 +1,96 @@ +const fs = require("fs"); +const path = require("path"); + +// Define the path to the directory where your files are located +const directoryPath = path.join(__dirname); +// console.log( __dirname ) +// Read the contents of the directory +fs.readdir(directoryPath, (err, files) => { + if (err) { + console.error(`Error reading directory: ${err}`); + return; + } + + // Filter the list of files to only include files with the ".jsx" extension + const jsxFiles = files.filter((file) => path.extname(file) === ".jsx"); + console.log(jsxFiles.length); + // Read the contents of each file + jsxFiles.forEach((file, i) => { + // console.log( `import {${ path.basename( file, ".jsx" ) }Template} from './pages/ecomAdmin/${ path.basename( file, '.jsx' ) }'` ) + fs.readFile(path.join(directoryPath, file), "utf-8", (err, contents) => { + // console.log( i ) + if (err) { + console.error(`Error reading file ${file}: ${err}`); + return; + } + + // if ( file.includes( "AdminEcomApiKeyListPage" ) ) { + // const search = `export default ${ path.basename( file, ".jsx" ) };` + // let newData = JSON.stringify( contents ) + // newData.replace( search, `${ search }\`` ) + // console.log( newData ) + // fs.writeFile( path.join( directoryPath, "test" ), newData, "utf-8", err => { + // if ( err ) throw err; + // console.log( `File ${ file } has been modified and saved.` ); + // } ); + + // console.log( newData.match( search ) ) + // } + // "../utils/MkdSDK"; + // "../authContext"; + // "../globalContext"; + //'react-quill'; + // "../utils/utils"; + // "../components/AddButton" + // "../components/ExportButton" + // "../components/PaginationBar"; + + let newData = contents; + // newData = newData.replace("../utils/MkdSDK", "Utils/MkdSDK"); + // newData = newData.replace("../authContext", "Src/authContext"); + // newData = newData.replace("../globalContext", "Src/globalContext"); + // newData = newData.replace("../utils/utils", "Utils/utils"); + newData = newData.replace( + "../../components/AddButton", + "Components/AddButton" + ); + newData = newData.replace( + "../../components/ExportButton", + "Components/ExportButton" + ); + newData = newData.replace( + "../../components/PaginationBar", + "Components/PaginationBar" + ); + newData = newData.replace( + "../components/AddButton", + "Components/AddButton" + ); + newData = newData.replace( + "../components/ExportButton", + "Components/ExportButton" + ); + newData = newData.replace( + "../components/PaginationBar", + "Components/PaginationBar" + ); + newData = newData.replace( + "../../components/DynamicContentType", + "Components/DynamicContentType" + ); + newData = newData.replace("../../globalContext", "Src/globalContext"); + newData = newData.replace("../Src/globalContext", "Src/globalContext"); + newData = newData.replace("../Src/authContext", "Src/authContext"); + newData = newData.replace("../../authContext", "Src/authContext"); + newData = newData.replace("../../utils/MkdSDK", "Utils/MkdSDK"); + newData = newData.replace("../../utils/utils", "Utils/utils"); + // save the new content to the same file + fs.writeFile(path.join(directoryPath, file), newData, "utf-8", (err) => { + if (err) throw err; + console.log(`File ${file} has been modified and saved.`); + }); + + console.log(`Contents of file ${file}:`); + }); + }); +}); diff --git a/src/pages/admin/AdminDashboardPage.jsx b/src/pages/admin/AdminDashboardPage.jsx new file mode 100644 index 0000000..56be8b3 --- /dev/null +++ b/src/pages/admin/AdminDashboardPage.jsx @@ -0,0 +1,82 @@ +import React from "react"; +import { AuthContext } from "../../authContext"; +import { GlobalContext } from "../../globalContext"; +import { + boat_lifts, + dealers, + wedges, + docks, + accessories, + ramps +} from "../../data"; +import MkdSDK from "Utils/MkdSDK"; + +const allData = [ + // { + // table: "dealers", + // data: dealers.model + // }, + { + table: "wedges", + data: wedges.model + }, + { + table: "docks", + data: docks.model + }, + { + table: "accessories", + data: accessories.model + }, + { + table: "ramps", + data: ramps.model + }, + { + table: "boat_lifts", + data: boat_lifts.model + } +]; + +const AdminDashboardPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const seedData = async () => { + const sdk = new MkdSDK(); + const promise = allData?.map(async (item) => { + sdk.setTable(item.table); + + const promise = item.data?.map(async (data) => + sdk.callRestAPI(data, "POST") + ); + return Promise.all(promise); + }); + await Promise.all(promise); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "admin" + } + }); + }, []); + return ( + <> +
+ Dashboard{" "} + {/* */} +
+ + ); +}; + +export default AdminDashboardPage; diff --git a/src/pages/admin/Auth/AdminForgotPage.jsx b/src/pages/admin/Auth/AdminForgotPage.jsx new file mode 100644 index 0000000..6b9020b --- /dev/null +++ b/src/pages/admin/Auth/AdminForgotPage.jsx @@ -0,0 +1,107 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; + +const AdminForgotPage = () => { + const schema = yup + .object({ + email: yup.string().email().required(), + }) + .required(); + + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const { dispatch } = React.useContext(GlobalContext); + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + try { + const result = await sdk.forgot(data.email); + + if (!result.error) { + showToast(dispatch, "Reset Code Sent"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("email", { + type: "manual", + message: error.message, + }); + } + }; + + return ( + <> +
+
+
+ + +

+ {errors.email?.message} +

+
+ +
+ + + Login? + +
+
+

+ © {new Date().getFullYear()} manaknightdigital inc. All rights + reserved. +

+
+ + ); +}; + +export default AdminForgotPage; diff --git a/src/pages/admin/Auth/AdminLoginPage.jsx b/src/pages/admin/Auth/AdminLoginPage.jsx new file mode 100644 index 0000000..1e7396a --- /dev/null +++ b/src/pages/admin/Auth/AdminLoginPage.jsx @@ -0,0 +1,126 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AuthContext } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +const AdminLoginPage = () => { + const schema = yup + .object({ + email: yup.string().email().required(), + password: yup.string().required(), + }) + .required(); + + const { dispatch } = React.useContext(AuthContext); + const navigate = useNavigate(); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + try { + const result = await sdk.login(data.email, data.password, "admin"); + if (!result.error) { + dispatch({ + type: "LOGIN", + payload: result, + }); + navigate("/admin/dashboard"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("email", { + type: "manual", + message: error.message, + }); + } + }; + + return ( +
+
+
+ + +

{errors.email?.message}

+
+ +
+ + +

+ {errors.password?.message} +

+
+
+ + + Forgot Password? + +
+
+

+ © {new Date().getFullYear()} manaknightdigital inc. All rights + reserved. +

+
+ ); +}; + +export default AdminLoginPage; diff --git a/src/pages/admin/Auth/AdminProfilePage.jsx b/src/pages/admin/Auth/AdminProfilePage.jsx new file mode 100644 index 0000000..9539215 --- /dev/null +++ b/src/pages/admin/Auth/AdminProfilePage.jsx @@ -0,0 +1,154 @@ +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const AdminProfilePage = () => { + const schema = yup + .object({ + email: yup.string().email().required(), + }) + .required(); + + const { dispatch } = React.useContext(GlobalContext); + const [oldEmail, setOldEmail] = useState(""); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "profile", + }, + }); + + (async function () { + try { + const result = await sdk.getProfile(); + setValue("email", result.email); + setOldEmail(result.email); + } catch (error) { + console.log("Error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + + const onSubmit = async (data) => { + try { + if (oldEmail !== data.email) { + const emailresult = await sdk.updateEmail(data.email); + if (!emailresult.error) { + showToast(dispatch, "Email Updated", 1000); + } else { + if (emailresult.validation) { + const keys = Object.keys(emailresult.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: emailresult.validation[field], + }); + } + } + } + } + + if (data.password.length > 0) { + const passwordresult = await sdk.updatePassword(data.password); + if (!passwordresult.error) { + showToast(dispatch, "Password Updated", 2000); + } else { + if (passwordresult.validation) { + const keys = Object.keys(passwordresult.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: passwordresult.validation[field], + }); + } + } + } + } + } catch (error) { + console.log("Error", error); + setError("email", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + return ( + <> +
+
+

Edit Profile

+
+
+ + +

+ {errors.email?.message} +

+
+
+ + +

+ {errors.password?.message} +

+
+
+ +
+
+
+
+ + ); +}; + +export default AdminProfilePage; diff --git a/src/pages/admin/Auth/AdminResetPage.jsx b/src/pages/admin/Auth/AdminResetPage.jsx new file mode 100644 index 0000000..f738dd5 --- /dev/null +++ b/src/pages/admin/Auth/AdminResetPage.jsx @@ -0,0 +1,156 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AuthContext } from "Src/authContext"; +import { showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; + +const AdminResetPage = () => { + const { dispatch } = React.useContext(AuthContext); + const search = window.location.search; + const params = new URLSearchParams(search); + const token = params.get("token"); + + const schema = yup + .object({ + code: yup.string().required(), + password: yup.string().required(), + confirmPassword: yup + .string() + .oneOf([yup.ref("password"), null], "Passwords must match"), + }) + .required(); + + const navigate = useNavigate(); + + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const onSubmit = async (data) => { + let sdk = new MkdSDK(); + try { + const result = await sdk.reset(token, data.code, data.password); + if (!result.error) { + showToast(dispatch, "Password Reset"); + setTimeout(() => { + navigate("/admin/login"); + }, 2000); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("code", { + type: "manual", + message: error.message, + }); + } + }; + + return ( + <> +
+
+
+ + +

+ {errors.code?.message} +

+
+
+ + +

+ {errors.password?.message} +

+
+
+ + +

+ {errors.confirmPassword?.message} +

+
+
+ + + Login? + +
+
+

+ © {new Date().getFullYear()} manaknightdigital inc. All rights + reserved. +

+
+ + ); +}; + +export default AdminResetPage; diff --git a/src/pages/admin/Edit/EditAdminAccessoriesPage.jsx b/src/pages/admin/Edit/EditAdminAccessoriesPage.jsx new file mode 100644 index 0000000..bfc8323 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminAccessoriesPage.jsx @@ -0,0 +1,278 @@ +import React, { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminAccessoriesPage = () => { + const { dispatch } = React.useContext(AuthContext); + const schema = yup + .object({ + name: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + thumbnail: yup.string().required(), + top_view: yup.string().required(), + }) + .required(); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + const navigate = useNavigate(); + const [name, setName] = useState(""); + const [length, setLength] = useState(0); + const [width, setWidth] = useState(0); + const [thumbnail, setThumbnail] = useState(""); + const [top_view, setTopView] = useState(""); + const [id, setId] = useState(0); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const params = useParams(); + + useEffect(function () { + (async function () { + try { + sdk.setTable("accessories"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setValue("name", result.model.name); + setValue("length", result.model.length); + setValue("width", result.model.width); + setValue("thumbnail", result.model.thumbnail); + setValue("top_view", result.model.top_view); + + setName(result.model.name); + setLength(result.model.length); + setWidth(result.model.width); + setThumbnail(result.model.thumbnail); + setTopView(result.model.top_view); + setId(result.model.id); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for (let item in fileObj) { + let formData = new FormData(); + formData.append("file", fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + name: data.name, + length: data.length, + width: data.width, + thumbnail: data.thumbnail, + top_view: data.top_view, + }, + "PUT" + ); + + if (!result.error) { + showToast(globalDispatch, "Updated"); + navigate("/admin/accessories"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("name", { + type: "manual", + message: error.message, + }); + } + }; + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "accessories", + }, + }); + }, []); + + return ( +
+

Edit Accessories

+
+
+ + +

{errors.name?.message}

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

{errors.width?.message}

+
+ +
+ + {fileObj["thumbnail"] ? ( + isImage(fileObj["thumbnail"]) ? ( + + ) : ( + <> + ) + ) : thumbnail ? ( + + ) : null} + + previewImage("thumbnail", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.thumbnail?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.thumbnail?.message} +

+
+ +
+ + {fileObj["top_view"] ? ( + isImage(fileObj["top_view"]) ? ( + + ) : ( + <> + ) + ) : top_view ? ( + + ) : null} + + previewImage("top_view", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.top_view?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.top_view?.message} +

+
+ + +
+
+ ); +}; + +export default EditAdminAccessoriesPage; diff --git a/src/pages/admin/Edit/EditAdminAnalyticLogPage.jsx b/src/pages/admin/Edit/EditAdminAnalyticLogPage.jsx new file mode 100644 index 0000000..759af82 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminAnalyticLogPage.jsx @@ -0,0 +1,315 @@ +import React, { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; + +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminAnalyticLogPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + user_id: yup.number().required().positive().integer(), + url: yup.string().required(), + path: yup.string().required(), + hostname: yup.string().required(), + ip: yup.string().required(), + role: yup.string().required(), + browser: yup.string().required(), + country: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ user_id, setUserId ] = useState( 0 ); const [ url, setUrl ] = useState( '' ); const [ path, setPath ] = useState( '' ); const [ hostname, setHostname ] = useState( '' ); const [ ip, setIp ] = useState( '' ); const [ role, setRole ] = useState( '' ); const [ browser, setBrowser ] = useState( '' ); const [ country, setCountry ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "analytic_log" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'user_id', result.model.user_id ); + setValue( 'url', result.model.url ); + setValue( 'path', result.model.path ); + setValue( 'hostname', result.model.hostname ); + setValue( 'ip', result.model.ip ); + setValue( 'role', result.model.role ); + setValue( 'browser', result.model.browser ); + setValue( 'country', result.model.country ); + + + setUserId( result.model.user_id ); + setUrl( result.model.url ); + setPath( result.model.path ); + setHostname( result.model.hostname ); + setIp( result.model.ip ); + setRole( result.model.role ); + setBrowser( result.model.browser ); + setCountry( result.model.country ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + user_id: data.user_id, + url: data.url, + path: data.path, + hostname: data.hostname, + ip: data.ip, + role: data.role, + browser: data.browser, + country: data.country, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/analytic_log" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "user_id", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "analytic_log", + }, + } ); + }, [] ); + + return ( +
+

Edit AnalyticLog

+
+ + +
+ + +

+ { errors.user_id?.message } +

+
+ + +
+ + +

+ { errors.url?.message } +

+
+ + +
+ + +

+ { errors.path?.message } +

+
+ + +
+ + +

+ { errors.hostname?.message } +

+
+ + +
+ + +

+ { errors.ip?.message } +

+
+ + +
+ + +

+ { errors.role?.message } +

+
+ + +
+ + +

+ { errors.browser?.message } +

+
+ + +
+ + +

+ { errors.country?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminAnalyticLogPage; diff --git a/src/pages/admin/Edit/EditAdminBoatLiftsPage.jsx b/src/pages/admin/Edit/EditAdminBoatLiftsPage.jsx new file mode 100644 index 0000000..79abdcf --- /dev/null +++ b/src/pages/admin/Edit/EditAdminBoatLiftsPage.jsx @@ -0,0 +1,348 @@ +import React, { useEffect, useState } from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +import { BoatLiftRange } from "Utils/constants"; + +let sdk = new MkdSDK(); + +const EditAdminBoatLiftsPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + model: yup.string().required(), + weight_capacity: yup.number().required(), + lift_range: yup.number().required().positive().integer(), + no_of_cylinders: yup.number().required().positive().integer(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ model, setModel ] = useState( '' ); const [ weight_capacity, setWeightCapacity ] = useState( 0 ); const [ lift_range, setLiftRange ] = useState( 0 ); const [ no_of_cylinders, setNoOfCylinders ] = useState( 0 ); const [ length, setLength ] = useState( 0 ); const [ width, setWidth ] = useState( 0 ); const [ image, setImage ] = useState( '' ); const [ top_view, setTopView ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "boat_lifts" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'model', result.model.model ); + setValue( 'weight_capacity', result.model.weight_capacity ); + setValue( 'lift_range', result.model.lift_range ); + setValue( 'no_of_cylinders', result.model.no_of_cylinders ); + setValue( 'length', result.model.length ); + setValue( 'width', result.model.width ); + setValue( 'image', result.model.image ); + setValue( 'top_view', result.model.top_view ); + + + setModel( result.model.model ); + setWeightCapacity( result.model.weight_capacity ); + setLiftRange( result.model.lift_range ); + setNoOfCylinders( result.model.no_of_cylinders ); + setLength( result.model.length ); + setWidth( result.model.width ); + setImage( result.model.image ); + setTopView( result.model.top_view ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + model: data.model, + weight_capacity: data.weight_capacity, + lift_range: data.lift_range, + no_of_cylinders: data.no_of_cylinders, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/boat_lifts" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "model", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "boat_lifts", + }, + } ); + }, [] ); + + return ( +
+

Edit BoatLifts

+
+ + +
+ + +

+ { errors.model?.message } +

+
+ + +
+ + +

+ { errors.weight_capacity?.message } +

+
+ + +
+ + +

+ { errors.lift_range?.message } +

+
+ + +
+ + +

+ { errors.no_of_cylinders?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : image ? ( + + ) : null } + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+ + { fileObj[ "top_view" ] ? ( + isImage( fileObj[ "top_view" ] ) ? ( + ) : ( + <> + ) + ) : top_view ? ( + + ) : null } + + previewImage( "top_view", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.top_view?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.top_view?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminBoatLiftsPage; diff --git a/src/pages/admin/Edit/EditAdminCmsPage.jsx b/src/pages/admin/Edit/EditAdminCmsPage.jsx new file mode 100644 index 0000000..1f06bdf --- /dev/null +++ b/src/pages/admin/Edit/EditAdminCmsPage.jsx @@ -0,0 +1,182 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "Utils/MkdSDK"; +import { useNavigate, useParams } from "react-router-dom"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import DynamicContentType from "Components/DynamicContentType"; + +let sdk = new MkdSDK(); + +const EditAdminCmsPage = () => { + const schema = yup + .object({ + page: yup.string().required(), + key: yup.string().required(), + type: yup.string().required(), + value: yup.string(), + }) + .required(); + + const selectType = [ + { key: "text", value: "Text" }, + { key: "image", value: "Image" }, + { key: "number", value: "Number" }, + { key: "kvp", value: "Key-Value Pair" }, + { key: "image-list", value: "Image List" }, + { key: "captioned-image-list", value: "Captioned Image List" }, + { key: "team-list", value: "Team List" }, + ]; + + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const navigate = useNavigate(); + const [id, setId] = useState(0); + const [contentType, setContentType] = React.useState(selectType[0]?.key); + const [contentValue, setContentValue] = React.useState(''); + + + + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const params = useParams(); + + useEffect(function () { + globalDispatch({ + type: "SETPATH", + payload: { + path: "cms", + }, + }); + + (async function () { + try { + sdk.setTable("cms"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setValue("page", result.model.page); + setValue("key", result.model.content_key); + setValue("type", result.model.content_type); + setValue("value", result.model.content_value); + setContentType(result.model.content_type); + setContentValue(result.model.content_value); + setId(result.model.id); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + + const onSubmit = async (data) => { + try { + const result = await sdk.cmsEdit( + id, + data.page, + data.key, + data.type, + contentValue + ); + + if (!result.error) { + showToast(globalDispatch, "Updated"); + navigate("/admin/cms"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("page", { + type: "manual", + message: error.message, + }); + tokenExpireError(dispatch, error.message); + } + }; + + return ( +
+

Edit CMS Content

+
+
+ + +
+
+ + +

{errors.key?.message}

+
+
+ + +
+ + + + +
+ ); +}; + +export default EditAdminCmsPage; diff --git a/src/pages/admin/Edit/EditAdminDealersPage.jsx b/src/pages/admin/Edit/EditAdminDealersPage.jsx new file mode 100644 index 0000000..c1c09fb --- /dev/null +++ b/src/pages/admin/Edit/EditAdminDealersPage.jsx @@ -0,0 +1,173 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminDealersPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + name: yup.string().required(), + address: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ name, setName ] = useState( '' ); const [ address, setAddress ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "dealers" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'name', result.model.name ); + setValue( 'address', result.model.address ); + + + setName( result.model.name ); + setAddress( result.model.address ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + name: data.name, + address: data.address, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/dealers" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "name", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "dealers", + }, + } ); + }, [] ); + + return ( +
+

Edit Dealers

+
+ + +
+ + +

+ { errors.name?.message } +

+
+ + +
+ + +

+ { errors.address?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminDealersPage; diff --git a/src/pages/admin/Edit/EditAdminDocksPage.jsx b/src/pages/admin/Edit/EditAdminDocksPage.jsx new file mode 100644 index 0000000..483527c --- /dev/null +++ b/src/pages/admin/Edit/EditAdminDocksPage.jsx @@ -0,0 +1,297 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminDocksPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + category: yup.string().required(), + materials: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ category, setCategory ] = useState( 0 ); const [ materials, setMaterials ] = useState( 0 ); const [ length, setLength ] = useState( 0 ); const [ width, setWidth ] = useState( 0 ); const [ image, setImage ] = useState( '' ); const [ top_view, setTopView ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "docks" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'category', result.model.category ); + setValue( 'materials', result.model.materials ); + setValue( 'length', result.model.length ); + setValue( 'width', result.model.width ); + setValue( 'image', result.model.image ); + setValue( 'top_view', result.model.top_view ); + + + setCategory( result.model.category ); + setMaterials( result.model.materials ); + setLength( result.model.length ); + setWidth( result.model.width ); + setImage( result.model.image ); + setTopView( result.model.top_view ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + + const result = await sdk.callRestAPI( + { + id: id, + category: data.category, + materials: data.materials, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/docks" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "category", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "docks", + }, + } ); + }, [] ); + + return ( +
+

Edit Docks

+
+ + +
+ + +

+ { errors.category?.message } +

+
+ + +
+ + +

+ { errors.materials?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : image ? ( + + ) : null } + + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+ + { fileObj[ "top_view" ] ? ( + isImage( fileObj[ "top_view" ] ) ? ( + ) : ( + <> + ) + ) : top_view ? ( + + ) : null } + + previewImage( "top_view", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.top_view?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.top_view?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminDocksPage; diff --git a/src/pages/admin/Edit/EditAdminEmailPage.jsx b/src/pages/admin/Edit/EditAdminEmailPage.jsx new file mode 100644 index 0000000..4707c1b --- /dev/null +++ b/src/pages/admin/Edit/EditAdminEmailPage.jsx @@ -0,0 +1,176 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const EditAdminEmailPage = () => { + const schema = yup + .object( { + subject: yup.string().required(), + html: yup.string().required(), + tag: yup.string().required(), + } ) + .required(); + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const navigate = useNavigate(); + const [ id, setId ] = useState( 0 ); + const [ slug, setSlug ] = useState( "" ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + globalDispatch( { + type: "SETPATH", + payload: { + path: "email", + }, + } ); + + ( async function () { + try { + sdk.setTable( "email" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + setValue( "subject", result.model.subject ); + setValue( "html", result.model.html ); + setValue( "tag", result.model.tag ); + setSlug( result.model.slug ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const onSubmit = async ( data ) => { + try { + const result = await sdk.callRestAPI( + { id, slug, subject: data.subject, html: data.html, tag: data.tag }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/email" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "html", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + return ( +
+

Edit Email

+
+
+ + +
+
+ + +

+ { errors.subject?.message } +

+
+
+ + +

{ errors.tag?.message }

+
+
+ + +

{ errors.html?.message }

+
+ +
+
+ ); +}; + +export default EditAdminEmailPage; diff --git a/src/pages/admin/Edit/EditAdminInstructionsPage.jsx b/src/pages/admin/Edit/EditAdminInstructionsPage.jsx new file mode 100644 index 0000000..7c74028 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminInstructionsPage.jsx @@ -0,0 +1,151 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminInstructionsPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + content: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ content, setContent ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "instructions" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'content', result.model.content ); + + + setContent( result.model.content ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + for ( let item in fileObj ) { + let uploadResult = await sdk.upload( fileObj[ item ].file ); + data[ item ] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + content: data.content, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/instructions" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "content", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "instructions", + }, + } ); + }, [] ); + + return ( +
+

Edit Instructions

+
+ + +
+ + +

+ { errors.content?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminInstructionsPage; diff --git a/src/pages/admin/Edit/EditAdminQuotesMailRecipientsPage.jsx b/src/pages/admin/Edit/EditAdminQuotesMailRecipientsPage.jsx new file mode 100644 index 0000000..d3d8fa8 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminQuotesMailRecipientsPage.jsx @@ -0,0 +1,152 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "Utils/MkdSDK"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { useNavigate, useParams } from "react-router-dom"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminQuotesMailRecipientsPage = () => { + const { dispatch } = React.useContext(AuthContext); + const schema = yup + .object({ + + email: yup.string().required(), + }) + .required(); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + const navigate = useNavigate(); +const [email, setEmail] = useState(''); + const [id, setId] = useState(0); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const params = useParams(); + + useEffect(function () { + (async function () { + try { + sdk.setTable("quotes_mail_recipients"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + + setValue('email', result.model.email); + + + setEmail(result.model.email); + setId(result.model.id); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + + const onSubmit = async (data) => { + try { + for (let item in fileObj) { + let formData = new FormData(); + formData.append('file', fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + const result = await sdk.callRestAPI( + { + id: id, + email: data.email, + }, + "PUT" + ); + + if (!result.error) { + showToast(globalDispatch, "Updated"); + navigate("/admin/quotes_mail_recipients"); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + } + } catch (error) { + console.log("Error", error); + setError("email", { + type: "manual", + message: error.message, + }); + } + }; + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "quotes_mail_recipients", + }, + }); + }, []); + + return ( +
+

Edit Quotes Mail Recipients

+
+ + +
+ + +

+ {errors.email?.message} +

+
+ + +
+
+ ); +}; + +export default EditAdminQuotesMailRecipientsPage; diff --git a/src/pages/admin/Edit/EditAdminQuotesPage.jsx b/src/pages/admin/Edit/EditAdminQuotesPage.jsx new file mode 100644 index 0000000..5e9b346 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminQuotesPage.jsx @@ -0,0 +1,489 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const EditAdminQuotesPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + first_name: yup.string().required(), + last_name: yup.string().required(), + email: yup.string().required(), + phone: yup.string().required(), + lake: yup.string().required(), + city: yup.string().required(), + has_dealer: yup.string().required(), + dealer_id: yup.string().required(), + dock_connection: yup.string().required(), + lake_bottom: yup.string().required(), + will_dock_boat: yup.string().required(), + comments: yup.string().required(), + dock_image: yup.string().required(), + selected_items: yup.string().required(), + country: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ first_name, setFirstName ] = useState( '' ); const [ last_name, setLastName ] = useState( '' ); const [ email, setEmail ] = useState( '' ); const [ phone, setPhone ] = useState( '' ); const [ lake, setLake ] = useState( '' ); const [ city, setCity ] = useState( '' ); const [ has_dealer, setHasDealer ] = useState( 0 ); const [ dealer_id, setDealerId ] = useState( 0 ); const [ dock_connection, setDockConnection ] = useState( 0 ); const [ lake_bottom, setLakeBottom ] = useState( 0 ); const [ will_dock_boat, setWillDockBoat ] = useState( 0 ); const [ comments, setComments ] = useState( '' ); const [ dock_image, setDockImage ] = useState( '' ); const [ selected_items, setSelectedItems ] = useState( '' ); const [ country, setCountry ] = useState( '' ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "quotes" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'first_name', result.model.first_name ); + setValue( 'last_name', result.model.last_name ); + setValue( 'email', result.model.email ); + setValue( 'phone', result.model.phone ); + setValue( 'lake', result.model.lake ); + setValue( 'city', result.model.city ); + setValue( 'has_dealer', result.model.has_dealer ); + setValue( 'dealer_id', result.model.dealer_id ); + setValue( 'dock_connection', result.model.dock_connection ); + setValue( 'lake_bottom', result.model.lake_bottom ); + setValue( 'will_dock_boat', result.model.will_dock_boat ); + setValue( 'comments', result.model.comments ); + setValue( 'dock_image', result.model.dock_image ); + setValue( 'selected_items', result.model.selected_items ); + setValue( 'country', result.model.country ); + + + setFirstName( result.model.first_name ); + setLastName( result.model.last_name ); + setEmail( result.model.email ); + setPhone( result.model.phone ); + setLake( result.model.lake ); + setCity( result.model.city ); + setHasDealer( result.model.has_dealer ); + setDealerId( result.model.dealer_id ); + setDockConnection( result.model.dock_connection ); + setLakeBottom( result.model.lake_bottom ); + setWillDockBoat( result.model.will_dock_boat ); + setComments( result.model.comments ); + setDockImage( result.model.dock_image ); + setSelectedItems( result.model.selected_items ); + setCountry( result.model.country ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + + const result = await sdk.callRestAPI( + { + id: id, + first_name: data.first_name, + last_name: data.last_name, + email: data.email, + phone: data.phone, + lake: data.lake, + city: data.city, + has_dealer: data.has_dealer, + dealer_id: data.dealer_id, + dock_connection: data.dock_connection, + lake_bottom: data.lake_bottom, + will_dock_boat: data.will_dock_boat, + comments: data.comments, + dock_image: data.dock_image, + selected_items: data.selected_items, + country: data.country, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/quotes" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "first_name", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "quotes", + }, + } ); + }, [] ); + + return ( +
+

Edit Quotes

+
+ + +
+ + +

+ { errors.first_name?.message } +

+
+ + +
+ + +

+ { errors.last_name?.message } +

+
+ + +
+ + +

+ { errors.email?.message } +

+
+ + +
+ + +

+ { errors.phone?.message } +

+
+ + +
+ + +

+ { errors.lake?.message } +

+
+ + +
+ + +

+ { errors.city?.message } +

+
+ + +
+ + +

+ { errors.has_dealer?.message } +

+
+ + +
+ + +

+ { errors.dealer_id?.message } +

+
+ + +
+ + +

+ { errors.dock_connection?.message } +

+
+ + +
+ + +

+ { errors.lake_bottom?.message } +

+
+ + +
+ + +

+ { errors.will_dock_boat?.message } +

+
+ + +
+ + +

+ { errors.comments?.message } +

+
+ + +
+ + +

+ { errors.dock_image?.message } +

+
+ + +
+ + +

+ { errors.selected_items?.message } +

+
+ + +
+ + +

+ { errors.country?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminQuotesPage; diff --git a/src/pages/admin/Edit/EditAdminRampsPage.jsx b/src/pages/admin/Edit/EditAdminRampsPage.jsx new file mode 100644 index 0000000..e89d0e2 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminRampsPage.jsx @@ -0,0 +1,317 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; +import { InteractiveButton } from "Components/InteractiveButton"; + +let sdk = new MkdSDK(); + +const EditAdminRampsPage = () => { + const { dispatch } = React.useContext(AuthContext); + const schema = yup + .object({ + category: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + material: yup.string().required(), + }) + .required(); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const [fileObj, setFileObj] = React.useState({}); + const navigate = useNavigate(); + const [category, setCategory] = useState(0); + const [length, setLength] = useState(0); + const [width, setWidth] = useState(0); + const [image, setImage] = useState(""); + const [top_view, setTopView] = useState(""); + const [material, setMaterial] = useState(0); + const [id, setId] = useState(0); + const [submitLoading, setSubmitLoading] = useState(false); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + const params = useParams(); + + useEffect(function () { + (async function () { + try { + sdk.setTable("ramps"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setValue("category", result.model.category); + setValue("length", result.model.length); + setValue("width", result.model.width); + setValue("image", result.model.image); + setValue("top_view", result.model.top_view); + setValue("material", result.model.material); + + setCategory(result.model.category); + setLength(result.model.length); + setWidth(result.model.width); + setImage(result.model.image); + setTopView(result.model.top_view); + setMaterial(result.model.material); + setId(result.model.id); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + + const previewImage = (field, target) => { + let tempFileObj = fileObj; + tempFileObj[field] = { + file: target.files[0], + tempURL: URL.createObjectURL(target.files[0]), + }; + setFileObj({ ...tempFileObj }); + }; + + const onSubmit = async (data) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + setSubmitLoading(true); + // S3 Uploads + for (let item in fileObj) { + let formData = new FormData(); + formData.append("file", fileObj[item].file); + let uploadResult = await sdk.uploadImage(formData); + data[item] = uploadResult.url; + } + + const result = await sdk.callRestAPI( + { + id: id, + category: data.category, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + material: data.material, + }, + "PUT" + ); + + if (!result.error) { + showToast(globalDispatch, "Updated"); + navigate("/admin/ramps"); + setSubmitLoading(false); + } else { + if (result.validation) { + const keys = Object.keys(result.validation); + for (let i = 0; i < keys.length; i++) { + const field = keys[i]; + setError(field, { + type: "manual", + message: result.validation[field], + }); + } + } + setSubmitLoading(false); + } + } catch (error) { + setSubmitLoading(false); + console.log("Error", error); + setError("category", { + type: "manual", + message: error.message, + }); + } + }; + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "ramps", + }, + }); + }, []); + + return ( +
+

Edit Ramps

+
+
+ + +

+ {errors.category?.message} +

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

{errors.width?.message}

+
+ +
+ + {fileObj["image"] ? ( + isImage(fileObj["image"]) ? ( + + ) : ( + <> + ) + ) : image ? ( + + ) : null} + + previewImage("image", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.image?.message ? "border-red-500" : "" + }`} + /> +

{errors.image?.message}

+
+ +
+ + {fileObj["top_view"] ? ( + isImage(fileObj["top_view"]) ? ( + + ) : ( + <> + ) + ) : top_view ? ( + + ) : null} + + previewImage("top_view", e.target), + })} + className={`"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ + errors.top_view?.message ? "border-red-500" : "" + }`} + /> +

+ {errors.top_view?.message} +

+
+ +
+ + +

+ {errors.material?.message} +

+
+ + + Submit + +
+
+ ); +}; + +export default EditAdminRampsPage; diff --git a/src/pages/admin/Edit/EditAdminUserPage.jsx b/src/pages/admin/Edit/EditAdminUserPage.jsx new file mode 100644 index 0000000..d09e6c2 --- /dev/null +++ b/src/pages/admin/Edit/EditAdminUserPage.jsx @@ -0,0 +1,228 @@ +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const EditAdminUserPage = () => { + const schema = yup + .object( { + email: yup.string().email().required(), + password: yup.string(), + role: yup.string(), + } ) + .required(); + + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const navigate = useNavigate(); + const params = useParams(); + const [ oldEmail, setOldEmail ] = useState( "" ); + const [ id, setId ] = useState( 0 ); + + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const selectRole = [ "admin", "employee" ]; + const selectStatus = [ + { key: "0", value: "Inactive" }, + { key: "2", value: "Suspend" }, + { key: "1", value: "Active" }, + ]; + + /** + * + * + * Use /user/put to update user info + * use /profile/put to update profile info + */ + const onSubmit = async ( data ) => { + try { + if ( oldEmail !== data.email ) { + const emailresult = await sdk.updateEmailByAdmin( data.email, id ); + if ( !emailresult.error ) { + showToast( globalDispatch, "Email Updated", 1000 ); + } else { + if ( emailresult.validation ) { + const keys = Object.keys( emailresult.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: emailresult.validation[ field ], + } ); + } + } + } + } + + if ( data.password.length > 0 ) { + const passwordresult = await sdk.updatePasswordByAdmin( data.password, id ); + if ( !passwordresult.error ) { + showToast( globalDispatch, "Password Updated", 2000 ); + } else { + if ( passwordresult.validation ) { + const keys = Object.keys( passwordresult.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: passwordresult.validation[ field ], + } ); + } + } + } + } + + sdk.setTable( "user" ); + + const result = await sdk.callRestAPI( { id, email: data.email, role: data.role, status: data.status }, "PUT" ); + + if ( !result.error ) { + showToast( globalDispatch, "Added", 4000 ); + navigate( "/admin/users" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "email", { + type: "manual", + message: error.message, + } ); + tokenExpireError( dispatch, error.message ); + } + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "users", + }, + } ); + + ( async function () { + try { + sdk.setTable( "user" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + + if ( !result.error ) { + setValue( "email", result.model.email ); + setValue( "role", result.model.role ); + setValue( "status", result.model.status ); + setOldEmail( result.model.email ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "Error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

Edit User

+
+
+ + +

{ errors.email?.message }

+
+
+ + +
+
+ + +
+
+ + +

{ errors.password?.message }

+
+ +
+
+ ); +}; + +export default EditAdminUserPage; diff --git a/src/pages/admin/Edit/EditAdminWedgesPage.jsx b/src/pages/admin/Edit/EditAdminWedgesPage.jsx new file mode 100644 index 0000000..8fe81cc --- /dev/null +++ b/src/pages/admin/Edit/EditAdminWedgesPage.jsx @@ -0,0 +1,298 @@ +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate, useParams } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { GlobalContext, showToast } from "Src/globalContext"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const EditAdminWedgesPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const schema = yup + .object( { + + category: yup.string().required(), + length: yup.string().required(), + width: yup.string().required(), + image: yup.string().required(), + top_view: yup.string().required(), + material: yup.string().required(), + } ) + .required(); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const [ fileObj, setFileObj ] = React.useState( {} ); + const navigate = useNavigate(); + const [ category, setCategory ] = useState( 0 ); const [ length, setLength ] = useState( 0 ); const [ width, setWidth ] = useState( 0 ); const [ image, setImage ] = useState( '' ); const [ top_view, setTopView ] = useState( '' ); const [ material, setMaterial ] = useState( 0 ); + const [ id, setId ] = useState( 0 ); + const { + register, + handleSubmit, + setError, + setValue, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const params = useParams(); + + useEffect( function () { + ( async function () { + try { + sdk.setTable( "wedges" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setValue( 'category', result.model.category ); + setValue( 'length', result.model.length ); + setValue( 'width', result.model.width ); + setValue( 'image', result.model.image ); + setValue( 'top_view', result.model.top_view ); + setValue( 'material', result.model.material ); + + + setCategory( result.model.category ); + setLength( result.model.length ); + setWidth( result.model.width ); + setImage( result.model.image ); + setTopView( result.model.top_view ); + setMaterial( result.model.material ); + setId( result.model.id ); + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + + const previewImage = ( field, target ) => { + let tempFileObj = fileObj; + tempFileObj[ field ] = { + file: target.files[ 0 ], + tempURL: URL.createObjectURL( target.files[ 0 ] ), + }; + setFileObj( { ...tempFileObj } ); + }; + + + const onSubmit = async ( data ) => { + try { + // Local Uploads + // for (let item in fileObj) { + // let uploadResult = await sdk.upload(fileObj[item].file); + // data[item] = uploadResult.url; + // } + + // S3 Uploads + for ( let item in fileObj ) { + let formData = new FormData(); + formData.append( 'file', fileObj[ item ].file ); + let uploadResult = await sdk.uploadImage( formData ); + data[ item ] = uploadResult.url; + } + + + const result = await sdk.callRestAPI( + { + id: id, + category: data.category, + length: data.length, + width: data.width, + image: data.image, + top_view: data.top_view, + material: data.material, + }, + "PUT" + ); + + if ( !result.error ) { + showToast( globalDispatch, "Updated" ); + navigate( "/admin/wedges" ); + } else { + if ( result.validation ) { + const keys = Object.keys( result.validation ); + for ( let i = 0; i < keys.length; i++ ) { + const field = keys[ i ]; + setError( field, { + type: "manual", + message: result.validation[ field ], + } ); + } + } + } + } catch ( error ) { + console.log( "Error", error ); + setError( "category", { + type: "manual", + message: error.message, + } ); + } + }; + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "wedges", + }, + } ); + }, [] ); + + return ( +
+

Edit Wedges

+
+ + +
+ + +

+ { errors.category?.message } +

+
+ + +
+ + +

+ { errors.length?.message } +

+
+ + +
+ + +

+ { errors.width?.message } +

+
+ + +
+ + { fileObj[ "image" ] ? ( + isImage( fileObj[ "image" ] ) ? ( + ) : ( + <> + ) + ) : image ? ( + + ) : null } + + previewImage( "image", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.image?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.image?.message } +

+
+ + +
+ + { fileObj[ "top_view" ] ? ( + isImage( fileObj[ "top_view" ] ) ? ( + ) : ( + <> + ) + ) : top_view ? ( + + ) : null } + + previewImage( "top_view", e.target ) } ) } + className={ `"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline ${ errors.top_view?.message ? "border-red-500" : "" + }` } + /> +

+ { errors.top_view?.message } +

+
+ + +
+ + +

+ { errors.material?.message } +

+
+ + +
+
+ ); +}; + +export default EditAdminWedgesPage; diff --git a/src/pages/admin/List/AdminAccessoriesListPage.jsx b/src/pages/admin/List/AdminAccessoriesListPage.jsx new file mode 100644 index 0000000..221b147 --- /dev/null +++ b/src/pages/admin/List/AdminAccessoriesListPage.jsx @@ -0,0 +1,349 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +// import PaginationBar from "Components/ExportButton"; +import { AddButton, PaginationBar } from "Components"; +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; +let sdk = new MkdSDK(); + +const columns = [ + { + header: "ID", + accessor: "id", + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Name", + accessor: "name", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Length", + accessor: "length", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Width", + accessor: "width", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Thumbnail", + accessor: "thumbnail", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Top View", + accessor: "top_view", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminAccessoriesListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [query, setQuery] = React.useState(""); + const [currentTableData, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(10); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const navigate = useNavigate(); + + const schema = yup.object({ + name: yup.string(), + }); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function onSort(columnIndex) { + console.log(columns[columnIndex]); + if (columns[columnIndex].isSorted) { + columns[columnIndex].isSortedDesc = !columns[columnIndex].isSortedDesc; + } else { + columns.map((i) => (i.isSorted = false)); + columns.map((i) => (i.isSortedDesc = false)); + columns[columnIndex].isSorted = true; + } + + (async function () { + await getData(0, pageSize); + })(); + } + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, currentTableData) { + try { + sdk.setTable("accessories"); + let sortField = columns.filter((col) => col.isSorted); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[0].accessor : "", + direction: sortField.length + ? sortField[0].isSortedDesc + ? "DESC" + : "ASC" + : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + const deleteItem = async (id) => { + try { + sdk.setTable("accessories"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + setCurrentTableData((list) => + list.filter((x) => Number(x.id) !== Number(id)) + ); + } catch (err) { + throw new Error(err); + } + }; + + const onSubmit = (data) => { + let name = getNonNullValue(data.name); + let filter = { + name, + }; + getData(1, pageSize, filter); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "accessories", + }, + }); + + (async function () { + await getData(1, pageSize); + })(); + }, []); + + return ( + <> +
+

Accessories Search

+
+
+ + +

+ {errors.name?.message} +

+
+
+ +
+ +
+
+

Accessories

+ +
+
+ + + + {columns.map((column, i) => ( + + ))} + + + + {currentTableData.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mappingExist) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
onSort(i)} + > + {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
+ + + + + {cell.mappings[row[cell.accessor]]} + + {["thumbnail", "top_view"].includes(cell.accessor) ? ( + + ) : ( + row[cell.accessor] + )} +
+
+
+ + + ); +}; + +export default AdminAccessoriesListPage; diff --git a/src/pages/admin/List/AdminAnalyticLogListPage.jsx b/src/pages/admin/List/AdminAnalyticLogListPage.jsx new file mode 100644 index 0000000..aca2bce --- /dev/null +++ b/src/pages/admin/List/AdminAnalyticLogListPage.jsx @@ -0,0 +1,534 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'User Id', + accessor: 'user_id', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Url', + accessor: 'url', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Path', + accessor: 'path', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Hostname', + accessor: 'hostname', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Ip', + accessor: 'ip', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Role', + accessor: 'role', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Browser', + accessor: 'browser', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Country', + accessor: 'country', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminAnalyticLogListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + + user_id: yup.number().positive().integer(), + url: yup.string(), + path: yup.string(), + hostname: yup.string(), + ip: yup.string(), + role: yup.string(), + browser: yup.string(), + country: yup.string(), + } ); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "analytic_log" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "analytic_log" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + const onSubmit = ( currentTableData ) => { + let id = getNonNullValue( data.id ); + let user_id = getNonNullValue( data.user_id ); + let url = getNonNullValue( data.url ); + let path = getNonNullValue( data.path ); + let hostname = getNonNullValue( data.hostname ); + let ip = getNonNullValue( data.ip ); + let role = getNonNullValue( data.role ); + let browser = getNonNullValue( data.browser ); + let country = getNonNullValue( data.country ); + let filter = { + id, + + user_id: user_id, + url: url, + path: path, + hostname: hostname, + ip: ip, + role: role, + browser: browser, + country: country, + }; + getData( 1, pageSize, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "analytic_log", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

AnalyticLog Search

+
+ +
+ + +

+ { errors.id?.message } +

+
+ +
+ + +

+ { errors.user_id?.message } +

+
+ +
+ + +

+ { errors.url?.message } +

+
+ +
+ + +

+ { errors.path?.message } +

+
+ +
+ + +

+ { errors.hostname?.message } +

+
+ +
+ + +

+ { errors.ip?.message } +

+
+ +
+ + +

+ { errors.role?.message } +

+
+ +
+ + +

+ { errors.browser?.message } +

+
+ +
+ + +

+ { errors.country?.message } +

+
+ +
+ +
+ +
+
+

AnalyticLog

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminAnalyticLogListPage; diff --git a/src/pages/admin/List/AdminBoatLiftsListPage.jsx b/src/pages/admin/List/AdminBoatLiftsListPage.jsx new file mode 100644 index 0000000..0806e1f --- /dev/null +++ b/src/pages/admin/List/AdminBoatLiftsListPage.jsx @@ -0,0 +1,433 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Model', + accessor: 'model', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Weight Capacity', + accessor: 'weight_capacity', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Lift Range', + accessor: 'lift_range', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'No Of Cylinders', + accessor: 'no_of_cylinders', + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 2: '2', 4: '4' } + }, + { + header: 'Length', + accessor: 'length', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Width', + accessor: 'width', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Image', + accessor: 'image', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Top View', + accessor: 'top_view', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminBoatLiftsListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + model: yup.string(), + weight_capacity: yup.string(), + lift_range: yup.string(), + no_of_cylinders: yup.string(), + } ); + + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "boat_lifts" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "boat_lifts" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + const onSubmit = ( data ) => { + let model = getNonNullValue( data.model ); + let weight_capacity = getNonNullValue( data.weight_capacity ); + let lift_range = getNonNullValue( data.lift_range ); + let no_of_cylinders = getNonNullValue( data.no_of_cylinders ); + let filter = { + model, + weight_capacity, + lift_range, + no_of_cylinders, + }; + getData( 1, pageSize, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "boat_lifts", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

BoatLifts Search

+
+
+ + +

+ { errors.model?.message } +

+
+ +
+ + +

+ { errors.weight_capacity?.message } +

+
+ +
+ + +

+ { errors.lift_range?.message } +

+
+ +
+ + +

+ { errors.no_of_cylinders?.message } +

+
+ + +
+ +
+ +
+
+

BoatLifts

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { [ "image", "top_view" ].includes( cell.accessor ) ? : row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminBoatLiftsListPage; diff --git a/src/pages/admin/List/AdminCmsListPage.jsx b/src/pages/admin/List/AdminCmsListPage.jsx new file mode 100644 index 0000000..336bca1 --- /dev/null +++ b/src/pages/admin/List/AdminCmsListPage.jsx @@ -0,0 +1,257 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Page", + accessor: "page", + }, + { + header: "Identifier", + accessor: "content_key", + }, + { + header: "Content Type", + accessor: "content_type", + }, + + { + header: "Action", + accessor: "", + }, +]; + +const AdminCmsListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ data, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 3 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + page: yup.string(), + key: yup.string(), + type: yup.string(), + } ); + + const selectType = [ + { key: "", value: "All" }, + { key: "text", value: "Text" }, + { key: "image", value: "Image" }, + { key: "number", value: "Number" }, + ]; + + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( currentPage + 1 <= pageCount ? currentPage + 1 : 0, pageSize ); + } )(); + } + + async function getData ( pageNum, limitNum, data ) { + try { + sdk.setTable( "cms" ); + const result = await sdk.callRestAPI( + { + payload: { ...data }, + page: pageNum, + limit: limitNum, + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const onSubmit = ( data ) => { + let page = getNonNullValue( data.page ); + let key = getNonNullValue( data.key ); + let type = getNonNullValue( data.type ); + let filter = { page, content_key: key, content_type: type }; + getData( 0, 10, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "cms", + }, + } ); + + ( async function () { + await getData( 0, 10 ); + } )(); + }, [] ); + + return ( + <> +
+

CMS Search

+
+
+ + +

{ errors.page?.message }

+
+
+ + +

{ errors.key?.message }

+
+ +
+ + +

+
+
+ + +
+ +
+
+

CMS

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { data.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mapping ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
+ { column.header } + { column.isSorted ? ( column.isSortedDesc ? " â–¼" : " â–²" ) : "" } +
+ + + { cell.mapping[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminCmsListPage; diff --git a/src/pages/admin/List/AdminDealersListPage.jsx b/src/pages/admin/List/AdminDealersListPage.jsx new file mode 100644 index 0000000..47e76c4 --- /dev/null +++ b/src/pages/admin/List/AdminDealersListPage.jsx @@ -0,0 +1,341 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Name', + accessor: 'name', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Address', + accessor: 'address', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminDealersListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + + name: yup.string(), + address: yup.string(), + } ); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "dealers" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "dealers" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + const onSubmit = ( data ) => { + let name = getNonNullValue( data.name ); + let address = getNonNullValue( data.address ); + let filter = { + name: name, + address: address, + }; + getData( 1, pageSize, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "dealers", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

Dealers Search

+
+ +
+ + +

+ { errors.name?.message } +

+
+ +
+ + +

+ { errors.address?.message } +

+
+ +
+ +
+ +
+
+

Dealers

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminDealersListPage; diff --git a/src/pages/admin/List/AdminDocksListPage.jsx b/src/pages/admin/List/AdminDocksListPage.jsx new file mode 100644 index 0000000..2433018 --- /dev/null +++ b/src/pages/admin/List/AdminDocksListPage.jsx @@ -0,0 +1,423 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Category', + accessor: 'category', + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: 'Floating Dock', 1: 'Sectional Dock', 2: 'Roll-In Dock' } + }, + { + header: 'Materials', + accessor: 'materials', + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: 'Wood Grain', 1: 'Perforated', 2: 'Grey Aluminium' } + }, + { + header: 'Length', + accessor: 'length', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Width', + accessor: 'width', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Image', + accessor: 'image', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Top View', + accessor: 'top_view', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminDocksListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + + category: yup.string(), + materials: yup.string(), + length: yup.string(), + width: yup.string(), + } ); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "docks" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "docks" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + const onSubmit = ( data ) => { + let category = getNonNullValue( data.category ); + let materials = getNonNullValue( data.materials ); + let length = getNonNullValue( data.length ); + let width = getNonNullValue( data.width ); + + let filter = { + + category: category, + materials: materials, + length: length, + width: width, + + }; + getData( 1, pageSize, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "docks", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

Docks Search

+
+ +
+ + +

+ { errors.category?.message } +

+
+ +
+ + +

+ { errors.materials?.message } +

+
+ +
+ + +

+ { errors.length?.message } +

+
+ +
+ + +

+ { errors.width?.message } +

+
+ + +
+ +
+ +
+
+

Docks

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { [ "image", "top_view" ].includes( cell.accessor ) ? : row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminDocksListPage; diff --git a/src/pages/admin/List/AdminEmailListPage.jsx b/src/pages/admin/List/AdminEmailListPage.jsx new file mode 100644 index 0000000..8a69405 --- /dev/null +++ b/src/pages/admin/List/AdminEmailListPage.jsx @@ -0,0 +1,162 @@ +import React from "react"; +import Skeleton from "react-loading-skeleton"; +import { useNavigate } from "react-router-dom"; + +import { AddButton } from "Components"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "ID", + accessor: "id", + }, + { + header: "Email Type", + accessor: "slug", + }, + { + header: "Subject", + accessor: "subject", + }, + { + header: "Tags", + accessor: "tag", + }, + + { + header: "Action", + accessor: "", + }, +]; + +const AdminEmailListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const [ data, setCurrentTableData ] = React.useState( [] ); + const [ loadingData, setLoadingData ] = React.useState( false ); + const navigate = useNavigate(); + + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + async function getData () { + try { + sdk.setTable( "email" ); + const result = await sdk.callRestAPI( {}, "GETALL" ); + + const { list } = result; + + setCurrentTableData( list ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "email", + }, + } ); + + ( async function () { + setLoadingData( true ); + await getData(); + setLoadingData( false ); + } )(); + }, [] ); + + return ( + <> +
+
+

Emails

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { data.length == 0 ? ( + + + + ) : null } + { data.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mapping ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
+ { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ { loadingData && } +
+ + + { cell.mapping[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + ); +}; + +export default AdminEmailListPage; diff --git a/src/pages/admin/List/AdminInstructionsListPage.jsx b/src/pages/admin/List/AdminInstructionsListPage.jsx new file mode 100644 index 0000000..f2aba59 --- /dev/null +++ b/src/pages/admin/List/AdminInstructionsListPage.jsx @@ -0,0 +1,333 @@ +import React from "react"; + +// import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +// import { yupResolver } from "@hookform/resolvers/yup"; +// import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Content', + accessor: 'content', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminInstructionsListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + // const schema = yup.object( { + + // id: yup.string(), + // } ); + // const { + // register, + // handleSubmit, + // setError, + // formState: { errors }, + // } = useForm( { + // resolver: yupResolver( schema ), + // } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "instructions" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "instructions" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + // const onSubmit = ( data ) => + // { + + // let id = getNonNullValue( Number( data.id ) ); + // let filter = { + // id: id, + // }; + + // getData( 1, pageSize, filter ); + // }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "instructions", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> + {/*
+

Instructions Search

+
+ +
+ + +

+ { errors.id?.message } +

+
+ +
+ + +

+ { errors.content?.message } +

+
+ +
+ +
*/} + +
+
+

Instructions

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminInstructionsListPage; diff --git a/src/pages/admin/List/AdminPhotoListPage.jsx b/src/pages/admin/List/AdminPhotoListPage.jsx new file mode 100644 index 0000000..c0ae445 --- /dev/null +++ b/src/pages/admin/List/AdminPhotoListPage.jsx @@ -0,0 +1,295 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import Skeleton from "react-loading-skeleton"; + +import PaginationBar from "Components/PaginationBar"; +import AddButton from "Components/AddButton"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Photos", + accessor: "url", + }, + { + header: "Create at", + accessor: "create_at", + }, + + { + header: "Action", + accessor: "", + }, +]; + +const AdminPhotoListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + const [query, setQuery] = React.useState(""); + const [data, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(3); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const [loadingData, setLoadingData] = React.useState(false); + const navigate = useNavigate(); + const globalContext = React.useContext(GlobalContext); + + const schema = yup.object({ + date: yup.string(), + id: yup.string(), + user_id: yup.string(), + }); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, data) { + try { + sdk.setTable("photo"); + const result = await sdk.callRestAPI( + { + payload: { ...data }, + page: pageNum, + limit: limitNum, + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "photos", + }, + }); + + (async function () { + setLoadingData(true); + await getData(0, 50); + setLoadingData(false); + })(); + }, []); + + async function deleteImage(id) { + sdk.setTable("photo"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + showToast(globalDispatch, "Deleted"); + await getData(0, 50); + } + + return ( + <> +
{ + let create_at = getNonNullValue(data.date); + let id = getNonNullValue(data.id); + let user_id = getNonNullValue(data.user_id); + let filter = { create_at, id, user_id }; + getData(0, 50, filter); + })} + > +

Photo Search

+
+
+ + +

+ {errors.date?.message} +

+
+
+ + +

{errors.id?.message}

+
+ +
+ + + +

+ {errors.user_id?.message} +

+
+
+ + +
+ +
+
+

Photos

+ +
+
+ + + + {columns.map((column, index) => ( + + ))} + + + + {data.length == 0 ? ( + + + + ) : null} + {data.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mapping) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
+ {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
{loadingData && }
+ + + {cell.mapping[row[cell.accessor]]} + + {cell.accessor == "url" ? ( + + ) : ( + row[cell.accessor] + )} +
+
+
+ + + ); +}; + +export default AdminPhotoListPage; diff --git a/src/pages/admin/List/AdminQuotesListPage.jsx b/src/pages/admin/List/AdminQuotesListPage.jsx new file mode 100644 index 0000000..d42898a --- /dev/null +++ b/src/pages/admin/List/AdminQuotesListPage.jsx @@ -0,0 +1,500 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import PaginationBar from "Components/PaginationBar"; + +import { AuthContext, tokenExpireError } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Action", + accessor: "", + }, + + { + header: "ID", + accessor: "id", + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "First Name", + accessor: "first_name", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Last Name", + accessor: "last_name", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Email", + accessor: "email", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Phone", + accessor: "phone", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Lake", + accessor: "lake", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "City", + accessor: "city", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Has Dealer", + accessor: "has_dealer", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "No", 1: "Yes" }, + }, + { + header: "Dealer Id", + accessor: "dealer_id", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Dock Connection", + accessor: "dock_connection", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "Resting", 1: "Bolted", 2: "Other" }, + }, + { + header: "Lake Bottom", + accessor: "lake_bottom", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "Rocky", 1: "Silty", 2: "Sandy" }, + }, + { + header: "Will Dock Boat", + accessor: "will_dock_boat", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "No", 1: "Yes" }, + }, + { + header: "Comments", + accessor: "comments", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Dock Image", + accessor: "dock_image", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + // { + // header: 'Selected Items', + // accessor: 'selected_items', + // isSorted: false, + // isSortedDesc: false, + // mappingExist: false, + // mappings: {} + // }, + { + header: "Country", + accessor: "country", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, +]; + +const AdminQuotesListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [query, setQuery] = React.useState(""); + const [currentTableData, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(10); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const navigate = useNavigate(); + + const schema = yup.object({ + first_name: yup.string(), + last_name: yup.string(), + email: yup.string(), + phone: yup.string(), + }); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function onSort(columnIndex) { + console.log(columns[columnIndex]); + if (columns[columnIndex].isSorted) { + columns[columnIndex].isSortedDesc = !columns[columnIndex].isSortedDesc; + } else { + columns.map((i) => (i.isSorted = false)); + columns.map((i) => (i.isSortedDesc = false)); + columns[columnIndex].isSorted = true; + } + + (async function () { + await getData(0, pageSize); + })(); + } + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, currentTableData) { + try { + sdk.setTable("quotes"); + let sortField = columns.filter((col) => col.isSorted); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[0].accessor : "", + direction: sortField.length + ? sortField[0].isSortedDesc + ? "DESC" + : "ASC" + : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + const deleteItem = async (id) => { + try { + sdk.setTable("quotes"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + setCurrentTableData((list) => + list.filter((x) => Number(x.id) !== Number(id)) + ); + } catch (err) { + throw new Error(err); + } + }; + + const onSubmit = (data) => { + let first_name = getNonNullValue(data.first_name); + let last_name = getNonNullValue(data.last_name); + let email = getNonNullValue(data.email); + let phone = getNonNullValue(data.phone); + + let filter = { + first_name: first_name, + last_name: last_name, + email: email, + phone: phone, + }; + getData(1, pageSize, filter); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "quotes", + }, + }); + + (async function () { + await getData(1, pageSize); + })(); + }, []); + + return ( + <> +
+

Quotes Search

+
+
+ + +

+ {errors.first_name?.message} +

+
+ +
+ + +

+ {errors.last_name?.message} +

+
+ +
+ + +

+ {errors.email?.message} +

+
+ +
+ + +

+ {errors.phone?.message} +

+
+
+ +
+ +
+
+

Quotes

+ {/* */} +
+
+ + + + {columns.map((column, i) => ( + + ))} + + + + {currentTableData.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mappingExist) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
onSort(i)} + > + {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
+ {/* */} + + + + {cell.mappings[row[cell.accessor]]} + + {cell.accessor === "dock_image" ? ( + + ) : ( + row[cell.accessor] + )} +
+
+
+ + + ); +}; + +export default AdminQuotesListPage; diff --git a/src/pages/admin/List/AdminQuotesMailRecipientsListPage.jsx b/src/pages/admin/List/AdminQuotesMailRecipientsListPage.jsx new file mode 100644 index 0000000..543016a --- /dev/null +++ b/src/pages/admin/List/AdminQuotesMailRecipientsListPage.jsx @@ -0,0 +1,378 @@ +import React from "react"; +import { AuthContext } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext } from "Src/globalContext"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import { getNonNullValue } from "Utils/utils"; +import PaginationBar from "Components/PaginationBar"; +import AddButton from "Components/AddButton"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Action", + accessor: "", + }, + + { + header: "ID", + accessor: "id", + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Email", + accessor: "email", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, +]; + +const AdminQuotesMailRecipientsListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [query, setQuery] = React.useState(""); + const [currentTableData, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(10); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const navigate = useNavigate(); + + const schema = yup.object({ + email: yup.string(), + }); + const { + register, + handleSubmit, + setError, + reset, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function onSort(columnIndex) { + console.log(columns[columnIndex]); + if (columns[columnIndex].isSorted) { + columns[columnIndex].isSortedDesc = !columns[columnIndex].isSortedDesc; + } else { + columns.map((i) => (i.isSorted = false)); + columns.map((i) => (i.isSortedDesc = false)); + columns[columnIndex].isSorted = true; + } + + (async function () { + await getData(0, pageSize); + })(); + } + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, currentTableData) { + try { + sdk.setTable("quotes_mail_recipients"); + let sortField = columns.filter((col) => col.isSorted); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[0].accessor : "", + direction: sortField.length + ? sortField[0].isSortedDesc + ? "DESC" + : "ASC" + : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + const deleteItem = async (id) => { + try { + sdk.setTable("quotes_mail_recipients"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + setCurrentTableData((list) => + list.filter((x) => Number(x.id) !== Number(id)) + ); + } catch (err) { + throw new Error(err); + } + }; + + const exportTable = async (id) => { + try { + sdk.setTable("quotes_mail_recipients"); + const result = await sdk.exportCSV(); + } catch (err) { + throw new Error(err); + } + }; + + const resetForm = async () => { + reset(); + await getData(0, pageSize); + }; + + const onSubmit = (data) => { + let id = getNonNullValue(data.id); + let email = getNonNullValue(data.email); + let filter = { + id, + + email: email, + }; + getData(1, pageSize, filter); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "quotes_mail_recipients", + }, + }); + + (async function () { + await getData(1, pageSize); + })(); + }, []); + + return ( + <> +
+

Quotes Mail Recipients Search

+
+
+ + +

{errors.id?.message}

+
+ +
+ + +

+ {errors.email?.message} +

+
+
+ + + +
+ +
+
+

Quotes Mail Recipients

+
+ + {/* */} +
+
+
+ + + + {columns.map((column, i) => ( + + ))} + + + + {currentTableData.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if (cell.accessor.indexOf("image") > -1) { + return ( + + ); + } + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mappingExist) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
onSort(i)} + > + {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
+ + + + + + + {cell.mappings[row[cell.accessor]]} + + {row[cell.accessor]} +
+
+
+ + + ); +}; + +export default AdminQuotesMailRecipientsListPage; diff --git a/src/pages/admin/List/AdminRampsListPage.jsx b/src/pages/admin/List/AdminRampsListPage.jsx new file mode 100644 index 0000000..6ce0d01 --- /dev/null +++ b/src/pages/admin/List/AdminRampsListPage.jsx @@ -0,0 +1,474 @@ +import React from "react"; +import { AuthContext, tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext } from "Src/globalContext"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import { getNonNullValue } from "Utils/utils"; +import PaginationBar from "Components/PaginationBar"; +import AddButton from "Components/AddButton"; +// import ExportButton from "Components/ExportButton"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Action", + accessor: "", + }, + + { + header: "Id", + accessor: "id", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + + // { + // header: "Create At", + // accessor: "create_at", + // isSorted: false, + // isSortedDesc: false, + // mappingExist: false, + // mappings: {}, + // }, + + // { + // header: "Update At", + // accessor: "update_at", + // isSorted: false, + // isSortedDesc: false, + // mappingExist: false, + // mappings: {}, + // }, + + { + header: "Category", + accessor: "category", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "Pivot Ramp", 1: "Truss Ramp" }, + }, + + { + header: "Length", + accessor: "length", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + + { + header: "Width", + accessor: "width", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + + { + header: "Image", + accessor: "image", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + + { + header: "Top View", + accessor: "top_view", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + + { + header: "Material", + accessor: "material", + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: "Wood Grain", 1: "Grey Aluminium", 2: "Perforated" }, + }, +]; + +const RampsListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [query, setQuery] = React.useState(""); + const [currentTableData, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(10); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const navigate = useNavigate(); + + const schema = yup.object({ + category: yup.string(), + + material: yup.string(), + }); + const { + register, + handleSubmit, + setError, + reset, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function onSort(columnIndex) { + console.log(columns[columnIndex]); + if (columns[columnIndex].isSorted) { + columns[columnIndex].isSortedDesc = !columns[columnIndex].isSortedDesc; + } else { + columns.map((i) => (i.isSorted = false)); + columns.map((i) => (i.isSortedDesc = false)); + columns[columnIndex].isSorted = true; + } + + (async function () { + await getData(0, pageSize); + })(); + } + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, currentTableData) { + try { + sdk.setTable("ramps"); + let sortField = columns.filter((col) => col.isSorted); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[0].accessor : "", + direction: sortField.length + ? sortField[0].isSortedDesc + ? "DESC" + : "ASC" + : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + const deleteItem = async (id) => { + try { + sdk.setTable("ramps"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + setCurrentTableData((list) => + list.filter((x) => Number(x.id) !== Number(id)) + ); + } catch (err) { + throw new Error(err); + } + }; + + const exportTable = async (id) => { + try { + sdk.setTable("notes"); + const result = await sdk.exportCSV(); + } catch (err) { + throw new Error(err); + } + }; + + const resetForm = async () => { + reset(); + await getData(0, pageSize); + }; + + const onSubmit = (_data) => { + let material = getNonNullValue(_data.material); + let category = getNonNullValue(_data.category); + let filter = { + category: category, + + material: material, + }; + getData(1, pageSize, filter); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "ramps", + }, + }); + + (async function () { + await getData(1, pageSize); + })(); + }, []); + + return ( + <> +
+

Ramps Search

+
+
+ + +

+ {errors.category?.message} +

+
+ +
+ + +

+ {errors.material?.message} +

+
+
+ +
+ +
+
+

Ramps

+
+ + {/* */} +
+
+
+ + + + {columns.map((column, i) => ( + + ))} + + + + {currentTableData.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if ( + cell.accessor.indexOf("image") > -1 || + cell.accessor === "top_view" + ) { + return ( + + ); + } + if ( + cell.accessor.indexOf("pdf") > -1 || + cell.accessor.indexOf("doc") > -1 || + cell.accessor.indexOf("file") > -1 || + cell.accessor.indexOf("video") > -1 + ) { + return ( + + ); + } + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mappingExist) { + return ( + + ); + } + if (["update_at", "create_at"].includes(cell.accessor)) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
onSort(i)} + > + {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
+ + + + {" "} + View + + + + + + + {cell.mappings[row[cell.accessor]]} + + {row[cell.accessor].split("T")[0]} + + {row[cell.accessor]} +
+
+
+ + + ); +}; + +export default RampsListPage; diff --git a/src/pages/admin/List/AdminReferenceItemsListPage.jsx b/src/pages/admin/List/AdminReferenceItemsListPage.jsx new file mode 100644 index 0000000..119b1ca --- /dev/null +++ b/src/pages/admin/List/AdminReferenceItemsListPage.jsx @@ -0,0 +1,443 @@ +import React from "react"; +import { AuthContext } from "../authContext"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { GlobalContext } from "../globalContext"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import PaginationBar from "Components/PaginationBar"; +import AddButton from "Components/AddButton"; +// import ExportButton from "Components/ExportButton"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "Action", + accessor: "", + }, + + { + header: "ID", + accessor: "id", + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Name", + accessor: "name", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Length", + accessor: "length", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Width", + accessor: "width", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, + { + header: "Image", + accessor: "image", + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {}, + }, +]; + +const AdminReferenceItemsListPage = () => { + const { dispatch } = React.useContext(AuthContext); + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const [query, setQuery] = React.useState(""); + const [currentTableData, setCurrentTableData] = React.useState([]); + const [pageSize, setPageSize] = React.useState(10); + const [pageCount, setPageCount] = React.useState(0); + const [dataTotal, setDataTotal] = React.useState(0); + const [currentPage, setPage] = React.useState(0); + const [canPreviousPage, setCanPreviousPage] = React.useState(false); + const [canNextPage, setCanNextPage] = React.useState(false); + const navigate = useNavigate(); + + const schema = yup.object({ + name: yup.string(), + length: yup.string(), + width: yup.string(), + image: yup.string(), + }); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm({ + resolver: yupResolver(schema), + }); + + function onSort(columnIndex) { + console.log(columns[columnIndex]); + if (columns[columnIndex].isSorted) { + columns[columnIndex].isSortedDesc = !columns[columnIndex].isSortedDesc; + } else { + columns.map((i) => (i.isSorted = false)); + columns.map((i) => (i.isSortedDesc = false)); + columns[columnIndex].isSorted = true; + } + + (async function () { + await getData(0, pageSize); + })(); + } + + function updatePageSize(limit) { + (async function () { + setPageSize(limit); + await getData(0, limit); + })(); + } + + function previousPage() { + (async function () { + await getData(currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize); + })(); + } + + function nextPage() { + (async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + })(); + } + + async function getData(pageNum, limitNum, currentTableData) { + try { + sdk.setTable("reference_items"); + let sortField = columns.filter((col) => col.isSorted); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[0].accessor : "", + direction: sortField.length + ? sortField[0].isSortedDesc + ? "DESC" + : "ASC" + : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData(list); + setPageSize(limit); + setPageCount(num_pages); + setPage(page); + setDataTotal(total); + setCanPreviousPage(page > 1); + setCanNextPage(page + 1 <= num_pages); + } catch (error) { + console.log("ERROR", error); + tokenExpireError(dispatch, error.message); + } + } + + const deleteItem = async (id) => { + try { + sdk.setTable("reference_items"); + const result = await sdk.callRestAPI({ id }, "DELETE"); + setCurrentTableData((list) => + list.filter((x) => Number(x.id) !== Number(id)) + ); + } catch (err) { + throw new Error(err); + } + }; + + const exportTable = async (id) => { + try { + sdk.setTable("reference_items"); + const result = await sdk.exportCSV(); + } catch (err) { + throw new Error(err); + } + }; + + const onSubmit = (data) => { + let id = getNonNullValue(data.id); + let name = getNonNullValue(data.name); + let length = getNonNullValue(data.length); + let width = getNonNullValue(data.width); + let image = getNonNullValue(data.image); + let filter = { + id, + + name: name, + length: length, + width: width, + image: image, + }; + getData(1, pageSize, filter); + }; + + React.useEffect(() => { + globalDispatch({ + type: "SETPATH", + payload: { + path: "reference_items", + }, + }); + + (async function () { + await getData(1, pageSize); + })(); + }, []); + + return ( + <> +
+

ReferenceItems Search

+
+
+ + +

{errors.id?.message}

+
+ +
+ + +

+ {errors.name?.message} +

+
+ +
+ + +

+ {errors.length?.message} +

+
+ +
+ + +

+ {errors.width?.message} +

+
+ +
+ + +

+ {errors.image?.message} +

+
+
+ +
+ +
+
+

ReferenceItems

+
+ + {/* */} +
+
+
+ + + + {columns.map((column, i) => ( + + ))} + + + + {currentTableData.map((row, i) => { + return ( + + {columns.map((cell, index) => { + if (cell.accessor == "") { + return ( + + ); + } + if (cell.mappingExist) { + return ( + + ); + } + return ( + + ); + })} + + ); + })} + +
onSort(i)} + > + {column.header} + + {column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : ""} + +
+ + + + + {cell.mappings[row[cell.accessor]]} + + {row[cell.accessor]} +
+
+
+ + + ); +}; + +export default AdminReferenceItemsListPage; diff --git a/src/pages/admin/List/AdminUserListPage.jsx b/src/pages/admin/List/AdminUserListPage.jsx new file mode 100644 index 0000000..ad09b6b --- /dev/null +++ b/src/pages/admin/List/AdminUserListPage.jsx @@ -0,0 +1,326 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const columns = [ + { + header: "ID", + accessor: "id", + }, + { + header: "Email", + accessor: "email", + isSorted: true, + isSortedDesc: false, + }, + { + header: "Role", + accessor: "role", + }, + { + header: "Status", + accessor: "status", + mapping: [ "Inactive", "Active", "Suspend" ], + }, + { + header: "Action", + accessor: "", + }, +]; +const AdminUserListPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + const { dispatch } = React.useContext( AuthContext ); + const [ query, setQuery ] = React.useState( "" ); + const [ tableColumns, setTableColumns ] = React.useState( columns ); + const [ data, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + id: yup.string(), + email: yup.string(), + status: yup.string(), + } ); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + const selectRole = [ "", "admin", "employee" ]; + const selectStatus = [ + { key: "", value: "All" }, + { key: "0", value: "Inactive" }, + { key: "1", value: "Active" }, + { key: "2", value: "Suspend" }, + ]; + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + function onSort ( accessor ) { + const columns = tableColumns + const index = columns.findIndex( ( column ) => column.accessor === accessor ) + const column = columns[ index ] + column.isSortedDesc = !column.isSortedDesc + columns.splice( index, 1, column ) + setTableColumns( () => [ ...columns ] ) + const sortedList = selector( data, column.isSortedDesc ) + setCurrentTableData( sortedList ); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + function selector ( users, isSortedDesc ) { + return users.sort( ( a, b ) => { + if ( isSortedDesc ) { + return a.email < b.email ? 1 : -1 + } + if ( !isSortedDesc ) { + return a.email < b.email ? -1 : 1 + } + } ) + } + + async function getData ( pageNum, limitNum, data ) { + try { + sdk.setTable( "user" ); + const result = await sdk.callRestAPI( + { + payload: { + ...data, + }, + page: pageNum, + limit: limitNum, + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + const sortedList = selector( list, false ) + setCurrentTableData( sortedList ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const onSubmit = ( data ) => { + const email = getNonNullValue( data.email ); + const status = getNonNullValue( data.status ); + const id = getNonNullValue( data.id ); + getData( 0, pageSize, { email, role: 'admin', status, id } ); + }; + + // React.useEffect(() => { + + // }, []); + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "users", + }, + } ); + + ( async function () { + await getData( 0, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

Search

+
+
+ + +

{ errors.id?.message }

+
+
+ + +

+ { errors.email?.message } +

+
+ +
+ + +

+
+
+ +
+ +
+
+

Users

+ +
+
+ + + + { tableColumns.map( ( column, index ) => ( + + ) ) } + + + + { data.map( ( row, i ) => { + return ( + + { tableColumns.map( ( cell, index ) => { + if ( cell.accessor === "" ) { + return ( + + ); + } + if ( cell.mapping ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( column.accessor ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + { cell.mapping[ row[ cell.accessor ] ] } + + { row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminUserListPage; diff --git a/src/pages/admin/List/AdminWedgesListPage.jsx b/src/pages/admin/List/AdminWedgesListPage.jsx new file mode 100644 index 0000000..6313719 --- /dev/null +++ b/src/pages/admin/List/AdminWedgesListPage.jsx @@ -0,0 +1,379 @@ +import React from "react"; + +import { useForm } from "react-hook-form"; +import { useNavigate } from "react-router-dom"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { AddButton, PaginationBar } from "Components"; + +import { AuthContext } from "Src/authContext"; +import { GlobalContext } from "Src/globalContext"; +import MkdSDK from "Utils/MkdSDK"; +import { getNonNullValue } from "Utils/utils"; +let sdk = new MkdSDK(); + +const columns = [ + + { + header: 'ID', + accessor: 'id', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Category', + accessor: 'category', + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: 'Floating Wedge', 1: 'Roll-In Wedge' } + }, + { + header: 'Length', + accessor: 'length', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Width', + accessor: 'width', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Image', + accessor: 'image', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Top View', + accessor: 'top_view', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Material', + accessor: 'material', + isSorted: false, + isSortedDesc: false, + mappingExist: true, + mappings: { 0: 'Wood Grain', 1: 'Grey Aluminium', 2: 'Perforated' } + }, + { + header: "Action", + accessor: "", + }, +]; + +const AdminWedgesListPage = () => { + const { dispatch } = React.useContext( AuthContext ); + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const [ query, setQuery ] = React.useState( "" ); + const [ currentTableData, setCurrentTableData ] = React.useState( [] ); + const [ pageSize, setPageSize ] = React.useState( 10 ); + const [ pageCount, setPageCount ] = React.useState( 0 ); + const [ dataTotal, setDataTotal ] = React.useState( 0 ); + const [ currentPage, setPage ] = React.useState( 0 ); + const [ canPreviousPage, setCanPreviousPage ] = React.useState( false ); + const [ canNextPage, setCanNextPage ] = React.useState( false ); + const navigate = useNavigate(); + + const schema = yup.object( { + + category: yup.string(), + material: yup.string(), + } ); + const { + register, + handleSubmit, + setError, + formState: { errors }, + } = useForm( { + resolver: yupResolver( schema ), + } ); + + function onSort ( columnIndex ) { + console.log( columns[ columnIndex ] ); + if ( columns[ columnIndex ].isSorted ) { + columns[ columnIndex ].isSortedDesc = !columns[ columnIndex ].isSortedDesc; + } else { + columns.map( ( i ) => ( i.isSorted = false ) ); + columns.map( ( i ) => ( i.isSortedDesc = false ) ); + columns[ columnIndex ].isSorted = true; + } + + ( async function () { + await getData( 0, pageSize ); + } )(); + } + + + function updatePageSize ( limit ) { + ( async function () { + setPageSize( limit ); + await getData( 0, limit ); + } )(); + } + + function previousPage () { + ( async function () { + await getData( currentPage - 1 > 0 ? currentPage - 1 : 0, pageSize ); + } )(); + } + + function nextPage () { + ( async function () { + await getData( + currentPage + 1 <= pageCount ? currentPage + 1 : 0, + pageSize + ); + } )(); + } + + async function getData ( pageNum, limitNum, currentTableData ) { + try { + sdk.setTable( "wedges" ); + let sortField = columns.filter( ( col ) => col.isSorted ); + const result = await sdk.callRestAPI( + { + payload: { ...currentTableData }, + page: pageNum, + limit: limitNum, + sortId: sortField.length ? sortField[ 0 ].accessor : "", + direction: sortField.length ? ( sortField[ 0 ].isSortedDesc ? "DESC" : "ASC" ) : "", + }, + "PAGINATE" + ); + + const { list, total, limit, num_pages, page } = result; + + setCurrentTableData( list ); + setPageSize( limit ); + setPageCount( num_pages ); + setPage( page ); + setDataTotal( total ); + setCanPreviousPage( page > 1 ); + setCanNextPage( page + 1 <= num_pages ); + } catch ( error ) { + console.log( "ERROR", error ); + tokenExpireError( dispatch, error.message ); + } + } + + const deleteItem = async ( id ) => { + try { + sdk.setTable( "wedges" ); + const result = await sdk.callRestAPI( { id }, "DELETE" ); + setCurrentTableData( list => list.filter( x => Number( x.id ) !== Number( id ) ) ); + } catch ( err ) { + throw new Error( err ); + } + + } + + const onSubmit = ( data ) => { + + let category = getNonNullValue( data.category ); + let material = getNonNullValue( data.material ); + let filter = { + + category: category, + material: material, + + }; + getData( 1, pageSize, filter ); + }; + + React.useEffect( () => { + globalDispatch( { + type: "SETPATH", + payload: { + path: "wedges", + }, + } ); + + ( async function () { + await getData( 1, pageSize ); + } )(); + }, [] ); + + return ( + <> +
+

Wedges Search

+
+ +
+ + +

+ { errors.category?.message } +

+
+ +
+ + +

+ { errors.material?.message } +

+
+ +
+ +
+ +
+
+

Wedges

+ +
+
+ + + + { columns.map( ( column, i ) => ( + + ) ) } + + + + { currentTableData.map( ( row, i ) => { + return ( + + { columns.map( ( cell, index ) => { + if ( cell.accessor == "" ) { + return ( + + ); + } + if ( cell.mappingExist ) { + return ( + + ); + } + return ( + + ); + } ) } + + ); + } ) } + +
onSort( i ) } + > + { column.header } + + { column.isSorted + ? column.isSortedDesc + ? " â–¼" + : " â–²" + : "" } + +
+ + + + + { cell.mappings[ row[ cell.accessor ] ] } + + { [ "image", "top_view" ].includes( cell.accessor ) ? : row[ cell.accessor ] } +
+
+
+ + + ); +}; + +export default AdminWedgesListPage; diff --git a/src/pages/admin/View/ViewAdminAccessoriesPage.jsx b/src/pages/admin/View/ViewAdminAccessoriesPage.jsx new file mode 100644 index 0000000..89fbaaa --- /dev/null +++ b/src/pages/admin/View/ViewAdminAccessoriesPage.jsx @@ -0,0 +1,78 @@ +import React from "react"; +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const ViewAdminAccessoriesPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const { dispatch } = React.useContext(GlobalContext); + const [viewModel, setViewModel] = React.useState({}); + + const params = useParams(); + + React.useEffect(function () { + (async function () { + try { + sdk.setTable("accessories"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setViewModel(result.model); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + return ( +
+

View Accessories

+ +
+
+
Name
+
{viewModel?.name}
+
+
+ +
+
+
Length
+
{viewModel?.length}
+
+
+ +
+
+
Width
+
{viewModel?.width}
+
+
+ +
+
+
Thumbnail
+
+ View +
+
+
+ +
+
+
Top View
+
+ View +
+
+
+
+ ); +}; + +export default ViewAdminAccessoriesPage; diff --git a/src/pages/admin/View/ViewAdminAnalyticLogPage.jsx b/src/pages/admin/View/ViewAdminAnalyticLogPage.jsx new file mode 100644 index 0000000..26ee598 --- /dev/null +++ b/src/pages/admin/View/ViewAdminAnalyticLogPage.jsx @@ -0,0 +1,104 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); + +const ViewAdminAnalyticLogPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "analytic_log" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View AnalyticLog

+ + +
+
+
User Id
+
{ viewModel?.user_id }
+
+
+ +
+
+
Url
+
{ viewModel?.url }
+
+
+ +
+
+
Path
+
{ viewModel?.path }
+
+
+ +
+
+
Hostname
+
{ viewModel?.hostname }
+
+
+ +
+
+
Ip
+
{ viewModel?.ip }
+
+
+ +
+
+
Role
+
{ viewModel?.role }
+
+
+ +
+
+
Browser
+
{ viewModel?.browser }
+
+
+ +
+
+
Country
+
{ viewModel?.country }
+
+
+ + + + +
+ ); +}; + +export default ViewAdminAnalyticLogPage; diff --git a/src/pages/admin/View/ViewAdminBoatLiftsPage.jsx b/src/pages/admin/View/ViewAdminBoatLiftsPage.jsx new file mode 100644 index 0000000..8348107 --- /dev/null +++ b/src/pages/admin/View/ViewAdminBoatLiftsPage.jsx @@ -0,0 +1,109 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); +const no_of_cylindersMapping = { '2': 2, '4': 4 } + +const ViewAdminBoatLiftsPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "boat_lifts" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View BoatLifts

+ + +
+
+
Model
+
{ viewModel?.model }
+
+
+ +
+
+
Weight Capacity
+
{ viewModel?.weight_capacity }
+
+
+ +
+
+
Lift Range
+
{ viewModel?.lift_range }
+
+
+ +
+
+
No Of Cylinders
+
{ no_of_cylindersMapping[ viewModel?.no_of_cylinders ] }
+
+
+ +
+
+
Length
+
{ viewModel?.length }
+
+
+ +
+
+
Width
+
{ viewModel?.width }
+
+
+ +
+
+
Image
+
+ View +
+
+
+ +
+
+
Top View
+
+ View +
+
+
+ + + + +
+ ); +}; + +export default ViewAdminBoatLiftsPage; diff --git a/src/pages/admin/View/ViewAdminDealersPage.jsx b/src/pages/admin/View/ViewAdminDealersPage.jsx new file mode 100644 index 0000000..876f1a2 --- /dev/null +++ b/src/pages/admin/View/ViewAdminDealersPage.jsx @@ -0,0 +1,63 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const ViewAdminDealersPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "dealers" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View Dealers

+ + +
+
+
Name
+
{ viewModel?.name }
+
+
+ +
+
+
Address
+
{ viewModel?.address }
+
+
+ + + + +
+ ); +}; + +export default ViewAdminDealersPage; diff --git a/src/pages/admin/View/ViewAdminDocksPage.jsx b/src/pages/admin/View/ViewAdminDocksPage.jsx new file mode 100644 index 0000000..830a4be --- /dev/null +++ b/src/pages/admin/View/ViewAdminDocksPage.jsx @@ -0,0 +1,94 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import { categoryMapping, materialsMapping } from 'Utils/constants'; + + +let sdk = new MkdSDK(); + +const ViewAdminDocksPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "docks" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View Docks

+ + +
+
+
Category
+
{ categoryMapping[ viewModel?.category ] }
+
+
+ +
+
+
Materials
+
{ materialsMapping[ viewModel?.materials ] }
+
+
+ +
+
+
Length
+
{ viewModel?.length }
+
+
+ +
+
+
Width
+
{ viewModel?.width }
+
+
+ +
+
+
Image
+
+ View +
+
+
+ +
+
+
Top View
+
+ View +
+
+
+ + + + +
+ ); +}; + +export default ViewAdminDocksPage; diff --git a/src/pages/admin/View/ViewAdminInstructionsPage.jsx b/src/pages/admin/View/ViewAdminInstructionsPage.jsx new file mode 100644 index 0000000..f1a5909 --- /dev/null +++ b/src/pages/admin/View/ViewAdminInstructionsPage.jsx @@ -0,0 +1,56 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const ViewAdminInstructionsPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "instructions" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View Instructions

+ + +
+
+
Content
+
{ viewModel?.content }
+
+
+ + + + +
+ ); +}; + +export default ViewAdminInstructionsPage; diff --git a/src/pages/admin/View/ViewAdminQuotesMailRecipientsPage.jsx b/src/pages/admin/View/ViewAdminQuotesMailRecipientsPage.jsx new file mode 100644 index 0000000..a9a0b2e --- /dev/null +++ b/src/pages/admin/View/ViewAdminQuotesMailRecipientsPage.jsx @@ -0,0 +1,58 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; +import MkdSDK from "Utils/MkdSDK"; +import { useNavigate, useParams } from "react-router-dom"; +import { tokenExpireError } from "Src/authContext"; +import { GlobalContext, showToast } from "Src/globalContext"; +import { isImage } from "Utils/utils"; + +let sdk = new MkdSDK(); + +const ViewAdminQuotesMailRecipientsPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const { dispatch } = React.useContext(GlobalContext); + const [viewModel, setViewModel] = React.useState({}); + + + + const params = useParams(); + + React.useEffect(function () { + (async function () { + try { + sdk.setTable("quotes_mail_recipients"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + + setViewModel(result.model); + + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + return ( +
+

View QuotesMailRecipients

+ + +
+
+
Email
+
{viewModel?.email}
+
+
+ + + + +
+ ); +}; + +export default ViewAdminQuotesMailRecipientsPage; diff --git a/src/pages/admin/View/ViewAdminQuotesPage.jsx b/src/pages/admin/View/ViewAdminQuotesPage.jsx new file mode 100644 index 0000000..9c032fb --- /dev/null +++ b/src/pages/admin/View/ViewAdminQuotesPage.jsx @@ -0,0 +1,239 @@ +import React from 'react' +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; +import * as XLSX from "xlsx"; +import { DownloadIcon, Loader } from 'Assets/svgs'; +import { Table } from 'Components/Table'; +import { selectedItemsTableColumns } from 'Utils/constants'; +import { useCallback } from 'react'; +import { ViewDockImageModal } from 'Components/ViewDockImageModal'; + + +let sdk = new MkdSDK(); +const has_dealerMapping = { 0: 'No', 1: 'Yes' } +const dock_connectionMapping = { 0: 'Resting', 1: 'Bolted', 2: 'Other' } +const lake_bottomMapping = { 0: 'Rocky', 1: 'Silty', 2: 'Sandy' } +const will_dock_boatMapping = { 0: 'No', 1: 'Yes' } +const ViewAdminQuotesPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + const [ viewModelSelectedItems, setViewModelSelectedItems ] = React.useState( "" ); + const [ viewModelLoading, setViewModelLoading ] = React.useState( false ); + const [ viewDockImageModal, setViewDockImageModal ] = React.useState( false ); + const [ dockImage, setDockImage ] = React.useState( '' ); + + const params = useParams(); + + const viewDockImageModalOpen = useCallback( ( image ) => { + setDockImage( image ) + setViewDockImageModal( true ) + }, [ dockImage, viewDockImageModal ] ) + + const viewDockImageModalClose = useCallback( () => { + setViewDockImageModal( false ) + }, [ viewDockImageModal ] ) + + const fetchData = useCallback( () => { + + ( async function () { + try { + setViewModelLoading( true ) + sdk.setTable( "quotes" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + setViewModelSelectedItems( result.model.selected_items ) + setViewModelLoading( false ) + + } + } catch ( error ) { + setViewModelLoading( false ) + console.log( "error", error ) + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [ viewModelLoading, viewModel, viewModelSelectedItems ] ) + const onDownloadClick = () => { + const data = viewModel + data.selected_items = null + data.dock_image = null + const selectedItems = viewModelSelectedItems && ( String( viewModelSelectedItems ).startsWith( "[{\"" ) ) ? JSON.parse( viewModelSelectedItems ) : [] + console.log( selectedItems ) + // return + const workbook = XLSX.utils.book_new(); + const worksheet = XLSX.utils.json_to_sheet( [ data ] ); + XLSX.utils.book_append_sheet( workbook, worksheet, "Quotation" ); + if ( selectedItems.length ) { + const worksheet2 = XLSX.utils.json_to_sheet( selectedItems ); + XLSX.utils.book_append_sheet( workbook, worksheet2, "Selected Items" ); + } + //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); + //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); + XLSX.writeFile( workbook, "Paradise_dock_quotation.xlsx" ); + } + + React.useEffect( function () { + if ( !viewModel.first_name ) { + fetchData() + } + }, [] ); + return ( + <> + { viewModelLoading ? +
+ +
+ + : +
+
+ +

View Quotes

+
+ +
+
+ +
+
+
First Name
+
{ viewModel?.first_name }
+
+
+ +
+
+
Last Name
+
{ viewModel?.last_name }
+
+
+ +
+
+
Email
+
{ viewModel?.email }
+
+
+ +
+
+
Phone
+
{ viewModel?.phone }
+
+
+ +
+
+
Lake
+
{ viewModel?.lake }
+
+
+ +
+
+
City
+
{ viewModel?.city }
+
+
+ +
+
+
Has Dealer
+
{ has_dealerMapping[ viewModel?.has_dealer ] }
+
+
+ +
+
+
Dealer Id
+
{ viewModel?.dealer_id }
+
+
+ +
+
+
Dock Connection
+
{ dock_connectionMapping[ viewModel?.dock_connection ] }
+
+
+ +
+
+
Lake Bottom
+
{ lake_bottomMapping[ viewModel?.lake_bottom ] }
+
+
+ +
+
+
Will Dock Boat
+
{ will_dock_boatMapping[ viewModel?.will_dock_boat ] }
+
+
+ +
+
+
Comments
+
{ viewModel?.comments }
+
+
+ +
+
+
Country
+
{ viewModel?.country }
+
+
+ + + + +
+
+
Selected Items
+
+ + + {/*
{ viewModel?.selected_items }
*/ } + + + + } + + + ); +}; + +export default ViewAdminQuotesPage; diff --git a/src/pages/admin/View/ViewAdminRampsPage.jsx b/src/pages/admin/View/ViewAdminRampsPage.jsx new file mode 100644 index 0000000..81f1b76 --- /dev/null +++ b/src/pages/admin/View/ViewAdminRampsPage.jsx @@ -0,0 +1,97 @@ +import React from "react"; +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); +const categoryMapping = { + 0: "Pivot Ramp", + 1: "Truss Ramp", +}; +const materialMapping = { + 0: "Wood Grain", + 1: "Perforated", + 2: "Grey Aluminium", +}; +const ViewRampsPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const { dispatch } = React.useContext(GlobalContext); + const [viewModel, setViewModel] = React.useState({}); + + const params = useParams(); + + React.useEffect(function () { + (async function () { + try { + sdk.setTable("ramps"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setViewModel(result.model); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + return ( +
+

View Wedges

+ +
+
+
Category
+
{categoryMapping[viewModel?.category]}
+
+
+ +
+
+
Length
+
{viewModel?.length}
+
+
+ +
+
+
Width
+
{viewModel?.width}
+
+
+ +
+
+
Image
+ +
+
+ +
+
+
Top View
+ +
+
+ +
+
+
Material
+
{materialMapping[viewModel?.material]}
+
+
+
+ ); +}; + +export default ViewRampsPage; diff --git a/src/pages/admin/View/ViewAdminReferenceItemsPage.jsx b/src/pages/admin/View/ViewAdminReferenceItemsPage.jsx new file mode 100644 index 0000000..98e3787 --- /dev/null +++ b/src/pages/admin/View/ViewAdminReferenceItemsPage.jsx @@ -0,0 +1,82 @@ +import React from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as yup from "yup"; + +import { useNavigate, useParams } from "react-router-dom"; +import { tokenExpireError } from "../authContext"; +import { GlobalContext, showToast } from "../globalContext"; +import MkdSDK from "Utils/MkdSDK"; + + +let sdk = new MkdSDK(); + +const ViewAdminReferenceItemsPage = () => { + const { dispatch: globalDispatch } = React.useContext( GlobalContext ); + + const { dispatch } = React.useContext( GlobalContext ); + const [ viewModel, setViewModel ] = React.useState( {} ); + + + + const params = useParams(); + + React.useEffect( function () { + ( async function () { + try { + sdk.setTable( "reference_items" ); + const result = await sdk.callRestAPI( { id: Number( params?.id ) }, "GET" ); + if ( !result.error ) { + + setViewModel( result.model ); + + } + } catch ( error ) { + console.log( "error", error ); + tokenExpireError( dispatch, error.message ); + } + } )(); + }, [] ); + return ( +
+

View ReferenceItems

+ + +
+
+
Name
+
{ viewModel?.name }
+
+
+ +
+
+
Length
+
{ viewModel?.length }
+
+
+ +
+
+
Width
+
{ viewModel?.width }
+
+
+ +
+
+
Image
+
+ View +
+
+
+ + + + +
+ ); +}; + +export default ViewAdminReferenceItemsPage; diff --git a/src/pages/admin/View/ViewAdminWedgesPage.jsx b/src/pages/admin/View/ViewAdminWedgesPage.jsx new file mode 100644 index 0000000..2917631 --- /dev/null +++ b/src/pages/admin/View/ViewAdminWedgesPage.jsx @@ -0,0 +1,98 @@ +import React from "react"; +import { useParams } from "react-router-dom"; + +import { GlobalContext } from "Src/globalContext"; +import { tokenExpireError } from "Src/authContext"; +import MkdSDK from "Utils/MkdSDK"; + +let sdk = new MkdSDK(); +const categoryMapping = { + 0: "Floating Dock", + 1: "Sectional Dock", + 2: "Roll-In Dock", +}; +const materialMapping = { + 0: "Wood Grain", + 1: "Perforated", + 2: "Grey Aluminium", +}; +const ViewAdminWedgesPage = () => { + const { dispatch: globalDispatch } = React.useContext(GlobalContext); + + const { dispatch } = React.useContext(GlobalContext); + const [viewModel, setViewModel] = React.useState({}); + + const params = useParams(); + + React.useEffect(function () { + (async function () { + try { + sdk.setTable("wedges"); + const result = await sdk.callRestAPI({ id: Number(params?.id) }, "GET"); + if (!result.error) { + setViewModel(result.model); + } + } catch (error) { + console.log("error", error); + tokenExpireError(dispatch, error.message); + } + })(); + }, []); + return ( +
+

View Wedges

+ +
+
+
Category
+
{categoryMapping[viewModel?.category]}
+
+
+ +
+
+
Length
+
{viewModel?.length}
+
+
+ +
+
+
Width
+
{viewModel?.width}
+
+
+ +
+
+
Image
+ +
+
+ +
+
+
Top View
+ +
+
+ +
+
+
Material
+
{materialMapping[viewModel?.material]}
+
+
+
+ ); +}; + +export default ViewAdminWedgesPage; diff --git a/src/pages/admin/index.js b/src/pages/admin/index.js new file mode 100644 index 0000000..e8dc1b9 --- /dev/null +++ b/src/pages/admin/index.js @@ -0,0 +1,60 @@ +export { default as AddAdminAccessoriesPage } from "./Add/AddAdminAccessoriesPage"; +export { default as AddAdminAnalyticLogPage } from "./Add/AddAdminAnalyticLogPage"; +export { default as AddAdminBoatLiftsPage } from "./Add/AddAdminBoatLiftsPage"; +export { default as AddAdminCmsPage } from "./Add/AddAdminCmsPage"; +export { default as AddAdminDealersPage } from "./Add/AddAdminDealersPage"; +export { default as AddAdminDocksPage } from "./Add/AddAdminDocksPage"; +export { default as AddAdminEmailPage } from "./Add/AddAdminEmailPage"; +export { default as AddAdminInstructionsPage } from "./Add/AddAdminInstructionsPage"; +export { default as AddAdminPhotoPage } from "./Add/AddAdminPhotoPage"; +export { default as AddAdminQuotesPage } from "./Add/AddAdminQuotesPage"; +export { default as AddAdminUserPage } from "./Add/AddAdminUserPage"; +export { default as AddAdminWedgesPage } from "./Add/AddAdminWedgesPage"; +export { default as AddAdminRampsPage } from "./Add/AddAdminRampsPage"; +export { default as AddAdminQuotesMailRecipientsPage } from "./Add/AddAdminQuotesMailRecipientsPage"; + +export { default as AdminAccessoriesListPage } from "./List/AdminAccessoriesListPage"; +export { default as AdminAnalyticLogListPage } from "./List/AdminAnalyticLogListPage"; +export { default as AdminBoatLiftsListPage } from "./List/AdminBoatLiftsListPage"; +export { default as AdminCmsListPage } from "./List/AdminCmsListPage"; +export { default as AdminDashboardPage } from "./AdminDashboardPage"; +export { default as AdminDealersListPage } from "./List/AdminDealersListPage"; +export { default as AdminDocksListPage } from "./List/AdminDocksListPage"; +export { default as AdminEmailListPage } from "./List/AdminEmailListPage"; +export { default as AdminForgotPage } from "./Auth/AdminForgotPage"; +export { default as AdminInstructionsListPage } from "./List/AdminInstructionsListPage"; +export { default as AdminPhotoListPage } from "./List/AdminPhotoListPage"; +export { default as AdminQuotesListPage } from "./List/AdminQuotesListPage"; +export { default as AdminUserListPage } from "./List/AdminUserListPage"; +export { default as AdminWedgesListPage } from "./List/AdminWedgesListPage"; +export { default as AdminRampsListPage } from "./List/AdminRampsListPage"; +export { default as AdminQuotesMailRecipientsListPage } from "./List/AdminQuotesMailRecipientsListPage"; + +export { default as EditAdminAccessoriesPage } from "./Edit/EditAdminAccessoriesPage"; +export { default as EditAdminAnalyticLogPage } from "./Edit/EditAdminAnalyticLogPage"; +export { default as EditAdminBoatLiftsPage } from "./Edit/EditAdminBoatLiftsPage"; +export { default as EditAdminCmsPage } from "./Edit/EditAdminCmsPage"; +export { default as EditAdminDealersPage } from "./Edit/EditAdminDealersPage"; +export { default as EditAdminDocksPage } from "./Edit/EditAdminDocksPage"; +export { default as EditAdminEmailPage } from "./Edit/EditAdminEmailPage"; +export { default as EditAdminInstructionsPage } from "./Edit/EditAdminInstructionsPage"; +export { default as EditAdminQuotesPage } from "./Edit/EditAdminQuotesPage"; +export { default as EditAdminUserPage } from "./Edit/EditAdminUserPage"; +export { default as EditAdminWedgesPage } from "./Edit/EditAdminWedgesPage"; +export { default as EditAdminRampsPage } from "./Edit/EditAdminRampsPage"; +export { default as EditAdminQuotesMailRecipientsPage } from "./Edit/EditAdminQuotesMailRecipientsPage"; + +export { default as ViewAdminAccessoriesPage } from "./View/ViewAdminAccessoriesPage"; +export { default as ViewAdminAnalyticLogPage } from "./View/ViewAdminAnalyticLogPage"; +export { default as ViewAdminBoatLiftsPage } from "./View/ViewAdminBoatLiftsPage"; +export { default as ViewAdminDealersPage } from "./View/ViewAdminDealersPage"; +export { default as ViewAdminDocksPage } from "./View/ViewAdminDocksPage"; +export { default as ViewAdminInstructionsPage } from "./View/ViewAdminInstructionsPage"; +export { default as ViewAdminQuotesPage } from "./View/ViewAdminQuotesPage"; +export { default as ViewAdminWedgesPage } from "./View/ViewAdminWedgesPage"; +export { default as ViewAdminRampsPage } from "./View/ViewAdminRampsPage"; +export { default as ViewAdminQuotesMailRecipientsPage } from "./View/ViewAdminQuotesMailRecipientsPage"; + +export { default as AdminLoginPage } from "./Auth/AdminLoginPage"; +export { default as AdminProfilePage } from "./Auth/AdminProfilePage"; +export { default as AdminResetPage } from "./Auth/AdminResetPage"; diff --git a/src/pages/dock/DockBuilderPage.jsx b/src/pages/dock/DockBuilderPage.jsx new file mode 100644 index 0000000..87dd492 --- /dev/null +++ b/src/pages/dock/DockBuilderPage.jsx @@ -0,0 +1,23 @@ +import React, { useContext } from 'react'; +import { Loader } from 'Assets/svgs'; +import { DockBuilder } from 'Components/DockBuilder'; +import { GlobalContext } from '../../globalContext'; + +export const DockBuilderPage = () => { + + const { state: { dockLoading } } = useContext( GlobalContext ) + + return ( +
+ { dockLoading ? +
+ +
+ : null + } + + + +
+ ) +} diff --git a/src/pages/dock/index.js b/src/pages/dock/index.js new file mode 100644 index 0000000..a7204fe --- /dev/null +++ b/src/pages/dock/index.js @@ -0,0 +1 @@ +export { DockBuilderPage } from './DockBuilderPage' \ No newline at end of file diff --git a/src/utils/DockBuilderUtils/clone.jsx b/src/utils/DockBuilderUtils/clone.jsx new file mode 100644 index 0000000..feaa9aa --- /dev/null +++ b/src/utils/DockBuilderUtils/clone.jsx @@ -0,0 +1,130 @@ +// import { useContext } from "react"; +// import { GlobalContext } from "../../globalContext"; +// import { scaleFactor, SnapType } from "../constants"; +import { SnapType } from "Utils/constants"; +import { reScaleXY, resolveHeight, resolveWidth } from "./edgeDetection"; +// const {state: {dockTop, dockLeft}, dispatch: GlobalDispatch} = useContext(GlobalContext) +export const clone = (selectedObj, editorMemo, detectedObj, dir) => { + const snapCloneFound = editorMemo.getObjects("image").find((o) => { + // console.log( o ) + + if (o.snapClone) { + return o; + } + }); + // console.log( snapCloneFound ) + if (snapCloneFound) { + return handlePosition( + dir, + detectedObj, + selectedObj, + snapCloneFound, + SnapType.SnapClone, + editorMemo + ); + } + + // console.log( "snapCloneFound skip" ) + selectedObj.clone(function (cloned) { + cloned.set({ + snapClone: true, + opacity: 0.02 + }); + + editorMemo.add(cloned).sendBackwards(cloned); + + editorMemo.getObjects("image").forEach((o) => { + if (o.snapClone) { + return handlePosition( + dir, + detectedObj, + selectedObj, + o, + SnapType.SnapClone, + editorMemo + ); + } + }); + }); +}; +export const clearClone = (editorMemo) => { + // return + // setTimeout( () => { + editorMemo.getObjects("image").forEach((o) => { + if (o.snapClone) { + editorMemo.remove(o); + } + }); + // }, 100 ) +}; + +export const handlePosition = ( + dir, + detectedObj, + selectedObj, + o, + objType, + editorMemo +) => { + // console.log( editorMemo ) + switch (dir) { + case "left": + o.left = detectedObj.left - reScaleXY(o, "width"); + if (objType === SnapType.Snap) { + // console.log( o ) + } + if (objType === SnapType.SnapClone) { + o.set({ + top: selectedObj.top + }); + } + editorMemo.renderAll(); + break; + case "right": + o.left = detectedObj.left + reScaleXY(detectedObj, "width"); + if (objType === SnapType.Snap) { + // console.log( o ) + } + if (objType === SnapType.SnapClone) { + o.set({ + top: selectedObj.top + }); + // o.top = selectedObj.top + } + editorMemo.renderAll(); + break; + + case "bottom": + o.top = detectedObj.top + reScaleXY(detectedObj, "height"); + if (objType === SnapType.Snap) { + // console.log( o ) + } + if (objType === SnapType.SnapClone) { + o.set({ + left: selectedObj.left + }); + } + editorMemo.renderAll(); + break; + + case "top": + o.top = detectedObj.top - reScaleXY(o, "height"); + if (objType === SnapType.Snap) { + // console.log( o ) + } + if (objType === SnapType.SnapClone) { + o.set({ + left: selectedObj.left + }); + } + editorMemo.renderAll(); + break; + } +}; + +// export const resolveTop = (o, detectedObj, objType) => { +// switch (objType) { +// case SnapType.SnapClone: + +// } +// } diff --git a/src/utils/DockBuilderUtils/edgeDetection.jsx b/src/utils/DockBuilderUtils/edgeDetection.jsx new file mode 100644 index 0000000..7bf22a0 --- /dev/null +++ b/src/utils/DockBuilderUtils/edgeDetection.jsx @@ -0,0 +1,170 @@ +import { edgeSnapThreshold } from "../constants"; +export const edgeDetection = (selectedObj, detectedObj, dir) => { + /** Function to determine the position of the selected dock */ + const isImageNotSnapClone = + !detectedObj.snapClone && ["image", "rect"].includes(detectedObj.type); + let isWithinSnapThreshold; + let isWithinTopRange; + let isWithinLeftRange; + + switch (dir) { + case "left": + isWithinSnapThreshold = + Math.abs( + resolveWidth(selectedObj) - detectedObj.getBoundingRect().left + ) < edgeSnapThreshold; + isWithinTopRange = compareTop(selectedObj, detectedObj); + return isWithinSnapThreshold && isWithinTopRange && isImageNotSnapClone; + + case "right": + isWithinSnapThreshold = + Math.abs( + resolveWidth(detectedObj) - selectedObj.getBoundingRect().left + ) < edgeSnapThreshold; + isWithinTopRange = compareTop(selectedObj, detectedObj); + return isWithinSnapThreshold && isWithinTopRange && isImageNotSnapClone; + + case "bottom": + isWithinSnapThreshold = + Math.abs( + Math.abs(selectedObj.getBoundingRect().top) - + resolveHeight(detectedObj) + ) < edgeSnapThreshold; + isWithinLeftRange = compareLeft(selectedObj, detectedObj, "bottom"); + return isWithinSnapThreshold && isWithinLeftRange && isImageNotSnapClone; + + case "top": + isWithinSnapThreshold = + Math.abs( + Math.abs(detectedObj.getBoundingRect().top) - + resolveHeight(selectedObj) + ) < edgeSnapThreshold; + isWithinLeftRange = compareLeft(selectedObj, detectedObj, "top"); + return isWithinSnapThreshold && isWithinLeftRange && isImageNotSnapClone; + + case "neutral": + return ( + !edgeDetection(selectedObj, detectedObj, "left") && + !edgeDetection(selectedObj, detectedObj, "right") && + !edgeDetection(selectedObj, detectedObj, "top") && + !edgeDetection(selectedObj, detectedObj, "bottom") + ); + } +}; + +export const handleIntersection = (selectedObj, detectedObj) => { + handleEdgeDetection(selectedObj, detectedObj); +}; + +export const handleEdgeDetection = (selectedObj, detectedObj) => { + if (edgeDetection(selectedObj, detectedObj, "left")) { + selectedObj.set({ + left: detectedObj.left - selectedObj.width, + }); + } + + if (edgeDetection(selectedObj, detectedObj, "right")) { + selectedObj.set({ + left: detectedObj.left + detectedObj.width, + }); + } + + if (edgeDetection(selectedObj, detectedObj, "top")) { + selectedObj.set({ + top: detectedObj.top - selectedObj.height, + }); + } + + if (edgeDetection(selectedObj, detectedObj, "bottom")) { + selectedObj.set({ + top: detectedObj.top + detectedObj.height, + }); + } +}; + +export const resolveWidth = (obj) => { + /** This Function add left position and width of dock to find the horizontal end of the dock */ + return Math.abs(obj.getBoundingRect().left + obj.getBoundingRect().width); +}; +export const resolveHeight = (obj) => { + /** This Function add top position and height of dock to find the vertical end of the dock */ + return Math.abs(obj.getBoundingRect().top + obj.getBoundingRect().height); +}; +export const reScaleXY = (obj, XY) => { + /** THis basically was to rescale the width or height of the dock but We are barely using this as the implementation has changed, we are not even rescaling in this function right now */ + switch (XY) { + case "width": + return obj.getBoundingRect().width; + case "height": + return obj.getBoundingRect().height; + } +}; + +export const compareTop = (selectedObj, detectedObj) => { + const detectedObjBound = detectedObj.getBoundingRect(); + const selectedObjBound = selectedObj.getBoundingRect(); + + return ( + Math.abs(selectedObjBound.top - detectedObjBound.top) < edgeSnapThreshold || + (selectedObjBound.top > detectedObjBound.top && + selectedObjBound.top < resolveHeight(detectedObj)) || + (selectedObjBound.top < detectedObjBound.top && + resolveHeight(selectedObj) > detectedObjBound.top) + ); +}; +export const compareLeft = (selectedObj, detectedObj, dir) => { + const detectedObjBound = detectedObj.getBoundingRect(); + const selectedObjBound = selectedObj.getBoundingRect(); + + switch (dir) { + case "top": + return ( + Math.abs(selectedObjBound.left - detectedObjBound.left) < + edgeSnapThreshold || + (selectedObjBound.left > detectedObjBound.left && + selectedObjBound.left <= resolveWidth(detectedObj) && + detectedObjBound.top > resolveHeight(selectedObj)) || + (selectedObjBound.left < detectedObjBound.left && + resolveWidth(selectedObj) > detectedObjBound.left) + ); + case "bottom": + return ( + Math.abs(selectedObjBound.left - detectedObjBound.left) < + edgeSnapThreshold || + (selectedObjBound.left > detectedObjBound.left && + selectedObjBound.left <= resolveWidth(detectedObj) && + selectedObj.top > resolveHeight(detectedObj) && + selectedObjBound.top - resolveHeight(detectedObj) < + edgeSnapThreshold) || + (selectedObjBound.left < detectedObjBound.left && + resolveWidth(selectedObj) > detectedObjBound.left) + ); + } +}; + +export const boatLiftLeft = (boatLiftObj, detectedObj) => { + const boatLiftWidth = reScaleXY(boatLiftObj, "width"); + const detectedObjWidth = reScaleXY(detectedObj, "width"); + if (boatLiftWidth > detectedObjWidth) { + const leftToSet = (boatLiftWidth - detectedObjWidth) / 2; + return detectedObj.left - leftToSet; + } else if (detectedObjWidth > boatLiftWidth) { + const leftToSet = (detectedObjWidth - boatLiftWidth) / 2; + return detectedObj.left + leftToSet; + } else { + return detectedObj.left; + } +}; +export const boatLiftTop = (boatLiftObj, detectedObj) => { + const boatLiftHeight = reScaleXY(boatLiftObj, "height"); + const detectedObjHeight = reScaleXY(detectedObj, "height"); + if (boatLiftHeight > detectedObjHeight) { + const topToSet = (boatLiftHeight - detectedObjHeight) / 2; + return detectedObj.top - topToSet; + } else if (detectedObjHeight > boatLiftHeight) { + const topToSet = (detectedObjHeight - boatLiftHeight) / 2; + return detectedObj.top + topToSet; + } else { + return detectedObj.top; + } +}; diff --git a/src/utils/DockBuilderUtils/index.js b/src/utils/DockBuilderUtils/index.js new file mode 100644 index 0000000..d412d75 --- /dev/null +++ b/src/utils/DockBuilderUtils/index.js @@ -0,0 +1,6 @@ +export { clearClone, clone } from "./clone" +export { + edgeDetection, + handleIntersection, + handleEdgeDetection +} from "./edgeDetection" \ No newline at end of file diff --git a/src/utils/MkdSDK.jsx b/src/utils/MkdSDK.jsx new file mode 100644 index 0000000..ff6e5eb --- /dev/null +++ b/src/utils/MkdSDK.jsx @@ -0,0 +1,1763 @@ +export default function MkdSDK() { + // this._baseurl = "https://paradisedocklift.manaknightdigital.com"; + // this._baseurl = "https://app.paradisedocklift.com"; + // this._baseurl = "http://localhost:3048"; + this._project_id = "paralift"; + this._secret = "grljhaqkwmb6cbadsabuj6ysla302xnxi"; + //this._project_id = "manaknight"; + //this._secret = "grljhaqkwmb6cbadsabuj6ysla302xnxi"; + this.fe_baseurl = "http://localhost:3000"; + // this._baseurl = "http://localhost:3048"; + // this._baseurl = "https://paradisedocklift.mkdlabs.com"; + this._baseurl = "https://paralift.mkdlabs.com"; + // this._project_id = "paralift"; + // this._secret = "grljhaqkwmb6cbadsabuj6ysla302xnxi"; + // this._project_id = "manaknight"; + // this._secret = "grljhaqkwmb6cbadsabuj6ysla302xnxi"; + // this.fe_baseurl = "http://localhost:3000"; + // this._project_id = "localproject"; + // this._secret = "ok5pdxzobdk9so4sgke7ie9zgmyxqmpu"; + + this._table = ""; + this._custom = ""; + this._method = ""; + + const raw = this._project_id + ":" + this._secret; + let base64Encode = btoa(raw); + + this.getItems = async (table) => { + const result = await fetch( + this._baseurl + `/v3/api/custom/paradisedock/${table}`, + { + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + } + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.requestQuote = async (Quotebody) => { + const result = await fetch( + this._baseurl + `/v3/api/custom/paradisedock/quotes`, + { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + }, + body: JSON.stringify(Quotebody) + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.login = async function (email, password, role) { + const result = await fetch(this._baseurl + "/v2/api/lambda/login", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + }, + body: JSON.stringify({ + email, + password, + role + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.getHeader = function () { + return { + Authorization: "Bearer " + localStorage.getItem("token"), + "x-project": base64Encode + }; + }; + + this.baseUrl = function () { + return this._baseurl; + }; + this.uploadUrl = function () { + return this._baseurl + "/v2/api/lambda/upload"; + }; + + this.upload = async function (file) { + let formData = new FormData(); + formData.append("file", file); + + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const uploadResult = await fetch(this._baseurl + `/v2/api/lambda/upload`, { + method: "post", + headers: header, + body: formData + }).then((res) => res.json()); + return uploadResult; + }; + this.getProfile = async function () { + const result = await fetch(this._baseurl + "/v2/api/lambda/profile", { + method: "get", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.check = async function (role) { + const result = await fetch(this._baseurl + "/v2/api/lambda/check", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + role + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + return json; + }; + + this.getProfilePreference = async function () { + const result = await fetch(this._baseurl + "/v2/api/lambda/preference", { + method: "get", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // update email + this.updateEmail = async function (email) { + const result = await fetch(this._baseurl + "/v2/api/lambda/update/email", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + email + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // update password + this.updatePassword = async function (password) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/update/password", + { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + password + }) + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // update email + this.updateEmailByAdmin = async function (email, id) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/admin/update/email", + { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + email, + id + }) + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // update password + this.updatePasswordByAdmin = async function (password, id) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/admin/update/password", + { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + password, + id + }) + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.sendEmailVerification = function () {}; + this.updateEmailVerification = function () {}; + + this.setTable = function (table) { + this._table = table; + }; + + this.getProjectId = function () { + return this._project_id; + }; + + this.logout = function () { + window.localStorage.clear(); + }; + + this.register = async function (email, password, role) { + const result = await fetch(this._baseurl + "/v2/api/lambda/register", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + }, + body: JSON.stringify({ + email, + password, + role + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.createUser = async function (email, password, role) { + const result = await fetch(this._baseurl + "/v2/api/lambda/register", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + email, + password, + role + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.forgot = async function (email) { + const result = await fetch(this._baseurl + "/v2/api/lambda/forgot", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + }, + body: JSON.stringify({ + email + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.reset = async function (token, code, password) { + const result = await fetch(this._baseurl + "/v2/api/lambda/reset", { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode + }, + body: JSON.stringify({ + token, + code, + password + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.callRestAPI = async function (payload, method) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + switch (method) { + case "GET": + const getResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/GET`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonGet = await getResult.json(); + + if (getResult.status === 401) { + throw new Error(jsonGet.message); + } + + if (getResult.status === 403) { + throw new Error(jsonGet.message); + } + return jsonGet; + case "POST": + const insertResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonInsert = await insertResult.json(); + + if (insertResult.status === 401) { + throw new Error(jsonInsert.message); + } + + if (insertResult.status === 403) { + throw new Error(jsonInsert.message); + } + return jsonInsert; + case "PUT": + const updateResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonUpdate = await updateResult.json(); + + if (updateResult.status === 401) { + throw new Error(jsonUpdate.message); + } + + if (updateResult.status === 403) { + throw new Error(jsonUpdate.message); + } + return jsonUpdate; + + // Part: Update Table Without Using ID + case "PUTWHERE": + const updateWhereRes = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) // Note: payload: {set: {[string]: any}, where: {[string]: any}} + } + ); + const jsonUpdateWhereRes = await updateWhereRes.json(); + + if (updateWhereRes.status === 401) { + throw new Error(jsonUpdateWhereRes.message); + } + + return jsonUpdateWhereRes; + + case "DELETE": + const deleteResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonDelete = await deleteResult.json(); + + if (deleteResult.status === 401) { + throw new Error(jsonDelete.message); + } + + if (deleteResult.status === 403) { + throw new Error(jsonDelete.message); + } + return jsonDelete; + case "DELETEALL": + const deleteAllResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonDeleteAll = await deleteAllResult.json(); + + if (deleteAllResult.status === 401) { + throw new Error(jsonDeleteAll.message); + } + + if (deleteAllResult.status === 403) { + throw new Error(jsonDeleteAll.message); + } + return jsonDeleteAll; + case "GETALL": + const getAllResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonGetAll = await getAllResult.json(); + + if (getAllResult.status === 401) { + throw new Error(jsonGetAll.message); + } + + if (getAllResult.status === 403) { + throw new Error(jsonGetAll.message); + } + return jsonGetAll; + case "PAGINATE": + if (!payload.page) { + payload.page = 1; + } + if (!payload.limit) { + payload.limit = 10; + } + const paginateResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonPaginate = await paginateResult.json(); + + if (paginateResult.status === 401) { + throw new Error(jsonPaginate.message); + } + + if (paginateResult.status === 403) { + throw new Error(jsonPaginate.message); + } + return jsonPaginate; + case "AUTOCOMPLETE": + const autocompleteResult = await fetch( + this._baseurl + `/v1/api/rest/${this._table}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonAutocomplete = await autocompleteResult.json(); + + if (autocompleteResult.status === 401) { + throw new Error(jsonAutocomplete.message); + } + + if (autocompleteResult.status === 403) { + throw new Error(jsonAutocomplete.message); + } + return jsonAutocomplete; + default: + break; + } + }; + + this.callRawAPI = async function (uri, payload, method) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const result = await fetch(this._baseurl + uri, { + method: method, + headers: header, + body: JSON.stringify(payload) + }); + const jsonResult = await result.json(); + + if (result.status === 401) { + throw new Error(jsonResult.message); + } + + if (result.status === 403) { + throw new Error(jsonResult.message); + } + return jsonResult; + }; + + // Part: Get All Data by Joining Two Columns + this.callJoinRestAPI = async function ( + table1, + table2, + join_id_1, + join_id_2, + select, + where, + method, + page, + limit + ) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + switch (method) { + case "GETALL": + const result = await fetch( + this._baseurl + `/v1/api/join/${table1}/${table2}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify({ + join_id_1, // "tableName1" + join_id_2, // "tableName2" + select, // "tableName1.field1, tableName2.field2" + where: where || "" // where: ["status=2424", "id=1"] + }) + } + ); + const jsonResult = await result.json(); + + if (result.status === 401) { + throw new Error(jsonResult.message); + } + + if (result.status === 403) { + throw new Error(jsonResult.message); + } + return jsonResult; + + case "PAGINATE": + if (!page) { + page = 1; + } + if (!limit) { + limit = 10; + } + const paginateResult = await fetch( + this._baseurl + `/v1/api/join/${table1}/${table2}/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify({ + join_id_1, // "tableName1" + join_id_2, // "tableName2" + select, // "tableName1.field1, tableName2.field2" + where: where || "", // where: ["status=2424", "id=1"] + page, + limit + }) + } + ); + const jsonPaginate = await paginateResult.json(); + + if (paginateResult.status === 401) { + throw new Error(jsonPaginate.message); + } + + if (paginateResult.status === 403) { + throw new Error(jsonPaginate.message); + } + return jsonPaginate; + + default: + break; + } + }; + + // Part: Get Data by Joining Multiple Columns with Pagination + this.callMultiJoinRestAPI = async function ( + tables, + joinIds, + selectStr, + where, + page, + limit, + method + ) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + if (!page) { + page = 1; + } + if (!limit) { + limit = 10; + } + const paginateResult = await fetch( + this._baseurl + `/v1/api/multi-join/${method}`, + { + method: "post", + headers: header, + body: JSON.stringify({ + tables, // ["tableName1", "tableName2"] + joinIds, // ["tableName1.id", "tableName2.id"] + selectStr, // "tableName1.field1, tableName2.field2" + where, // ["status=2424", "id=1"] + page, + limit + }) + } + ); + const jsonPaginate = await paginateResult.json(); + + if (paginateResult.status === 401) { + throw new Error(jsonPaginate.message); + } + + if (paginateResult.status === 403) { + throw new Error(jsonPaginate.message); + } + return jsonPaginate; + }; + + this.subscribe = function (payload) {}; + this.subscribeChannel = function (channel, payload) {}; + this.subscribeListen = function (channel) {}; + this.unSubscribeChannel = function (channel, payload) {}; + this.broadcast = function (payload) {}; + this.exportCSV = function (payload) {}; + + this.cmsAdd = async function (page, key, type, value) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const insertResult = await fetch(this._baseurl + `/v2/api/lambda/cms`, { + method: "post", + headers: header, + body: JSON.stringify({ + page, + key, + type, + value + }) + }); + const jsonInsert = await insertResult.json(); + + if (insertResult.status === 401) { + throw new Error(jsonInsert.message); + } + + if (insertResult.status === 403) { + throw new Error(jsonInsert.message); + } + return jsonInsert; + }; + + this.cmsEdit = async function (id, page, key, type, value) { + const header = { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const updateResult = await fetch( + this._baseurl + `/v2/api/lambda/cms/` + id, + { + method: "put", + headers: header, + body: JSON.stringify({ + page, + key, + type, + value + }) + } + ); + const jsonInsert = await updateResult.json(); + + if (updateResult.status === 401) { + throw new Error(jsonInsert.message); + } + + if (updateResult.status === 403) { + throw new Error(jsonInsert.message); + } + return jsonInsert; + }; + + this.getToken = function () { + return window.localStorage.getItem("token"); + }; + + // get chat room + this.getMyRoom = async function () { + const result = await fetch( + this._baseurl + "/v3/api/lambda/realtime/room/my", + { + method: "GET", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + } + ); + + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // get chat id + this.getChatId = async function (room_id) { + const result = await fetch( + this._baseurl + `/v2/api/lambda/room?room_id=${room_id}`, + { + method: "get", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + } + ); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // post chat + this.getChats = async function (room_id, chat_id, date) { + const result = await fetch(this._baseurl + `/v3/api/lambda/realtime/chat`, { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + room_id, + chat_id, + date + }) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.restoreChat = async function (room_id) { + await fetch( + this._baseurl + `/v2/api/lambda/v2/api/lambda/room/poll?room=${room_id}`, + { + method: "get", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + } + ); + }; + + // post a new message + this.postMessage = async function (messageDetails) { + const result = await fetch(this._baseurl + `/v3/api/lambda/realtime/send`, { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(messageDetails) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.uploadImage = async function (file) { + const result = await fetch(this._baseurl + `/v2/api/lambda/s3/upload`, { + method: "post", + headers: { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: file + }); + + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.createRoom = async function (roomDetails) { + const result = await fetch(this._baseurl + `/v3/api/lambda/realtime/room`, { + method: "post", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(roomDetails) + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + this.getAllUsers = async function () { + const result = await fetch(this._baseurl + `/v1/api/rest/user/GETALL`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + }); + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + // start pooling + this.startPooling = async function (user_id) { + const result = await fetch( + this._baseurl + `/v3/api/lambda/realtime/room/poll?user_id=${user_id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + } + } + ); + + const json = await result.json(); + + if (result.status === 401) { + throw new Error(json.message); + } + + if (result.status === 403) { + throw new Error(json.message); + } + return json; + }; + + /** + * start stripe functions + */ + + this.addStripeProduct = async function (data) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/stripe/product", + { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.getStripeProducts = async function (paginationParams, filterParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/products?${paginationQuery}&${filterQuery}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getStripeProduct = async function (id) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/product/${id}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.updateStripeProduct = async function (id, payload) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/product/${id}`, + { + method: "put", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.addStripePrice = async function (data) { + const result = await fetch(this._baseurl + "/v2/api/lambda/stripe/price", { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + }); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.getStripePrices = async function (paginationParams, filterParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/prices?${paginationQuery}&${filterQuery}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getStripePrice = async function (id) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/price/${id}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.updateStripePrice = async function (id, payload) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/price/${id}`, + { + method: "put", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getStripeSubscriptions = async function ( + paginationParams, + filterParams + ) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/subscriptions?${paginationQuery}&${filterQuery}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.adminCancelStripeSubscription = async function (subId, data) { + const result = await fetch( + this._baseurl + `/v2/api/lambda/stripe/subscription/${subId}`, + { + method: "delete", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + + return json; + }; + + this.adminCreateUsageCharge = async function (subId, quantity) { + const result = await fetch( + this._baseurl + `/v2/api/lambda/stripe/subscription/usage-charge`, + { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify({ + subId, + quantity + }) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + + return json; + }; + + this.getStripeInvoices = async function (paginationParams, filterParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/invoices?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + this.getStripeInvoicesV2 = async function (paginationParams, filterParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/invoices-v2?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getStripeOrders = async function (paginationParams, filterParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/orders?${paginationQuery}&${filterQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + /** + * ------------------------------------------------------- + */ + + this.initCheckoutSession = async function (data) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/stripe/checkout", + { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.registerAndSubscribe = async function (data) { + /** + * + * @param {object} data {email, password, cardToken, planId} + * @returns + */ + const result = await fetch( + this._baseurl + "/v2/api/lambda/stripe/customer/register-subscribe", + { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.createStripeCustomer = async function (payload) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.createCustomerStripeCard = async function (payload) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer/card`, + { + method: "post", + headers: header, + body: JSON.stringify(payload) + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.createStripeSubscription = async function (data) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/stripe/customer/subscription", + { + method: "post", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.getCustomerStripeSubscription = async function (userId) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer/subscription`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getCustomerStripeSubscriptions = async function ( + paginationParams, + filterParams + ) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const filterQuery = new URLSearchParams(filterParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/customer/subscriptions?${paginationQuery}&${filterQuery}`, + { + method: "get", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.changeStripeSubscription = async function (data) { + const result = await fetch( + this._baseurl + "/v2/api/lambda/stripe/customer/subscription", + { + method: "put", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.cancelStripeSubscription = async function (subId, data) { + const result = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer/subscription/${subId}`, + { + method: "delete", + headers: { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }, + body: JSON.stringify(data) + } + ); + + const json = await result.json(); + if ([401, 403, 500].includes(result.status)) { + throw new Error(json.message); + } + return json; + }; + + this.getCustomerStripeDetails = async function () { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getCustomerStripeCards = async function (paginationParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer/cards?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getCustomerStripeInvoices = async function (paginationParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/customer/invoices?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getCustomerStripeCharges = async function (paginationParams) { + const header = { + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const paginationQuery = new URLSearchParams(paginationParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/customer/charges?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.getCustomerStripeOrders = async function (paginationParams) { + const header = { + Authorization: "Bearer " + localStorage.getItem("token"), + "x-project": base64Encode + }; + const paginationQuery = new URLSearchParams(paginationParams); + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/customer/orders?${paginationQuery}`, + { + method: "get", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.projectHealth = async function () { + const header = { + "x-project": base64Encode + }; + + const getResult = await fetch( + this._baseurl + `/v3/api/custom/paradisedock/health`, + { + method: "GET", + headers: header + } + ); + + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.setStripeCustomerDefaultCard = async function (cardId) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + + `/v2/api/lambda/stripe/customer/card/${cardId}/set-default`, + { + method: "put", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + this.deleteCustomerStripeCard = async function (cardId) { + const header = { + "content-Type": "application/json", + "x-project": base64Encode, + Authorization: "Bearer " + localStorage.getItem("token") + }; + const getResult = await fetch( + this._baseurl + `/v2/api/lambda/stripe/customer/card/${cardId}`, + { + method: "delete", + headers: header + } + ); + const jsonGet = await getResult.json(); + + if ([401, 403, 500].includes(getResult.status)) { + throw new Error(jsonGet.message); + } + + return jsonGet; + }; + + /** end stripe functions */ + return this; +} diff --git a/src/utils/constants/constants.jsx b/src/utils/constants/constants.jsx new file mode 100644 index 0000000..6129a0f --- /dev/null +++ b/src/utils/constants/constants.jsx @@ -0,0 +1,110 @@ +export const TabNames = { + Builder: "Builder", + SelectedItems: "Selected Items", +}; + +export const CanvasModes = { + Pan: "Pan", + Still: "Still", +}; + +export const DockPanelCategories = { + RollIn: "ROLL-IN", + Floating: "FLOATING", + Sectional: "SECTIONAL", + Wedges: "WEDGES", + Ramps: "RAMPS", + BoatLift2: "BOAT LIFTS (2 CYLINDER)", + BoatLift4: "BOAT LIFTS (4 CYLINDER)", + Accessories: "ACCESSORIES", +}; +export const Tables = { + Docks: "docks", + Boat_lifts: "boat_lifts", + Accessories: "accessories", + Wedges: "wedges", + Ramps: "ramps", + ReferenceItems: "reference_items", + Dealers: "dealers", + Quotes: "quotes", +}; +export const BoatLiftRange = ["4", "5", "6"]; + +export const categoryMapping = { + 0: "Floating Dock", + 1: "Sectional Dock", + 2: "Roll-In Dock", +}; +export const wedgesCategoryMapping = { + 0: "Floating Wedges", + 1: "Roll-In Wedges", +}; +export const rampsCategoryMapping = { + 0: "Pivot Ramp", + 1: "Truss Ramp", +}; +export const materialsMapping = { + 0: "Wood Grain", + 1: "Perforated", + 2: "Grey Aluminium", +}; +export const wedgesAndRampsMaterialsMapping = { + 0: "Wood Grain", + 1: "Grey Aluminium", + 2: "Perforated", +}; + +export const MaterialType = { + Woodgrain: 0, + Perforated: 1, + Gray: 2, +}; +export const DockPanelCategoryMap = { + Floating: 0, + Sectional: 1, + RollIn: 2, +}; +export const CylinderType = { + "BOAT LIFTS (2 CYLINDER)": 2, + "BOAT LIFTS (4 CYLINDER)": 4, +}; +export const EstimateSteps = { + ContactInformation: 1, + LakeSurrounding: 2, + Comments: 3, + InquirySubmitted: 4, +}; +// export const IsDealer = { +// YES: "Yes", +// NO: "No", +// } +export const Truthy = { + True: true, + False: false, +}; +export const SnapType = { + Snap: "Snap", + SnapClone: "SnapClone", +}; +export const scaleFactor = 0.2; +export const oneFeet = 303 * scaleFactor; +// export const edgeSnapThreshold = 100 +export const edgeSnapThreshold = 15; +export const nintyDeg = 90; +export const fortyFiveDeg = 45; +export const twoSeventyDeg = 270; +export const threeFifteenDeg = 315; +export const oneEightyDeg = 180; +export const twoTwentyFiveDeg = 225; +export const onThirtyFiveDeg = 135; +// export const edgeSnapThreshold = 100 / 1.889 +export const deleteIcon = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAA+VBMVEVHcEz/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////NhAsAAAAUnRSTlMAEnBvAv4H0xYGExTPXPBgn4QeVvb13s5eXWfNe57xZVT0M3ZYlwgwNDLg0d1aX2FbIKZ6px+F0sx9gqmoeFdzmJqUnZWZ4dBm8hliZJxjfNcVFGav3QAAATZJREFUKM91kmdjgjAQhqMyRBFQUMSFo9Y6a622ru692/f//5gmARFpvQ9cLk9yee84QjZ2UsoaRrY0IxETRip8sx0hTI7OAW1S0fXKVAP6zS05sDAYKt5ayRRgXQZExL2yPSi1IZp+NgtXuy+3kO9yBSpuo6ra6DMtIxSUKFIGyFCnYsiiWCLNXDoRY85BmVYKjV+KQ04SkpQR51JqYo6UMOE5UjJllMgpHq7hkjOcevnZtsyvMqsgS6o4JgELCHlDNYR+aLe+X/3gEx/7En7RhM+Y/idjhXcy2y+e2LzwSMlFzPn3b6MOexiz9tq4i6JrPPFf3cyjtUsu0Oh4K1PEgxTKdgOxvgnMPHqOD6XiIxr17cEunafaeqHri9ULUO7sDFum7M+aOB8LUVU5d2kYSzcXbPwCfA4wgva2ZmUAAAAASUVORK5CYII="; +// export const deleteIcon = "data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='595.275px' height='595.275px' viewBox='200 215 230 470' xml:space='preserve'%3E%3Ccircle style='fill:%23F44336;' cx='299.76' cy='439.067' r='218.516'/%3E%3Cg%3E%3Crect x='267.162' y='307.978' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -222.6202 340.6915)' style='fill:white;' width='65.545' height='262.18'/%3E%3Crect x='266.988' y='308.153' transform='matrix(0.7071 0.7071 -0.7071 0.7071 398.3889 -83.3116)' style='fill:white;' width='65.544' height='262.179'/%3E%3C/g%3E%3C/svg%3E"; + +export const rotateIcon = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAeCAMAAAAfOR5kAAABJlBMVEVHcEz///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+w8PQxAAAAYXRSTlMAAQnFCtXWJwj3/NLyWEH1K/YZ3LXxDkX+YpQwpgZMBfCNdFpuiw+ezGeqFhqZ6yaXICUL+NnjQ5KbI5yCxgMSOVx/1Mhv76tmLP0tt+oMXsrP3q/TFJ+aR49PDcmA+gfk0N+uwAAAAOZJREFUKM9t0NdSAkEQheEhLMsSJZkDJsScc0DAHFAxgNn//V/CQmzKcrovpk59cy6mx5j2FPxGmXIsYGPtCbYs7XogdWzpTRLn8j+W7uHq0epWac317eleIvyH97m4q3s/d97USod3cpwZkz6MTxYjROaD4nFS5+20vLRG1BXfxZGOG2Vh8TcHHbalspolMyvrJBkX9weYkTxNfkjyXIxNyaODvs7TxjjSPjN8gqt5g3eNX8hq7JLR+JMPjV/JaZzmS+MEbxpXeFbUt05T4WE2DpTdQ0wo5X5CI7Z293oD2jv6elrnN1VOI4sPCr1mAAAAAElFTkSuQmCC"; +// export const rotateIcon = +// "data:image/svg+xml,%3Csvg height='32px' width='32px' fill='%23000000' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' x='0px' y='0px' viewBox='0 0 33.317 28' enable-background='new 0 0 33.317 28' xml:space='preserve'%3E%3Cpath d='M16.659,24c-5.078,0-9.213-3.987-9.475-9h2.975l-4.5-5l-4.5,5h3.025c0.264,6.671,5.74,12,12.475,12c3.197,0,6.104-1.21,8.315-3.185l-2.122-2.122C21.188,23.127,19.027,24,16.659,24z'%3E%3C/path%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M29.133,14c-0.265-6.669-5.74-12-12.475-12c-3.197,0-6.104,1.21-8.315,3.185l2.122,2.122C12.129,5.873,14.29,5,16.659,5c5.077,0,9.213,3.987,9.475,9h-2.975l4.5,5l4.5-5H29.133z'%3E%3C/path%3E%3C/svg%3E"; +export const dock_image = + "data:image/png;base64,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"; diff --git a/src/utils/constants/index.js b/src/utils/constants/index.js new file mode 100644 index 0000000..2da2609 --- /dev/null +++ b/src/utils/constants/index.js @@ -0,0 +1,31 @@ +export { + TabNames, + MaterialType, + DockPanelCategories, + BoatLiftRange, + CanvasModes, + categoryMapping, + materialsMapping, + DockPanelCategoryMap, + CylinderType, + EstimateSteps, + Tables, + Truthy, + dock_image, + scaleFactor, + edgeSnapThreshold, + oneFeet, + SnapType, + fortyFiveDeg, + nintyDeg, + onThirtyFiveDeg, + oneEightyDeg, + threeFifteenDeg, + twoSeventyDeg, + twoTwentyFiveDeg, + deleteIcon, + rotateIcon, +} from './constants' +export { + selectedItemsTableColumns, +} from './tableColumns' \ No newline at end of file diff --git a/src/utils/constants/tableColumns.jsx b/src/utils/constants/tableColumns.jsx new file mode 100644 index 0000000..2534a93 --- /dev/null +++ b/src/utils/constants/tableColumns.jsx @@ -0,0 +1,113 @@ + +export const selectedItemsTableColumns = [ + // { + // header: "Action", + // accessor: "", + // }, + + + { + header: 'Item Name', + accessor: 'itemName', + isSorted: true, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Category', + accessor: 'category', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Name', + accessor: 'name', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Materials', + accessor: 'materials', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Length', + accessor: 'length', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Width', + accessor: 'width', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'Model', + accessor: 'model', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'LIFT RANGE', + accessor: 'lift_range', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: { 0: 'No', 1: 'Yes' } + }, + { + header: 'NO OF CYLINDERS', + accessor: 'no_of_cylinders', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, + { + header: 'WEIGHT CAPACITY', + accessor: 'weight_capacity', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: { 0: 'Resting', 1: 'Bolted', 2: 'Other' } + }, + { + header: 'IMAGE', + accessor: 'image', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: { 0: 'Rocky', 1: 'Silty', 2: 'Sandy' } + }, + { + header: 'TOP VIEW', + accessor: 'top_view', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: { 0: 'No', 1: 'Yes' } + }, + { + header: 'THUMBNAIL', + accessor: 'thumbnail', + isSorted: false, + isSortedDesc: false, + mappingExist: false, + mappings: {} + }, +]; diff --git a/src/utils/helper/helper.js b/src/utils/helper/helper.js new file mode 100644 index 0000000..65fa025 --- /dev/null +++ b/src/utils/helper/helper.js @@ -0,0 +1,8 @@ +export const capitalize = (string) => { + const removedSpecialCharacters = string.replace(/[^a-zA-Z0-9]/g, " ") + + const splitWords = removedSpecialCharacters.split(' ') + const capitalized = splitWords.map((dt) => `${dt[0].toUpperCase()}${dt.substring(1).toLowerCase()}`) + + return capitalized.join(" ") +} \ No newline at end of file diff --git a/src/utils/helper/index.js b/src/utils/helper/index.js new file mode 100644 index 0000000..5b938ec --- /dev/null +++ b/src/utils/helper/index.js @@ -0,0 +1 @@ +export { capitalize } from "./helper" \ No newline at end of file diff --git a/src/utils/utils.jsx b/src/utils/utils.jsx new file mode 100644 index 0000000..7ad846c --- /dev/null +++ b/src/utils/utils.jsx @@ -0,0 +1,143 @@ +import { categoryMapping, materialsMapping, MaterialType } from "./constants"; +import { + rampsCategoryMapping, + wedgesAndRampsMaterialsMapping, + wedgesCategoryMapping, +} from "./constants/constants"; + +export function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} + +export const getNonNullValue = (value) => { + if (value !== "") { + return value; + } else { + return undefined; + } +}; + +export function filterEmptyFields(object) { + Object.keys(object).forEach((key) => { + if (empty(object[key])) { + delete object[key]; + } + }); + return object; +} + +export function empty(value) { + return ( + value === "" || + value === null || + value === undefined || + value === "undefined" + ); +} + +export const isImage = (file) => { + const validImageTypes = ["image/gif", "image/jpeg", "image/jpg", "image/png"]; + if (validImageTypes.includes(file.file.type)) return true; + return false; +}; + +export class Stack { + constructor() { + this._undo = []; + this._redo = []; + this._update = false; + this._updateCount = 0; + } + + updateStack(data) { + const duplicateState = this._undo.find((state) => state === data); + if (duplicateState) { + return this._undo[this._undo.length - 1]; + } + const parsed = JSON.parse(data); + const snapCloneFound = parsed.objects.find((object) => object.snapClone); + if (snapCloneFound) { + return this._undo[this._undo.length - 1]; + } + + this._undo.push(data); + this._update = true; + this._updateCount += 1; + // console.log( this._undo ) + return this._undo[this._undo.length - 1]; + } + undo() { + if (this._updateCount === 1) { + const redoLast = this._undo.splice(0, 1); + this._redo.push(redoLast[0]); + this._update = false; + // console.log( this._undo, "undo" ) + // console.log( this._redo, "redo" ) + this._update = false; + this._updateCount -= 1; + return { + currentCount: 0, + }; + } else if (this._updateCount > 1) { + const redoLast = this._undo.splice(this._undo.length - 1, 1); + this._redo.push(redoLast[0]); + const currentState = this._undo[this._undo.length - 1]; + this._updateCount -= 1; + this._update = false; + console.log("UNDO"); + // console.log( this._undo, "undo" ) + // console.log( this._redo, "redo" ) + return { + currentState, + currentCount: this._undo.length, + }; + } else { + return { + currentCount: -1, + }; + } + } + redo() { + console.log(this._update, "update"); + if (this._update) { + if (this._redo.length > 0) { + this._redo = []; + } + return { + currentState: null, + }; + } else { + if (this._redo.length > 0) { + const undoLast = this._redo.splice(this._redo.length - 1, 1); + this._undo.push(undoLast[0]); + this._updateCount += 1; + this._update = false; + + console.log("REDO"); + // console.log( this._undo, "undo" ) + // console.log( this._redo, "redo" ) + return { + currentState: undoLast, + }; + } else { + return { currentState: null }; + } + } + } +} + +export const getMaterial = (materials) => { + return materialsMapping[materials]; +}; +export const getWedgesAndRampsMaterial = (materials) => { + return wedgesAndRampsMaterialsMapping[materials]; +}; +export const getCategory = (category) => { + return categoryMapping[category]; +}; +export const getWedgesCategory = (category) => { + return wedgesCategoryMapping[category]; +}; +export const getRampsCategory = (category) => { + return rampsCategoryMapping[category]; +}; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..159a23b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./src/**/*.{js,jsx,ts,tsx}", "./node_modules/tw-elements/dist/js/**/*.js"], + theme: { + extend: {}, + }, + plugins: [require("tw-elements/dist/plugin")], +}; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..1e82720 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "path"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + Components: path.resolve(__dirname, "./src/components"), + Pages: path.resolve(__dirname, "./src/pages"), + Utils: path.resolve(__dirname, "./src/utils"), + Assets: path.resolve(__dirname, "./src/assets"), + Src: path.resolve(__dirname, "./src") + } + }, + server: { + port: 3001 + } +});