From 54cdb5aca3a6308292a901d3e75a092a57431c0e Mon Sep 17 00:00:00 2001 From: Daniel K Date: Sat, 8 Jun 2024 13:48:24 +0100 Subject: [PATCH] refactor: Introduce @karrio/core package to extract core module packages from the dashboard app --- apps/dashboard/package.json | 1 + .../src/components/shipment-preview.tsx | 59 - .../src/components/workflow-event-preview.tsx | 28 - apps/dashboard/src/context/metadata.ts | 12 - apps/dashboard/src/layouts/section-layout.tsx | 48 - .../src/modules/Admin/carrier_connections.tsx | 56 - apps/dashboard/src/modules/Admin/index.tsx | 312 -- .../modules/Admin/organization_accounts.tsx | 30 - .../src/modules/Admin/surcharges.tsx | 34 - .../src/modules/Admin/user_accounts.tsx | 41 - apps/dashboard/src/modules/Billing/index.tsx | 77 - .../src/modules/Developers/event.tsx | 108 - .../src/modules/Developers/events.tsx | 155 - .../src/modules/Developers/index.tsx | 212 - apps/dashboard/src/modules/Developers/log.tsx | 258 - .../dashboard/src/modules/Developers/logs.tsx | 172 - .../src/modules/Developers/webhooks.tsx | 180 - apps/dashboard/src/modules/Home/index.tsx | 335 -- .../modules/Manifests/create_manifests.tsx | 287 - .../dashboard/src/modules/Manifests/index.tsx | 259 - .../src/modules/Orders/draft_order.tsx | 389 -- apps/dashboard/src/modules/Orders/index.tsx | 382 -- apps/dashboard/src/modules/Orders/order.tsx | 313 -- .../src/modules/Password/reset/index.tsx | 128 - .../src/modules/Password/reset/sent.tsx | 32 - .../src/modules/Registration/signup.tsx | 157 - .../src/modules/Resources/graphiql.tsx | 47 - .../src/modules/Settings/addresses.tsx | 217 - .../src/modules/Settings/organization.tsx | 79 - .../src/modules/Settings/parcels.tsx | 206 - .../src/modules/Settings/template.tsx | 222 - .../src/modules/Settings/templates.tsx | 177 - .../dashboard/src/modules/Shipments/index.tsx | 322 -- .../src/modules/Shipments/shipment.tsx | 566 -- apps/dashboard/src/modules/Trackers/index.tsx | 219 - .../src/modules/Trackers/tracking-page.tsx | 125 - .../dashboard/src/modules/Workflows/event.tsx | 186 - .../src/modules/Workflows/events.tsx | 173 - .../dashboard/src/modules/Workflows/index.tsx | 107 - .../src/modules/Workflows/workflow.tsx | 637 --- apps/dashboard/src/pages/_app.tsx | 20 +- .../_sites/[site]/accept-invite/index.tsx | 5 +- .../[site]/admin/carrier_connections.tsx | 5 +- .../src/pages/_sites/[site]/admin/index.tsx | 2 +- .../[site]/admin/organization_accounts.tsx | 5 +- .../pages/_sites/[site]/admin/surcharges.tsx | 5 +- .../_sites/[site]/admin/user_accounts.tsx | 5 +- .../src/pages/_sites/[site]/billing/index.tsx | 2 +- .../pages/_sites/[site]/connections/index.tsx | 2 +- .../_sites/[site]/connections/rate-sheets.tsx | 5 +- .../_sites/[site]/connections/system.tsx | 5 +- .../_sites/[site]/create_label/index.tsx | 5 +- .../pages/_sites/[site]/developers/api.tsx | 2 +- .../_sites/[site]/developers/apikeys.tsx | 5 +- .../_sites/[site]/developers/events/[id].tsx | 5 +- .../_sites/[site]/developers/events/index.tsx | 5 +- .../pages/_sites/[site]/developers/index.tsx | 2 +- .../_sites/[site]/developers/logs/[id].tsx | 5 +- .../_sites/[site]/developers/logs/index.tsx | 5 +- .../_sites/[site]/developers/webhooks.tsx | 5 +- .../pages/_sites/[site]/draft_orders/[id].tsx | 5 +- .../src/pages/_sites/[site]/email/[token].tsx | 5 +- .../src/pages/_sites/[site]/email/change.tsx | 5 +- .../src/pages/_sites/[site]/index.tsx | 2 +- .../src/pages/_sites/[site]/login/index.tsx | 5 +- .../[site]/manifests/create_manifests.tsx | 5 +- .../pages/_sites/[site]/manifests/index.tsx | 2 +- .../src/pages/_sites/[site]/orders/[id].tsx | 2 +- .../_sites/[site]/orders/create_label.tsx | 5 +- .../_sites/[site]/orders/create_labels.tsx | 5 +- .../_sites/[site]/orders/create_shipment.tsx | 5 +- .../src/pages/_sites/[site]/orders/index.tsx | 2 +- .../_sites/[site]/password/reset/done.tsx | 5 +- .../_sites/[site]/password/reset/index.tsx | 5 +- .../_sites/[site]/password/reset/request.tsx | 5 +- .../_sites/[site]/password/reset/sent.tsx | 5 +- .../_sites/[site]/resources/graphiql.tsx | 5 +- .../_sites/[site]/resources/reference.tsx | 5 +- .../pages/_sites/[site]/settings/account.tsx | 5 +- .../_sites/[site]/settings/addresses.tsx | 5 +- .../_sites/[site]/settings/organization.tsx | 5 +- .../pages/_sites/[site]/settings/parcels.tsx | 5 +- .../pages/_sites/[site]/settings/profile.tsx | 5 +- .../pages/_sites/[site]/settings/template.tsx | 5 +- .../_sites/[site]/settings/templates.tsx | 5 +- .../pages/_sites/[site]/shipments/[id].tsx | 5 +- .../_sites/[site]/shipments/create_labels.tsx | 5 +- .../pages/_sites/[site]/shipments/index.tsx | 2 +- .../src/pages/_sites/[site]/signup/index.tsx | 5 +- .../pages/_sites/[site]/signup/success.tsx | 5 +- .../[site]/test/admin/carrier_connections.tsx | 5 +- .../pages/_sites/[site]/test/admin/index.tsx | 2 +- .../test/admin/organization_accounts.tsx | 5 +- .../_sites/[site]/test/admin/surcharges.tsx | 5 +- .../[site]/test/admin/user_accounts.tsx | 5 +- .../_sites/[site]/test/connections/index.tsx | 2 +- .../[site]/test/connections/rate-sheets.tsx | 5 +- .../_sites/[site]/test/connections/system.tsx | 5 +- .../_sites/[site]/test/create_label/index.tsx | 5 +- .../_sites/[site]/test/developers/api.tsx | 2 +- .../_sites/[site]/test/developers/apikeys.tsx | 5 +- .../[site]/test/developers/events/[id].tsx | 5 +- .../[site]/test/developers/events/index.tsx | 5 +- .../_sites/[site]/test/developers/index.tsx | 2 +- .../[site]/test/developers/logs/[id].tsx | 5 +- .../[site]/test/developers/logs/index.tsx | 5 +- .../[site]/test/developers/webhooks.tsx | 5 +- .../_sites/[site]/test/draft_orders/[id].tsx | 5 +- .../src/pages/_sites/[site]/test/index.tsx | 2 +- .../test/manifests/create_manifests.tsx | 5 +- .../_sites/[site]/test/manifests/index.tsx | 2 +- .../pages/_sites/[site]/test/orders/[id].tsx | 2 +- .../[site]/test/orders/create_label.tsx | 5 +- .../[site]/test/orders/create_labels.tsx | 5 +- .../[site]/test/orders/create_shipment.tsx | 5 +- .../pages/_sites/[site]/test/orders/index.tsx | 2 +- .../_sites/[site]/test/resources/graphiql.tsx | 5 +- .../[site]/test/resources/reference.tsx | 5 +- .../_sites/[site]/test/settings/account.tsx | 5 +- .../_sites/[site]/test/settings/addresses.tsx | 5 +- .../[site]/test/settings/organization.tsx | 5 +- .../_sites/[site]/test/settings/parcels.tsx | 5 +- .../_sites/[site]/test/settings/profile.tsx | 5 +- .../_sites/[site]/test/settings/template.tsx | 5 +- .../_sites/[site]/test/settings/templates.tsx | 5 +- .../_sites/[site]/test/shipments/[id].tsx | 5 +- .../[site]/test/shipments/create_labels.tsx | 5 +- .../_sites/[site]/test/shipments/index.tsx | 2 +- .../_sites/[site]/test/trackers/index.tsx | 2 +- .../_sites/[site]/test/workflows/[id].tsx | 5 +- .../[site]/test/workflows/events/[id].tsx | 5 +- .../[site]/test/workflows/events/index.tsx | 5 +- .../_sites/[site]/test/workflows/index.tsx | 2 +- .../pages/_sites/[site]/trackers/index.tsx | 2 +- .../src/pages/_sites/[site]/tracking/[id].tsx | 5 +- .../pages/_sites/[site]/workflows/[id].tsx | 5 +- .../_sites/[site]/workflows/events/[id].tsx | 5 +- .../_sites/[site]/workflows/events/index.tsx | 5 +- .../pages/_sites/[site]/workflows/index.tsx | 2 +- .../src/pages/accept-invite/index.tsx | 5 +- .../src/pages/admin/carrier_connections.tsx | 5 +- apps/dashboard/src/pages/admin/index.tsx | 2 +- .../src/pages/admin/organization_accounts.tsx | 5 +- apps/dashboard/src/pages/admin/surcharges.tsx | 5 +- .../src/pages/admin/user_accounts.tsx | 5 +- .../src/pages/api/auth/[...nextauth].ts | 66 +- apps/dashboard/src/pages/billing/index.tsx | 2 +- .../dashboard/src/pages/connections/index.tsx | 2 +- .../src/pages/connections/rate-sheets.tsx | 5 +- .../src/pages/connections/system.tsx | 5 +- .../src/pages/create_label/index.tsx | 5 +- apps/dashboard/src/pages/developers/api.tsx | 2 +- .../src/pages/developers/apikeys.tsx | 5 +- .../src/pages/developers/events/[id].tsx | 5 +- .../src/pages/developers/events/index.tsx | 5 +- apps/dashboard/src/pages/developers/index.tsx | 2 +- .../src/pages/developers/logs/[id].tsx | 5 +- .../src/pages/developers/logs/index.tsx | 5 +- .../src/pages/developers/webhooks.tsx | 5 +- .../dashboard/src/pages/draft_orders/[id].tsx | 5 +- apps/dashboard/src/pages/email/[token].tsx | 5 +- apps/dashboard/src/pages/email/change.tsx | 5 +- apps/dashboard/src/pages/index.tsx | 2 +- apps/dashboard/src/pages/login/index.tsx | 5 +- .../src/pages/manifests/create_manifests.tsx | 5 +- apps/dashboard/src/pages/manifests/index.tsx | 2 +- apps/dashboard/src/pages/orders/[id].tsx | 2 +- .../src/pages/orders/create_label.tsx | 5 +- .../src/pages/orders/create_labels.tsx | 5 +- .../src/pages/orders/create_shipment.tsx | 5 +- apps/dashboard/src/pages/orders/index.tsx | 2 +- .../src/pages/password/reset/done.tsx | 5 +- .../src/pages/password/reset/index.tsx | 5 +- .../src/pages/password/reset/request.tsx | 5 +- .../src/pages/password/reset/sent.tsx | 5 +- .../src/pages/resources/graphiql.tsx | 5 +- .../src/pages/resources/reference.tsx | 5 +- apps/dashboard/src/pages/settings/account.tsx | 5 +- .../src/pages/settings/addresses.tsx | 5 +- .../src/pages/settings/organization.tsx | 5 +- apps/dashboard/src/pages/settings/parcels.tsx | 5 +- apps/dashboard/src/pages/settings/profile.tsx | 5 +- .../dashboard/src/pages/settings/template.tsx | 5 +- .../src/pages/settings/templates.tsx | 5 +- apps/dashboard/src/pages/shipments/[id].tsx | 5 +- .../src/pages/shipments/create_labels.tsx | 5 +- apps/dashboard/src/pages/shipments/index.tsx | 2 +- apps/dashboard/src/pages/signup/index.tsx | 5 +- apps/dashboard/src/pages/signup/success.tsx | 5 +- .../pages/test/admin/carrier_connections.tsx | 5 +- apps/dashboard/src/pages/test/admin/index.tsx | 2 +- .../test/admin/organization_accounts.tsx | 5 +- .../src/pages/test/admin/surcharges.tsx | 5 +- .../src/pages/test/admin/user_accounts.tsx | 5 +- .../src/pages/test/connections/index.tsx | 2 +- .../pages/test/connections/rate-sheets.tsx | 5 +- .../src/pages/test/connections/system.tsx | 5 +- .../src/pages/test/create_label/index.tsx | 5 +- .../src/pages/test/developers/api.tsx | 2 +- .../src/pages/test/developers/apikeys.tsx | 5 +- .../src/pages/test/developers/events/[id].tsx | 5 +- .../pages/test/developers/events/index.tsx | 5 +- .../src/pages/test/developers/index.tsx | 2 +- .../src/pages/test/developers/logs/[id].tsx | 5 +- .../src/pages/test/developers/logs/index.tsx | 5 +- .../src/pages/test/developers/webhooks.tsx | 5 +- .../src/pages/test/draft_orders/[id].tsx | 5 +- apps/dashboard/src/pages/test/index.tsx | 2 +- .../pages/test/manifests/create_manifests.tsx | 5 +- .../src/pages/test/manifests/index.tsx | 2 +- apps/dashboard/src/pages/test/orders/[id].tsx | 2 +- .../src/pages/test/orders/create_label.tsx | 5 +- .../src/pages/test/orders/create_labels.tsx | 5 +- .../src/pages/test/orders/create_shipment.tsx | 5 +- .../dashboard/src/pages/test/orders/index.tsx | 2 +- .../src/pages/test/resources/graphiql.tsx | 5 +- .../src/pages/test/resources/reference.tsx | 5 +- .../src/pages/test/settings/account.tsx | 5 +- .../src/pages/test/settings/addresses.tsx | 5 +- .../src/pages/test/settings/organization.tsx | 5 +- .../src/pages/test/settings/parcels.tsx | 5 +- .../src/pages/test/settings/profile.tsx | 5 +- .../src/pages/test/settings/template.tsx | 5 +- .../src/pages/test/settings/templates.tsx | 5 +- .../src/pages/test/shipments/[id].tsx | 5 +- .../pages/test/shipments/create_labels.tsx | 5 +- .../src/pages/test/shipments/index.tsx | 2 +- .../src/pages/test/trackers/index.tsx | 2 +- .../src/pages/test/workflows/[id].tsx | 5 +- .../src/pages/test/workflows/events/[id].tsx | 5 +- .../src/pages/test/workflows/events/index.tsx | 5 +- .../src/pages/test/workflows/index.tsx | 2 +- apps/dashboard/src/pages/trackers/index.tsx | 2 +- apps/dashboard/src/pages/tracking/[id].tsx | 5 +- apps/dashboard/src/pages/workflows/[id].tsx | 5 +- .../src/pages/workflows/events/[id].tsx | 5 +- .../src/pages/workflows/events/index.tsx | 5 +- apps/dashboard/src/pages/workflows/index.tsx | 2 +- package-lock.json | 38 + packages/core/.eslintrc.js | 3 + .../core}/components/event-preview.tsx | 115 +- .../core}/components/log-preview.tsx | 115 +- .../core}/components/order-preview.tsx | 117 +- packages/core/components/shipment-preview.tsx | 67 + .../core}/components/tracking-preview.tsx | 0 .../components/workflow-event-preview.tsx | 31 + .../src => packages/core}/context/main.ts | 0 packages/core/context/metadata.ts | 14 + .../src => packages/core}/context/tracker.ts | 70 +- packages/core/index.tsx | 0 .../core}/layouts/admin-layout.tsx | 0 .../core}/layouts/authenticated-page.tsx | 0 .../core}/layouts/dashboard-layout.tsx | 0 .../core}/layouts/embed-layout.tsx | 0 .../core}/layouts/main-layout.tsx | 0 packages/core/layouts/section-layout.tsx | 64 + .../modules/Admin/carrier_connections.tsx | 58 + packages/core/modules/Admin/index.tsx | 328 ++ .../modules/Admin/organization_accounts.tsx | 32 + packages/core/modules/Admin/surcharges.tsx | 36 + packages/core/modules/Admin/user_accounts.tsx | 42 + packages/core/modules/Billing/index.tsx | 100 + .../core}/modules/Connections/index.tsx | 234 +- .../core}/modules/Connections/rate-sheets.tsx | 175 +- .../core}/modules/Connections/system.tsx | 176 +- .../core}/modules/Developers/apikeys.tsx | 326 +- packages/core/modules/Developers/event.tsx | 131 + packages/core/modules/Developers/events.tsx | 202 + packages/core/modules/Developers/index.tsx | 301 + packages/core/modules/Developers/log.tsx | 384 ++ packages/core/modules/Developers/logs.tsx | 246 + packages/core/modules/Developers/webhooks.tsx | 250 + packages/core/modules/Home/index.tsx | 492 ++ .../modules/Invitation/accept-invite.tsx | 153 +- .../core}/modules/Labels/create_labels.tsx | 4868 ++++++++--------- .../modules/Manifests/create_manifests.tsx | 425 ++ packages/core/modules/Manifests/index.tsx | 369 ++ .../core}/modules/Orders/create_label.tsx | 3372 ++++++------ packages/core/modules/Orders/draft_order.tsx | 569 ++ packages/core/modules/Orders/index.tsx | 662 +++ packages/core/modules/Orders/order.tsx | 417 ++ .../core}/modules/Password/reset/done.tsx | 64 +- .../core/modules/Password/reset/index.tsx | 166 + .../core}/modules/Password/reset/request.tsx | 212 +- packages/core/modules/Password/reset/sent.tsx | 40 + .../modules/Registration/confirm_email.tsx | 98 +- .../Registration/confirm_email_change.tsx | 155 +- .../core}/modules/Registration/login.tsx | 244 +- packages/core/modules/Registration/signup.tsx | 227 + .../modules/Registration/signup_success.tsx | 70 +- packages/core/modules/Resources/graphiql.tsx | 54 + .../core}/modules/Resources/reference.tsx | 76 +- .../core}/modules/Settings/account.tsx | 220 +- packages/core/modules/Settings/addresses.tsx | 312 ++ .../core/modules/Settings/organization.tsx | 108 + packages/core/modules/Settings/parcels.tsx | 277 + .../core}/modules/Settings/profile.tsx | 241 +- packages/core/modules/Settings/template.tsx | 300 + packages/core/modules/Settings/templates.tsx | 242 + .../core}/modules/Shipments/create_label.tsx | 3588 ++++++------ packages/core/modules/Shipments/index.tsx | 581 ++ packages/core/modules/Shipments/shipment.tsx | 773 +++ packages/core/modules/Trackers/index.tsx | 369 ++ .../core/modules/Trackers/tracking-page.tsx | 157 + packages/core/modules/Workflows/event.tsx | 252 + packages/core/modules/Workflows/events.tsx | 201 + packages/core/modules/Workflows/index.tsx | 123 + packages/core/modules/Workflows/workflow.tsx | 1103 ++++ packages/core/package.json | 26 + packages/core/tsconfig.json | 13 + packages/ui/admin/rate-sheet-management.tsx | 182 +- packages/ui/tsconfig.json | 26 +- tsconfig.json | 6 +- 313 files changed, 18866 insertions(+), 14898 deletions(-) delete mode 100644 apps/dashboard/src/components/shipment-preview.tsx delete mode 100644 apps/dashboard/src/components/workflow-event-preview.tsx delete mode 100644 apps/dashboard/src/context/metadata.ts delete mode 100644 apps/dashboard/src/layouts/section-layout.tsx delete mode 100644 apps/dashboard/src/modules/Admin/carrier_connections.tsx delete mode 100644 apps/dashboard/src/modules/Admin/index.tsx delete mode 100644 apps/dashboard/src/modules/Admin/organization_accounts.tsx delete mode 100644 apps/dashboard/src/modules/Admin/surcharges.tsx delete mode 100644 apps/dashboard/src/modules/Admin/user_accounts.tsx delete mode 100644 apps/dashboard/src/modules/Billing/index.tsx delete mode 100644 apps/dashboard/src/modules/Developers/event.tsx delete mode 100644 apps/dashboard/src/modules/Developers/events.tsx delete mode 100644 apps/dashboard/src/modules/Developers/index.tsx delete mode 100644 apps/dashboard/src/modules/Developers/log.tsx delete mode 100644 apps/dashboard/src/modules/Developers/logs.tsx delete mode 100644 apps/dashboard/src/modules/Developers/webhooks.tsx delete mode 100644 apps/dashboard/src/modules/Home/index.tsx delete mode 100644 apps/dashboard/src/modules/Manifests/create_manifests.tsx delete mode 100644 apps/dashboard/src/modules/Manifests/index.tsx delete mode 100644 apps/dashboard/src/modules/Orders/draft_order.tsx delete mode 100644 apps/dashboard/src/modules/Orders/index.tsx delete mode 100644 apps/dashboard/src/modules/Orders/order.tsx delete mode 100644 apps/dashboard/src/modules/Password/reset/index.tsx delete mode 100644 apps/dashboard/src/modules/Password/reset/sent.tsx delete mode 100644 apps/dashboard/src/modules/Registration/signup.tsx delete mode 100644 apps/dashboard/src/modules/Resources/graphiql.tsx delete mode 100644 apps/dashboard/src/modules/Settings/addresses.tsx delete mode 100644 apps/dashboard/src/modules/Settings/organization.tsx delete mode 100644 apps/dashboard/src/modules/Settings/parcels.tsx delete mode 100644 apps/dashboard/src/modules/Settings/template.tsx delete mode 100644 apps/dashboard/src/modules/Settings/templates.tsx delete mode 100644 apps/dashboard/src/modules/Shipments/index.tsx delete mode 100644 apps/dashboard/src/modules/Shipments/shipment.tsx delete mode 100644 apps/dashboard/src/modules/Trackers/index.tsx delete mode 100644 apps/dashboard/src/modules/Trackers/tracking-page.tsx delete mode 100644 apps/dashboard/src/modules/Workflows/event.tsx delete mode 100644 apps/dashboard/src/modules/Workflows/events.tsx delete mode 100644 apps/dashboard/src/modules/Workflows/index.tsx delete mode 100644 apps/dashboard/src/modules/Workflows/workflow.tsx create mode 100644 packages/core/.eslintrc.js rename {apps/dashboard/src => packages/core}/components/event-preview.tsx (57%) rename {apps/dashboard/src => packages/core}/components/log-preview.tsx (57%) rename {apps/dashboard/src => packages/core}/components/order-preview.tsx (57%) create mode 100644 packages/core/components/shipment-preview.tsx rename {apps/dashboard/src => packages/core}/components/tracking-preview.tsx (100%) create mode 100644 packages/core/components/workflow-event-preview.tsx rename {apps/dashboard/src => packages/core}/context/main.ts (100%) create mode 100644 packages/core/context/metadata.ts rename {apps/dashboard/src => packages/core}/context/tracker.ts (93%) create mode 100644 packages/core/index.tsx rename {apps/dashboard/src => packages/core}/layouts/admin-layout.tsx (100%) rename {apps/dashboard/src => packages/core}/layouts/authenticated-page.tsx (100%) rename {apps/dashboard/src => packages/core}/layouts/dashboard-layout.tsx (100%) rename {apps/dashboard/src => packages/core}/layouts/embed-layout.tsx (100%) rename {apps/dashboard/src => packages/core}/layouts/main-layout.tsx (100%) create mode 100644 packages/core/layouts/section-layout.tsx create mode 100644 packages/core/modules/Admin/carrier_connections.tsx create mode 100644 packages/core/modules/Admin/index.tsx create mode 100644 packages/core/modules/Admin/organization_accounts.tsx create mode 100644 packages/core/modules/Admin/surcharges.tsx create mode 100644 packages/core/modules/Admin/user_accounts.tsx create mode 100644 packages/core/modules/Billing/index.tsx rename {apps/dashboard/src => packages/core}/modules/Connections/index.tsx (62%) rename {apps/dashboard/src => packages/core}/modules/Connections/rate-sheets.tsx (73%) rename {apps/dashboard/src => packages/core}/modules/Connections/system.tsx (66%) rename {apps/dashboard/src => packages/core}/modules/Developers/apikeys.tsx (55%) create mode 100644 packages/core/modules/Developers/event.tsx create mode 100644 packages/core/modules/Developers/events.tsx create mode 100644 packages/core/modules/Developers/index.tsx create mode 100644 packages/core/modules/Developers/log.tsx create mode 100644 packages/core/modules/Developers/logs.tsx create mode 100644 packages/core/modules/Developers/webhooks.tsx create mode 100644 packages/core/modules/Home/index.tsx rename {apps/dashboard/src => packages/core}/modules/Invitation/accept-invite.tsx (61%) rename {apps/dashboard/src => packages/core}/modules/Labels/create_labels.tsx (98%) create mode 100644 packages/core/modules/Manifests/create_manifests.tsx create mode 100644 packages/core/modules/Manifests/index.tsx rename {apps/dashboard/src => packages/core}/modules/Orders/create_label.tsx (97%) create mode 100644 packages/core/modules/Orders/draft_order.tsx create mode 100644 packages/core/modules/Orders/index.tsx create mode 100644 packages/core/modules/Orders/order.tsx rename {apps/dashboard/src => packages/core}/modules/Password/reset/done.tsx (61%) create mode 100644 packages/core/modules/Password/reset/index.tsx rename {apps/dashboard/src => packages/core}/modules/Password/reset/request.tsx (50%) create mode 100644 packages/core/modules/Password/reset/sent.tsx rename {apps/dashboard/src => packages/core}/modules/Registration/confirm_email.tsx (53%) rename {apps/dashboard/src => packages/core}/modules/Registration/confirm_email_change.tsx (60%) rename {apps/dashboard/src => packages/core}/modules/Registration/login.tsx (58%) create mode 100644 packages/core/modules/Registration/signup.tsx rename {apps/dashboard/src => packages/core}/modules/Registration/signup_success.tsx (50%) create mode 100644 packages/core/modules/Resources/graphiql.tsx rename {apps/dashboard/src => packages/core}/modules/Resources/reference.tsx (58%) rename {apps/dashboard/src => packages/core}/modules/Settings/account.tsx (58%) create mode 100644 packages/core/modules/Settings/addresses.tsx create mode 100644 packages/core/modules/Settings/organization.tsx create mode 100644 packages/core/modules/Settings/parcels.tsx rename {apps/dashboard/src => packages/core}/modules/Settings/profile.tsx (56%) create mode 100644 packages/core/modules/Settings/template.tsx create mode 100644 packages/core/modules/Settings/templates.tsx rename {apps/dashboard/src => packages/core}/modules/Shipments/create_label.tsx (97%) create mode 100644 packages/core/modules/Shipments/index.tsx create mode 100644 packages/core/modules/Shipments/shipment.tsx create mode 100644 packages/core/modules/Trackers/index.tsx create mode 100644 packages/core/modules/Trackers/tracking-page.tsx create mode 100644 packages/core/modules/Workflows/event.tsx create mode 100644 packages/core/modules/Workflows/events.tsx create mode 100644 packages/core/modules/Workflows/index.tsx create mode 100644 packages/core/modules/Workflows/workflow.tsx create mode 100644 packages/core/package.json create mode 100644 packages/core/tsconfig.json diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 4ce0ee7d1a..cbae44a37a 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -16,6 +16,7 @@ "@codemirror/lang-xml": "^6.0.1", "@fortawesome/fontawesome-free": "^6.5.1", "@headlessui/react": "^1.7.14", + "@karrio/core": "*", "@karrio/hooks": "*", "@karrio/lib": "*", "@karrio/types": "*", diff --git a/apps/dashboard/src/components/shipment-preview.tsx b/apps/dashboard/src/components/shipment-preview.tsx deleted file mode 100644 index 669a7d2e2b..0000000000 --- a/apps/dashboard/src/components/shipment-preview.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { ShipmentComponent } from '@/modules/Shipments/shipment'; -import { ConfirmModal } from '@karrio/ui/modals/confirm-modal'; -import { useLocation } from '@karrio/lib'; -import React, { useState } from 'react'; - -type ShipmentPreviewContextType = { - previewShipment: (shipmentId: string) => void, -}; - -interface ShipmentPreviewComponent { - children?: React.ReactNode; -} - -export const ShipmentPreviewContext = React.createContext({} as ShipmentPreviewContextType); - -export const ShipmentPreview: React.FC = ({ children }) => { - const { addUrlParam, removeUrlParam } = useLocation(); - const [isActive, setIsActive] = useState(false); - const [key, setKey] = useState(`shipment-${Date.now()}`); - const [shipmentId, setShipmentId] = useState(); - - const previewShipment = (shipmentId: string) => { - setShipmentId(shipmentId); - setIsActive(true); - setKey(`shipment-${Date.now()}`); - addUrlParam('modal', shipmentId); - }; - const dismiss = (_?: any) => { - setShipmentId(undefined); - setIsActive(false); - setKey(`shipment-${Date.now()}`); - removeUrlParam('modal'); - }; - - return ( - <> - - {children} - - -
-
- - {(isActive && shipmentId) &&
-
- - - - - -
-
} - - - -
- - ) -}; diff --git a/apps/dashboard/src/components/workflow-event-preview.tsx b/apps/dashboard/src/components/workflow-event-preview.tsx deleted file mode 100644 index ca06c60b9e..0000000000 --- a/apps/dashboard/src/components/workflow-event-preview.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { ModalFormProps, useModal } from '@karrio/ui/modals/modal'; -import { Component } from '@/modules/Workflows/event'; -import React from 'react'; - -type EventPreviewModalProps = { - eventId?: string; -}; - -export const WorkflowPreviewModal: React.FC> = ({ trigger, ...args }) => { - const modal = useModal(); - - const ModalComponent: React.FC = props => { - const { eventId } = props; - - return ( -
- -
- ) - }; - - return React.cloneElement(trigger, { - onClick: () => modal.open( - , - { className: 'is-medium-modal', backgroundDismiss: true } - ) - }); -}; \ No newline at end of file diff --git a/apps/dashboard/src/context/metadata.ts b/apps/dashboard/src/context/metadata.ts deleted file mode 100644 index cb5c61fe5c..0000000000 --- a/apps/dashboard/src/context/metadata.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { loadAPIMetadata } from "@/context/main"; -import { GetServerSideProps } from "next"; - - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const { res } = ctx; - const metadata = await loadAPIMetadata(ctx).catch(_ => _); - - res.setHeader('Cache-Control', 'public, s-maxage=30, stale-while-revalidate=59') - - return { props: { ...metadata } }; -}; diff --git a/apps/dashboard/src/layouts/section-layout.tsx b/apps/dashboard/src/layouts/section-layout.tsx deleted file mode 100644 index f4e9eed618..0000000000 --- a/apps/dashboard/src/layouts/section-layout.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { Metadata, SessionType } from '@karrio/types'; -import MainLayout from '@/layouts/main-layout'; -import { ServerError, p, url$ } from '@karrio/lib'; -import Link from 'next/link'; -import React from 'react'; - -type SectionLayoutProps = { - metadata?: Metadata, - error?: ServerError, - session?: SessionType, - children?: React.ReactNode, -}; - -export const SectionLayout: React.FC = ({ metadata, error, children }) => { - return ( - -
- -
- - - {children} - -
- -
-
-

- {metadata?.APP_NAME.includes("Karrio") && <> - - © {metadata?.APP_NAME} - - - Documentation - - } -

-
-
- -
-
- ); -}; diff --git a/apps/dashboard/src/modules/Admin/carrier_connections.tsx b/apps/dashboard/src/modules/Admin/carrier_connections.tsx deleted file mode 100644 index 7c7e20126c..0000000000 --- a/apps/dashboard/src/modules/Admin/carrier_connections.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; -import { SystemCarrierManagement } from "@karrio/ui/admin/system-carrier-management"; -import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; -import { ConnectProviderModal } from "@karrio/ui/modals/connect-provider-modal"; -import { RateSheetManagement } from "@karrio/ui/admin/rate-sheet-management"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { bundleContexts } from '@karrio/hooks/utils'; -import { AdminLayout } from "@/layouts/admin-layout"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - ConfirmModal, - ConnectProviderModal, - LabelTemplateEditModalProvider, - RateSheetEditModalProvider, -]); - - -export default function Page(pageProps: any) { - const { APP_NAME } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> -
- Carrier connections -
- - {/* System carriers */} - - -
- - {/* System carriers */} - - -
- - ); - }; - - return AuthenticatedPage(( - - {`Carriers - ${APP_NAME}`} - - - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Admin/index.tsx b/apps/dashboard/src/modules/Admin/index.tsx deleted file mode 100644 index 09f8235784..0000000000 --- a/apps/dashboard/src/modules/Admin/index.tsx +++ /dev/null @@ -1,312 +0,0 @@ -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { Switch } from "@karrio/ui/components/switch"; -import { AdminLayout } from "@/layouts/admin-layout"; -import getConfig from 'next/config'; -import { url$ } from "@karrio/lib"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -const { publicRuntimeConfig } = getConfig(); - - -export default function Page(pageProps: any) { - const { APP_NAME } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - const { metadata } = useAPIMetadata(); - - return ( - <> -
- Platform details -
- - {/* Platfrom config */} -
- -
- Platform -
- -
-
- -
- -
- -

Platform name

-

{metadata?.APP_NAME}

- -
- -
- -
- -

API hostname

-

- {url$`${metadata?.HOST}`.replace('http://', '').replace('https://', '')} -

- -
- -
- -
- -

Dashboard hostname

-

{location.host}

- -
- -
- -
- - {/* Feature flags */} -
- -
- Feature flags -
-
- -
- -
- -
-
- -

Allow user signup

-
-
- -
-
- -
- -
-
- -

user signup will require admin approval

-
-
- -
-
- -
- -
-
- -

Toggle audit logging functionality

-
-
- -
-
- -
- -
-
- -

Enable apps

-
-
- -
-
- -
- -
-
- -

Enable order management panel

-
-
- -
-
- -
- -
- -
- - {/* Email config */} -
- -
- Email config -
-
- -
- -
- -
- -
- -
-

Determine whether the configuration support TLS

-
- -
- -
- -
-

The authentication user (email). e.g: admin@karrio.io

-
- -
- -
- -
-

The authentication password

-
- -
- -
- -
-

The mail server host. e.g: smtp.gmail.com

-
- -
- -
- -
-

The mail server port. e.g: 465 (SSL required) or 587 (TLS required)

-
- -
- -
- -
-

Email sent from. e.g: noreply@karrio.io

-
- -
- -
- -
- - {/* Address validation */} -
- -
- Address Validation Service -
-
- -
- -
- -
- -
- -
-

A Google GeoCoding API key

-
- -
- -
- -
-

The Canada Post AddressComplete service API Key

-
- -
- -
- -
- - {/* Data Retention */} -
- -
- Data retention -
-
- -
- -
- -
- -
- -
-

Order data retention period (in days)

-
- -
- -
- -
-

Tracker data retention period (in days)

-
- -
- -
- -
-

Shipment data retention period (in days)

-
- -
- -
- -
-

API request and SDK tracing logs retention period (in days)

-
- -
- -
- -
- -
- -
- - ); - }; - - return AuthenticatedPage(( - - {`Platform - ${APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Admin/organization_accounts.tsx b/apps/dashboard/src/modules/Admin/organization_accounts.tsx deleted file mode 100644 index 9062ef3593..0000000000 --- a/apps/dashboard/src/modules/Admin/organization_accounts.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { AdminLayout } from "@/layouts/admin-layout"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function Page(pageProps: any) { - const { APP_NAME } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> -
- Organization accounts -
- - ); - }; - - return AuthenticatedPage(( - - {`Organizations - ${APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Admin/surcharges.tsx b/apps/dashboard/src/modules/Admin/surcharges.tsx deleted file mode 100644 index ff338e7338..0000000000 --- a/apps/dashboard/src/modules/Admin/surcharges.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { SurchargeManagement } from "@karrio/ui/admin/surcharge-management"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { AdminLayout } from "@/layouts/admin-layout"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function Page(pageProps: any) { - const { APP_NAME } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> -
- Surcharge & discounts -
- - {/* Surcharges */} - - - ); - }; - - return AuthenticatedPage(( - - {`Surcharges - ${APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Admin/user_accounts.tsx b/apps/dashboard/src/modules/Admin/user_accounts.tsx deleted file mode 100644 index f88ec234bf..0000000000 --- a/apps/dashboard/src/modules/Admin/user_accounts.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { StaffManagement } from "@karrio/ui/admin/staff-management"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { AdminLayout } from "@/layouts/admin-layout"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - ModalProvider, -]); - - -export default function Page(pageProps: any) { - const { APP_NAME } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - return ( - <> -
- User and permissions -
- - {/* Staff */} - - - ); - }; - - return AuthenticatedPage(( - - {`Users - ${APP_NAME}`} - - - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Billing/index.tsx b/apps/dashboard/src/modules/Billing/index.tsx deleted file mode 100644 index eb08fb891a..0000000000 --- a/apps/dashboard/src/modules/Billing/index.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { loadAPIMetadata, checkSubscription, createPortalSession } from "@/context/main"; -import { Metadata, SubscriptionType } from "@karrio/types"; -import { GetServerSideProps, NextPage } from "next"; -import { getSession } from "next-auth/react"; -import { isNone, p } from "@karrio/lib"; -import Head from "next/head"; -import React from "react"; - -type BillingPageProps = { metadata: Metadata, subscription?: SubscriptionType, session_url?: string }; - -const Billing: NextPage = ({ metadata, subscription, session_url }) => { - return ( - <> - {`Billing - ${metadata?.APP_NAME}`} - -
- -
- - - - {!subscription?.is_owner &&
-
-

Your subscription has expired.

-

Please notify your account admnistrator.

-
-
} - - {subscription?.is_owner &&
-
- -

Billing Setup

-

Update your billing and subscription on Stripe.

- - - Open Customer Portal - - -
-
} - -
- -
- - ) -}; - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const session = await getSession(ctx); - - if (isNone((session as any)?.accessToken)) { - return { - redirect: { - permanent: false, - destination: '/login' - } - } - } - - const metadata = await loadAPIMetadata(ctx).catch(_ => _); - const subscription = await checkSubscription(session, metadata.metadata).catch(_ => _); - const portal_session = await createPortalSession( - session, - ctx.req.headers.host as string, - subscription.subscription, - metadata.metadata, - ); - - return { props: { ...metadata, ...subscription, ...portal_session } }; -}; - -export default Billing; diff --git a/apps/dashboard/src/modules/Developers/event.tsx b/apps/dashboard/src/modules/Developers/event.tsx deleted file mode 100644 index a2dae4a6c3..0000000000 --- a/apps/dashboard/src/modules/Developers/event.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { formatDateTimeLong, isNone, notEmptyJSON } from "@karrio/lib"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { useRouter } from "next/dist/client/router"; -import json from 'highlight.js/lib/languages/json'; -import React, { useEffect, useState } from "react"; -import { useEvent } from "@karrio/hooks/event"; -import hljs from "highlight.js"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -hljs.registerLanguage('json', json); - - -export const EventComponent: React.FC<{ eventId?: string }> = ({ eventId }) => { - const router = useRouter(); - const { setLoading } = useLoader(); - const entity_id = eventId || router.query.id as string; - const [data, setData] = useState(); - const { query: { data: { event } = {}, ...query } } = useEvent(entity_id); - - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (event !== undefined) { - setData(JSON.stringify(event || {}, null, 2)); - } - }); - - return ( - <> - {!isNone(event?.id) && <> - -
-
- EVENT -
- {event?.type} -
- -
-

- -

- {!isNone(eventId) &&

- - - - - -

} -
-
- -
- -
-
- Date
- {formatDateTimeLong(event?.created_at)} -
- -
- -
- Source
- Automatic -
-
- -

Event data

-
- - {notEmptyJSON(data) && -
- -
-              
-            
-
} - - } - - ); -}; - - -export default function EventPage(pageProps: any) { - return AuthenticatedPage(( - - {`Event - ${(pageProps as any).metadata?.APP_NAME}`} - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Developers/events.tsx b/apps/dashboard/src/modules/Developers/events.tsx deleted file mode 100644 index 8e0ee6114d..0000000000 --- a/apps/dashboard/src/modules/Developers/events.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { formatDateTimeLong, getURLSearchParams, isNoneOrEmpty } from "@karrio/lib"; -import { EventPreview, EventPreviewContext } from "@/components/event-preview"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { EventsFilter } from "@karrio/ui/filters/events-filter"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { useLoader } from "@karrio/ui/components/loader"; -import { Spinner } from "@karrio/ui/components/spinner"; -import React, { useContext, useEffect } from "react"; -import { useRouter } from "next/dist/client/router"; -import { useEvents } from "@karrio/hooks/event"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -export default function EventsPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const context = useEvents(); - const { setLoading } = useLoader(); - const { previewEvent } = useContext(EventPreviewContext); - const [initialized, setInitialized] = React.useState(false); - const { query: { data: { events } = {}, ...query }, filter, setFilter } = context; - - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - } - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - previewEvent(router.query.modal as string); - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
- Developers -
- -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - API Keys - -
  • -
  • - - Webhooks - -
  • -
  • - - Events - -
  • -
  • - - Logs - -
  • -
-
- - {!query.isFetched && } - - {(query.isFetched && (events?.edges || []).length > 0) && <> -
- - - - - - - - - {(events?.edges || []).map(({ node: event }) => ( - - previewEvent(event.id)}> - - - - - ))} - - -
EVENT
- {`${event.type}`} - - {formatDateTimeLong(event.created_at)} -
-
- -
- - {(events?.edges || []).length} results - - -
- - -
-
- } - - - {(query.isFetched && (events?.edges || []).length == 0) && -
- -
-

No API events found.

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - {`Events - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Developers/index.tsx b/apps/dashboard/src/modules/Developers/index.tsx deleted file mode 100644 index c620432657..0000000000 --- a/apps/dashboard/src/modules/Developers/index.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { SelectField } from "@karrio/ui/components"; -import { useAPIUsage } from "@karrio/hooks/usage"; -import Head from "next/head"; -import { Bar, BarChart, CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis } from "recharts"; -import moment from "moment"; - -export { getServerSideProps } from "@/context/main"; - - -export default function ApiPage(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - const { query: { data: { usage } = {} }, setFilter, filter, USAGE_FILTERS, DAYS_LIST, currentFilter } = useAPIUsage(); - - return ( - <> - -
- Developers -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - API Keys - -
  • -
  • - - Webhooks - -
  • -
  • - - Events - -
  • -
  • - - Logs - -
  • -
-
- - -
- - {/* API usage stats */} -
- -
- Your Integration -
- setFilter(JSON.parse(e.target.value))} - > - {Object.entries(USAGE_FILTERS).map(([key, value]) => ( - - ))} - -
-
-
- -
- -
-
-
- API requests -
- -

{!!usage ? usage?.total_requests : 0}

- -
- {!!usage?.api_requests && - - ({ - name: ((i === 0 || i === DAYS_LIST[currentFilter() || "15 days"].length - 1) ? _ : ''), - total: usage!.api_requests!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - } margin={{ top: 5, right: 15, left: 15, bottom: 5 }}> - - - - - - } -
-
-
- -
-
-
- API errors -
- -

{!!usage ? usage?.total_errors : 0}

- -
- {!!usage?.api_errors && - - ({ - name: ((i === 0 || i === DAYS_LIST[currentFilter() || "15 days"].length - 1) ? _ : ''), - total: usage!.api_errors!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - } margin={{ top: 5, right: 15, left: 15, bottom: 5 }}> - - - - - - } -
-
-
- -
-
- -
- - {/* APIs Overview */} -
- -
- API Details -
-
-
- -
-
- -
-
-
- API Version -
-
{references?.VERSION}
-
-
-
- REST API -
-
- -
-
-
-
- GRAPHQL API -
-
- -
-
-
- -
-
- -
- -
- -
- - - ); - }; - - return AuthenticatedPage(( - - {`API Keys - ${references?.APP_NAME}`} - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Developers/log.tsx b/apps/dashboard/src/modules/Developers/log.tsx deleted file mode 100644 index c1a9f6768a..0000000000 --- a/apps/dashboard/src/modules/Developers/log.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { failsafe, formatDateTimeLong, groupBy, isNone, jsonify, notEmptyJSON } from "@karrio/lib"; -import { Tabs, TabStateProvider } from "@karrio/ui/components/tabs"; -import { StatusCode } from "@karrio/ui/components/status-code-badge"; -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { useRouter } from "next/dist/client/router"; -import json from 'highlight.js/lib/languages/json'; -import { useLog } from "@karrio/hooks/log"; -import hljs from "highlight.js"; -import Head from "next/head"; -import React from "react"; -import moment from "moment"; - -export { getServerSideProps } from "@/context/main"; - -hljs.registerLanguage('json', json); - - -export const LogComponent: React.FC<{ logId?: string }> = ({ logId }) => { - const router = useRouter(); - const { setLoading } = useLoader(); - const entity_id = logId || router.query.id as string; - const [data, setData] = React.useState(); - const [response, setResponse] = React.useState(); - const [query_params, setQueryParams] = React.useState(); - const { query: { data: { log } = {}, ...query } } = useLog(entity_id); - - React.useEffect(() => { - (window as any).moment = moment; - setLoading(query.isFetching); - }, [query.isFetching]); - React.useEffect(() => { - if (log !== undefined) { - setQueryParams(failsafe(() => jsonify(log?.query_params), '{}')); - setResponse(failsafe(() => jsonify(log?.response), '{}')); - setData(failsafe(() => jsonify(log?.data), '{}')); - } - }); - - return ( - <> - {log !== undefined && <> - -
-
- LOG -
- - {log?.method} {log?.path} - -
- {!isNone(logId) &&
- - - - - -
} -
- -
- -
-
-
ID
-
{log?.id}
-
-
-
Date
-
{formatDateTimeLong(log?.requested_at)}
-
-
-
Origin
-
{log?.remote_addr}
-
-
-
IP Address
-
{log?.host}
-
-
- - - - - {notEmptyJSON(response) &&
- -

Response body

- -
- -
-                  
-                
-
- -
} - -
- {notEmptyJSON(query_params) && query_params !== data && <> - -

Request query params

- -
-
-                    
-                  
-
- -
- } - - {notEmptyJSON(data) && <> - -

Request {log?.method} body

- -
- -
-                    
-                  
-
- - } -
- -
- {(log?.records || []).length == 0 &&
- No tracing records... -
} - {(log?.records || []).length > 0 && <> - - {Object.values(groupBy(log!.records, (r: any) => r.record?.request_id)).map((records: any, key) => { - const request = records.find((r: any) => r.key === "request"); - const response = records.find((r: any) => r.key !== "request"); - const request_data = parseRecordData(request?.record); - const response_data = parseRecordData(response?.record); - const tabs = [...(request ? ["request"] : []), ...(response ? [response.key] : [])]; - - return (
-
-

- Carrier: {(request || response)?.meta?.carrier_name} -

-

- Connection: {(request || response)?.meta?.carrier_id} -

-

- URL: {(request?.record || response?.record)?.url} -

-

- Request ID: {(request?.record || response?.record)?.request_id} -

- {request?.timestamp &&

- Request Timestamp: {moment(request.timestamp * 1000).format('LTS')} -

} - {response?.timestamp &&

- Response Timestamp: {moment(response.timestamp * 1000).format('LTS')} -

} -
- - - - {request &&
- -
-                            
-                          
-
} - - {response_data &&
- <", ">\n<")} - style={{ position: 'absolute', right: 0, zIndex: 1 }} - className="button is-primary is-small m-1" - /> -
-                            <", ">\n<"),
-                                  { language: response.record?.format || 'json' }
-                                ).value
-                              }}
-                            />
-                          
-
} - -
-
-
); - })} - - } -
- -
-
- - } - - ); -}; - -export default function LogPage(pageProps: any) { - return AuthenticatedPage(( - - {`Log - ${(pageProps as any).metadata?.APP_NAME}`} - - - ), pageProps); -} - -function parseRecordData(record: any) { - if (!record) return null; - if (record?.format === 'xml') { - return (record.data || record.response || record.error); - } - - return failsafe( - () => jsonify(record.data || record.response || record.error), - (record.data || record.response || record.error) - ) -} diff --git a/apps/dashboard/src/modules/Developers/logs.tsx b/apps/dashboard/src/modules/Developers/logs.tsx deleted file mode 100644 index b3b0d933a5..0000000000 --- a/apps/dashboard/src/modules/Developers/logs.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { formatDateTimeLong, getURLSearchParams, isNone, isNoneOrEmpty } from "@karrio/lib"; -import { LogPreview, LogPreviewContext } from "@/components/log-preview"; -import { StatusCode } from "@karrio/ui/components/status-code-badge"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { LogsFilter } from "@karrio/ui/filters/logs-filter"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useLoader } from "@karrio/ui/components/loader"; -import { Spinner } from "@karrio/ui/components/spinner"; -import React, { useContext, useEffect } from "react"; -import { useRouter } from "next/dist/client/router"; -import { useLogs } from "@karrio/hooks/log"; -import Head from "next/head"; -import { AppLink } from "@karrio/ui/components/app-link"; - -export { getServerSideProps } from "@/context/main"; - - -export default function LogsPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const { setLoading } = useLoader(); - const { previewLog } = useContext(LogPreviewContext); - const [initialized, setInitialized] = React.useState(false); - const context = useLogs(); - const { query: { data: { logs } = {}, ...query }, filter, setFilter } = context; - - const updateFilter = (extra: any = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - } - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - previewLog(router.query.modal as string); - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
- Developers -
- -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - API Keys - -
  • -
  • - - Webhooks - -
  • -
  • - - Events - -
  • -
  • - - Logs - -
  • -
-
- - - - {!query.isFetched && } - - - {(query.isFetched && (logs?.edges || []).length > 0) && <> -
- - - - - - - - - - {(logs?.edges || []).map(({ node: log }) => ( - - previewLog(log.id as any)}> - - - - - - ))} - - -
STATUSDESCRIPTION
{`${log.method} ${log.path}`} - {formatDateTimeLong(log.requested_at)} -
-
- -
- - {(logs?.edges || []).length} results - - -
- - -
-
- } - - - {(query.isFetched && (logs?.edges || []).length == 0) &&
- -
-

No API logs found.

-

Use the API to send shipping requests.

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - {`Logs - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Developers/webhooks.tsx b/apps/dashboard/src/modules/Developers/webhooks.tsx deleted file mode 100644 index 01baddbc82..0000000000 --- a/apps/dashboard/src/modules/Developers/webhooks.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { WebhookTestModal, useTestWebhookModal } from "@karrio/ui/modals/webhook-test-modal"; -import { WebhookEditModal, useWebhookModal } from "@karrio/ui/modals/webhook-edit-modal"; -import { ConfirmModal, ConfirmModalContext } from "@karrio/ui/modals/confirm-modal"; -import { useWebhookMutation, useWebhooks } from "@karrio/hooks/webhook"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { NotificationType, WebhookType } from "@karrio/types"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { formatDateTime, isNoneOrEmpty } from "@karrio/lib"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Notify } from "@karrio/ui/components/notifier"; -import { useRouter } from "next/dist/client/router"; -import { useContext, useEffect } from "react"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - - -export default function WebhooksPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const { query } = useWebhooks(); - const mutation = useWebhookMutation(); - const { notify } = useContext(Notify); - const { editWebhook } = useWebhookModal(); - const { testWebhook } = useTestWebhookModal(); - const { confirm: confirmDeletion } = useContext(ConfirmModalContext); - const [initialized, setInitialized] = React.useState(false); - - const remove = (id: string) => async () => { - await mutation.deleteWebhook.mutateAsync({ id }); - }; - const toggle = ({ disabled, id }: WebhookType) => async () => { - try { - await mutation.updateWebhook.mutateAsync({ id, disabled: !disabled }); - notify({ - type: NotificationType.success, - message: `webhook ${!disabled ? 'activated' : 'deactivated'}!` - }); - } catch (message: any) { - notify({ type: NotificationType.error, message }); - } - }; - - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - const webhook = query.data?.webhooks.edges.find(c => c.node.id === router.query.modal); - webhook && editWebhook({ webhook } as any); - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
- Developers -
- -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - API Keys - -
  • -
  • - - Webhooks - -
  • -
  • - - Events - -
  • -
  • - - Logs - -
  • -
-
- - {((query.data?.webhooks.edges || []).length > 0) && <> -
- - - - - - - - - - - {(query.data?.webhooks.edges || []).map(({ node: webhook }) => ( - - - - - - - ))} - - -
URLMODELAST EVENT
editWebhook({ webhook })}> - {webhook.url} - editWebhook({ webhook })}> - - {webhook.test_mode ? 'test' : 'live'} - - editWebhook({ webhook })}> - - {webhook.last_event_at ? formatDateTime(webhook.last_event_at as any) : "No recent event"} - - - - - -
-
- } - - {(query.isFetched && (query.data?.webhooks.edges || []).length == 0) && -
- -
-

No webhooks added yet.

-

Use the Add Enpoint button above to add

-
- -
} - - - ); - }; - - return AuthenticatedPage(<> - - {`Webhooks - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - - - - - , pageProps); -} diff --git a/apps/dashboard/src/modules/Home/index.tsx b/apps/dashboard/src/modules/Home/index.tsx deleted file mode 100644 index 85ab6726a6..0000000000 --- a/apps/dashboard/src/modules/Home/index.tsx +++ /dev/null @@ -1,335 +0,0 @@ -import { Bar, BarChart, Tooltip, ResponsiveContainer, XAxis } from "recharts"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { SelectField } from "@karrio/ui/components"; -import { useAppMode } from "@karrio/hooks/app-mode"; -import { useAPIUsage } from "@karrio/hooks/usage"; -import { useUser } from "@karrio/hooks/user"; -import { useRouter } from "next/router"; -import Head from "next/head"; -import moment from "moment"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - - -export default function ApiPage(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - const router = useRouter(); - const { basePath } = useAppMode(); - const { query: { data: { user } = {} } } = useUser(); - const { query: { data: { usage } = {} }, setFilter, filter, USAGE_FILTERS, DAYS_LIST, currentFilter } = useAPIUsage(); - - return ( - <> - -
- {`Welcome${!!user?.full_name ? ', ' + user.full_name : ''}`} -
-
- - {/* Usage stats */} -
- setFilter(JSON.parse(e.target.value))} - > - {Object.entries(USAGE_FILTERS).map(([key, value]) => ( - - ))} - - -
- -
-
-
-
-
- - - - Shipment -
- -

Total shipments

-

{!!usage ? usage?.total_shipments : 0}

- -
- {!!usage?.shipment_count && - - ({ - name: _, - total: usage!.shipment_count!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - }> - - - - - } -
-
-
-
-
-
- - - - Tracker -
- -

Total trackers

-

{!!usage ? usage?.total_trackers : 0}

- -
- {!!usage?.tracker_count && - - ({ - name: _, - total: usage!.tracker_count!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - }> - - - - - } -
-
-
-
-
- -
-
-
-
-
- - - - Order -
- -

Fullfilled orders volume

-

- - - - - {!!usage ? usage?.order_volume : 0} - -

- -
- {!!usage?.order_volumes && - - ({ - name: _, - value: usage!.order_volumes!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - }> - - - - - } -
-
-
-
-
-
- - - - Spend -
- -

Estimated shipping spend

-

- - - - - {!!usage ? usage?.total_shipping_spend : 0} - -

- -
- {!!usage?.shipping_spend && - - ({ - name: _, - value: usage!.shipping_spend!.find(({ date }) => moment(date).format('MMM D') === _)?.count || 0 - })) - }> - - - - - } -
-
-
-
-
- -
-
- - {/* Next actions */} -
Things to do next
- - {(!!usage?.unfulfilled_orders && usage.unfulfilled_orders > 0) &&
- - - - - - {`${usage.unfulfilled_orders} order${usage.unfulfilled_orders > 1 ? 's' : ''} to fulfill`} - - -
} - -
- -
-
router.push(`${basePath}/settings/addresses`)}> -
- - - -
-

Add shipping location address

-

Add one or multiple warehouse locations.

-
- - - -
-
-
- -
-
router.push(`${basePath}/connections`)}> -
- - - -
-

Set up carrier accounts

-

Connect your carrier accounts to start

-
- - - -
-
-
- -
-
router.push(`/test/create_label?shipment_id=new`)}> -
- - - -
-

Print a test label

-

Generate a test label for a sample shipment.

-
- - - -
-
-
- -
-
router.push(`${basePath}/trackers?modal=new`)}> -
- - - -
-

Add a tracking number

-

Add one or multiple shipments to track.

-
- - - -
-
-
- -
-
router.push(`${basePath}/developers/apikeys`)}> -
- - - -
-

Set up an API connection

-

Retrieve your API key to connect via API.

-
- - - -
-
-
- -
-
router.push(`${basePath}/developers/logs`)}> -
- - - -
-

Review your API request

-

Audit your API requests logs and system health.

-
- - - -
-
-
- -
- - - ); - }; - - return AuthenticatedPage(( - - {`Home - ${references?.APP_NAME}`} - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Manifests/create_manifests.tsx b/apps/dashboard/src/modules/Manifests/create_manifests.tsx deleted file mode 100644 index 0003d5ac1a..0000000000 --- a/apps/dashboard/src/modules/Manifests/create_manifests.tsx +++ /dev/null @@ -1,287 +0,0 @@ -import { formatAddressLocationShort, formatAddressShort, formatCarrierSlug, formatDateTime, formatRef, isNone, preventPropagation, url$ } from '@karrio/lib'; -import { useSystemCarrierConnections } from '@karrio/hooks/admin/connections'; -import { CreateManifestModal } from '@karrio/ui/modals/create-manifest-modal'; -import { useCarrierConnections } from '@karrio/hooks/user-connection'; -import { CarrierImage } from '@karrio/ui/components/carrier-image'; -import { ShipmentMenu } from '@karrio/ui/components/shipment-menu'; -import { AuthenticatedPage } from '@/layouts/authenticated-page'; -import { StatusBadge } from '@karrio/ui/components/status-badge'; -import { ConfirmModal } from '@karrio/ui/modals/confirm-modal'; -import { DashboardLayout } from '@/layouts/dashboard-layout'; -import { useAPIMetadata } from '@karrio/hooks/api-metadata'; -import { AddressType, ShipmentType } from '@karrio/types'; -import { useLoader } from '@karrio/ui/components/loader'; -import { AppLink } from '@karrio/ui/components/app-link'; -import { ModalProvider } from '@karrio/ui/modals/modal'; -import { useShipments } from '@karrio/hooks/shipment'; -import { ManifestData } from '@karrio/types/rest/api'; -import { bundleContexts } from '@karrio/hooks/utils'; -import { Spinner } from '@karrio/ui/components'; -import { useRouter } from 'next/router'; -import Head from 'next/head'; -import React from 'react'; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - ModalProvider, - ConfirmModal, -]); - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - - // General context data ----------------------------------------------------------- - //#region - - const router = useRouter(); - const loader = useLoader(); - const { metadata } = useAPIMetadata(); - const [allChecked, setAllChecked] = React.useState(false); - const [selection, setSelection] = React.useState([]); - const { query: { data: { user_connections } = {} } } = useCarrierConnections(); - const { query: { data: { system_connections } = {} } } = useSystemCarrierConnections(); - const context = useShipments({ - status: ['purchased'] as any, - meta_key: 'manifest_required', - meta_value: true, - has_manifest: false, - preloadNextPage: true, - }); - const { query: { data: { shipments } = {}, ...query }, filter, setFilter } = context; - - //#endregion - - // Helper functions ----------------------------------------------------------- - //#region - - const getRate = (shipment: any) => ( - shipment.selected_rate - || (shipment?.rates || []).find(_ => _.service === shipment?.options?.preferred_service) - || (shipment?.rates || [])[0] - || shipment - ); - const getCarrier = (rate?: ShipmentType['rates'][0]) => ( - user_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - || system_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - ); - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...extra, - }; - - setFilter(query); - }; - const updatedSelection = (selectedShipments: string[], current: typeof shipments) => { - const shipment_ids = (current?.edges || []).map(({ node: shipment }) => shipment.id); - const selection = selectedShipments.filter(id => shipment_ids.includes(id)); - const selected = selection.length > 0 && selection.length === (shipment_ids || []).length; - setAllChecked(selected); - if (selectedShipments.filter(id => !shipment_ids.includes(id)).length > 0) { - setSelection(selection); - } - }; - const handleSelection = (e: React.ChangeEvent) => { - const { checked, name } = e.target as HTMLInputElement; - if (name === "all") { - setSelection(!checked ? [] : (shipments?.edges || []).map(({ node: { id } }) => id)); - } else { - setSelection(checked ? [...selection, name] : selection.filter(id => id !== name)); - } - }; - const compatibleCarrierSelection = (selection: string[]) => { - const carrier_id = shipments?.edges?.find(({ node: shipment }) => selection.includes(shipment.id))?.node?.carrier_id; - return (shipments?.edges || []).filter(({ node: shipment }) => ( - selection.includes(shipment.id) && shipment.carrier_id == carrier_id - )).length === selection.length; - }; - const computeManifestData = (selection: string[], current: typeof shipments): ManifestData => { - const shipment = (current?.edges || []).find(({ node: shipment }) => selection.includes(shipment.id))?.node; - const { id, ...address } = shipment?.shipper || {} as any - - return { - address, - shipment_ids: selection, - reference: shipment?.reference as string, - carrier_name: shipment?.carrier_name as string, - }; - } - - - //#endregion - - React.useEffect(() => { updateFilter(); }, [router.query]); - React.useEffect(() => { loader.setLoading(query.isLoading); }, [query.isLoading]); - React.useEffect(() => { updatedSelection(selection, shipments); }, [selection, shipments]); - - return ( - <> - -
- Manifests -
-
- -
- -
- - {query.isLoading && } - - {(query.isLoading === false && (shipments?.edges || []).length > 0) && <> -
- - - - - - - - {selection.length > 0 && } - - {selection.length === 0 && <> - - - - - - - } - - - {(shipments?.edges || []).map(({ node: shipment }) => ( - - - - - - - - - - ))} - - - -
- - -
- - - Create Manifests - - } - /> - -
-
SHIPPING SERVICERECIPIENTREFERENCEDATE
- - -
- -
- {shipment.tracking_number} -
- - {formatRef(((getRate(shipment).meta as any)?.service_name || getRate(shipment).service) as string)} - -
-
-
- - -
-

- {formatAddressShort(shipment.recipient as AddressType)} -

-

{formatAddressLocationShort(shipment.recipient as AddressType)}

-
-
- {shipment.reference || ''} - -

- {formatDateTime(shipment.created_at)} -

-
- -
-
- -
- - {(shipments?.edges || []).length} results - - -
- - -
-
- } - - {(query.isLoading === false && (shipments?.edges || []).length == 0) && <> -
- -
-

No pending shipment found.

-
- -
- } - - - ); - }; - - return AuthenticatedPage(( - - {`Manifests - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Manifests/index.tsx b/apps/dashboard/src/modules/Manifests/index.tsx deleted file mode 100644 index 3b55d51bbf..0000000000 --- a/apps/dashboard/src/modules/Manifests/index.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { formatAddressLocationShort, formatAddressShort, formatCarrierSlug, formatDateTime, isNone, preventPropagation, url$ } from '@karrio/lib'; -import { useSystemCarrierConnections } from '@karrio/hooks/admin/connections'; -import { useCarrierConnections } from '@karrio/hooks/user-connection'; -import { CarrierImage } from '@karrio/ui/components/carrier-image'; -import { AuthenticatedPage } from '@/layouts/authenticated-page'; -import { DashboardLayout } from '@/layouts/dashboard-layout'; -import { useAPIMetadata } from '@karrio/hooks/api-metadata'; -import { MenuComponent } from '@karrio/ui/components/menu'; -import { AddressType, ManifestType } from '@karrio/types'; -import { useLoader } from '@karrio/ui/components/loader'; -import { AppLink } from '@karrio/ui/components/app-link'; -import { ModalProvider } from '@karrio/ui/modals/modal'; -import { useManifests } from '@karrio/hooks/manifests'; -import { bundleContexts } from '@karrio/hooks/utils'; -import { Spinner } from '@karrio/ui/components'; -import { useRouter } from 'next/router'; -import Head from 'next/head'; -import React from 'react'; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - ModalProvider, -]); - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - - // General context data ----------------------------------------------------------- - //#region - - const router = useRouter(); - const loader = useLoader(); - const { metadata } = useAPIMetadata(); - const [allChecked, setAllChecked] = React.useState(false); - const [selection, setSelection] = React.useState([]); - const { query: { data: { user_connections } = {} } } = useCarrierConnections(); - const { query: { data: { system_connections } = {} } } = useSystemCarrierConnections(); - const { query: { data: { manifests } = {}, ...query }, filter, setFilter } = useManifests({ - preloadNextPage: true, - }); - - //#endregion - - // Helper functions ----------------------------------------------------------- - //#region - - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...extra, - }; - - setFilter(query); - }; - const updatedSelection = (selectedManifests: string[], current: typeof manifests) => { - const manifest_ids = (current?.edges || []).map(({ node: manifest }) => manifest.id); - const selection = selectedManifests.filter(id => manifest_ids.includes(id)); - const selected = selection.length > 0 && selection.length === (manifest_ids || []).length; - setAllChecked(selected); - if (selectedManifests.filter(id => !manifest_ids.includes(id)).length > 0) { - setSelection(selection); - } - }; - const handleSelection = (e: React.ChangeEvent) => { - const { checked, name } = e.target as HTMLInputElement; - if (name === "all") { - setSelection(!checked ? [] : (manifests?.edges || []).map(({ node: { id } }) => id)); - } else { - setSelection(checked ? [...selection, name] : selection.filter(id => id !== name)); - } - }; - const getCarrier = (manifest: ManifestType) => ( - user_connections?.find(_ => _.id === manifest?.meta?.carrier || _.carrier_id === manifest?.carrier_id) - || system_connections?.find(_ => _.id === manifest?.meta?.carrier || _.carrier_id === manifest?.carrier_id) - ); - - //#endregion - - React.useEffect(() => { updateFilter(); }, [router.query]); - React.useEffect(() => { loader.setLoading(query.isLoading); }, [query.isLoading]); - React.useEffect(() => { updatedSelection(selection, manifests); }, [selection, manifests]); - - return ( - <> - -
- Manifests -
-
- -
-
    -
  • - Ready -
  • -
  • - Pending Shipments -
  • -
-
- - {query.isLoading && } - - {(query.isLoading === false && (manifests?.edges || []).length > 0) && <> -
- - - - - - - {selection.length > 0 && } - - {selection.length === 0 && <> - - - - - - } - - - {(manifests?.edges || []).map(({ node: manifest }) => ( - - - - - - - - - ))} - - - -
- - - - CARRIERADDRESSREFERENCEDATE
- - -
- -
- {manifest.id} -
- - {manifest.shipment_identifiers[0] || ' - '} - -
-
-
-
-

- {formatAddressShort(manifest.address as AddressType)} -

-

{formatAddressLocationShort(manifest.address as AddressType)}

-
-
- {manifest.reference || ''} - -

- {formatDateTime(manifest.created_at)} -

-
-
- - - - - - } - > - - Print Manifest - - -
-
-
- -
- - {(manifests?.edges || []).length} results - - -
- - -
-
- } - - {(query.isLoading === false && (manifests?.edges || []).length == 0) && <> -
- -
-

No manifest found.

-
- -
- } - - - ); - }; - - return AuthenticatedPage(( - - {`Manifests - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Orders/draft_order.tsx b/apps/dashboard/src/modules/Orders/draft_order.tsx deleted file mode 100644 index da79e91e87..0000000000 --- a/apps/dashboard/src/modules/Orders/draft_order.tsx +++ /dev/null @@ -1,389 +0,0 @@ -import { CommodityEditModalProvider, CommodityStateContext } from '@karrio/ui/modals/commodity-edit-modal'; -import { MetadataEditor, MetadataEditorContext } from '@karrio/ui/forms/metadata-editor'; -import { GoogleGeocodingScript } from '@karrio/ui/components/google-geocoding-script'; -import { CommodityDescription } from '@karrio/ui/components/commodity-description'; -import { AddressDescription } from '@karrio/ui/components/address-description'; -import { formatRef, isEqual, isNone, isNoneOrEmpty, useLocation } from '@karrio/lib'; -import { MetadataObjectTypeEnum, PaidByEnum } from '@karrio/types'; -import { AddressModalEditor } from '@karrio/ui/modals/form-modals'; -import { AuthenticatedPage } from '@/layouts/authenticated-page'; -import { InputField } from '@karrio/ui/components/input-field'; -import { DashboardLayout } from '@/layouts/dashboard-layout'; -import { useLoader } from '@karrio/ui/components/loader'; -import { ModalProvider } from '@karrio/ui/modals/modal'; -import { bundleContexts } from '@karrio/hooks/utils'; -import { useOrderForm } from '@karrio/hooks/order'; -import React, { useEffect, useState } from 'react'; -import { AddressType } from '@karrio/types'; -import Head from 'next/head'; -import { Spinner } from '@karrio/ui/components'; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - CommodityEditModalProvider, - ModalProvider, -]); - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - const loader = useLoader(); - const router = useLocation(); - const { id } = router.query; - const [ready, setReady] = useState(false); - const [loading, setLoading] = useState(false); - const [key, setKey] = useState(`order-${Date.now()}`); - const { order, current, isNew, DEFAULT_STATE, query, ...mutation } = useOrderForm({ id }); - - const handleChange = async (changes?: Partial) => { - if (changes === undefined) { return; } - await mutation.updateOrder({ id, ...changes }); - setKey(`${id}-${Date.now()}`); - }; - useEffect(() => { - if ( - !ready && query.isFetched && - id === 'new' && - !isNone(order.line_items) - ) { - setTimeout(() => setReady(true), 1000); - } - if ( - !ready && query.isFetched && - !isNoneOrEmpty(id) && - id !== 'new' && - !isNone(order.line_items) - ) { - setReady(true); - } - }, [query.isFetched, id]); - useEffect(() => { - if (ready) { - setKey(`order-${id}-${Date.now()}`); - } - }, [ready]); - - - return ( - <> - - -
- {`${id === 'new' ? 'Create' : 'Edit'} order`} -
- -
-
- - {!ready && } - - {ready &&
-
- - {/* Line Items */} -
- -
- LINE ITEMS -
- {({ editCommodity }) => ( - - )} -
-
- -
- -
- {(order.line_items || []).map((item, index) => - {index > 0 &&
} -
- -
- {({ editCommodity }) => ( - - )} - -
-
-
)} - - {(order.line_items || []).length === 0 &&
- Add one or more product to create a order. -
} -
- -
- - - {/* Order options section */} -
- -
- OPTIONS -
- -
- -
- - {/* order date */} - handleChange({ order_date: e.target.value })} - /> - - - {/* invoice */} - handleChange({ options: { ...order.options, invoice_number: e.target.value } })} - /> - - - {/* invoice date */} - handleChange({ options: { ...order.options, invoice_date: e.target.value } })} - /> - -
- -
- -
- - -
- - - - - -
- - {(order.options?.paid_by && order.options?.paid_by !== PaidByEnum.sender) && -
- handleChange({ options: { ...order.options, account_number: e.target.value } })} - /> -
} -
- -
- -
- -
- -
-
- - {/* Summary section */} - {!isNone(order.line_items) &&
- -
- SUMMARY -
- -
-

- {`ITEMS (${order.line_items.reduce((_, { quantity }) => _ + (isNone(quantity) ? 1 : quantity as any), 0)})`} -

- -
- - {order.line_items.map((item, index) => -
- -
)} - -
-
- -
-

- TOTAL: {{order.line_items.reduce((_, { quantity, value_amount }) => _ + ((isNone(quantity) ? 1 : quantity as any) * (isNone(value_amount) ? 1.0 : value_amount as any)), 0.0)} {order.line_items[0]?.value_currency}} -

-

- TOTAL WEIGHT: {{order.line_items.reduce((_, { quantity, weight }) => _ + ((isNone(quantity) ? 1 : quantity as any) * (isNone(weight) ? 1.0 : weight as any)), 0.0)} {order.line_items[0]?.weight_unit}} -

-
- -
} - - {/* Address section */} -
-
- -
- Customer -
- handleChange({ shipping_to: address })} - trigger={ - - } - /> -
-
- - {Object.values(order.shipping_to || {}).length > 0 && - } - - {Object.values(order.shipping_to || {}).length === 0 && -
- Please specify the customer address. -
} - -
- -
- -
- -
- Billing Address -
- handleChange({ billing_address: address })} - trigger={ - - } - /> -
-
- - - {Object.values(order.billing_address || {}).length > 0 && - } - - {Object.values(order.billing_address || {}).length === 0 && -
- Same as shipping address. -
} - -
-
- - {/* Metadata section */} -
- -
- handleChange({ metadata })} - > - {({ isEditing, editMetadata }) => (<> - -
- METADATA -
- -
-
- - )}
-
-
- -
- -
-
-
} - -
- - ) - }; - - return AuthenticatedPage(( - - - {`Draft order - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Orders/index.tsx b/apps/dashboard/src/modules/Orders/index.tsx deleted file mode 100644 index d31f89f219..0000000000 --- a/apps/dashboard/src/modules/Orders/index.tsx +++ /dev/null @@ -1,382 +0,0 @@ -import { formatAddressLocationShort, formatAddressShort, formatCarrierSlug, formatDateTime, formatRef, getURLSearchParams, isListEqual, isNone, isNoneOrEmpty, url$ } from "@karrio/lib"; -import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; -import { OrderPreview, OrderPreviewContext } from "@/components/order-preview"; -import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; -import { useDocumentTemplates } from "@karrio/hooks/document-template"; -import { useCarrierConnections } from "@karrio/hooks/user-connection"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import React, { ChangeEvent, useContext, useEffect } from "react"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { OrdersFilter } from "@karrio/ui/filters/orders-filter"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { OrderMenu } from "@karrio/ui/components/order-menu"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AddressType, ShipmentType } from "@karrio/types"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useRouter } from "next/dist/client/router"; -import { useOrders } from "@karrio/hooks/order"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - OrderPreview, - ConfirmModal, - ModalProvider, -]); - - -export default function OrdersPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const { setLoading } = useLoader(); - const { references } = useAPIMetadata(); - const { previewOrder } = useContext(OrderPreviewContext); - const [allChecked, setAllChecked] = React.useState(false); - const [initialized, setInitialized] = React.useState(false); - const [selection, setSelection] = React.useState([]); - const context = useOrders({ setVariablesToURL: true, preloadNextPage: true }); - const { query: { data: { user_connections } = {} } } = useCarrierConnections(); - const { query: { data: { system_connections } = {} } } = useSystemCarrierConnections(); - const { query: { data: { orders } = {}, ...query }, filter, setFilter } = context; - const { query: { data: { document_templates } = {} } } = useDocumentTemplates({ - related_object: "order" as any - }); - - const preventPropagation = (e: React.MouseEvent) => e.stopPropagation(); - const getRate = (shipment: any, default_rate?: any) => ( - default_rate - || shipment?.selected_rate - || (shipment?.rates || []).find(_ => _.service === shipment?.options?.preferred_service) - || (shipment?.rates || [])[0] - || shipment - ); - const getCarrier = (rate?: ShipmentType['rates'][0]) => ( - user_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - || system_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - ); - const updatedSelection = (selectedOrders: string[], current: typeof orders) => { - const order_ids = (current?.edges || []).map(({ node: order }) => order.id); - const selection = selectedOrders.filter(id => order_ids.includes(id)); - const selected = selection.length > 0 && selection.length === (order_ids || []).length; - setAllChecked(selected); - if (selectedOrders.filter(id => !order_ids.includes(id)).length > 0) { - setSelection(selection); - } - }; - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - }; - const handleSelection = (e: ChangeEvent) => { - const { checked, name } = e.target as HTMLInputElement; - if (name === "all") { - setSelection(!checked ? [] : (orders?.edges || []).map(({ node: { id } }) => id)); - } else { - setSelection(checked ? [...selection, name] : selection.filter(id => id !== name)); - } - }; - const unfulfilledSelection = (selection: string[]) => { - return (orders?.edges || []).filter(({ node: order }) => ( - selection.includes(order.id) && - !["cancelled", "fulfilled"].includes(order.status) - )).length === selection.length; - }; - const computeDocFormat = (selection: string[]): string | null => { - const _order = (orders?.edges || []).find(({ node: order }) => (order.id == selection[0])); - return (_order?.node?.shipments || [])[0]?.label_type; - }; - const compatibleTypeSelection = (selection: string[]) => { - const format = computeDocFormat(selection); - return (orders?.edges || []).filter(({ node: order }) => ( - selection.includes(order.id) && - !!order.shipments.find(({ label_type }) => label_type === format) - )).length === selection.length; - }; - const computeOrderService = (order: any) => { - const shipment = ( - order.shipments.find(({ status, tracking_number }) => !!tracking_number && !["cancelled", "draft"].includes(status)) || - order.shipments.find(({ status }) => ["draft"].includes(status)) - ); - const rate = getRate(shipment); - - if (!shipment) { - const _shipment = ( - order.shipments.find(({ status }) => !["cancelled", "draft"].includes(status)) || - order.shipments.find(({ status }) => !["draft"].includes(status)) - ); - const _rate = getRate(_shipment); - - return <> - -
- {` - `}
- - {!isNone(_rate?.carrier_name) && formatRef((_rate.meta?.service_name || _rate.service) as string)} - {isNone(_rate?.carrier_name) && "UNFULFILLED"} - -
- - } - - return ( - <> - -
- - {!isNone(shipment.carrier_name) && {shipment.tracking_number}} - {isNone(shipment.carrier_name) && {` - `}} -
- - {!isNone(rate.carrier_name) && formatRef((rate.meta?.service_name || rate.service) as string)} - {isNone(rate.carrier_name) && "UNFULFILLED"} - -
- - ) - }; - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { updatedSelection(selection, orders); }, [selection, orders]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - previewOrder(router.query.modal as string); - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
-
- Orders -
-
- - Create order - - - Manage manifests - - -
-
- - - - {!query.isFetched && } - - {(query.isFetched && (orders?.edges || []).length > 0) && <> -
- - - - - - - {selection.length > 0 && } - - {selection.length === 0 && <> - - - - - - - - - } - - - {(orders?.edges || []).map(({ node: order }) => ( - - - - - - - - - - - - ))} - - -
- - -
- - Create labels - - - Print Labels - - - Print Invoices - - {(document_templates?.edges || []).map(({ node: template }) => - - Print {template.name} - - )} -
-
ORDER #ITEMSSHIP TOTOTALDATESHIPPING SERVICE
- - previewOrder(order.id)}> -
-

- {order.order_id} -

-

- {order.source} -

-
-
previewOrder(order.id)}> - - previewOrder(order.id)}> -
-

- {((items: number): any => `${items} item${items === 1 ? '' : 's'}`)( - order.line_items.reduce((acc, item) => acc + (item.quantity as number) || 1, 0) - )} -

-

- {order.line_items.length > 1 ? "(Multiple)" : order.line_items[0].title || order.line_items[0].description || order.line_items[0].sku} -

-
-
previewOrder(order.id)}> -
-

- {formatAddressShort(order.shipping_to as AddressType)} -

-

{formatAddressLocationShort(order.shipping_to as AddressType)}

-
-
previewOrder(order.id)}> -

- {order.line_items.reduce((acc, item) => acc + ((item.quantity as number) * (item.value_amount as number)), 0)} - {` `} - {order.options.currency || order.line_items[0].value_currency} -

-
previewOrder(order.id)}> -

- {formatDateTime(order.created_at)} -

-
-
{computeOrderService(order)}
-
- -
-
- -
- - {(orders?.edges || []).length} results - - -
- - -
-
- } - - {(query.isFetched && (orders?.edges || []).length == 0) && -
- -
-

No order found.

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - - {`Orders - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps) -}; diff --git a/apps/dashboard/src/modules/Orders/order.tsx b/apps/dashboard/src/modules/Orders/order.tsx deleted file mode 100644 index 810a73d7de..0000000000 --- a/apps/dashboard/src/modules/Orders/order.tsx +++ /dev/null @@ -1,313 +0,0 @@ -import { MetadataEditor, MetadataEditorContext } from "@karrio/ui/forms/metadata-editor"; -import { formatAddressLocation, formatDateTime, formatRef, isNone } from "@karrio/lib"; -import { CommodityDescription } from "@karrio/ui/components/commodity-description"; -import { AddressDescription } from "@karrio/ui/components/address-description"; -import { StatusCode } from "@karrio/ui/components/status-code-badge"; -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { OrderMenu } from "@karrio/ui/components/order-menu"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { MetadataObjectTypeEnum } from "@karrio/types"; -import { useRouter } from "next/dist/client/router"; -import { useEvents } from "@karrio/hooks/event"; -import { useOrder } from "@karrio/hooks/order"; -import { useLogs } from "@karrio/hooks/log"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - - -export const OrderComponent: React.FC<{ orderId?: string }> = ({ orderId }) => { - const router = useRouter(); - const { setLoading } = useLoader(); - const entity_id = orderId || router.query.id as string; - const { query: logs } = useLogs({ entity_id }); - const { query: events } = useEvents({ entity_id }); - const { query: { data: { order } = {}, ...query } } = useOrder(entity_id); - - React.useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - - return ( - <> - - {!query.isFetched && query.isFetching && } - - {order && <> - - {/* Header section */} -
- -
- ORDER -
- {order?.order_id} - -
- -
-
- -
-
- - {!isNone(orderId) && - - - - - } - -
- -
- -
-
- -
- - {/* Reference and highlights section */} -
- -
-
- Date
- - {formatDateTime(order?.created_at)} - -
- - {!isNone(order?.source) && <> -
-
- Source
- {order?.source} -
- } -
- - -

Order Details

-
- -
- - {/* address and line items section */} -
- {/* Shipping Address section */} -
-

ADDRESS

- -

{order?.shipping_to.person_name}

-

{order?.shipping_to.company_name}

-

{order?.shipping_to.email}

-

{order?.shipping_to.phone_number}

-

- {order?.shipping_to.address_line1} - {!isNone(order?.shipping_to.address_line2) && {order?.shipping_to.address_line2}} -

-

{formatAddressLocation(order?.shipping_to)}

-
- - {/* Line Items section */} -
-

- LINE ITEMS ({order?.line_items.reduce((_, { quantity }) => _ + (quantity || 1), 0)}) -

- -
- {order?.line_items.map((item, index) => -
- -
)} -
-
-
- - {/* Options section */} -
- {(Object.values(order?.options as object).length > 0) &&
-

ORDER OPTIONS

- - {Object.entries(order?.options).map(([key, value]: any, index) => -

- {formatRef(key).toLowerCase()}: {String(value)} -

-
)} - -
} -
- - {/* Billing address section */} -
- {order?.billing_address &&
-

BILL TO

- - - -
} -
- -
- - {/* Metadata section */} - - {({ isEditing, editMetadata }) => (<> - -
-

Metadata

- - -
- -
- - )}
-
- -
- - {/* Shipments section */} -

Shipments

- - {(order?.shipments || []).length == 0 &&
No shipments
} - - {(order?.shipments || []).length > 0 &&
- - - {(order?.shipments || []).map(shipment => ( - - - - - - ))} - -
- - - - - - {shipment.id}{shipment.tracking_number && ` - ${shipment.tracking_number}`} - - - - {formatDateTime(shipment.created_at)} - -
-
} - -
- - {/* Logs section */} -

Logs

- - {logs.isFetching && } - - {logs.isFetched && (logs.data?.logs.edges || []).length == 0 &&
No logs
} - - {logs.isFetched && (logs.data?.logs.edges || []).length > 0 && -
- - - {(logs.data?.logs.edges || []).map(({ node: log }) => ( - - - - - - ))} - -
- - - - - - {`${log.method} ${log.path}`} - - - - {formatDateTime(log.requested_at)} - -
-
} - -
- - {/* Events section */} -

Events

- - {events.isFetching && } - - {events.isFetched && (events.data?.events.edges || []).length == 0 &&
No events
} - - {events.isFetched && (events.data?.events.edges || []).length > 0 && -
- - - {(events.data?.events.edges || []).map(({ node: event }) => ( - - - - - ))} - -
- - {`${event.type}`} - - - - {formatDateTime(event.created_at)} - -
-
} - - } - - {query.isFetched && isNone(order) &&
- -
-

Uh Oh!

-

{"We couldn't find any order with that reference"}

-
- -
} - - ); -}; - -export default function OrderPage(pageProps: any) { - return AuthenticatedPage(( - - {`Order - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Password/reset/index.tsx b/apps/dashboard/src/modules/Password/reset/index.tsx deleted file mode 100644 index 6352dcd77b..0000000000 --- a/apps/dashboard/src/modules/Password/reset/index.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { FormEvent, useContext, useEffect, useReducer } from "react"; -import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; -import { ConfirmPasswordResetMutationInput } from "@karrio/types"; -import { ButtonField } from "@karrio/ui/components/button-field"; -import { InputField } from "@karrio/ui/components/input-field"; -import { SectionLayout } from "@/layouts/section-layout"; -import { useUserMutation } from "@karrio/hooks/user"; -import { useRouter } from "next/dist/client/router"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/metadata'; - -const DEFAULT_VALUE: Partial = { - new_password1: "", - new_password2: "" -}; - -function reducer(state: Partial, { name, value }: { name: string, value: string | object }) { - switch (name) { - case "full": - return { ...(value as object) }; - case "partial": - return { ...state, ...(value as object) }; - default: - return { ...state, [name]: value }; - } -} - -const Component: React.FC<{}> = () => { - const router = useRouter(); - const { uidb64, token } = router.query; - const { loading, setLoading } = useContext(Loading); - const [data, dispatch] = useReducer(reducer, DEFAULT_VALUE, () => DEFAULT_VALUE); - const { confirmPasswordReset: { error, mutateAsync } } = useUserMutation(); - - const handleChange = (event: React.ChangeEvent) => { - const value: string = event.target.value; - const name: string = event.target.name; - - dispatch({ name, value }); - }; - const onSubmit = async (e: FormEvent) => { - e.preventDefault(); - try { - setLoading(true); - await mutateAsync(data as ConfirmPasswordResetMutationInput); - router.push('/password/reset/done'); - } catch (error: any) { - console.log(error) - } - setLoading(false); - }; - const renderFieldError = (check: CallableFunction, errorData: any) => { - const validation = (errorData?.data?.errors || [])[0]?.validation || {}; - return (<> - {Object.entries(validation) - .filter(([key, _]) => check(key)) - .map(([_, messages]: any) => ( - messages.map((message: string, index: number) => -

{message}

) - ))} - ); - } - - useEffect(() => { dispatch({ name: "partial", value: { uid: uidb64, token } }); }, [uidb64, token]); - - return ( - <> -
-
-

New Password

-

Enter your new email and password.

- - {((error as any)?.data?.errors || []).map((_: any, index: number) => (<> -

{_.message}

- ))} - -
- - - {renderFieldError((_: string) => _ === 'new_password1', error)} - - - - {renderFieldError((_: string) => _ === 'new_password2', error)} - - - - - Change my password - - -
- -
-
- -
- Return to Sign in -
- - ) -}; - -export default function Page(pageProps: any) { - return ( - <> - - {`Password Reset - ${pageProps.metadata?.APP_NAME}`} - - - - - - - - ) -} diff --git a/apps/dashboard/src/modules/Password/reset/sent.tsx b/apps/dashboard/src/modules/Password/reset/sent.tsx deleted file mode 100644 index 1ead647acb..0000000000 --- a/apps/dashboard/src/modules/Password/reset/sent.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SectionLayout } from "@/layouts/section-layout"; -import Head from "next/head"; -import Link from "next/link"; -import React from "react"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function Page(pageProps: any) { - return ( - <> - - {`Password Reset Sent - ${pageProps.metadata?.APP_NAME}`} - -
-
-

Password Reset Sent

- -

We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.

-

If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder.

- -
-
- -
- Sign in -
- -
- - ); -} diff --git a/apps/dashboard/src/modules/Registration/signup.tsx b/apps/dashboard/src/modules/Registration/signup.tsx deleted file mode 100644 index 95b3e90be2..0000000000 --- a/apps/dashboard/src/modules/Registration/signup.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { RegisterUserMutationInput, register_user_register_user_errors } from "@karrio/types"; -import React, { FormEvent, useContext, useEffect, useReducer, useState } from "react"; -import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; -import { ButtonField } from "@karrio/ui/components/button-field"; -import { InputField } from "@karrio/ui/components/input-field"; -import { SectionLayout } from "@/layouts/section-layout"; -import { isNone, isNoneOrEmpty } from "@karrio/lib"; -import { useRouter } from "next/dist/client/router"; -import { useUserMutation } from "@karrio/hooks/user"; -import { p } from "@karrio/lib"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/metadata'; - -const DEFAULT_VALUE: Partial = { - email: "", - full_name: "", - password1: "", - password2: "", -}; - -function reducer(state: Partial, { name, value }: { name: string, value: string | object }) { - switch (name) { - case "full": - return { ...(value as object) }; - case "partial": - return { ...state, ...(value as object) }; - default: - return { ...state, [name]: value }; - } -} - -const Component: React.FC = () => { - const router = useRouter(); - const { email } = router.query; - const mutation = useUserMutation(); - const { loading, setLoading } = useContext(Loading); - const [user, dispatch] = useReducer(reducer, DEFAULT_VALUE, () => DEFAULT_VALUE); - const [errors, setErrors] = useState([]); - - const handleChange = (event: React.ChangeEvent) => { - const value: string = event.target.value; - const name: string = event.target.name; - - dispatch({ name, value }); - }; - const onSubmit = async (e: FormEvent) => { - e.preventDefault(); - try { - setLoading(true); - await mutation.registerUser.mutateAsync({ - ...user, redirect_url: location.origin + p`/email` - } as RegisterUserMutationInput); - router.push(p`/signup/success`); - } catch (error: any) { - setErrors(Array.isArray(error) ? error : [error]); - } - setLoading(false); - }; - - useEffect(() => { - if (!isNoneOrEmpty(email)) { - dispatch({ name: 'email', value: email as string }); - } - }, [email]); - - return ( - <> -
-
-

Sign up with credentials

- - {(errors as any[]).filter(error => isNone(error.field)).map(({ message }, index) => ( -

{message}

- ))} - -
- - - {errors.filter(error => error.field === 'email').map(({ messages }) => ( - messages.map((message, index) =>

{message}

) - ))} -
- - - {errors.filter(error => error.field === 'full_name').map(({ messages }) => ( - messages.map((message, index) =>

{message}

) - ))} -
- - - {errors.filter(error => error.field === 'password1').map(({ messages }) => ( - messages.map((message, index) =>

{message}

) - ))} -
- - - {errors.filter(error => error.field === 'password2').map(({ messages }) => ( - messages.map((message, index) =>

{message}

) - ))} -
- - - - Create account - - -
- -
-
- -
- Have an account? Sign in -
- - ) -}; - -function SignUp(pageProps: any) { - return ( - <> - - {`Sign Up - ${pageProps.metadata?.APP_NAME}`} - - - - - - - - ) -}; - -export default SignUp; diff --git a/apps/dashboard/src/modules/Resources/graphiql.tsx b/apps/dashboard/src/modules/Resources/graphiql.tsx deleted file mode 100644 index c6a120ffeb..0000000000 --- a/apps/dashboard/src/modules/Resources/graphiql.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { createGraphiQLFetcher } from '@graphiql/toolkit'; -import { useSyncedSession } from "@karrio/hooks/session"; -import { EmbedLayout } from "@/layouts/embed-layout"; -import { GraphiQL } from 'graphiql'; -import Head from "next/head"; -import React from "react"; - -import 'graphiql/graphiql.css'; - -export { getServerSideProps } from "@/context/main"; - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - const { metadata } = useAPIMetadata(); - const { query: { data: session } } = useSyncedSession(); - - const fetcher = React.useMemo(() => { - return createGraphiQLFetcher({ - url: metadata?.GRAPHQL, - headers: { - ...(!!session?.orgId ? { 'x-org-id': session.orgId } : {}), - ...(!!session?.testMode ? { 'x-test-mode': session.testMode } : {}), - ...(!!session?.accessToken ? { 'authorization': `Bearer ${session.accessToken}` } : {}) - }, - }) - }, [session.accessToken]); - - return ( -
- - - -
- ); - }; - - return AuthenticatedPage(( - - {`GraphiQL - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Settings/addresses.tsx b/apps/dashboard/src/modules/Settings/addresses.tsx deleted file mode 100644 index 4088f9bfa1..0000000000 --- a/apps/dashboard/src/modules/Settings/addresses.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { formatAddressLocationShort, formatAddressShort, getURLSearchParams, isNoneOrEmpty } from "@karrio/lib"; -import { AddressEditModal, AddressEditContext } from "@karrio/ui/modals/address-edit-modal"; -import { useAddressTemplateMutation, useAddressTemplates } from "@karrio/hooks/address"; -import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; -import { ConfirmModal, ConfirmModalContext } from "@karrio/ui/modals/confirm-modal"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Loading } from "@karrio/ui/components/loader"; -import React, { useContext, useEffect } from "react"; -import { useRouter } from "next/dist/client/router"; -import { AddressType } from "@karrio/types"; -import Head from "next/head"; - -export { getServerSideProps } from '@/context/main'; - - -export default function AddressPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - const router = useRouter(); - const { setLoading } = useContext(Loading); - const { confirm } = useContext(ConfirmModalContext); - const { editAddress } = useContext(AddressEditContext); - const { deleteAddressTemplate } = useAddressTemplateMutation(); - const [initialized, setInitialized] = React.useState(false); - const { query: { data: { address_templates } = {}, ...query }, filter, setFilter } = useAddressTemplates({ - setVariablesToURL: true, - }); - - const remove = (id: string) => async () => { - await deleteAddressTemplate.mutateAsync({ id }); - }; - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - }; - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - const templates = address_templates?.edges || []; - const addressTemplate: any = templates - .find(c => c.node.id === router.query.modal)?.node; - if (addressTemplate || router.query.modal === 'new') { - editAddress({ addressTemplate }); - } - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
- Settings -
- -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- - {((address_templates?.edges || []).length > 0) && <> -
- - - - - - - - - - - - {(address_templates?.edges || []).map(({ node: template }) => ( - - - - - - - - - - ))} - - -
LABELADDRESSEMAIL
- {template.label} - - - {formatAddressShort(template.address as AddressType)} - -
- - {formatAddressLocationShort(template.address as AddressType)} - -
- {template.address.email} - - {template.is_default && - default - } - -
- - -
-
-
- -
- - {(address_templates?.edges || []).length} results - - -
- - -
-
- } - - {(query.isFetched && (address_templates?.edges || []).length == 0) && -
- -
-

{`There aren't any results for that query.`}

-

{`Create a new address`}

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - - {`Addresses Settings - ${APP_NAME}`} - - - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Settings/organization.tsx b/apps/dashboard/src/modules/Settings/organization.tsx deleted file mode 100644 index 98a1eb8db8..0000000000 --- a/apps/dashboard/src/modules/Settings/organization.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { OrganizationManagement } from "@karrio/ui/forms/organization-management"; -import { InviteMemberProvider } from "@karrio/ui/modals/invite-member-modal"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function AccountPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> - -
- Settings -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- - {MULTI_ORGANIZATIONS &&
- - - -
} - - ); - }; - - return AuthenticatedPage(( - - {`Organization Settings - ${APP_NAME}`} - - - - - - - ), pageProps) -} diff --git a/apps/dashboard/src/modules/Settings/parcels.tsx b/apps/dashboard/src/modules/Settings/parcels.tsx deleted file mode 100644 index 922799697d..0000000000 --- a/apps/dashboard/src/modules/Settings/parcels.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import { ParcelEditModal, ParcelEditContext } from "@karrio/ui/modals/parcel-edit-modal"; -import { useParcelTemplateMutation, useParcelTemplates } from "@karrio/hooks/parcel"; -import { ConfirmModal, ConfirmModalContext } from "@karrio/ui/modals/confirm-modal"; -import { ParcelDescription } from "@karrio/ui/components/parcel-description"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Loading } from "@karrio/ui/components/loader"; -import { useRouter } from "next/dist/client/router"; -import { useContext, useEffect } from "react"; -import { getURLSearchParams, isNoneOrEmpty } from "@karrio/lib"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - - -export default function ParcelsPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - const router = useRouter(); - const { setLoading } = useContext(Loading); - const mutation = useParcelTemplateMutation(); - const { editParcel } = useContext(ParcelEditContext); - const [initialized, setInitialized] = React.useState(false); - const { confirm: confirmDeletion } = useContext(ConfirmModalContext); - const { query: { data: { parcel_templates } = {}, ...query }, filter, setFilter } = useParcelTemplates({ - setVariablesToURL: true, - }); - - const remove = (id: string) => async () => { - await mutation.deleteParcelTemplate.mutateAsync({ id }); - }; - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - }; - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - const parcelTemplate = (parcel_templates?.edges || []) - .find(c => c.node.id === router.query.modal) - ?.node; - if (parcelTemplate || router.query.modal === 'new') { - editParcel({ parcelTemplate } as any); - } - setInitialized(true); - } - query.isFetched && setInitialized(true); - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
- Settings -
- -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- - {((parcel_templates?.edges || []).length > 0) && <> -
- - - - - - - - - - - - {(parcel_templates?.edges || []).map(({ node: template }) => ( - - - - - - - - - ))} - - -
LABELPARCEL
- {template.label} - - - - {template.is_default && - default - } - -
- - -
-
-
- -
- {(parcel_templates?.edges || []).length} results - -
- - -
-
- } - - {(query.isFetched && (parcel_templates?.edges || [])?.length == 0) && -
- -
-

{`There aren't any results for that query.`}

-

{`Create a new parcel`}

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - {`Parcels Settings - ${APP_NAME}`} - - - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Settings/template.tsx b/apps/dashboard/src/modules/Settings/template.tsx deleted file mode 100644 index 53cbb521bb..0000000000 --- a/apps/dashboard/src/modules/Settings/template.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { DocumentTemplateType, DOCUMENT_RELATED_OBJECTS, NotificationType, TemplateType } from '@karrio/types'; -import { isEqual, isNoneOrEmpty, url$, useLocation, validationMessage, validityCheck } from '@karrio/lib'; -import { useDocumentTemplate, useDocumentTemplateMutation } from '@karrio/hooks/document-template'; -import { TextAreaField } from '@karrio/ui/components/textarea-field'; -import { AuthenticatedPage } from '@/layouts/authenticated-page'; -import React, { useEffect, useReducer, useState } from 'react'; -import { InputField } from '@karrio/ui/components/input-field'; -import { DEFAULT_DOCUMENT_TEMPLATE } from '@karrio/lib/sample'; -import { useAPIMetadata } from '@karrio/hooks/api-metadata'; -import { useNotifier } from '@karrio/ui/components/notifier'; -import { useLoader } from '@karrio/ui/components/loader'; -import { AppLink } from '@karrio/ui/components/app-link'; -import CodeMirror from '@uiw/react-codemirror'; -import { html } from '@codemirror/lang-html'; -import Head from 'next/head'; - -export { getServerSideProps } from "@/context/main"; - -type stateValue = string | boolean | string[] | Partial; -const DEFAULT_STATE = { - related_object: 'order', - template: DEFAULT_DOCUMENT_TEMPLATE, -}; - -function reducer(state: any, { name, value }: { name: string, value: stateValue }) { - switch (name) { - case 'partial': - return { ...(value as TemplateType) }; - default: - return { ...state, [name]: value } - } -} - -export default function DocumentTemplatePage(pageProps: any) { - const Component: React.FC = () => { - const loader = useLoader(); - const router = useLocation(); - const { id } = router.query; - const notifier = useNotifier(); - const { references } = useAPIMetadata(); - const [isNew, setIsNew] = useState(); - const mutation = useDocumentTemplateMutation(); - const [template, dispatch] = useReducer(reducer, DEFAULT_STATE, () => DEFAULT_STATE); - const { query: { data: { document_template } = {}, ...query }, docId, setDocId } = useDocumentTemplate({ - setVariablesToURL: true, - id: id as string, - }); - - const computeParams = (template: DocumentTemplateType) => { - if (isNoneOrEmpty(template.related_object)) { return ''; } - return `?${template.related_object}s=sample`; - }; - const handleChange = (event: React.ChangeEvent) => { - const target = event.target; - let value = target.type === 'checkbox' ? target.checked : target.value; - let name: string = target.name; - - if (target.multiple === true) { - value = Array.from(target.selectedOptions).map((o: any) => o.value) - } - - dispatch({ name, value }); - }; - const handleSubmit = async (evt: React.FormEvent) => { - evt.preventDefault(); - loader.setLoading(true); - const { updated_at, ...data } = template; - - try { - if (isNew) { - const { create_document_template } = await mutation.createDocumentTemplate.mutateAsync(data); - notifier.notify({ type: NotificationType.success, message: `Document template created successfully` }); - loader.setLoading(false); - - setDocId(create_document_template.template?.id as string); - } else { - await mutation.updateDocumentTemplate.mutateAsync(data); - query.refetch(); - notifier.notify({ type: NotificationType.success, message: `Document template updated successfully` }); - loader.setLoading(false); - } - } catch (message: any) { - notifier.notify({ type: NotificationType.error, message }); - loader.setLoading(false); - } - }; - - useEffect(() => { setIsNew(docId === 'new'); }, [docId]); - useEffect(() => { - if (docId !== 'new') { - dispatch({ name: 'partial', value: document_template as any }); - } - }, [document_template]); - - return ( -
- -
-
- - - - - - Edit document template -
-
- - Preview Template - - -
-
- -
- -
- -
- - - - - -
- - -
-
- -
-
-
- - - -
-
-

Editing your template

-

- To edit your template, use HTML, CSS, and - {' '} - Jinja variables - - {' '} for documents. -

-

- The template can be styled with - {' '} - bulma css framework - - {' '}. -

-
-
- -
- -
- -
-
- dispatch({ name: 'template', value })} - /> -
-
- -
- -
- ) - }; - - return AuthenticatedPage(( - <> - {`Template - ${(pageProps as any).metadata?.APP_NAME}`} - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Settings/templates.tsx b/apps/dashboard/src/modules/Settings/templates.tsx deleted file mode 100644 index b42e62aa1b..0000000000 --- a/apps/dashboard/src/modules/Settings/templates.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { useDocumentTemplateMutation, useDocumentTemplates } from "@karrio/hooks/document-template"; -import { ConfirmModal, ConfirmModalContext } from "@karrio/ui/modals/confirm-modal"; -import { TemplateDescription } from "@karrio/ui/components/template-description"; -import { DocumentTemplateType, NotificationType } from "@karrio/types"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Notify } from "@karrio/ui/components/notifier"; -import { useContext } from "react"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - - -export default function TemplatesPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - const { notify } = useContext(Notify); - const mutation = useDocumentTemplateMutation(); - const { confirm: confirmDeletion } = useContext(ConfirmModalContext); - const { query: { data: { document_templates } = {}, ...query }, filter, setFilter } = useDocumentTemplates(); - - const remove = (id: string) => async () => { - await mutation.deleteDocumentTemplate.mutateAsync({ id }); - }; - const toggle = ({ active, id }: DocumentTemplateType) => async () => { - try { - await mutation.updateDocumentTemplate.mutateAsync({ id, active: !active }); - notify({ - type: NotificationType.success, - message: `template ${!active ? 'enabled' : 'disabled'}!` - }); - } catch (message: any) { - notify({ type: NotificationType.error, message }); - } - }; - - return ( - <> - -
- Settings -
- - Create template - -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- - {((document_templates?.edges || [])?.length > 0) && <> -
- - - - - - - - - {(document_templates?.edges || []).map(({ node: template }) => ( - - - - - - - ))} - - -
DOCUMENT TEMPLATES
- - -
- - - - - - - -
-
-
- -
- - {(document_templates?.edges || []).length} results - - -
- - -
-
- } - - {(query.isFetched && (document_templates?.edges || [])?.length == 0) &&
- -
-

{`There aren't any results for that query.`}

-

{`Create a new template`}

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - {`Templates Settings - ${APP_NAME}`} - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Shipments/index.tsx b/apps/dashboard/src/modules/Shipments/index.tsx deleted file mode 100644 index dcba4bd82c..0000000000 --- a/apps/dashboard/src/modules/Shipments/index.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import { formatAddressShort, formatAddressLocationShort, formatDateTime, formatRef, getURLSearchParams, isListEqual, isNone, isNoneOrEmpty, formatCarrierSlug, url$, preventPropagation } from "@karrio/lib"; -import { ShipmentPreview, ShipmentPreviewContext } from "@/components/shipment-preview"; -import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; -import { useDocumentTemplates } from "@karrio/hooks/document-template"; -import { useCarrierConnections } from "@karrio/hooks/user-connection"; -import { ShipmentsFilter } from "@karrio/ui/filters/shipments-filter"; -import { ShipmentMenu } from "@karrio/ui/components/shipment-menu"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AddressType, ShipmentType } from "@karrio/types"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { useShipments } from "@karrio/hooks/shipment"; -import React, { useContext, useEffect } from "react"; -import { useRouter } from "next/dist/client/router"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function ShipmentsPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const { setLoading } = useLoader(); - const { metadata } = useAPIMetadata(); - const [allChecked, setAllChecked] = React.useState(false); - const [initialized, setInitialized] = React.useState(false); - const [selection, setSelection] = React.useState([]); - const { previewShipment } = useContext(ShipmentPreviewContext); - const { query: { data: { user_connections } = {} } } = useCarrierConnections(); - const { query: { data: { system_connections } = {} } } = useSystemCarrierConnections(); - const context = useShipments({ - status: ['purchased', 'delivered', 'in_transit', 'cancelled', 'needs_attention', 'out_for_delivery', 'delivery_failed'] as any, - setVariablesToURL: true, - preloadNextPage: true, - }) - const { query: { data: { shipments } = {}, ...query }, filter, setFilter } = context; - const { query: { data: { document_templates } = {} } } = useDocumentTemplates({ - related_object: "shipment" as any, active: true, - }); - - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - }; - const updatedSelection = (selectedShipments: string[], current: typeof shipments) => { - const shipment_ids = (current?.edges || []).map(({ node: shipment }) => shipment.id); - const selection = selectedShipments.filter(id => shipment_ids.includes(id)); - const selected = selection.length > 0 && selection.length === (shipment_ids || []).length; - setAllChecked(selected); - if (selectedShipments.filter(id => !shipment_ids.includes(id)).length > 0) { - setSelection(selection); - } - }; - const handleSelection = (e: React.ChangeEvent) => { - const { checked, name } = e.target as HTMLInputElement; - if (name === "all") { - setSelection(!checked ? [] : (shipments?.edges || []).map(({ node: { id } }) => id)); - } else { - setSelection(checked ? [...selection, name] : selection.filter(id => id !== name)); - } - }; - const computeDocFormat = (selection: string[]) => { - const _shipment = (shipments?.edges || []).find(({ node: shipment }) => (shipment.id == selection[0])); - return (_shipment?.node || {}).label_type; - }; - const compatibleTypeSelection = (selection: string[]) => { - const format = computeDocFormat(selection); - return (shipments?.edges || []).filter(({ node: shipment }) => ( - selection.includes(shipment.id) && shipment.label_type == format - )).length === selection.length; - }; - const draftSelection = (selection: string[]) => { - return (shipments?.edges || []).filter(({ node: shipment }) => ( - selection.includes(shipment.id) && shipment.status == "draft" - )).length === selection.length; - }; - const getRate = (shipment: any) => ( - shipment.selected_rate - || (shipment?.rates || []).find(_ => _.service === shipment?.options?.preferred_service) - || (shipment?.rates || [])[0] - || shipment - ); - const getCarrier = (rate?: ShipmentType['rates'][0]) => ( - user_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - || system_connections?.find(_ => _.id === rate?.meta?.carrier_connection_id || _.carrier_id === rate?.carrier_id) - ); - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { updatedSelection(selection, shipments); }, [selection, shipments]); - useEffect(() => { - if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - previewShipment(router.query.modal as string); - setInitialized(true); - } - }, [router.query.modal, query.isFetched]); - - return ( - <> - -
-
- Shipments -
-
- - Create Label - - - Manage manifests - - -
-
- - - - {!query.isFetched && } - - {(query.isFetched && (shipments?.edges || []).length > 0) && <> -
- - - - - - - {selection.length > 0 && } - - {selection.length === 0 && <> - - - - - - - } - - - {(shipments?.edges || []).map(({ node: shipment }) => ( - - - - - - - - - - ))} - - - -
- - -
- - Create labels - - - Print Labels - - - Print Invoices - - {(document_templates?.edges || []).map(({ node: template }) => - - Print {template.name} - - )} -
-
SHIPPING SERVICERECIPIENTREFERENCEDATE
- - previewShipment(shipment.id)} - title={( - isNone(getRate(shipment)) - ? "UNFULFILLED" - : formatRef(((shipment.meta as any)?.service_name || getRate(shipment).service) as string) - )} - > -
- -
- - {!isNone(shipment.tracking_number) && {shipment.tracking_number}} - {isNone(shipment.tracking_number) && - } -
- - {!isNone(getRate(shipment).carrier_name) && formatRef(((getRate(shipment).meta as any)?.service_name || getRate(shipment).service) as string)} - {isNone(getRate(shipment).carrier_name) && "UNFULFILLED"} - -
-
-
previewShipment(shipment.id)}> - - previewShipment(shipment.id)}> -
-

- {formatAddressShort(shipment.recipient as AddressType)} -

-

{formatAddressLocationShort(shipment.recipient as AddressType)}

-
-
previewShipment(shipment.id)}> - {shipment.reference || ''} - previewShipment(shipment.id)}> -

- {formatDateTime(shipment.created_at)} -

-
- -
-
- -
- - {(shipments?.edges || []).length} results - - -
- - -
-
- } - - {(query.isFetched && (shipments?.edges || []).length == 0) && -
- -
-

No shipment found.

-
- -
} - - - ); - }; - - return AuthenticatedPage(( - - {`Shipments - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - - - ), pageProps) -}; diff --git a/apps/dashboard/src/modules/Shipments/shipment.tsx b/apps/dashboard/src/modules/Shipments/shipment.tsx deleted file mode 100644 index d3ca3497c4..0000000000 --- a/apps/dashboard/src/modules/Shipments/shipment.tsx +++ /dev/null @@ -1,566 +0,0 @@ -import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; -import { MetadataEditor, MetadataEditorContext } from "@karrio/ui/forms/metadata-editor"; -import { useUploadRecordMutation, useUploadRecords } from "@karrio/hooks/upload-record"; -import { CommodityDescription } from "@karrio/ui/components/commodity-description"; -import { OptionsDescription } from "@karrio/ui/components/options-description"; -import { formatDateTime, formatDayDate, formatRef, isNone } from "@karrio/lib"; -import { AddressDescription } from "@karrio/ui/components/address-description"; -import { ParcelDescription } from "@karrio/ui/components/parcel-description"; -import { CustomsType, NotificationType, ParcelType } from "@karrio/types"; -import { StatusCode } from "@karrio/ui/components/status-code-badge"; -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { CarrierBadge } from "@karrio/ui/components/carrier-badge"; -import { ShipmentMenu } from "@karrio/ui/components/shipment-menu"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { SelectField } from "@karrio/ui/components/select-field"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { InputField } from "@karrio/ui/components/input-field"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useNotifier } from "@karrio/ui/components/notifier"; -import { DocumentUploadData } from "@karrio/types/rest/api"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { MetadataObjectTypeEnum } from "@karrio/types"; -import { useShipment } from "@karrio/hooks/shipment"; -import { useRouter } from "next/dist/client/router"; -import { useEvents } from "@karrio/hooks/event"; -import { useLogs } from "@karrio/hooks/log"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - -type FileDataType = DocumentUploadData['document_files'][0]; - -export const ShipmentComponent: React.FC<{ shipmentId?: string }> = ({ shipmentId }) => { - const router = useRouter(); - const notifier = useNotifier(); - const { setLoading } = useLoader(); - const $fileInput = React.useRef(); - const $fileSelectInput = React.useRef(); - const { references: { carrier_capabilities = {} } } = useAPIMetadata(); - const entity_id = shipmentId || router.query.id as string; - const { query: logs } = useLogs({ entity_id }); - const { query: events } = useEvents({ entity_id }); - const { query: { data: { shipment } = {}, ...query } } = useShipment(entity_id); - const { uploadDocument } = useUploadRecordMutation(); - const { query: { data: { results: uploads } = {}, ...documents } } = useUploadRecords({ shipmentId: entity_id }); - const [fileData, setFileData] = React.useState({} as FileDataType) - - const handleFileChange = (e: React.ChangeEvent) => { - e.preventDefault(); - try { - if (!!e.target.files && !!e.target.files[0]) { - let file = e.target.files[0]; - let reader = new FileReader(); - reader.readAsDataURL(file); - reader.onloadend = () => { - let sections = (reader.result as string).split(','); - let doc_file = sections[sections.length - 1]; - let doc_name = file.name; - setFileData({ ...fileData, doc_name, doc_file }); - }; - } else { - setFileData({ doc_file: fileData.doc_file } as FileDataType); - } - } catch (_) { - setFileData({ doc_file: fileData.doc_file } as FileDataType); - } - }; - const uploadCustomsDocument = async () => { - try { - await uploadDocument.mutateAsync({ - shipment_id: entity_id, - document_files: [fileData], - }); - notifier.notify({ - type: NotificationType.success, - message: `document updloaded successfully`, - }); - if (!!$fileInput.current) $fileInput.current.value = ''; - if (!!$fileSelectInput.current) $fileSelectInput.current.value = 'other'; - } catch (message: any) { - notifier.notify({ type: NotificationType.error, message }); - } - }; - - React.useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - - return ( - <> - - {!query.isFetched && query.isFetching && } - - {shipment && <> - - {/* Header Section */} -
-
- SHIPMENT -
- {shipment.tracking_number || "UNFULFILLED"} - -
- -
-
- -
-
- - {!isNone(shipmentId) && - - - - } - -
- -
- -
-
-
- -
- - {/* Reference and highlights section */} -
-
- Date
- {formatDateTime(shipment.created_at)} -
- - {!isNone(shipment.service) && <> -
-
- Courier
- -
- -
-
- Service Level
- - {formatRef(((shipment.meta as any)?.service_name || shipment.service) as string)} - -
- } - - {!isNone(shipment.reference) && <> -
-
- Reference
- {shipment.reference} -
- } -
- - {!isNone(shipment.selected_rate) && <> - -

Service Details

-
- -
-
-
-
-
Service
-
- {formatRef(((shipment.meta as any)?.service_name || shipment.service) as string)} -
-
-
-
Courier
-
- {formatRef(shipment.meta.carrier as string)} -
-
-
-
Rate
-
- {shipment.selected_rate?.total_charge} - {shipment.selected_rate?.currency} -
-
-
-
Rate Provider
-
- {formatRef(shipment.meta.ext as string)} -
-
-
-
Tracking Number
-
- {shipment.tracking_number as string} -
-
-
- - {(shipment.selected_rate?.extra_charges || []).length > 0 && <> -
-

CHARGES

-
- - {(shipment.selected_rate?.extra_charges || []).map((charge, index) =>
-
- {charge?.name?.toLocaleLowerCase()} -
-
- {charge?.amount} - {!isNone(charge?.currency) && {charge?.currency}} -
-
)} -
- } - -
-
- - } - - {!isNone(shipment.tracker) && <> - -

- Tracking Details - - - -

-
-
-
- -
- {!isNone(shipment.tracker?.estimated_delivery) &&
-
{shipment.tracker?.delivered ? 'Delivered' : 'Estimated Delivery'}
-
- {formatDayDate(shipment.tracker!.estimated_delivery as string)} -
-
} -
-
Last event
-
-

- {formatDayDate((shipment.tracker?.events || [])[0]?.date as string)} - {' '} - {(shipment.tracker?.events || [])[0]?.time} -

-
-
- {!isNone((shipment.tracker?.events || [])[0]?.location) &&
-
-
- {(shipment.tracker?.events || [])[0]?.location} -
-
} - {!isNone((shipment.tracker?.events || [])[0]?.description) &&
-
-
- {(shipment.tracker?.events || [])[0]?.description} -
-
} -
- -
-
- - } - - - {/* Shipment details section */} -

Shipment Details

-
- -
- -
- {/* Recipient Address section */} -
-

ADDRESS

- - -
- - {/* Options section */} - {(Object.values(shipment.options as object).length > 0) &&
-

OPTIONS

- - - -
} -
- - {/* Parcels section */} -
-

- PARCEL{shipment.parcels.length > 1 && "S"} -

- - {shipment.parcels.map((parcel: ParcelType, index) => - -
- -
- - {/* Parcel details */} -
- -
- - {/* Parcel items */} - {((parcel.items || []).length > 0) && -
-

- ITEMS {" "} - ({(parcel.items || []).reduce((acc, { quantity }) => acc + (quantity || 0), 0)}) -

- -
- {(parcel.items || []).map((item, index) => -
- -
)} -
-
} - -
- -
)} -
- - {/* Customs section */} -
- - {/* Customs details */} - {!isNone(shipment.customs) &&
-

CUSTOMS DECLARATION

- - -
} - - {/* Customs commodities */} - {(!isNone(shipment.customs) && (shipment.customs?.commodities || []).length > 0) &&
-

- COMMODITIES {" "} - ({(shipment.customs?.commodities || []).reduce((acc, { quantity }) => acc + (quantity || 0), 0)}) -

- - {(shipment.customs?.commodities || []).map((commodity, index) => -
- -
)} -
} - -
- -
- - - {/* Document section */} - {((carrier_capabilities[shipment.carrier_name as string] || []) as any).includes("paperless") && ("paperless_trade" in shipment.options) && <> - -

Paperless Trade Documents

- - {(!documents.isFetched && documents.isFetching) && } - - {(documents.isFetched && !documents.isFetching) && [...(uploads || []), ...(shipment.options.doc_files || [])].length == 0 && <> -
-
No documents uploaded
- } - - {documents.isFetched && [...(uploads || []), ...(shipment.options.doc_files || [])].length > 0 && -
- - - {(uploads || []).map(upload => - {(upload.documents || []).map(doc => ( - - - - - ))} - )} - {(shipment.options.doc_files || []).map((doc: any, idx: number) => ( - - - - - ))} - -
- {doc.file_name} - - uploaded -
- {doc.doc_name} - - uploaded -
-
} - -
-
- setFileData({ ...fileData, doc_type: e.target.value })} - defaultValue="other" - className="is-small is-fullwidth"> - - - - - - - -
- - -
- -
- } - - - {/* Metadata section */} - - {({ isEditing, editMetadata }) => (<> - -
-

Metadata

- - -
- -
- - )}
-
- -
- - {/* Logs section */} -

Logs

- - {!logs.isFetched && logs.isFetching && } - - {logs.isFetched && (logs.data?.logs.edges || []).length == 0 &&
No logs
} - - {logs.isFetched && (logs.data?.logs.edges || []).length > 0 && -
- - - {(logs.data?.logs.edges || []).map(({ node: log }) => ( - - - - - - ))} - -
- - - - - - {`${log.method} ${log.path}`} - - - - {formatDateTime(log.requested_at)} - -
-
} - -
- - {/* Events section */} -

Events

- - {!events.isFetched && events.isFetching && } - - {events.isFetched && (events.data?.events.edges || []).length == 0 &&
No events
} - - {events.isFetched && (events.data?.events.edges || []).length > 0 && -
- - - {(events.data?.events.edges || []).map(({ node: event }) => ( - - - - - ))} - -
- - {`${event.type}`} - - - - {formatDateTime(event.created_at)} - -
-
} - } - - {query.isFetched && isNone(shipment) &&
- -
-

Uh Oh!

-

{"We couldn't find any shipment with that reference"}

-
- -
} - - ); -}; - -export default function ShipmentPage(pageProps: any) { - return AuthenticatedPage(( - - {`Shipment - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Trackers/index.tsx b/apps/dashboard/src/modules/Trackers/index.tsx deleted file mode 100644 index 263d8e9cfe..0000000000 --- a/apps/dashboard/src/modules/Trackers/index.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { TrackerModalProvider, TrackerModalContext } from "@karrio/ui/modals/track-shipment-modal"; -import { TrackingPreview, TrackingPreviewContext } from "@/components/tracking-preview"; -import { ConfirmModal, ConfirmModalContext } from "@karrio/ui/modals/confirm-modal"; -import { formatRef, getURLSearchParams, isNone, isNoneOrEmpty } from "@karrio/lib"; -import { useTrackerMutation, useTrackers } from "@karrio/hooks/tracker"; -import { TrackersFilter } from "@karrio/ui/filters/trackers-filter"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useLoader } from "@karrio/ui/components/loader"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { TrackingEvent } from "@karrio/types/rest/api"; -import React, { useContext, useEffect } from "react"; -import { useRouter } from "next/dist/client/router"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function TrackersPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - const { modal } = router.query; - const { setLoading } = useLoader(); - const mutation = useTrackerMutation(); - const { addTracker } = useContext(TrackerModalContext); - const { previewTracker } = useContext(TrackingPreviewContext); - const { confirm: confirmDeletion } = useContext(ConfirmModalContext); - const [initialized, setInitialized] = React.useState(false); - const context = useTrackers({ setVariablesToURL: true, preloadNextPage: true }); - const { query: { data: { trackers } = {}, ...query }, filter, setFilter } = context; - - const remove = (id: string) => async () => { - await mutation.deleteTracker.mutateAsync({ idOrTrackingNumber: id }); - }; - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra, - }; - - setFilter(query); - } - - useEffect(() => { updateFilter(); }, [router.query]); - useEffect(() => { setLoading(query.isFetching); }, [query.isFetching]); - useEffect(() => { - if (query.isFetching && !initialized && !isNoneOrEmpty(modal)) { - const tracker = (trackers?.edges || []).find(t => t.node.id === modal)?.node; - (modal === 'new') && addTracker({ onChange: updateFilter }); - tracker && previewTracker(tracker); - setInitialized(true); - } - }, [modal, query.isFetched]); - - return ( - <> -
- Trackers -
- - -
-
- - - - {(!query.isFetched && query.isFetching) && } - - {(query.isFetched && (trackers?.edges || []).length > 0) && <> -
- - - - - - - - - - - - {(trackers?.edges || []).map(({ node: tracker }) => ( - previewTracker(tracker)}> - - - - - - - ))} - - - -
SHIPPING SERVICELAST EVENT
-
- -
- {tracker.tracking_number} -
- - {formatRef(tracker.info?.shipment_service || tracker.shipment?.meta?.service_name || tracker.shipment?.service || `SERVICE UNKNOWN`)} - -
-
-
- - - - {isNoneOrEmpty(tracker?.events) ? "" : formatEventDescription((tracker?.events as TrackingEvent[])[0])} - - -

- {isNoneOrEmpty(tracker?.events) ? "" : formatEventDate((tracker?.events as TrackingEvent[])[0])} -

-
- -
- -
- -
- {(trackers?.edges || []).length} results - -
- - -
-
- } - - {(query.isFetched && (trackers?.edges || []).length == 0) &&
- -
-

No shipment trackers found.

-
- -
} - - - ); - }; - - - return AuthenticatedPage(( - - {`Trackers - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - - - - - ), pageProps) -} - -function formatEventDescription(last_event?: TrackingEvent): string { - return last_event?.description || ''; -} - -function formatEventDate(last_event?: TrackingEvent): string { - if (isNone(last_event)) return ''; - - return [ - last_event?.date, - last_event?.time - ].filter(a => !isNone(a) && a !== "").join(" "); -} diff --git a/apps/dashboard/src/modules/Trackers/tracking-page.tsx b/apps/dashboard/src/modules/Trackers/tracking-page.tsx deleted file mode 100644 index 4c93b881de..0000000000 --- a/apps/dashboard/src/modules/Trackers/tracking-page.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { TrackingEvent, TrackingStatus } from "@karrio/types/rest/api"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { formatDayDate, isNone } from "@karrio/lib"; -import { Metadata } from "@karrio/types"; -import { NextPage } from "next"; -import Head from "next/head"; -import Link from "next/link"; -import React from "react"; - -export { getServerSideProps } from '@/context/tracker'; - -type DayEvents = { [k: string]: TrackingEvent[] }; - -const Tracking: NextPage<{ id: string, metadata: Metadata, tracker?: TrackingStatus, message?: string }> = ({ metadata, id, tracker, message }) => { - - const computeEvents = (tracker: TrackingStatus): DayEvents => { - return (tracker?.events || []).reduce((days, event: TrackingEvent) => { - const daydate = formatDayDate(event.date as string); - return { ...days, [daydate]: [...(days[daydate] || []), event] }; - }, {} as DayEvents); - }; - - return ( - <> - {`Tracking - ${tracker?.tracking_number || id} - ${metadata?.APP_NAME}`} - -
- -
- -
- - {metadata?.APP_NAME} - -
- - {!isNone(tracker) && <> -
-
- -
- -
- - -

- Tracking ID {tracker?.tracking_number} -

- - {!isNone(tracker?.estimated_delivery) &&

- {tracker?.delivered ? 'Delivered' : 'Estimated Delivery'} {' '} - {formatDayDate(tracker!.estimated_delivery as string)} -

} - -
- -
- - {(tracker?.status === 'delivered') && -

Delivered

} - - {(tracker?.status === 'in_transit') && -

In-Transit

} - - {(tracker?.status !== 'delivered' && tracker?.status !== 'in_transit') && -

Pending

} - -
- -
- -
- -
- - - -
- - } - - {!isNone(message) &&
-
-

{message}

-
-
} - -
- -
- -
-
-

- - Powered by © {metadata.APP_NAME} - -

-
-
- -
- - ) -}; - -export default Tracking; diff --git a/apps/dashboard/src/modules/Workflows/event.tsx b/apps/dashboard/src/modules/Workflows/event.tsx deleted file mode 100644 index 9fa47254fe..0000000000 --- a/apps/dashboard/src/modules/Workflows/event.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { failsafe, formatDateTimeLong, isNone, jsonify } from "@karrio/lib"; -import { Tabs, TabStateProvider } from "@karrio/ui/components/tabs"; -import { CopiableLink } from "@karrio/ui/components/copiable-link"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useWorkflowEvent } from "@karrio/hooks/workflow-events"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { useRouter } from "next/dist/client/router"; -import json from 'highlight.js/lib/languages/json'; -import hljs from "highlight.js"; -import Head from "next/head"; -import moment from "moment"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - -hljs.registerLanguage('json', json); - - -export const Component: React.FC<{ eventId?: string }> = ({ eventId }) => { - const router = useRouter(); - const { setLoading } = useLoader(); - const entity_id = eventId || router.query.id as string; - const { query: { data: { workflow_event } = {}, ...query } } = useWorkflowEvent(entity_id); - - React.useEffect(() => { - (window as any).moment = moment; - setLoading(query.isFetching); - }, [query.isFetching]); - - return ( - <> - {workflow_event !== undefined && <> - -
-
- WORKFLOW EVENT -
-
- {workflow_event?.event_type} - -
-
- {!isNone(eventId) &&
- - - - - -
} -
- -
- -
-
-
ID
-
{workflow_event?.id}
-
-
-
Workflow ID
-
{workflow_event?.workflow?.id}
-
-
-
Workflow name
-
{workflow_event?.workflow?.name}
-
-
-
Date
-
{formatDateTimeLong(workflow_event?.created_at)}
-
-
- - - - -
- -

Event parameters

- - {(Object.keys(workflow_event?.parameters || {}).length == 0) &&
- No parameters provided... -
} - - {(Object.keys(workflow_event?.parameters || {}).length > 0) && <> -
- -
-                    
-                  
-
- } - -
- -
- {(workflow_event?.records || []).length == 0 &&
- No tracing records... -
} - - {(workflow_event?.records || []).length > 0 && <> - - {workflow_event!.records.map((trace) => { - - return (
-
-

- Record type: {trace.key} -

- {!!trace.record.url &&

- URL: {trace.record.url} -

} - {!!trace.record.request_id &&

- Request ID: {trace.record.request_id} -

} - {!!trace.record.action_name &&

- Action: {trace.record.action_name} -

} - {trace?.timestamp &&

- Request Timestamp: {moment(trace.timestamp * 1000).format('LTS')} -

} -
- -
- -
-                        
-                      
-
- -
); - })} - - } -
- -
-
- - } - - ); -}; - -export default function Page(pageProps: any) { - return AuthenticatedPage(( - - {`Workflow Event - ${(pageProps as any).metadata?.APP_NAME}`} - - - ), pageProps); -} - -export function parseWorkflowEventRecordData(record: any) { - if (!record) return null; - if (record?.format === 'xml') { - return (record.data || record.response || record.error); - } - - return failsafe( - () => jsonify(record.data || record.response || record.error || record.parameters), - (record.data || record.response || record.error) - ) -} diff --git a/apps/dashboard/src/modules/Workflows/events.tsx b/apps/dashboard/src/modules/Workflows/events.tsx deleted file mode 100644 index 329d2ecf2a..0000000000 --- a/apps/dashboard/src/modules/Workflows/events.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { WorkflowPreviewModal } from "@/components/workflow-event-preview"; -import { formatDateTimeLong, getURLSearchParams } from "@karrio/lib"; -import { useWorkflowEvents } from "@karrio/hooks/workflow-events"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { StatusBadge } from "@karrio/ui/components/status-badge"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { Spinner } from "@karrio/ui/components"; -import { useRouter } from "next/router"; -import Head from "next/head"; -import React from "react"; -import { WorkflowEventFilter } from "@karrio/types/graphql/ee"; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ - ModalProvider, -]); - -export const WorkflowEventList: React.FC<{ defaultFilter?: WorkflowEventFilter }> = ({ defaultFilter }) => { - const router = useRouter(); - const loader = useLoader(); - const { query: { data: { workflow_events } = {}, ...query }, filter, setFilter } = useWorkflowEvents({ - setVariablesToURL: true, - ...(defaultFilter || {}), - }); - - const updateFilter = (extra: Partial = {}) => { - const query = { - ...filter, - ...getURLSearchParams(), - ...extra - }; - - setFilter(query); - } - - React.useEffect(() => { updateFilter(); }, [router.query]); - React.useEffect(() => { loader.setLoading(query.isFetching); }, [query.isFetching]); - React.useEffect(() => { - // if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { - // previewEvent(router.query.modal as string); - // setInitialized(true); - // } - }, [router.query.modal, query.isFetched]); - - return ( - <> - - {/* APIs Overview */} - {!query.isFetched && } - - {(query.isFetched && (workflow_events?.edges || []).length > 0) && <> -
- - - - - - - - - - {(workflow_events?.edges || []).map(({ node: event }) => ( - - - - - - - } - /> - - ))} - - -
EVENTDATE
- - - - {`${event.event_type} trigger of ${event.workflow.name}`} - - - {formatDateTimeLong(event.created_at)} -
-
- -
- - {(workflow_events?.edges || []).length} results - - -
- - -
-
- } - - {(query.isFetched && (workflow_events?.edges || []).length == 0) &&
- -
-

No Workflow events found.

-
- -
} - - - ) -}; - -export default function Page(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - return ( - <> - -
-
- Workflows - BETA -
-
-
- -
-
    -
  • - - Overview - -
  • -
  • - - Event history - -
  • -
-
- - - - - ); - }; - - return AuthenticatedPage(( - - {`Workflows - ${references?.APP_NAME}`} - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Workflows/index.tsx b/apps/dashboard/src/modules/Workflows/index.tsx deleted file mode 100644 index 16795464ad..0000000000 --- a/apps/dashboard/src/modules/Workflows/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { WorkflowMenu } from "@karrio/ui/components/workflow-menu"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { useWorkflows } from "@karrio/hooks/workflows"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { Spinner } from "@karrio/ui/components"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ - ModalProvider, -]); - - -export default function Page(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - const { query: { data: { workflows } = {}, ...query } } = useWorkflows(); - - return ( - <> - -
-
- Workflows - BETA -
-
- - Create Workflow - -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - Event history - -
  • -
-
- - {!query.isFetched && } - - {(query.isFetched && (workflows?.edges || []).length > 0) && <> - - - - } - - {(query.isFetched && (workflows?.edges || []).length == 0) && <> -
- -
-

No workflow found.

-
- -
- } - - - ); - }; - - return AuthenticatedPage(( - - {`Workflows - ${references?.APP_NAME}`} - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/modules/Workflows/workflow.tsx b/apps/dashboard/src/modules/Workflows/workflow.tsx deleted file mode 100644 index 9a543903ef..0000000000 --- a/apps/dashboard/src/modules/Workflows/workflow.tsx +++ /dev/null @@ -1,637 +0,0 @@ -import { ActionNodeInput, AutomationActionType, AutomationAuthType, AutomationEventStatus, AutomationTriggerType } from '@karrio/types/graphql/ee'; -import { ConnectionModalEditor } from '@karrio/ui/modals/workflow-connection-edit-modal'; -import { ActionModalEditor } from '@karrio/ui/modals/workflow-action-edit-modal'; -import { isEqual, isNone, isNoneOrEmpty, url$, useLocation } from '@karrio/lib'; -import { TextAreaField } from '@karrio/ui/components/textarea-field'; -import { TabStateProvider, Tabs } from '@karrio/ui/components/tabs'; -import { WorkflowActionType } from '@karrio/hooks/workflow-actions'; -import { ConfirmModalWrapper } from '@karrio/ui/modals/form-modals'; -import { CopiableLink } from '@karrio/ui/components/copiable-link'; -import { AuthenticatedPage } from '@/layouts/authenticated-page'; -import { InputField } from '@karrio/ui/components/input-field'; -import { useAPIMetadata } from '@karrio/hooks/api-metadata'; -import { useWorkflowForm } from '@karrio/hooks/workflows'; -import { useLoader } from '@karrio/ui/components/loader'; -import { AppLink } from '@karrio/ui/components/app-link'; -import { ModalProvider } from '@karrio/ui/modals/modal'; -import django from 'highlight.js/lib/languages/django'; -import { parseWorkflowEventRecordData } from './event'; -import { bundleContexts } from '@karrio/hooks/utils'; -import { jsonLanguage } from '@codemirror/lang-json'; -import { htmlLanguage } from '@codemirror/lang-html'; -import { SelectField } from '@karrio/ui/components'; -import json from 'highlight.js/lib/languages/json'; -import { Disclosure } from '@headlessui/react'; -import CodeMirror from '@uiw/react-codemirror'; -import { WorkflowEventList } from './events'; -import React, { useState } from 'react'; -import hljs from "highlight.js"; -import Head from 'next/head'; -import moment from 'moment'; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ModalProvider]); -hljs.registerLanguage('django', django); -hljs.registerLanguage('json', json); - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - const loader = useLoader(); - const router = useLocation(); - const { id } = router.query; - const { metadata } = useAPIMetadata() - const [key, setKey] = useState(`workflow-${Date.now()}`); - const { workflow, current, isNew, DEFAULT_STATE, query, zipActionWithNode, debug_event, ...mutation } = useWorkflowForm({ id: id as string }); - - const handleChange = async (changes?: Partial) => { - if (changes === undefined) { return; } - await mutation.updateWorkflow({ id, ...changes }); - setKey(`${id}-${Date.now()}`); - }; - - const NextIndicator = () => ( -
- - - -
- ); - - return ( -
- -
-
- - - - - - Edit workflow -
-
- -
-
- - - - <> - {(query.isFetched && !!workflow.actions) &&
- - {/* Workflow fields section */} -
- - handleChange({ name: e.target.value })} - placeholder="ERP orders sync" - className="is-small" - required - /> - - handleChange({ description: e.target.value })} - placeholder="Automate ERP orders syncing for fulfillment" - className="is-small" - /> - -
- -
- -
- -
- - {/* Workflow related objects section */} -
- - {/* Trigger section */} - - -
- Trigger -

How the workflow is tiggered

-
-
- -
- -
- - {/* trigger type */} - handleChange({ trigger: { ...workflow.trigger, trigger_type: e.target.value } })} - > - {Array.from(new Set(Object.values(AutomationTriggerType))).map( - unit => - )} - - - {/* trigger schedule */} -
- - handleChange({ trigger: { ...workflow.trigger, schedule: e.target.value } })} - /> - -
- - {/* webhook options */} -
- - handleChange({ trigger: { ...workflow.trigger, secret: e.target.value } })} - /> - - handleChange({ trigger: { ...workflow.trigger, secret_key: e.target.value } })} - /> - -
- -
- -
- - {/* webhook URL */} - } - readOnly - /> -
-
- - - - {/* Actions section */} - {zipActionWithNode(workflow.actions as WorkflowActionType[], workflow.action_nodes as ActionNodeInput[]).map(([action, node], index) => ( - - - -
- Action -

- {action.name || 'An action to perform'} -

-
-
- - - - } - /> - - - - } - /> -
-
- -
- - - - -
-
- mutation.updateAction(index, action?.id)({ parameters_template: value })} - /> -
-
- -
- - {(debug_event?.records || []).filter(_ => (_.meta.workflow_action_slug === action.slug && _.key === "action-input")).length == 0 && -
- No input data sample... -
} - - {(debug_event?.records || []).length > 0 && <> - - {debug_event!.records.filter(_ => (_.meta.workflow_action_slug === action.slug && _.key === "action-input")) - .map((trace) => { - return ( -
- -
- ); - })} - - } - -
- -
- - {(debug_event?.records || []).filter(_ => (_.meta.workflow_action_slug === action.slug && _.key === "action-output")).length == 0 && -
- No output data sample... -
} - - {(debug_event?.records || []).length > 0 && <> - - {debug_event!.records.filter(_ => (_.meta.workflow_action_slug === action.slug && _.key === "action-output")) - .map((trace) => { - return ( -
- -
- ); - })} - - } - -
- -
- - {(debug_event?.records || []).length == 0 &&
- No tracing records... -
} - - {(debug_event?.records || []).length > 0 && <> - - {debug_event!.records.filter(_ => _.meta.workflow_action_id == action.id).map((trace) => { - return (
-
-

- Record type: {trace.key} -

- {!!trace.record.url &&

- URL: {trace.record.url} -

} - {!!trace.record.request_id &&

- Request id: {trace.record.request_id} -

} - {trace?.timestamp &&

- Request timestamp: {moment(trace.timestamp * 1000).format('LTS')} -

} - {!!trace.record.status &&

- Step status: {trace.record.status} -

} -
- -
- -
-                                            
-                                          
-
- -
); - })} - - } - -
- -
-
- - {/* action type */} -
-
- Action type -
-
{action.action_type}
-
- - {/* action description */} - {!!action.description &&
-
- description -
-
{action.description}
-
} - - {/* data mapping options */} - {action.action_type == AutomationActionType.data_mapping && <> - - {/* action parameters type */} -
-
- format -
-
{action.content_type}
-
- - } - - {/* http request options */} - {action.action_type == AutomationActionType.http_request && <> - - {/* action method */} -
-
- Method -
-
{action.method?.toLocaleUpperCase()}
-
- - {/* action host */} -
-
- Host -
-
{action.host}
-
- - {/* host port */} - {!isNone(action.port) &&
-
- Port -
-
{action.port}
-
} - - {/* action endpoint */} - {!isNoneOrEmpty(action.endpoint) &&
-
- Endpoint -
-
{action.endpoint}
-
} - - {/* action content type */} -
-
- Content Type -
-
{action.content_type}
-
- - {/* action parameters type */} -
-
- Parameters Type -
-
{action.parameters_type}
-
- - } - -
- - - -
- Connection -

- {action.connection?.name || 'A connection for the action'} -

-
-
- - - - - - } - /> - - - - } - /> -
-
- -
- - {!action.connection &&
-
- No connection defined! -
-
} - - {!!action.connection &&
- - {/* auth type */} -
-
- Auth type -
-
{action.connection.auth_type}
-
- - {/* connection description */} - {!!action.connection.description &&
-
- description -
-
{action.connection.description}
-
} - - {/* http request options */} - {[AutomationAuthType.oauth2, AutomationAuthType.jwt].includes(action.connection.auth_type as any) && <> - - {/* connection host */} -
-
- Host -
-
{action.connection.host}
-
- - {/* host port */} - {!isNone(action.connection.port) &&
-
- Port -
-
{action.connection.port}
-
} - - {/* endpoint */} - {!isNoneOrEmpty(action.connection.endpoint) &&
-
- Endpoint -
-
{action.connection.endpoint}
-
} - - } - - {/* connection auth template */} -
-
- Auth template -
-
-
-                                              
-                                            
-
-
- - {[AutomationAuthType.oauth2, AutomationAuthType.jwt].includes(action.connection.auth_type as any) && <> - - {/* parameters template */} -
-
- Template -
-
-
-                                                
-                                              
-
-
- - } - -
} -
-
-
- -
-
- -
-
- - -
- ))} - - - {/* Add action button */} - - -
- } - /> -
- -
- -
- -
-
- -
} - - - <> - {!!id && } - - - - - - ) - }; - - return AuthenticatedPage(( - <> - {`Workflow - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - - - ), pageProps); -} diff --git a/apps/dashboard/src/pages/_app.tsx b/apps/dashboard/src/pages/_app.tsx index 2cf1a39e50..76160550f8 100644 --- a/apps/dashboard/src/pages/_app.tsx +++ b/apps/dashboard/src/pages/_app.tsx @@ -1,14 +1,14 @@ -import '@fortawesome/fontawesome-free/css/all.min.css'; -import 'highlight.js/styles/stackoverflow-light.css'; -import '@/styles/theme.scss'; -import '@/styles/dashboard.scss'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import APIMetadataProvider from '@karrio/hooks/api-metadata'; -import { NextPostHogProvider } from '@karrio/hooks/posthog'; -import { ClientProvider } from '@karrio/hooks/karrio'; +import "@fortawesome/fontawesome-free/css/all.min.css"; +import "highlight.js/styles/stackoverflow-light.css"; +import "@/styles/theme.scss"; +import "@/styles/dashboard.scss"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import APIMetadataProvider from "@karrio/hooks/api-metadata"; +import { NextPostHogProvider } from "@karrio/hooks/posthog"; +import { ClientProvider } from "@karrio/hooks/karrio"; import { SessionProvider } from "next-auth/react"; -import MainLayout from '@/layouts/main-layout'; -import type { AppProps } from 'next/app'; +import MainLayout from "@karrio/core/layouts/main-layout"; +import type { AppProps } from "next/app"; const queryClient = new QueryClient(); diff --git a/apps/dashboard/src/pages/_sites/[site]/accept-invite/index.tsx b/apps/dashboard/src/pages/_sites/[site]/accept-invite/index.tsx index dce5918b12..444f5ac286 100644 --- a/apps/dashboard/src/pages/_sites/[site]/accept-invite/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/accept-invite/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Invitation/accept-invite"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Invitation/accept-invite"; diff --git a/apps/dashboard/src/pages/_sites/[site]/admin/carrier_connections.tsx b/apps/dashboard/src/pages/_sites/[site]/admin/carrier_connections.tsx index af6521ba4b..72b044f4c8 100644 --- a/apps/dashboard/src/pages/_sites/[site]/admin/carrier_connections.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/admin/carrier_connections.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/carrier_connections"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/carrier_connections"; diff --git a/apps/dashboard/src/pages/_sites/[site]/admin/index.tsx b/apps/dashboard/src/pages/_sites/[site]/admin/index.tsx index e881b44a45..063349b377 100644 --- a/apps/dashboard/src/pages/_sites/[site]/admin/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/admin/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Admin"; +export { default, getServerSideProps } from "@karrio/core/modules/Admin"; diff --git a/apps/dashboard/src/pages/_sites/[site]/admin/organization_accounts.tsx b/apps/dashboard/src/pages/_sites/[site]/admin/organization_accounts.tsx index c62b5778fe..7f377c2285 100644 --- a/apps/dashboard/src/pages/_sites/[site]/admin/organization_accounts.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/admin/organization_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/organization_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/organization_accounts"; diff --git a/apps/dashboard/src/pages/_sites/[site]/admin/surcharges.tsx b/apps/dashboard/src/pages/_sites/[site]/admin/surcharges.tsx index cab9bb71b0..7fd5770943 100644 --- a/apps/dashboard/src/pages/_sites/[site]/admin/surcharges.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/admin/surcharges.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/surcharges"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/surcharges"; diff --git a/apps/dashboard/src/pages/_sites/[site]/admin/user_accounts.tsx b/apps/dashboard/src/pages/_sites/[site]/admin/user_accounts.tsx index d778def63c..ce4e6c0393 100644 --- a/apps/dashboard/src/pages/_sites/[site]/admin/user_accounts.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/admin/user_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/user_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/user_accounts"; diff --git a/apps/dashboard/src/pages/_sites/[site]/billing/index.tsx b/apps/dashboard/src/pages/_sites/[site]/billing/index.tsx index cdab7d8d9a..1f8d74b600 100644 --- a/apps/dashboard/src/pages/_sites/[site]/billing/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/billing/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Billing"; +export { default, getServerSideProps } from "@karrio/core/modules/Billing"; diff --git a/apps/dashboard/src/pages/_sites/[site]/connections/index.tsx b/apps/dashboard/src/pages/_sites/[site]/connections/index.tsx index 99847a5318..16c03d10a0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/connections/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/connections/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Connections"; +export { default, getServerSideProps } from "@karrio/core/modules/Connections"; diff --git a/apps/dashboard/src/pages/_sites/[site]/connections/rate-sheets.tsx b/apps/dashboard/src/pages/_sites/[site]/connections/rate-sheets.tsx index 0a4b779836..48f8ad19a1 100644 --- a/apps/dashboard/src/pages/_sites/[site]/connections/rate-sheets.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/connections/rate-sheets.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/rate-sheets"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/rate-sheets"; diff --git a/apps/dashboard/src/pages/_sites/[site]/connections/system.tsx b/apps/dashboard/src/pages/_sites/[site]/connections/system.tsx index b6031d4652..194251aa62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/connections/system.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/connections/system.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/system"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/system"; diff --git a/apps/dashboard/src/pages/_sites/[site]/create_label/index.tsx b/apps/dashboard/src/pages/_sites/[site]/create_label/index.tsx index c065e37469..a2e87d7e21 100644 --- a/apps/dashboard/src/pages/_sites/[site]/create_label/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/create_label/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/api.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/api.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/api.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/api.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/apikeys.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/apikeys.tsx index 7a8e40d082..ce5090c3fe 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/apikeys.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/apikeys.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/apikeys"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/apikeys"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/events/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/developers/events/[id].tsx index f75b388b4f..4867c6b17f 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/events/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/event"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/events/index.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/events/index.tsx index 970ceabc56..a2418bec35 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/events/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/events"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/index.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/index.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/logs/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/developers/logs/[id].tsx index 0bf03a1fbd..ff135aa7ba 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/logs/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/logs/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/log"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/log"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/logs/index.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/logs/index.tsx index f6daba5776..fa5a5c01dc 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/logs/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/logs/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/logs"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/logs"; diff --git a/apps/dashboard/src/pages/_sites/[site]/developers/webhooks.tsx b/apps/dashboard/src/pages/_sites/[site]/developers/webhooks.tsx index ec0ba0059c..58e20d1f51 100644 --- a/apps/dashboard/src/pages/_sites/[site]/developers/webhooks.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/developers/webhooks.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/webhooks"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/webhooks"; diff --git a/apps/dashboard/src/pages/_sites/[site]/draft_orders/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/draft_orders/[id].tsx index a48bb75bd6..8943565757 100644 --- a/apps/dashboard/src/pages/_sites/[site]/draft_orders/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/draft_orders/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/draft_order"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/draft_order"; diff --git a/apps/dashboard/src/pages/_sites/[site]/email/[token].tsx b/apps/dashboard/src/pages/_sites/[site]/email/[token].tsx index b65468f36d..e638551b4a 100644 --- a/apps/dashboard/src/pages/_sites/[site]/email/[token].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/email/[token].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/confirm_email"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/confirm_email"; diff --git a/apps/dashboard/src/pages/_sites/[site]/email/change.tsx b/apps/dashboard/src/pages/_sites/[site]/email/change.tsx index 78e2888af6..22deb1ddae 100644 --- a/apps/dashboard/src/pages/_sites/[site]/email/change.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/email/change.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/confirm_email_change"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/confirm_email_change"; diff --git a/apps/dashboard/src/pages/_sites/[site]/index.tsx b/apps/dashboard/src/pages/_sites/[site]/index.tsx index a8b3962245..32f1f9e65d 100644 --- a/apps/dashboard/src/pages/_sites/[site]/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Home"; +export { default, getServerSideProps } from "@karrio/core/modules/Home"; diff --git a/apps/dashboard/src/pages/_sites/[site]/login/index.tsx b/apps/dashboard/src/pages/_sites/[site]/login/index.tsx index 7680bc7cc7..b4929b2a9b 100644 --- a/apps/dashboard/src/pages/_sites/[site]/login/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/login/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/login"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/login"; diff --git a/apps/dashboard/src/pages/_sites/[site]/manifests/create_manifests.tsx b/apps/dashboard/src/pages/_sites/[site]/manifests/create_manifests.tsx index 142257a141..e33ed6ad24 100644 --- a/apps/dashboard/src/pages/_sites/[site]/manifests/create_manifests.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/manifests/create_manifests.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Manifests/create_manifests"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Manifests/create_manifests"; diff --git a/apps/dashboard/src/pages/_sites/[site]/manifests/index.tsx b/apps/dashboard/src/pages/_sites/[site]/manifests/index.tsx index 564587c0c2..0788e5778c 100644 --- a/apps/dashboard/src/pages/_sites/[site]/manifests/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/manifests/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Manifests"; +export { default, getServerSideProps } from "@karrio/core/modules/Manifests"; diff --git a/apps/dashboard/src/pages/_sites/[site]/orders/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/orders/[id].tsx index e966e1ebc0..f2c06f54b2 100644 --- a/apps/dashboard/src/pages/_sites/[site]/orders/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/orders/[id].tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders/order"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders/order"; diff --git a/apps/dashboard/src/pages/_sites/[site]/orders/create_label.tsx b/apps/dashboard/src/pages/_sites/[site]/orders/create_label.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/orders/create_label.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/orders/create_label.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/orders/create_labels.tsx b/apps/dashboard/src/pages/_sites/[site]/orders/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/_sites/[site]/orders/create_labels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/orders/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/orders/create_shipment.tsx b/apps/dashboard/src/pages/_sites/[site]/orders/create_shipment.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/orders/create_shipment.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/orders/create_shipment.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/orders/index.tsx b/apps/dashboard/src/pages/_sites/[site]/orders/index.tsx index ac3ae429a0..8f83e3cee3 100644 --- a/apps/dashboard/src/pages/_sites/[site]/orders/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/orders/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders"; diff --git a/apps/dashboard/src/pages/_sites/[site]/password/reset/done.tsx b/apps/dashboard/src/pages/_sites/[site]/password/reset/done.tsx index 54890617fa..a16571d451 100644 --- a/apps/dashboard/src/pages/_sites/[site]/password/reset/done.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/password/reset/done.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/done"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/done"; diff --git a/apps/dashboard/src/pages/_sites/[site]/password/reset/index.tsx b/apps/dashboard/src/pages/_sites/[site]/password/reset/index.tsx index 85319eff8a..4a9f0ef916 100644 --- a/apps/dashboard/src/pages/_sites/[site]/password/reset/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/password/reset/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset"; diff --git a/apps/dashboard/src/pages/_sites/[site]/password/reset/request.tsx b/apps/dashboard/src/pages/_sites/[site]/password/reset/request.tsx index 53ab9fec11..2aa1410f5c 100644 --- a/apps/dashboard/src/pages/_sites/[site]/password/reset/request.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/password/reset/request.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/request"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/request"; diff --git a/apps/dashboard/src/pages/_sites/[site]/password/reset/sent.tsx b/apps/dashboard/src/pages/_sites/[site]/password/reset/sent.tsx index d5daaaa2a1..d9d7336db5 100644 --- a/apps/dashboard/src/pages/_sites/[site]/password/reset/sent.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/password/reset/sent.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/sent"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/sent"; diff --git a/apps/dashboard/src/pages/_sites/[site]/resources/graphiql.tsx b/apps/dashboard/src/pages/_sites/[site]/resources/graphiql.tsx index 28fb505a54..2922027cd0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/resources/graphiql.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/resources/graphiql.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/graphiql"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/graphiql"; diff --git a/apps/dashboard/src/pages/_sites/[site]/resources/reference.tsx b/apps/dashboard/src/pages/_sites/[site]/resources/reference.tsx index 42b6b7c9f6..ea58e62a1b 100644 --- a/apps/dashboard/src/pages/_sites/[site]/resources/reference.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/resources/reference.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/reference"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/reference"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/account.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/account.tsx index e41e20c3e2..6b2d50b0ee 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/account.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/account.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/account"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/account"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/addresses.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/addresses.tsx index fd11e01992..9acc14665e 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/addresses.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/addresses.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/addresses"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/addresses"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/organization.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/organization.tsx index 9cb2f826b8..db48d9bca1 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/organization.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/organization.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/organization"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/organization"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/parcels.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/parcels.tsx index 29543bbd2e..2a46b139de 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/parcels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/parcels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/parcels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/parcels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/profile.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/profile.tsx index 0e24062159..98fb97653d 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/profile.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/profile.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/profile"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/profile"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/template.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/template.tsx index 19e96963e3..0fae716036 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/template.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/template.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/template"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/template"; diff --git a/apps/dashboard/src/pages/_sites/[site]/settings/templates.tsx b/apps/dashboard/src/pages/_sites/[site]/settings/templates.tsx index c913c440a3..c9605bbe6a 100644 --- a/apps/dashboard/src/pages/_sites/[site]/settings/templates.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/settings/templates.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/templates"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/templates"; diff --git a/apps/dashboard/src/pages/_sites/[site]/shipments/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/shipments/[id].tsx index 28e609de21..18ba8bcedb 100644 --- a/apps/dashboard/src/pages/_sites/[site]/shipments/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/shipments/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/shipment"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/shipment"; diff --git a/apps/dashboard/src/pages/_sites/[site]/shipments/create_labels.tsx b/apps/dashboard/src/pages/_sites/[site]/shipments/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/_sites/[site]/shipments/create_labels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/shipments/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/shipments/index.tsx b/apps/dashboard/src/pages/_sites/[site]/shipments/index.tsx index d046041d24..ee4ee94ce0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/shipments/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/shipments/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Shipments"; +export { default, getServerSideProps } from "@karrio/core/modules/Shipments"; diff --git a/apps/dashboard/src/pages/_sites/[site]/signup/index.tsx b/apps/dashboard/src/pages/_sites/[site]/signup/index.tsx index c26ef1f3ab..6b70879422 100644 --- a/apps/dashboard/src/pages/_sites/[site]/signup/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/signup/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/signup"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/signup"; diff --git a/apps/dashboard/src/pages/_sites/[site]/signup/success.tsx b/apps/dashboard/src/pages/_sites/[site]/signup/success.tsx index 67daf40496..10a4a90f1f 100644 --- a/apps/dashboard/src/pages/_sites/[site]/signup/success.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/signup/success.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/signup_success"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/signup_success"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/admin/carrier_connections.tsx b/apps/dashboard/src/pages/_sites/[site]/test/admin/carrier_connections.tsx index af6521ba4b..72b044f4c8 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/admin/carrier_connections.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/admin/carrier_connections.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/carrier_connections"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/carrier_connections"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/admin/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/admin/index.tsx index e881b44a45..063349b377 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/admin/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/admin/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Admin"; +export { default, getServerSideProps } from "@karrio/core/modules/Admin"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/admin/organization_accounts.tsx b/apps/dashboard/src/pages/_sites/[site]/test/admin/organization_accounts.tsx index c62b5778fe..7f377c2285 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/admin/organization_accounts.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/admin/organization_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/organization_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/organization_accounts"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/admin/surcharges.tsx b/apps/dashboard/src/pages/_sites/[site]/test/admin/surcharges.tsx index cab9bb71b0..7fd5770943 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/admin/surcharges.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/admin/surcharges.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/surcharges"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/surcharges"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/admin/user_accounts.tsx b/apps/dashboard/src/pages/_sites/[site]/test/admin/user_accounts.tsx index d778def63c..ce4e6c0393 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/admin/user_accounts.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/admin/user_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/user_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/user_accounts"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/connections/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/connections/index.tsx index 99847a5318..16c03d10a0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/connections/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/connections/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Connections"; +export { default, getServerSideProps } from "@karrio/core/modules/Connections"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/connections/rate-sheets.tsx b/apps/dashboard/src/pages/_sites/[site]/test/connections/rate-sheets.tsx index 0a4b779836..48f8ad19a1 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/connections/rate-sheets.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/connections/rate-sheets.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/rate-sheets"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/rate-sheets"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/connections/system.tsx b/apps/dashboard/src/pages/_sites/[site]/test/connections/system.tsx index b6031d4652..194251aa62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/connections/system.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/connections/system.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/system"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/system"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/create_label/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/create_label/index.tsx index c065e37469..a2e87d7e21 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/create_label/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/create_label/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/api.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/api.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/api.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/api.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/apikeys.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/apikeys.tsx index 7a8e40d082..ce5090c3fe 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/apikeys.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/apikeys.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/apikeys"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/apikeys"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/events/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/events/[id].tsx index f75b388b4f..4867c6b17f 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/events/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/event"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/events/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/events/index.tsx index 970ceabc56..a2418bec35 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/events/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/events"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/index.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/[id].tsx index 0bf03a1fbd..ff135aa7ba 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/log"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/log"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/index.tsx index f6daba5776..fa5a5c01dc 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/logs/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/logs"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/logs"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/developers/webhooks.tsx b/apps/dashboard/src/pages/_sites/[site]/test/developers/webhooks.tsx index ec0ba0059c..58e20d1f51 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/developers/webhooks.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/developers/webhooks.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/webhooks"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/webhooks"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/draft_orders/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/draft_orders/[id].tsx index a48bb75bd6..8943565757 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/draft_orders/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/draft_orders/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/draft_order"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/draft_order"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/index.tsx index a8b3962245..32f1f9e65d 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Home"; +export { default, getServerSideProps } from "@karrio/core/modules/Home"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/manifests/create_manifests.tsx b/apps/dashboard/src/pages/_sites/[site]/test/manifests/create_manifests.tsx index 142257a141..e33ed6ad24 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/manifests/create_manifests.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/manifests/create_manifests.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Manifests/create_manifests"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Manifests/create_manifests"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/manifests/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/manifests/index.tsx index 564587c0c2..0788e5778c 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/manifests/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/manifests/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Manifests"; +export { default, getServerSideProps } from "@karrio/core/modules/Manifests"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/orders/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/orders/[id].tsx index e966e1ebc0..f2c06f54b2 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/orders/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/orders/[id].tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders/order"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders/order"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_label.tsx b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_label.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_label.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_label.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_labels.tsx b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_labels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_shipment.tsx b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_shipment.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/orders/create_shipment.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/orders/create_shipment.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/orders/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/orders/index.tsx index ac3ae429a0..8f83e3cee3 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/orders/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/orders/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/resources/graphiql.tsx b/apps/dashboard/src/pages/_sites/[site]/test/resources/graphiql.tsx index 28fb505a54..2922027cd0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/resources/graphiql.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/resources/graphiql.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/graphiql"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/graphiql"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/resources/reference.tsx b/apps/dashboard/src/pages/_sites/[site]/test/resources/reference.tsx index 42b6b7c9f6..ea58e62a1b 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/resources/reference.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/resources/reference.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/reference"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/reference"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/account.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/account.tsx index e41e20c3e2..6b2d50b0ee 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/account.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/account.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/account"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/account"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/addresses.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/addresses.tsx index fd11e01992..9acc14665e 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/addresses.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/addresses.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/addresses"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/addresses"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/organization.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/organization.tsx index 9cb2f826b8..db48d9bca1 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/organization.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/organization.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/organization"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/organization"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/parcels.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/parcels.tsx index 29543bbd2e..2a46b139de 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/parcels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/parcels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/parcels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/parcels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/profile.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/profile.tsx index 0e24062159..98fb97653d 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/profile.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/profile.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/profile"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/profile"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/template.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/template.tsx index 19e96963e3..0fae716036 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/template.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/template.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/template"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/template"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/settings/templates.tsx b/apps/dashboard/src/pages/_sites/[site]/test/settings/templates.tsx index c913c440a3..c9605bbe6a 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/settings/templates.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/settings/templates.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/templates"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/templates"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/shipments/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/shipments/[id].tsx index 28e609de21..18ba8bcedb 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/shipments/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/shipments/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/shipment"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/shipment"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/shipments/create_labels.tsx b/apps/dashboard/src/pages/_sites/[site]/test/shipments/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/shipments/create_labels.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/shipments/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/shipments/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/shipments/index.tsx index d046041d24..ee4ee94ce0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/shipments/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/shipments/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Shipments"; +export { default, getServerSideProps } from "@karrio/core/modules/Shipments"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/trackers/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/trackers/index.tsx index 67447eac7a..c57cd16834 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/trackers/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/trackers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Trackers"; +export { default, getServerSideProps } from "@karrio/core/modules/Trackers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/workflows/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/workflows/[id].tsx index 2a5acdbe28..b0bb1cf3e0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/workflows/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/workflows/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/workflow"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/workflow"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/[id].tsx index 22650fc525..38e58f753b 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/event"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/index.tsx index 25140fadc6..496e48c057 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/workflows/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/events"; diff --git a/apps/dashboard/src/pages/_sites/[site]/test/workflows/index.tsx b/apps/dashboard/src/pages/_sites/[site]/test/workflows/index.tsx index 183f69bab5..8580b4f81f 100644 --- a/apps/dashboard/src/pages/_sites/[site]/test/workflows/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/test/workflows/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Workflows"; +export { default, getServerSideProps } from "@karrio/core/modules/Workflows"; diff --git a/apps/dashboard/src/pages/_sites/[site]/trackers/index.tsx b/apps/dashboard/src/pages/_sites/[site]/trackers/index.tsx index 67447eac7a..c57cd16834 100644 --- a/apps/dashboard/src/pages/_sites/[site]/trackers/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/trackers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Trackers"; +export { default, getServerSideProps } from "@karrio/core/modules/Trackers"; diff --git a/apps/dashboard/src/pages/_sites/[site]/tracking/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/tracking/[id].tsx index 44aaa0bc94..5b369f9de6 100644 --- a/apps/dashboard/src/pages/_sites/[site]/tracking/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/tracking/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Trackers/tracking-page"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Trackers/tracking-page"; diff --git a/apps/dashboard/src/pages/_sites/[site]/workflows/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/workflows/[id].tsx index 2a5acdbe28..b0bb1cf3e0 100644 --- a/apps/dashboard/src/pages/_sites/[site]/workflows/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/workflows/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/workflow"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/workflow"; diff --git a/apps/dashboard/src/pages/_sites/[site]/workflows/events/[id].tsx b/apps/dashboard/src/pages/_sites/[site]/workflows/events/[id].tsx index 22650fc525..38e58f753b 100644 --- a/apps/dashboard/src/pages/_sites/[site]/workflows/events/[id].tsx +++ b/apps/dashboard/src/pages/_sites/[site]/workflows/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/event"; diff --git a/apps/dashboard/src/pages/_sites/[site]/workflows/events/index.tsx b/apps/dashboard/src/pages/_sites/[site]/workflows/events/index.tsx index 25140fadc6..496e48c057 100644 --- a/apps/dashboard/src/pages/_sites/[site]/workflows/events/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/workflows/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/events"; diff --git a/apps/dashboard/src/pages/_sites/[site]/workflows/index.tsx b/apps/dashboard/src/pages/_sites/[site]/workflows/index.tsx index 183f69bab5..8580b4f81f 100644 --- a/apps/dashboard/src/pages/_sites/[site]/workflows/index.tsx +++ b/apps/dashboard/src/pages/_sites/[site]/workflows/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Workflows"; +export { default, getServerSideProps } from "@karrio/core/modules/Workflows"; diff --git a/apps/dashboard/src/pages/accept-invite/index.tsx b/apps/dashboard/src/pages/accept-invite/index.tsx index dce5918b12..444f5ac286 100644 --- a/apps/dashboard/src/pages/accept-invite/index.tsx +++ b/apps/dashboard/src/pages/accept-invite/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Invitation/accept-invite"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Invitation/accept-invite"; diff --git a/apps/dashboard/src/pages/admin/carrier_connections.tsx b/apps/dashboard/src/pages/admin/carrier_connections.tsx index af6521ba4b..72b044f4c8 100644 --- a/apps/dashboard/src/pages/admin/carrier_connections.tsx +++ b/apps/dashboard/src/pages/admin/carrier_connections.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/carrier_connections"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/carrier_connections"; diff --git a/apps/dashboard/src/pages/admin/index.tsx b/apps/dashboard/src/pages/admin/index.tsx index e881b44a45..063349b377 100644 --- a/apps/dashboard/src/pages/admin/index.tsx +++ b/apps/dashboard/src/pages/admin/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Admin"; +export { default, getServerSideProps } from "@karrio/core/modules/Admin"; diff --git a/apps/dashboard/src/pages/admin/organization_accounts.tsx b/apps/dashboard/src/pages/admin/organization_accounts.tsx index c62b5778fe..7f377c2285 100644 --- a/apps/dashboard/src/pages/admin/organization_accounts.tsx +++ b/apps/dashboard/src/pages/admin/organization_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/organization_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/organization_accounts"; diff --git a/apps/dashboard/src/pages/admin/surcharges.tsx b/apps/dashboard/src/pages/admin/surcharges.tsx index cab9bb71b0..7fd5770943 100644 --- a/apps/dashboard/src/pages/admin/surcharges.tsx +++ b/apps/dashboard/src/pages/admin/surcharges.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/surcharges"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/surcharges"; diff --git a/apps/dashboard/src/pages/admin/user_accounts.tsx b/apps/dashboard/src/pages/admin/user_accounts.tsx index d778def63c..ce4e6c0393 100644 --- a/apps/dashboard/src/pages/admin/user_accounts.tsx +++ b/apps/dashboard/src/pages/admin/user_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/user_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/user_accounts"; diff --git a/apps/dashboard/src/pages/api/auth/[...nextauth].ts b/apps/dashboard/src/pages/api/auth/[...nextauth].ts index be56ee7894..1c7b8caafc 100644 --- a/apps/dashboard/src/pages/api/auth/[...nextauth].ts +++ b/apps/dashboard/src/pages/api/auth/[...nextauth].ts @@ -1,46 +1,50 @@ -import { isNoneOrEmpty, parseJwt, computeTestMode, Auth, logger } from '@karrio/lib'; +import { + isNoneOrEmpty, + parseJwt, + computeTestMode, + Auth, + logger, +} from "@karrio/lib"; import CredentialProvider from "next-auth/providers/credentials"; -import { NextApiRequest, NextApiResponse } from 'next'; -import { loadAPIMetadata } from '@/context/main'; -import { JWT } from 'next-auth/jwt'; -import getConfig from 'next/config'; -import NextAuth from 'next-auth'; -import moment from 'moment'; +import { NextApiRequest, NextApiResponse } from "next"; +import { loadAPIMetadata } from "@karrio/core/context/main"; +import { JWT } from "next-auth/jwt"; +import getConfig from "next/config"; +import NextAuth from "next-auth"; +import moment from "moment"; const { serverRuntimeConfig } = getConfig(); const secret = serverRuntimeConfig?.JWT_SECRET; - async function AuthAPI(req: NextApiRequest, res: NextApiResponse) { - const { metadata } = await loadAPIMetadata({ req, res }).catch(_ => _); + const { metadata } = await loadAPIMetadata({ req, res }).catch((_) => _); const auth = Auth(metadata?.HOST); return NextAuth({ secret, - pages: { signIn: '/login' }, - session: { strategy: 'jwt' }, + pages: { signIn: "/login" }, + session: { strategy: "jwt" }, providers: [ CredentialProvider({ - name: 'Credentials', + name: "Credentials", credentials: { email: { label: "Email", type: "email", placeholder: "email" }, - password: { label: "Password", type: "password" } + password: { label: "Password", type: "password" }, }, async authorize({ orgId, ...credentials }: any, _) { try { const token = await auth.authenticate(credentials as any); const testMode = req.headers.referer?.includes("/test"); - const org = (metadata?.MULTI_ORGANIZATIONS + const org = metadata?.MULTI_ORGANIZATIONS ? await auth.getCurrentOrg(token.access, orgId) - : { id: null } - ); + : { id: null }; return { email: credentials.email, accessToken: token.access, refreshToken: token.refresh, orgId: org?.id, - testMode + testMode, } as any; } catch (e) { logger.error(e); @@ -48,8 +52,8 @@ async function AuthAPI(req: NextApiRequest, res: NextApiResponse) { // Return null if user data could not be retrieved return null; - } - }) + }, + }), ], callbacks: { jwt: async ({ token, user, trigger, session }: any): Promise => { @@ -65,32 +69,40 @@ async function AuthAPI(req: NextApiRequest, res: NextApiResponse) { if (trigger === "update" && session?.orgId) { // Note, that `session` can be any arbitrary object, remember to validate it! - const org = await auth.getCurrentOrg((token as any).accessToken, session.orgId); + const org = await auth.getCurrentOrg( + (token as any).accessToken, + session.orgId, + ); token.orgId = org?.id; } // Return previous token if the access token has not expired yet - if (moment().subtract(3, 'm').toDate().getTime() < (token.expiration as number) * 1000) { + if ( + moment().subtract(3, "m").toDate().getTime() < + (token.expiration as number) * 1000 + ) { return token; } // Access token has expired, try to update it try { - logger.info('Refreshing expired token...'); - const { access, refresh } = await auth.refreshToken(token.refreshToken as string); + logger.info("Refreshing expired token..."); + const { access, refresh } = await auth.refreshToken( + token.refreshToken as string, + ); return { ...token, accessToken: access, refreshToken: refresh, - expiration: parseJwt(access).exp + expiration: parseJwt(access).exp, }; } catch (error) { logger.error(error); return { error: "RefreshAccessTokenError", - } + }; } }, session: async ({ session, token, user }: any) => { @@ -107,8 +119,8 @@ async function AuthAPI(req: NextApiRequest, res: NextApiResponse) { }; return session; - } - } + }, + }, })(req, res); } diff --git a/apps/dashboard/src/pages/billing/index.tsx b/apps/dashboard/src/pages/billing/index.tsx index cdab7d8d9a..1f8d74b600 100644 --- a/apps/dashboard/src/pages/billing/index.tsx +++ b/apps/dashboard/src/pages/billing/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Billing"; +export { default, getServerSideProps } from "@karrio/core/modules/Billing"; diff --git a/apps/dashboard/src/pages/connections/index.tsx b/apps/dashboard/src/pages/connections/index.tsx index 99847a5318..16c03d10a0 100644 --- a/apps/dashboard/src/pages/connections/index.tsx +++ b/apps/dashboard/src/pages/connections/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Connections"; +export { default, getServerSideProps } from "@karrio/core/modules/Connections"; diff --git a/apps/dashboard/src/pages/connections/rate-sheets.tsx b/apps/dashboard/src/pages/connections/rate-sheets.tsx index 0a4b779836..48f8ad19a1 100644 --- a/apps/dashboard/src/pages/connections/rate-sheets.tsx +++ b/apps/dashboard/src/pages/connections/rate-sheets.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/rate-sheets"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/rate-sheets"; diff --git a/apps/dashboard/src/pages/connections/system.tsx b/apps/dashboard/src/pages/connections/system.tsx index b6031d4652..194251aa62 100644 --- a/apps/dashboard/src/pages/connections/system.tsx +++ b/apps/dashboard/src/pages/connections/system.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/system"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/system"; diff --git a/apps/dashboard/src/pages/create_label/index.tsx b/apps/dashboard/src/pages/create_label/index.tsx index c065e37469..a2e87d7e21 100644 --- a/apps/dashboard/src/pages/create_label/index.tsx +++ b/apps/dashboard/src/pages/create_label/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/create_label"; diff --git a/apps/dashboard/src/pages/developers/api.tsx b/apps/dashboard/src/pages/developers/api.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/developers/api.tsx +++ b/apps/dashboard/src/pages/developers/api.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/developers/apikeys.tsx b/apps/dashboard/src/pages/developers/apikeys.tsx index 7a8e40d082..ce5090c3fe 100644 --- a/apps/dashboard/src/pages/developers/apikeys.tsx +++ b/apps/dashboard/src/pages/developers/apikeys.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/apikeys"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/apikeys"; diff --git a/apps/dashboard/src/pages/developers/events/[id].tsx b/apps/dashboard/src/pages/developers/events/[id].tsx index f75b388b4f..4867c6b17f 100644 --- a/apps/dashboard/src/pages/developers/events/[id].tsx +++ b/apps/dashboard/src/pages/developers/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/event"; diff --git a/apps/dashboard/src/pages/developers/events/index.tsx b/apps/dashboard/src/pages/developers/events/index.tsx index 970ceabc56..a2418bec35 100644 --- a/apps/dashboard/src/pages/developers/events/index.tsx +++ b/apps/dashboard/src/pages/developers/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/events"; diff --git a/apps/dashboard/src/pages/developers/index.tsx b/apps/dashboard/src/pages/developers/index.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/developers/index.tsx +++ b/apps/dashboard/src/pages/developers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/developers/logs/[id].tsx b/apps/dashboard/src/pages/developers/logs/[id].tsx index 0bf03a1fbd..ff135aa7ba 100644 --- a/apps/dashboard/src/pages/developers/logs/[id].tsx +++ b/apps/dashboard/src/pages/developers/logs/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/log"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/log"; diff --git a/apps/dashboard/src/pages/developers/logs/index.tsx b/apps/dashboard/src/pages/developers/logs/index.tsx index f6daba5776..fa5a5c01dc 100644 --- a/apps/dashboard/src/pages/developers/logs/index.tsx +++ b/apps/dashboard/src/pages/developers/logs/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/logs"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/logs"; diff --git a/apps/dashboard/src/pages/developers/webhooks.tsx b/apps/dashboard/src/pages/developers/webhooks.tsx index ec0ba0059c..58e20d1f51 100644 --- a/apps/dashboard/src/pages/developers/webhooks.tsx +++ b/apps/dashboard/src/pages/developers/webhooks.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/webhooks"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/webhooks"; diff --git a/apps/dashboard/src/pages/draft_orders/[id].tsx b/apps/dashboard/src/pages/draft_orders/[id].tsx index a48bb75bd6..8943565757 100644 --- a/apps/dashboard/src/pages/draft_orders/[id].tsx +++ b/apps/dashboard/src/pages/draft_orders/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/draft_order"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/draft_order"; diff --git a/apps/dashboard/src/pages/email/[token].tsx b/apps/dashboard/src/pages/email/[token].tsx index b65468f36d..e638551b4a 100644 --- a/apps/dashboard/src/pages/email/[token].tsx +++ b/apps/dashboard/src/pages/email/[token].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/confirm_email"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/confirm_email"; diff --git a/apps/dashboard/src/pages/email/change.tsx b/apps/dashboard/src/pages/email/change.tsx index 78e2888af6..22deb1ddae 100644 --- a/apps/dashboard/src/pages/email/change.tsx +++ b/apps/dashboard/src/pages/email/change.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/confirm_email_change"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/confirm_email_change"; diff --git a/apps/dashboard/src/pages/index.tsx b/apps/dashboard/src/pages/index.tsx index a8b3962245..32f1f9e65d 100644 --- a/apps/dashboard/src/pages/index.tsx +++ b/apps/dashboard/src/pages/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Home"; +export { default, getServerSideProps } from "@karrio/core/modules/Home"; diff --git a/apps/dashboard/src/pages/login/index.tsx b/apps/dashboard/src/pages/login/index.tsx index 7680bc7cc7..b4929b2a9b 100644 --- a/apps/dashboard/src/pages/login/index.tsx +++ b/apps/dashboard/src/pages/login/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/login"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/login"; diff --git a/apps/dashboard/src/pages/manifests/create_manifests.tsx b/apps/dashboard/src/pages/manifests/create_manifests.tsx index 142257a141..e33ed6ad24 100644 --- a/apps/dashboard/src/pages/manifests/create_manifests.tsx +++ b/apps/dashboard/src/pages/manifests/create_manifests.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Manifests/create_manifests"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Manifests/create_manifests"; diff --git a/apps/dashboard/src/pages/manifests/index.tsx b/apps/dashboard/src/pages/manifests/index.tsx index 564587c0c2..0788e5778c 100644 --- a/apps/dashboard/src/pages/manifests/index.tsx +++ b/apps/dashboard/src/pages/manifests/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Manifests"; +export { default, getServerSideProps } from "@karrio/core/modules/Manifests"; diff --git a/apps/dashboard/src/pages/orders/[id].tsx b/apps/dashboard/src/pages/orders/[id].tsx index e966e1ebc0..f2c06f54b2 100644 --- a/apps/dashboard/src/pages/orders/[id].tsx +++ b/apps/dashboard/src/pages/orders/[id].tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders/order"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders/order"; diff --git a/apps/dashboard/src/pages/orders/create_label.tsx b/apps/dashboard/src/pages/orders/create_label.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/orders/create_label.tsx +++ b/apps/dashboard/src/pages/orders/create_label.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/orders/create_labels.tsx b/apps/dashboard/src/pages/orders/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/orders/create_labels.tsx +++ b/apps/dashboard/src/pages/orders/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/orders/create_shipment.tsx b/apps/dashboard/src/pages/orders/create_shipment.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/orders/create_shipment.tsx +++ b/apps/dashboard/src/pages/orders/create_shipment.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/orders/index.tsx b/apps/dashboard/src/pages/orders/index.tsx index ac3ae429a0..8f83e3cee3 100644 --- a/apps/dashboard/src/pages/orders/index.tsx +++ b/apps/dashboard/src/pages/orders/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders"; diff --git a/apps/dashboard/src/pages/password/reset/done.tsx b/apps/dashboard/src/pages/password/reset/done.tsx index 54890617fa..a16571d451 100644 --- a/apps/dashboard/src/pages/password/reset/done.tsx +++ b/apps/dashboard/src/pages/password/reset/done.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/done"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/done"; diff --git a/apps/dashboard/src/pages/password/reset/index.tsx b/apps/dashboard/src/pages/password/reset/index.tsx index 85319eff8a..4a9f0ef916 100644 --- a/apps/dashboard/src/pages/password/reset/index.tsx +++ b/apps/dashboard/src/pages/password/reset/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset"; diff --git a/apps/dashboard/src/pages/password/reset/request.tsx b/apps/dashboard/src/pages/password/reset/request.tsx index 53ab9fec11..2aa1410f5c 100644 --- a/apps/dashboard/src/pages/password/reset/request.tsx +++ b/apps/dashboard/src/pages/password/reset/request.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/request"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/request"; diff --git a/apps/dashboard/src/pages/password/reset/sent.tsx b/apps/dashboard/src/pages/password/reset/sent.tsx index d5daaaa2a1..d9d7336db5 100644 --- a/apps/dashboard/src/pages/password/reset/sent.tsx +++ b/apps/dashboard/src/pages/password/reset/sent.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Password/reset/sent"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Password/reset/sent"; diff --git a/apps/dashboard/src/pages/resources/graphiql.tsx b/apps/dashboard/src/pages/resources/graphiql.tsx index 28fb505a54..2922027cd0 100644 --- a/apps/dashboard/src/pages/resources/graphiql.tsx +++ b/apps/dashboard/src/pages/resources/graphiql.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/graphiql"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/graphiql"; diff --git a/apps/dashboard/src/pages/resources/reference.tsx b/apps/dashboard/src/pages/resources/reference.tsx index 42b6b7c9f6..ea58e62a1b 100644 --- a/apps/dashboard/src/pages/resources/reference.tsx +++ b/apps/dashboard/src/pages/resources/reference.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/reference"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/reference"; diff --git a/apps/dashboard/src/pages/settings/account.tsx b/apps/dashboard/src/pages/settings/account.tsx index e41e20c3e2..6b2d50b0ee 100644 --- a/apps/dashboard/src/pages/settings/account.tsx +++ b/apps/dashboard/src/pages/settings/account.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/account"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/account"; diff --git a/apps/dashboard/src/pages/settings/addresses.tsx b/apps/dashboard/src/pages/settings/addresses.tsx index fd11e01992..9acc14665e 100644 --- a/apps/dashboard/src/pages/settings/addresses.tsx +++ b/apps/dashboard/src/pages/settings/addresses.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/addresses"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/addresses"; diff --git a/apps/dashboard/src/pages/settings/organization.tsx b/apps/dashboard/src/pages/settings/organization.tsx index 9cb2f826b8..db48d9bca1 100644 --- a/apps/dashboard/src/pages/settings/organization.tsx +++ b/apps/dashboard/src/pages/settings/organization.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/organization"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/organization"; diff --git a/apps/dashboard/src/pages/settings/parcels.tsx b/apps/dashboard/src/pages/settings/parcels.tsx index 29543bbd2e..2a46b139de 100644 --- a/apps/dashboard/src/pages/settings/parcels.tsx +++ b/apps/dashboard/src/pages/settings/parcels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/parcels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/parcels"; diff --git a/apps/dashboard/src/pages/settings/profile.tsx b/apps/dashboard/src/pages/settings/profile.tsx index 0e24062159..98fb97653d 100644 --- a/apps/dashboard/src/pages/settings/profile.tsx +++ b/apps/dashboard/src/pages/settings/profile.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/profile"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/profile"; diff --git a/apps/dashboard/src/pages/settings/template.tsx b/apps/dashboard/src/pages/settings/template.tsx index 19e96963e3..0fae716036 100644 --- a/apps/dashboard/src/pages/settings/template.tsx +++ b/apps/dashboard/src/pages/settings/template.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/template"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/template"; diff --git a/apps/dashboard/src/pages/settings/templates.tsx b/apps/dashboard/src/pages/settings/templates.tsx index c913c440a3..c9605bbe6a 100644 --- a/apps/dashboard/src/pages/settings/templates.tsx +++ b/apps/dashboard/src/pages/settings/templates.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/templates"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/templates"; diff --git a/apps/dashboard/src/pages/shipments/[id].tsx b/apps/dashboard/src/pages/shipments/[id].tsx index 28e609de21..18ba8bcedb 100644 --- a/apps/dashboard/src/pages/shipments/[id].tsx +++ b/apps/dashboard/src/pages/shipments/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/shipment"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/shipment"; diff --git a/apps/dashboard/src/pages/shipments/create_labels.tsx b/apps/dashboard/src/pages/shipments/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/shipments/create_labels.tsx +++ b/apps/dashboard/src/pages/shipments/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/shipments/index.tsx b/apps/dashboard/src/pages/shipments/index.tsx index d046041d24..ee4ee94ce0 100644 --- a/apps/dashboard/src/pages/shipments/index.tsx +++ b/apps/dashboard/src/pages/shipments/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Shipments"; +export { default, getServerSideProps } from "@karrio/core/modules/Shipments"; diff --git a/apps/dashboard/src/pages/signup/index.tsx b/apps/dashboard/src/pages/signup/index.tsx index c26ef1f3ab..6b70879422 100644 --- a/apps/dashboard/src/pages/signup/index.tsx +++ b/apps/dashboard/src/pages/signup/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/signup"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/signup"; diff --git a/apps/dashboard/src/pages/signup/success.tsx b/apps/dashboard/src/pages/signup/success.tsx index 67daf40496..10a4a90f1f 100644 --- a/apps/dashboard/src/pages/signup/success.tsx +++ b/apps/dashboard/src/pages/signup/success.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Registration/signup_success"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Registration/signup_success"; diff --git a/apps/dashboard/src/pages/test/admin/carrier_connections.tsx b/apps/dashboard/src/pages/test/admin/carrier_connections.tsx index af6521ba4b..72b044f4c8 100644 --- a/apps/dashboard/src/pages/test/admin/carrier_connections.tsx +++ b/apps/dashboard/src/pages/test/admin/carrier_connections.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/carrier_connections"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/carrier_connections"; diff --git a/apps/dashboard/src/pages/test/admin/index.tsx b/apps/dashboard/src/pages/test/admin/index.tsx index e881b44a45..063349b377 100644 --- a/apps/dashboard/src/pages/test/admin/index.tsx +++ b/apps/dashboard/src/pages/test/admin/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Admin"; +export { default, getServerSideProps } from "@karrio/core/modules/Admin"; diff --git a/apps/dashboard/src/pages/test/admin/organization_accounts.tsx b/apps/dashboard/src/pages/test/admin/organization_accounts.tsx index c62b5778fe..7f377c2285 100644 --- a/apps/dashboard/src/pages/test/admin/organization_accounts.tsx +++ b/apps/dashboard/src/pages/test/admin/organization_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/organization_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/organization_accounts"; diff --git a/apps/dashboard/src/pages/test/admin/surcharges.tsx b/apps/dashboard/src/pages/test/admin/surcharges.tsx index cab9bb71b0..7fd5770943 100644 --- a/apps/dashboard/src/pages/test/admin/surcharges.tsx +++ b/apps/dashboard/src/pages/test/admin/surcharges.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/surcharges"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/surcharges"; diff --git a/apps/dashboard/src/pages/test/admin/user_accounts.tsx b/apps/dashboard/src/pages/test/admin/user_accounts.tsx index d778def63c..ce4e6c0393 100644 --- a/apps/dashboard/src/pages/test/admin/user_accounts.tsx +++ b/apps/dashboard/src/pages/test/admin/user_accounts.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Admin/user_accounts"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Admin/user_accounts"; diff --git a/apps/dashboard/src/pages/test/connections/index.tsx b/apps/dashboard/src/pages/test/connections/index.tsx index 99847a5318..16c03d10a0 100644 --- a/apps/dashboard/src/pages/test/connections/index.tsx +++ b/apps/dashboard/src/pages/test/connections/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Connections"; +export { default, getServerSideProps } from "@karrio/core/modules/Connections"; diff --git a/apps/dashboard/src/pages/test/connections/rate-sheets.tsx b/apps/dashboard/src/pages/test/connections/rate-sheets.tsx index 0a4b779836..48f8ad19a1 100644 --- a/apps/dashboard/src/pages/test/connections/rate-sheets.tsx +++ b/apps/dashboard/src/pages/test/connections/rate-sheets.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/rate-sheets"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/rate-sheets"; diff --git a/apps/dashboard/src/pages/test/connections/system.tsx b/apps/dashboard/src/pages/test/connections/system.tsx index b6031d4652..194251aa62 100644 --- a/apps/dashboard/src/pages/test/connections/system.tsx +++ b/apps/dashboard/src/pages/test/connections/system.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Connections/system"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Connections/system"; diff --git a/apps/dashboard/src/pages/test/create_label/index.tsx b/apps/dashboard/src/pages/test/create_label/index.tsx index c065e37469..a2e87d7e21 100644 --- a/apps/dashboard/src/pages/test/create_label/index.tsx +++ b/apps/dashboard/src/pages/test/create_label/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/create_label"; diff --git a/apps/dashboard/src/pages/test/developers/api.tsx b/apps/dashboard/src/pages/test/developers/api.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/test/developers/api.tsx +++ b/apps/dashboard/src/pages/test/developers/api.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/test/developers/apikeys.tsx b/apps/dashboard/src/pages/test/developers/apikeys.tsx index 7a8e40d082..ce5090c3fe 100644 --- a/apps/dashboard/src/pages/test/developers/apikeys.tsx +++ b/apps/dashboard/src/pages/test/developers/apikeys.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/apikeys"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/apikeys"; diff --git a/apps/dashboard/src/pages/test/developers/events/[id].tsx b/apps/dashboard/src/pages/test/developers/events/[id].tsx index f75b388b4f..4867c6b17f 100644 --- a/apps/dashboard/src/pages/test/developers/events/[id].tsx +++ b/apps/dashboard/src/pages/test/developers/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/event"; diff --git a/apps/dashboard/src/pages/test/developers/events/index.tsx b/apps/dashboard/src/pages/test/developers/events/index.tsx index 970ceabc56..a2418bec35 100644 --- a/apps/dashboard/src/pages/test/developers/events/index.tsx +++ b/apps/dashboard/src/pages/test/developers/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/events"; diff --git a/apps/dashboard/src/pages/test/developers/index.tsx b/apps/dashboard/src/pages/test/developers/index.tsx index c5809a06c1..ed94dd24ad 100644 --- a/apps/dashboard/src/pages/test/developers/index.tsx +++ b/apps/dashboard/src/pages/test/developers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Developers"; +export { default, getServerSideProps } from "@karrio/core/modules/Developers"; diff --git a/apps/dashboard/src/pages/test/developers/logs/[id].tsx b/apps/dashboard/src/pages/test/developers/logs/[id].tsx index 0bf03a1fbd..ff135aa7ba 100644 --- a/apps/dashboard/src/pages/test/developers/logs/[id].tsx +++ b/apps/dashboard/src/pages/test/developers/logs/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/log"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/log"; diff --git a/apps/dashboard/src/pages/test/developers/logs/index.tsx b/apps/dashboard/src/pages/test/developers/logs/index.tsx index f6daba5776..fa5a5c01dc 100644 --- a/apps/dashboard/src/pages/test/developers/logs/index.tsx +++ b/apps/dashboard/src/pages/test/developers/logs/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/logs"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/logs"; diff --git a/apps/dashboard/src/pages/test/developers/webhooks.tsx b/apps/dashboard/src/pages/test/developers/webhooks.tsx index ec0ba0059c..58e20d1f51 100644 --- a/apps/dashboard/src/pages/test/developers/webhooks.tsx +++ b/apps/dashboard/src/pages/test/developers/webhooks.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Developers/webhooks"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Developers/webhooks"; diff --git a/apps/dashboard/src/pages/test/draft_orders/[id].tsx b/apps/dashboard/src/pages/test/draft_orders/[id].tsx index a48bb75bd6..8943565757 100644 --- a/apps/dashboard/src/pages/test/draft_orders/[id].tsx +++ b/apps/dashboard/src/pages/test/draft_orders/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/draft_order"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/draft_order"; diff --git a/apps/dashboard/src/pages/test/index.tsx b/apps/dashboard/src/pages/test/index.tsx index a8b3962245..32f1f9e65d 100644 --- a/apps/dashboard/src/pages/test/index.tsx +++ b/apps/dashboard/src/pages/test/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Home"; +export { default, getServerSideProps } from "@karrio/core/modules/Home"; diff --git a/apps/dashboard/src/pages/test/manifests/create_manifests.tsx b/apps/dashboard/src/pages/test/manifests/create_manifests.tsx index 142257a141..e33ed6ad24 100644 --- a/apps/dashboard/src/pages/test/manifests/create_manifests.tsx +++ b/apps/dashboard/src/pages/test/manifests/create_manifests.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Manifests/create_manifests"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Manifests/create_manifests"; diff --git a/apps/dashboard/src/pages/test/manifests/index.tsx b/apps/dashboard/src/pages/test/manifests/index.tsx index 564587c0c2..0788e5778c 100644 --- a/apps/dashboard/src/pages/test/manifests/index.tsx +++ b/apps/dashboard/src/pages/test/manifests/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Manifests"; +export { default, getServerSideProps } from "@karrio/core/modules/Manifests"; diff --git a/apps/dashboard/src/pages/test/orders/[id].tsx b/apps/dashboard/src/pages/test/orders/[id].tsx index e966e1ebc0..f2c06f54b2 100644 --- a/apps/dashboard/src/pages/test/orders/[id].tsx +++ b/apps/dashboard/src/pages/test/orders/[id].tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders/order"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders/order"; diff --git a/apps/dashboard/src/pages/test/orders/create_label.tsx b/apps/dashboard/src/pages/test/orders/create_label.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/test/orders/create_label.tsx +++ b/apps/dashboard/src/pages/test/orders/create_label.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/test/orders/create_labels.tsx b/apps/dashboard/src/pages/test/orders/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/test/orders/create_labels.tsx +++ b/apps/dashboard/src/pages/test/orders/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/test/orders/create_shipment.tsx b/apps/dashboard/src/pages/test/orders/create_shipment.tsx index c68ac58845..a07d54de62 100644 --- a/apps/dashboard/src/pages/test/orders/create_shipment.tsx +++ b/apps/dashboard/src/pages/test/orders/create_shipment.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Orders/create_label"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Orders/create_label"; diff --git a/apps/dashboard/src/pages/test/orders/index.tsx b/apps/dashboard/src/pages/test/orders/index.tsx index ac3ae429a0..8f83e3cee3 100644 --- a/apps/dashboard/src/pages/test/orders/index.tsx +++ b/apps/dashboard/src/pages/test/orders/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Orders"; +export { default, getServerSideProps } from "@karrio/core/modules/Orders"; diff --git a/apps/dashboard/src/pages/test/resources/graphiql.tsx b/apps/dashboard/src/pages/test/resources/graphiql.tsx index 28fb505a54..2922027cd0 100644 --- a/apps/dashboard/src/pages/test/resources/graphiql.tsx +++ b/apps/dashboard/src/pages/test/resources/graphiql.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/graphiql"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/graphiql"; diff --git a/apps/dashboard/src/pages/test/resources/reference.tsx b/apps/dashboard/src/pages/test/resources/reference.tsx index 42b6b7c9f6..ea58e62a1b 100644 --- a/apps/dashboard/src/pages/test/resources/reference.tsx +++ b/apps/dashboard/src/pages/test/resources/reference.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Resources/reference"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Resources/reference"; diff --git a/apps/dashboard/src/pages/test/settings/account.tsx b/apps/dashboard/src/pages/test/settings/account.tsx index e41e20c3e2..6b2d50b0ee 100644 --- a/apps/dashboard/src/pages/test/settings/account.tsx +++ b/apps/dashboard/src/pages/test/settings/account.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/account"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/account"; diff --git a/apps/dashboard/src/pages/test/settings/addresses.tsx b/apps/dashboard/src/pages/test/settings/addresses.tsx index fd11e01992..9acc14665e 100644 --- a/apps/dashboard/src/pages/test/settings/addresses.tsx +++ b/apps/dashboard/src/pages/test/settings/addresses.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/addresses"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/addresses"; diff --git a/apps/dashboard/src/pages/test/settings/organization.tsx b/apps/dashboard/src/pages/test/settings/organization.tsx index 9cb2f826b8..db48d9bca1 100644 --- a/apps/dashboard/src/pages/test/settings/organization.tsx +++ b/apps/dashboard/src/pages/test/settings/organization.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/organization"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/organization"; diff --git a/apps/dashboard/src/pages/test/settings/parcels.tsx b/apps/dashboard/src/pages/test/settings/parcels.tsx index 29543bbd2e..2a46b139de 100644 --- a/apps/dashboard/src/pages/test/settings/parcels.tsx +++ b/apps/dashboard/src/pages/test/settings/parcels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/parcels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/parcels"; diff --git a/apps/dashboard/src/pages/test/settings/profile.tsx b/apps/dashboard/src/pages/test/settings/profile.tsx index 0e24062159..98fb97653d 100644 --- a/apps/dashboard/src/pages/test/settings/profile.tsx +++ b/apps/dashboard/src/pages/test/settings/profile.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/profile"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/profile"; diff --git a/apps/dashboard/src/pages/test/settings/template.tsx b/apps/dashboard/src/pages/test/settings/template.tsx index 19e96963e3..0fae716036 100644 --- a/apps/dashboard/src/pages/test/settings/template.tsx +++ b/apps/dashboard/src/pages/test/settings/template.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/template"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/template"; diff --git a/apps/dashboard/src/pages/test/settings/templates.tsx b/apps/dashboard/src/pages/test/settings/templates.tsx index c913c440a3..c9605bbe6a 100644 --- a/apps/dashboard/src/pages/test/settings/templates.tsx +++ b/apps/dashboard/src/pages/test/settings/templates.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Settings/templates"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Settings/templates"; diff --git a/apps/dashboard/src/pages/test/shipments/[id].tsx b/apps/dashboard/src/pages/test/shipments/[id].tsx index 28e609de21..18ba8bcedb 100644 --- a/apps/dashboard/src/pages/test/shipments/[id].tsx +++ b/apps/dashboard/src/pages/test/shipments/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Shipments/shipment"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Shipments/shipment"; diff --git a/apps/dashboard/src/pages/test/shipments/create_labels.tsx b/apps/dashboard/src/pages/test/shipments/create_labels.tsx index 6e37689957..a37f2dfdaa 100644 --- a/apps/dashboard/src/pages/test/shipments/create_labels.tsx +++ b/apps/dashboard/src/pages/test/shipments/create_labels.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Labels/create_labels"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Labels/create_labels"; diff --git a/apps/dashboard/src/pages/test/shipments/index.tsx b/apps/dashboard/src/pages/test/shipments/index.tsx index d046041d24..ee4ee94ce0 100644 --- a/apps/dashboard/src/pages/test/shipments/index.tsx +++ b/apps/dashboard/src/pages/test/shipments/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Shipments"; +export { default, getServerSideProps } from "@karrio/core/modules/Shipments"; diff --git a/apps/dashboard/src/pages/test/trackers/index.tsx b/apps/dashboard/src/pages/test/trackers/index.tsx index 67447eac7a..c57cd16834 100644 --- a/apps/dashboard/src/pages/test/trackers/index.tsx +++ b/apps/dashboard/src/pages/test/trackers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Trackers"; +export { default, getServerSideProps } from "@karrio/core/modules/Trackers"; diff --git a/apps/dashboard/src/pages/test/workflows/[id].tsx b/apps/dashboard/src/pages/test/workflows/[id].tsx index 2a5acdbe28..b0bb1cf3e0 100644 --- a/apps/dashboard/src/pages/test/workflows/[id].tsx +++ b/apps/dashboard/src/pages/test/workflows/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/workflow"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/workflow"; diff --git a/apps/dashboard/src/pages/test/workflows/events/[id].tsx b/apps/dashboard/src/pages/test/workflows/events/[id].tsx index 22650fc525..38e58f753b 100644 --- a/apps/dashboard/src/pages/test/workflows/events/[id].tsx +++ b/apps/dashboard/src/pages/test/workflows/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/event"; diff --git a/apps/dashboard/src/pages/test/workflows/events/index.tsx b/apps/dashboard/src/pages/test/workflows/events/index.tsx index 25140fadc6..496e48c057 100644 --- a/apps/dashboard/src/pages/test/workflows/events/index.tsx +++ b/apps/dashboard/src/pages/test/workflows/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/events"; diff --git a/apps/dashboard/src/pages/test/workflows/index.tsx b/apps/dashboard/src/pages/test/workflows/index.tsx index 183f69bab5..8580b4f81f 100644 --- a/apps/dashboard/src/pages/test/workflows/index.tsx +++ b/apps/dashboard/src/pages/test/workflows/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Workflows"; +export { default, getServerSideProps } from "@karrio/core/modules/Workflows"; diff --git a/apps/dashboard/src/pages/trackers/index.tsx b/apps/dashboard/src/pages/trackers/index.tsx index 67447eac7a..c57cd16834 100644 --- a/apps/dashboard/src/pages/trackers/index.tsx +++ b/apps/dashboard/src/pages/trackers/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Trackers"; +export { default, getServerSideProps } from "@karrio/core/modules/Trackers"; diff --git a/apps/dashboard/src/pages/tracking/[id].tsx b/apps/dashboard/src/pages/tracking/[id].tsx index 44aaa0bc94..5b369f9de6 100644 --- a/apps/dashboard/src/pages/tracking/[id].tsx +++ b/apps/dashboard/src/pages/tracking/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Trackers/tracking-page"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Trackers/tracking-page"; diff --git a/apps/dashboard/src/pages/workflows/[id].tsx b/apps/dashboard/src/pages/workflows/[id].tsx index 2a5acdbe28..b0bb1cf3e0 100644 --- a/apps/dashboard/src/pages/workflows/[id].tsx +++ b/apps/dashboard/src/pages/workflows/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/workflow"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/workflow"; diff --git a/apps/dashboard/src/pages/workflows/events/[id].tsx b/apps/dashboard/src/pages/workflows/events/[id].tsx index 22650fc525..38e58f753b 100644 --- a/apps/dashboard/src/pages/workflows/events/[id].tsx +++ b/apps/dashboard/src/pages/workflows/events/[id].tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/event"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/event"; diff --git a/apps/dashboard/src/pages/workflows/events/index.tsx b/apps/dashboard/src/pages/workflows/events/index.tsx index 25140fadc6..496e48c057 100644 --- a/apps/dashboard/src/pages/workflows/events/index.tsx +++ b/apps/dashboard/src/pages/workflows/events/index.tsx @@ -1 +1,4 @@ -export { default, getServerSideProps } from "@/modules/Workflows/events"; +export { + default, + getServerSideProps, +} from "@karrio/core/modules/Workflows/events"; diff --git a/apps/dashboard/src/pages/workflows/index.tsx b/apps/dashboard/src/pages/workflows/index.tsx index 183f69bab5..8580b4f81f 100644 --- a/apps/dashboard/src/pages/workflows/index.tsx +++ b/apps/dashboard/src/pages/workflows/index.tsx @@ -1 +1 @@ -export { default, getServerSideProps } from "@/modules/Workflows"; +export { default, getServerSideProps } from "@karrio/core/modules/Workflows"; diff --git a/package-lock.json b/package-lock.json index e62187cf8f..fc86950f9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@codemirror/lang-xml": "^6.0.1", "@fortawesome/fontawesome-free": "^6.5.1", "@headlessui/react": "^1.7.14", + "@karrio/core": "*", "@karrio/hooks": "*", "@karrio/lib": "*", "@karrio/types": "*", @@ -5146,6 +5147,10 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@karrio/core": { + "resolved": "packages/core", + "link": true + }, "node_modules/@karrio/dashboard": { "resolved": "apps/dashboard", "link": true @@ -34664,6 +34669,39 @@ "url": "https://github.com/sponsors/wooorm" } }, + "packages/core": { + "version": "0.0.0", + "license": "Apache-2.0", + "dependencies": { + "@karrio/hooks": "*", + "@karrio/lib": "*", + "@karrio/types": "*", + "@karrio/ui": "*" + }, + "devDependencies": { + "@karrio/eslint-config-custom": "*", + "@karrio/tsconfig": "*", + "@turbo/gen": "^1.10.12", + "@types/node": "^20.5.2", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "react": "^18.2.0", + "typescript": "^4.5.2" + } + }, + "packages/core/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "packages/eslint-config-custom": { "name": "@karrio/eslint-config-custom", "version": "0.0.0", diff --git a/packages/core/.eslintrc.js b/packages/core/.eslintrc.js new file mode 100644 index 0000000000..d750d83f71 --- /dev/null +++ b/packages/core/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["custom/react-internal"], +}; diff --git a/apps/dashboard/src/components/event-preview.tsx b/packages/core/components/event-preview.tsx similarity index 57% rename from apps/dashboard/src/components/event-preview.tsx rename to packages/core/components/event-preview.tsx index 3c291589fe..dfbfc6c0e7 100644 --- a/apps/dashboard/src/components/event-preview.tsx +++ b/packages/core/components/event-preview.tsx @@ -1,54 +1,61 @@ -import { EventComponent } from '@/modules/Developers/event'; -import { useLocation } from '@karrio/lib'; -import React, { useState } from 'react'; - -type EventPreviewContextType = { - previewEvent: (eventId: string) => void, -}; - -interface EventPreviewComponent { - children?: React.ReactNode; -} - -export const EventPreviewContext = React.createContext({} as EventPreviewContextType); - -export const EventPreview: React.FC = ({ children }) => { - const { addUrlParam, removeUrlParam } = useLocation(); - const [isActive, setIsActive] = useState(false); - const [key, setKey] = useState(`event-${Date.now()}`); - const [eventId, setEventId] = useState(); - - const previewEvent = (eventId: string) => { - setEventId(eventId); - setIsActive(true); - setKey(`event-${Date.now()}`); - addUrlParam('modal', eventId); - }; - const dismiss = (_?: React.MouseEvent) => { - setEventId(undefined); - setIsActive(false); - setKey(`event-${Date.now()}`); - removeUrlParam('modal'); - }; - - return ( - <> - - {children} - - -
-
- - {isActive &&
-
- -
-
} - - - -
- - ) -}; +import { EventComponent } from "../modules/Developers/event"; +import { useLocation } from "@karrio/lib"; +import React, { useState } from "react"; + +type EventPreviewContextType = { + previewEvent: (eventId: string) => void; +}; + +interface EventPreviewComponent { + children?: React.ReactNode; +} + +export const EventPreviewContext = React.createContext( + {} as EventPreviewContextType, +); + +export const EventPreview: React.FC = ({ children }) => { + const { addUrlParam, removeUrlParam } = useLocation(); + const [isActive, setIsActive] = useState(false); + const [key, setKey] = useState(`event-${Date.now()}`); + const [eventId, setEventId] = useState(); + + const previewEvent = (eventId: string) => { + setEventId(eventId); + setIsActive(true); + setKey(`event-${Date.now()}`); + addUrlParam("modal", eventId); + }; + const dismiss = (_?: React.MouseEvent) => { + setEventId(undefined); + setIsActive(false); + setKey(`event-${Date.now()}`); + removeUrlParam("modal"); + }; + + return ( + <> + + {children} + + +
+
+ + {isActive && ( +
+
+ +
+
+ )} + + +
+ + ); +}; diff --git a/apps/dashboard/src/components/log-preview.tsx b/packages/core/components/log-preview.tsx similarity index 57% rename from apps/dashboard/src/components/log-preview.tsx rename to packages/core/components/log-preview.tsx index 734e76103e..428821ca6e 100644 --- a/apps/dashboard/src/components/log-preview.tsx +++ b/packages/core/components/log-preview.tsx @@ -1,54 +1,61 @@ -import { LogComponent } from '@/modules/Developers/log'; -import { useLocation } from '@karrio/lib'; -import React, { useState } from 'react'; - -type LogPreviewContextType = { - previewLog: (logId: string) => void, -}; - -interface LogPreviewComponent { - children?: React.ReactNode; -} - -export const LogPreviewContext = React.createContext({} as LogPreviewContextType); - -export const LogPreview: React.FC = ({ children }) => { - const { addUrlParam, removeUrlParam } = useLocation(); - const [isActive, setIsActive] = useState(false); - const [key, setKey] = useState(`log-${Date.now()}`); - const [logId, setLogId] = useState(); - - const previewLog = (logId: string) => { - setLogId(logId); - setIsActive(true); - setKey(`log-${Date.now()}`); - addUrlParam('modal', logId); - }; - const dismiss = (_?: React.MouseEvent) => { - setLogId(undefined); - setIsActive(false); - setKey(`log-${Date.now()}`); - removeUrlParam('modal'); - }; - - return ( - <> - - {children} - - -
-
- - {isActive &&
-
- -
-
} - - - -
- - ) -}; +import { LogComponent } from "../modules/Developers/log"; +import { useLocation } from "@karrio/lib"; +import React, { useState } from "react"; + +type LogPreviewContextType = { + previewLog: (logId: string) => void; +}; + +interface LogPreviewComponent { + children?: React.ReactNode; +} + +export const LogPreviewContext = React.createContext( + {} as LogPreviewContextType, +); + +export const LogPreview: React.FC = ({ children }) => { + const { addUrlParam, removeUrlParam } = useLocation(); + const [isActive, setIsActive] = useState(false); + const [key, setKey] = useState(`log-${Date.now()}`); + const [logId, setLogId] = useState(); + + const previewLog = (logId: string) => { + setLogId(logId); + setIsActive(true); + setKey(`log-${Date.now()}`); + addUrlParam("modal", logId); + }; + const dismiss = (_?: React.MouseEvent) => { + setLogId(undefined); + setIsActive(false); + setKey(`log-${Date.now()}`); + removeUrlParam("modal"); + }; + + return ( + <> + + {children} + + +
+
+ + {isActive && ( +
+
+ +
+
+ )} + + +
+ + ); +}; diff --git a/apps/dashboard/src/components/order-preview.tsx b/packages/core/components/order-preview.tsx similarity index 57% rename from apps/dashboard/src/components/order-preview.tsx rename to packages/core/components/order-preview.tsx index 97b6b3d8d4..78cedd0512 100644 --- a/apps/dashboard/src/components/order-preview.tsx +++ b/packages/core/components/order-preview.tsx @@ -1,56 +1,61 @@ -import { OrderComponent } from '@/modules/Orders/order'; -import { useLocation } from '@karrio/lib'; -import React, { useState } from 'react'; - -type OrderPreviewContextType = { - previewOrder: (orderId: string) => void, -}; - -interface OrderPreviewComponent { - children?: React.ReactNode; -} - -export const OrderPreviewContext = React.createContext({} as OrderPreviewContextType); - -export const OrderPreview: React.FC = ({ children }) => { - const { addUrlParam, removeUrlParam } = useLocation(); - const [isActive, setIsActive] = useState(false); - const [key, setKey] = useState(`order-${Date.now()}`); - const [orderId, setOrderId] = useState(); - - const previewOrder = (orderId: string) => { - setOrderId(orderId); - setIsActive(true); - setKey(`order-${Date.now()}`); - addUrlParam('modal', orderId); - }; - const dismiss = (_?: any) => { - setOrderId(undefined); - setIsActive(false); - setKey(`order-${Date.now()}`); - removeUrlParam('modal'); - }; - - return ( - <> - - {children} - - -
-
- - {isActive &&
-
- - - -
-
} - - - -
- - ) -}; +import { OrderComponent } from "../modules/Orders/order"; +import { useLocation } from "@karrio/lib"; +import React, { useState } from "react"; + +type OrderPreviewContextType = { + previewOrder: (orderId: string) => void; +}; + +interface OrderPreviewComponent { + children?: React.ReactNode; +} + +export const OrderPreviewContext = React.createContext( + {} as OrderPreviewContextType, +); + +export const OrderPreview: React.FC = ({ children }) => { + const { addUrlParam, removeUrlParam } = useLocation(); + const [isActive, setIsActive] = useState(false); + const [key, setKey] = useState(`order-${Date.now()}`); + const [orderId, setOrderId] = useState(); + + const previewOrder = (orderId: string) => { + setOrderId(orderId); + setIsActive(true); + setKey(`order-${Date.now()}`); + addUrlParam("modal", orderId); + }; + const dismiss = (_?: any) => { + setOrderId(undefined); + setIsActive(false); + setKey(`order-${Date.now()}`); + removeUrlParam("modal"); + }; + + return ( + <> + + {children} + + +
+
+ + {isActive && ( +
+
+ +
+
+ )} + + +
+ + ); +}; diff --git a/packages/core/components/shipment-preview.tsx b/packages/core/components/shipment-preview.tsx new file mode 100644 index 0000000000..59f1f60b89 --- /dev/null +++ b/packages/core/components/shipment-preview.tsx @@ -0,0 +1,67 @@ +import { ShipmentComponent } from "../modules/Shipments/shipment"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { useLocation } from "@karrio/lib"; +import React, { useState } from "react"; + +type ShipmentPreviewContextType = { + previewShipment: (shipmentId: string) => void; +}; + +interface ShipmentPreviewComponent { + children?: React.ReactNode; +} + +export const ShipmentPreviewContext = + React.createContext( + {} as ShipmentPreviewContextType, + ); + +export const ShipmentPreview: React.FC = ({ + children, +}) => { + const { addUrlParam, removeUrlParam } = useLocation(); + const [isActive, setIsActive] = useState(false); + const [key, setKey] = useState(`shipment-${Date.now()}`); + const [shipmentId, setShipmentId] = useState(); + + const previewShipment = (shipmentId: string) => { + setShipmentId(shipmentId); + setIsActive(true); + setKey(`shipment-${Date.now()}`); + addUrlParam("modal", shipmentId); + }; + const dismiss = (_?: any) => { + setShipmentId(undefined); + setIsActive(false); + setKey(`shipment-${Date.now()}`); + removeUrlParam("modal"); + }; + + return ( + <> + + {children} + + +
+
+ + {isActive && shipmentId && ( +
+
+ + + +
+
+ )} + + +
+ + ); +}; diff --git a/apps/dashboard/src/components/tracking-preview.tsx b/packages/core/components/tracking-preview.tsx similarity index 100% rename from apps/dashboard/src/components/tracking-preview.tsx rename to packages/core/components/tracking-preview.tsx diff --git a/packages/core/components/workflow-event-preview.tsx b/packages/core/components/workflow-event-preview.tsx new file mode 100644 index 0000000000..4ecd3f15c1 --- /dev/null +++ b/packages/core/components/workflow-event-preview.tsx @@ -0,0 +1,31 @@ +import { ModalFormProps, useModal } from "@karrio/ui/modals/modal"; +import { Component } from "../modules/Workflows/event"; +import React from "react"; + +type EventPreviewModalProps = { + eventId?: string; +}; + +export const WorkflowPreviewModal: React.FC< + ModalFormProps +> = ({ trigger, ...args }) => { + const modal = useModal(); + + const ModalComponent: React.FC = (props) => { + const { eventId } = props; + + return ( +
+ +
+ ); + }; + + return React.cloneElement(trigger, { + onClick: () => + modal.open(, { + className: "is-medium-modal", + backgroundDismiss: true, + }), + }); +}; diff --git a/apps/dashboard/src/context/main.ts b/packages/core/context/main.ts similarity index 100% rename from apps/dashboard/src/context/main.ts rename to packages/core/context/main.ts diff --git a/packages/core/context/metadata.ts b/packages/core/context/metadata.ts new file mode 100644 index 0000000000..c9edf213b3 --- /dev/null +++ b/packages/core/context/metadata.ts @@ -0,0 +1,14 @@ +import { loadAPIMetadata } from "../context/main"; +import { GetServerSideProps } from "next"; + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const { res } = ctx; + const metadata = await loadAPIMetadata(ctx).catch((_) => _); + + res.setHeader( + "Cache-Control", + "public, s-maxage=30, stale-while-revalidate=59", + ); + + return { props: { ...metadata } }; +}; diff --git a/apps/dashboard/src/context/tracker.ts b/packages/core/context/tracker.ts similarity index 93% rename from apps/dashboard/src/context/tracker.ts rename to packages/core/context/tracker.ts index b858485117..f5f5b440ed 100644 --- a/apps/dashboard/src/context/tracker.ts +++ b/packages/core/context/tracker.ts @@ -1,35 +1,35 @@ -import { KARRIO_API } from "@karrio/hooks/karrio"; -import { loadAPIMetadata } from "@/context/main"; -import { KarrioClient } from "@karrio/types"; -import { url$, logger } from "@karrio/lib"; -import { GetServerSideProps } from "next"; - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - const { res, params } = ctx; - const id = params?.id as string; - const metadata = await loadAPIMetadata(ctx).catch((_) => _); - const client = new KarrioClient({ - basePath: url$`${metadata.metadata?.HOST || KARRIO_API}`, - }); - - try { - // Retrieve tracker by id - const data = await client.trackers - .retrieve({ idOrTrackingNumber: id }) - .then(({ data }) => ({ tracker: JSON.parse(JSON.stringify(data)) })) - .catch((_) => { - console.log(_.response?.data?.errors || _.response); - return { message: `No Tracker ID nor Tracking Number found for ${id}` }; - }); - - res.setHeader( - "Cache-Control", - "public, s-maxage=10, stale-while-revalidate=59", - ); - - return { props: { id, ...metadata, ...data } }; - } catch (e) { - logger.error(e, "Failed to retrieve tracking info"); - return { props: { id, ...metadata, ...(e as {}) } }; - } -}; +import { KARRIO_API } from "@karrio/hooks/karrio"; +import { loadAPIMetadata } from "../context/main"; +import { KarrioClient } from "@karrio/types"; +import { url$, logger } from "@karrio/lib"; +import { GetServerSideProps } from "next"; + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const { res, params } = ctx; + const id = params?.id as string; + const metadata = await loadAPIMetadata(ctx).catch((_) => _); + const client = new KarrioClient({ + basePath: url$`${metadata.metadata?.HOST || KARRIO_API}`, + }); + + try { + // Retrieve tracker by id + const data = await client.trackers + .retrieve({ idOrTrackingNumber: id }) + .then(({ data }) => ({ tracker: JSON.parse(JSON.stringify(data)) })) + .catch((_) => { + console.log(_.response?.data?.errors || _.response); + return { message: `No Tracker ID nor Tracking Number found for ${id}` }; + }); + + res.setHeader( + "Cache-Control", + "public, s-maxage=10, stale-while-revalidate=59", + ); + + return { props: { id, ...metadata, ...data } }; + } catch (e) { + logger.error(e, "Failed to retrieve tracking info"); + return { props: { id, ...metadata, ...(e as {}) } }; + } +}; diff --git a/packages/core/index.tsx b/packages/core/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/dashboard/src/layouts/admin-layout.tsx b/packages/core/layouts/admin-layout.tsx similarity index 100% rename from apps/dashboard/src/layouts/admin-layout.tsx rename to packages/core/layouts/admin-layout.tsx diff --git a/apps/dashboard/src/layouts/authenticated-page.tsx b/packages/core/layouts/authenticated-page.tsx similarity index 100% rename from apps/dashboard/src/layouts/authenticated-page.tsx rename to packages/core/layouts/authenticated-page.tsx diff --git a/apps/dashboard/src/layouts/dashboard-layout.tsx b/packages/core/layouts/dashboard-layout.tsx similarity index 100% rename from apps/dashboard/src/layouts/dashboard-layout.tsx rename to packages/core/layouts/dashboard-layout.tsx diff --git a/apps/dashboard/src/layouts/embed-layout.tsx b/packages/core/layouts/embed-layout.tsx similarity index 100% rename from apps/dashboard/src/layouts/embed-layout.tsx rename to packages/core/layouts/embed-layout.tsx diff --git a/apps/dashboard/src/layouts/main-layout.tsx b/packages/core/layouts/main-layout.tsx similarity index 100% rename from apps/dashboard/src/layouts/main-layout.tsx rename to packages/core/layouts/main-layout.tsx diff --git a/packages/core/layouts/section-layout.tsx b/packages/core/layouts/section-layout.tsx new file mode 100644 index 0000000000..27599f2052 --- /dev/null +++ b/packages/core/layouts/section-layout.tsx @@ -0,0 +1,64 @@ +import { Metadata, SessionType } from "@karrio/types"; +import MainLayout from "../layouts/main-layout"; +import { ServerError, p, url$ } from "@karrio/lib"; +import Link from "next/link"; +import React from "react"; + +type SectionLayoutProps = { + metadata?: Metadata; + error?: ServerError; + session?: SessionType; + children?: React.ReactNode; +}; + +export const SectionLayout: React.FC = ({ + metadata, + error, + children, +}) => { + return ( + +
+
+ + + {children} +
+ +
+
+

+ {metadata?.APP_NAME.includes("Karrio") && ( + <> + + © {metadata?.APP_NAME} + + + Documentation + + + )} +

+
+
+
+
+ ); +}; diff --git a/packages/core/modules/Admin/carrier_connections.tsx b/packages/core/modules/Admin/carrier_connections.tsx new file mode 100644 index 0000000000..aa7e5fa86e --- /dev/null +++ b/packages/core/modules/Admin/carrier_connections.tsx @@ -0,0 +1,58 @@ +import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; +import { SystemCarrierManagement } from "@karrio/ui/admin/system-carrier-management"; +import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; +import { ConnectProviderModal } from "@karrio/ui/modals/connect-provider-modal"; +import { RateSheetManagement } from "@karrio/ui/admin/rate-sheet-management"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { AdminLayout } from "../../layouts/admin-layout"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ + ConfirmModal, + ConnectProviderModal, + LabelTemplateEditModalProvider, + RateSheetEditModalProvider, +]); + +export default function Page(pageProps: any) { + const { APP_NAME } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ + Carrier connections + +
+ + {/* System carriers */} + + +
+ + {/* System carriers */} + + +
+ + ); + }; + + return AuthenticatedPage( + + + {`Carriers - ${APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Admin/index.tsx b/packages/core/modules/Admin/index.tsx new file mode 100644 index 0000000000..04e75f2e5b --- /dev/null +++ b/packages/core/modules/Admin/index.tsx @@ -0,0 +1,328 @@ +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { Switch } from "@karrio/ui/components/switch"; +import { AdminLayout } from "../../layouts/admin-layout"; +import getConfig from "next/config"; +import { url$ } from "@karrio/lib"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +const { publicRuntimeConfig } = getConfig(); + +export default function Page(pageProps: any) { + const { APP_NAME } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + const { metadata } = useAPIMetadata(); + + return ( + <> +
+ + Platform details + +
+ + {/* Platfrom config */} +
+
+ + Platform + +
+ +
+
+ +
+ +
+

Platform name

+

+ {metadata?.APP_NAME} +

+
+ +
+ +
+

API hostname

+

+ {url$`${metadata?.HOST}` + .replace("http://", "") + .replace("https://", "")} +

+
+ +
+ +
+

+ Dashboard hostname +

+

+ {location.host} +

+
+
+ +
+ + {/* Feature flags */} +
+
+ + Feature flags + +
+
+ +
+ +
+
+
+ +

Allow user signup

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

user signup will require admin approval

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

Toggle audit logging functionality

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

Enable apps

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

Enable order management panel

+
+
+ +
+
+
+
+ +
+ + {/* Email config */} +
+
+ + Email config + +
+
+ +
+ +
+
+ +
+ +
+

+ Determine whether the configuration support TLS +

+
+ +
+ +
+ +
+

+ The authentication user (email). e.g: admin@karrio.io +

+
+ +
+ +
+ +
+

The authentication password

+
+ +
+ +
+ +
+

The mail server host. e.g: smtp.gmail.com

+
+ +
+ +
+ +
+

+ The mail server port. e.g: 465 (SSL required) or 587 (TLS + required) +

+
+ +
+ +
+ +
+

Email sent from. e.g: noreply@karrio.io

+
+
+
+ +
+ + {/* Address validation */} +
+
+ + Address Validation Service + +
+
+ +
+ +
+
+ +
+ +
+

A Google GeoCoding API key

+
+ +
+ +
+ +
+

+ The Canada Post AddressComplete service API Key +

+
+
+
+ +
+ + {/* Data Retention */} +
+
+ + Data retention + +
+
+ +
+ +
+
+ +
+ +
+

Order data retention period (in days)

+
+ +
+ +
+ +
+

Tracker data retention period (in days)

+
+ +
+ +
+ +
+

Shipment data retention period (in days)

+
+ +
+ +
+ +
+

+ API request and SDK tracing logs retention period (in days) +

+
+
+
+ +
+ +
+ +
+ + ); + }; + + return AuthenticatedPage( + + + {`Platform - ${APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Admin/organization_accounts.tsx b/packages/core/modules/Admin/organization_accounts.tsx new file mode 100644 index 0000000000..db5bd8a521 --- /dev/null +++ b/packages/core/modules/Admin/organization_accounts.tsx @@ -0,0 +1,32 @@ +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { AdminLayout } from "../../layouts/admin-layout"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function Page(pageProps: any) { + const { APP_NAME } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ + Organization accounts + +
+ + ); + }; + + return AuthenticatedPage( + + + {`Organizations - ${APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Admin/surcharges.tsx b/packages/core/modules/Admin/surcharges.tsx new file mode 100644 index 0000000000..bc5975b0ac --- /dev/null +++ b/packages/core/modules/Admin/surcharges.tsx @@ -0,0 +1,36 @@ +import { SurchargeManagement } from "@karrio/ui/admin/surcharge-management"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { AdminLayout } from "../../layouts/admin-layout"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function Page(pageProps: any) { + const { APP_NAME } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ + Surcharge & discounts + +
+ + {/* Surcharges */} + + + ); + }; + + return AuthenticatedPage( + + + {`Surcharges - ${APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Admin/user_accounts.tsx b/packages/core/modules/Admin/user_accounts.tsx new file mode 100644 index 0000000000..76ade1f92a --- /dev/null +++ b/packages/core/modules/Admin/user_accounts.tsx @@ -0,0 +1,42 @@ +import { StaffManagement } from "@karrio/ui/admin/staff-management"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { AdminLayout } from "../../layouts/admin-layout"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ModalProvider]); + +export default function Page(pageProps: any) { + const { APP_NAME } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ + User and permissions + +
+ + {/* Staff */} + + + ); + }; + + return AuthenticatedPage( + + + {`Users - ${APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Billing/index.tsx b/packages/core/modules/Billing/index.tsx new file mode 100644 index 0000000000..3c0a65d469 --- /dev/null +++ b/packages/core/modules/Billing/index.tsx @@ -0,0 +1,100 @@ +import { + loadAPIMetadata, + checkSubscription, + createPortalSession, +} from "../../context/main"; +import { Metadata, SubscriptionType } from "@karrio/types"; +import { GetServerSideProps, NextPage } from "next"; +import { getSession } from "next-auth/react"; +import { isNone, p } from "@karrio/lib"; +import Head from "next/head"; +import React from "react"; + +type BillingPageProps = { + metadata: Metadata; + subscription?: SubscriptionType; + session_url?: string; +}; + +const Billing: NextPage = ({ + metadata, + subscription, + session_url, +}) => { + return ( + <> + + {`Billing - ${metadata?.APP_NAME}`} + + +
+
+ + + {!subscription?.is_owner && ( +
+
+

Your subscription has expired.

+

Please notify your account admnistrator.

+
+
+ )} + + {subscription?.is_owner && ( +
+
+

Billing Setup

+

+ Update your billing and subscription on Stripe. +

+ + + Open Customer Portal + +
+
+ )} +
+
+ + ); +}; + +export const getServerSideProps: GetServerSideProps = async (ctx) => { + const session = await getSession(ctx); + + if (isNone((session as any)?.accessToken)) { + return { + redirect: { + permanent: false, + destination: "/login", + }, + }; + } + + const metadata = await loadAPIMetadata(ctx).catch((_) => _); + const subscription = await checkSubscription( + session, + metadata.metadata, + ).catch((_) => _); + const portal_session = await createPortalSession( + session, + ctx.req.headers.host as string, + subscription.subscription, + metadata.metadata, + ); + + return { props: { ...metadata, ...subscription, ...portal_session } }; +}; + +export default Billing; diff --git a/apps/dashboard/src/modules/Connections/index.tsx b/packages/core/modules/Connections/index.tsx similarity index 62% rename from apps/dashboard/src/modules/Connections/index.tsx rename to packages/core/modules/Connections/index.tsx index d307e619c9..4c58d907d0 100644 --- a/apps/dashboard/src/modules/Connections/index.tsx +++ b/packages/core/modules/Connections/index.tsx @@ -1,104 +1,130 @@ -import { ConnectProviderModal, ConnectProviderModalContext } from "@karrio/ui/modals/connect-provider-modal"; -import { useCarrierConnectionMutation, useCarrierConnections } from "@karrio/hooks/user-connection"; -import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; -import { Tabs, TabStateContext, TabStateProvider } from "@karrio/ui/components/tabs"; -import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; -import { SystemConnectionList } from "@karrio/ui/forms/system-carrier-list"; -import { UserConnectionList } from "@karrio/ui/forms/user-carrier-list"; -import { useSystemConnections } from "@karrio/hooks/system-connection"; -import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Loading } from "@karrio/ui/components/loader"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useRouter } from "next/dist/client/router"; -import { useContext, useEffect } from "react"; -import Head from "next/head"; -import { AppLink } from "@karrio/ui/components/app-link"; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ - ModalProvider, - ConfirmModal, - ConnectProviderModal, - LabelTemplateEditModalProvider, - RateSheetEditModalProvider, -]); - - -export default function ConnectionsPage(pageProps: any) { - const tabs = ['Your Accounts', 'System Accounts', 'Rate Sheets']; - - const Component: React.FC = () => { - const router = useRouter(); - const { modal } = router.query; - const { setLoading } = useContext(Loading); - const mutation = useCarrierConnectionMutation(); - const { selectTab } = useContext(TabStateContext); - const { query: systemQuery } = useSystemConnections(); - const { query: carrierQuery } = useCarrierConnections(); - const { editConnection } = useContext(ConnectProviderModalContext); - - useEffect(() => { setLoading(carrierQuery.isFetching || systemQuery.isFetching); }); - useEffect(() => { - if (modal === 'new') { - editConnection({ - onConfirm: async () => { selectTab(tabs[0]); }, - create: mutation.updateCarrierConnection.mutateAsync, - }); - } - }, [modal]); - - return ( - <> - -
- Carriers -
- -
-
- -
-
    -
  • - - Your Accounts - -
  • -
  • - - System Accounts - -
  • -
  • - - Rate Sheets - -
  • -
-
- - - - - ); - }; - - return AuthenticatedPage(( - - {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps); -} +import { + ConnectProviderModal, + ConnectProviderModalContext, +} from "@karrio/ui/modals/connect-provider-modal"; +import { + useCarrierConnectionMutation, + useCarrierConnections, +} from "@karrio/hooks/user-connection"; +import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; +import { + Tabs, + TabStateContext, + TabStateProvider, +} from "@karrio/ui/components/tabs"; +import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; +import { SystemConnectionList } from "@karrio/ui/forms/system-carrier-list"; +import { UserConnectionList } from "@karrio/ui/forms/user-carrier-list"; +import { useSystemConnections } from "@karrio/hooks/system-connection"; +import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Loading } from "@karrio/ui/components/loader"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useRouter } from "next/dist/client/router"; +import { useContext, useEffect } from "react"; +import Head from "next/head"; +import { AppLink } from "@karrio/ui/components/app-link"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ + ModalProvider, + ConfirmModal, + ConnectProviderModal, + LabelTemplateEditModalProvider, + RateSheetEditModalProvider, +]); + +export default function ConnectionsPage(pageProps: any) { + const tabs = ["Your Accounts", "System Accounts", "Rate Sheets"]; + + const Component: React.FC = () => { + const router = useRouter(); + const { modal } = router.query; + const { setLoading } = useContext(Loading); + const mutation = useCarrierConnectionMutation(); + const { selectTab } = useContext(TabStateContext); + const { query: systemQuery } = useSystemConnections(); + const { query: carrierQuery } = useCarrierConnections(); + const { editConnection } = useContext(ConnectProviderModalContext); + + useEffect(() => { + setLoading(carrierQuery.isFetching || systemQuery.isFetching); + }); + useEffect(() => { + if (modal === "new") { + editConnection({ + onConfirm: async () => { + selectTab(tabs[0]); + }, + create: mutation.updateCarrierConnection.mutateAsync, + }); + } + }, [modal]); + + return ( + <> +
+ Carriers +
+ +
+
+ +
+
    +
  • + + Your Accounts + +
  • +
  • + + System Accounts + +
  • +
  • + + Rate Sheets + +
  • +
+
+ + + + ); + }; + + return AuthenticatedPage( + + + {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Connections/rate-sheets.tsx b/packages/core/modules/Connections/rate-sheets.tsx similarity index 73% rename from apps/dashboard/src/modules/Connections/rate-sheets.tsx rename to packages/core/modules/Connections/rate-sheets.tsx index d30fa7de94..2bb9859fbd 100644 --- a/apps/dashboard/src/modules/Connections/rate-sheets.tsx +++ b/packages/core/modules/Connections/rate-sheets.tsx @@ -1,84 +1,91 @@ -import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; -import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; -import { ConnectProviderModal } from "@karrio/ui/modals/connect-provider-modal"; -import { RateSheetModalEditor } from "@karrio/ui/modals/rate-sheet-editor"; -import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useRateSheetMutation } from "@karrio/hooks/rate-sheet"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { TabStateContext } from "@karrio/ui/components/tabs"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useContext } from "react"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ - ModalProvider, - ConfirmModal, - ConnectProviderModal, - LabelTemplateEditModalProvider, - RateSheetEditModalProvider, -]); - - -export default function ConnectionsPage(pageProps: any) { - const Component: React.FC = () => { - const mutation = useRateSheetMutation(); - - return ( - <> - -
- Carriers -
- mutation.createRateSheet.mutateAsync(_)} - trigger={ - - } - /> -
-
- -
-
    -
  • - - Your Accounts - -
  • -
  • - - System Accounts - -
  • -
  • - - Rate Sheets - -
  • -
-
- - - - - ); - }; - - return AuthenticatedPage(( - - {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps); -} +import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; +import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; +import { ConnectProviderModal } from "@karrio/ui/modals/connect-provider-modal"; +import { RateSheetModalEditor } from "@karrio/ui/modals/rate-sheet-editor"; +import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useRateSheetMutation } from "@karrio/hooks/rate-sheet"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { TabStateContext } from "@karrio/ui/components/tabs"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useContext } from "react"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ + ModalProvider, + ConfirmModal, + ConnectProviderModal, + LabelTemplateEditModalProvider, + RateSheetEditModalProvider, +]); + +export default function ConnectionsPage(pageProps: any) { + const Component: React.FC = () => { + const mutation = useRateSheetMutation(); + + return ( + <> +
+ Carriers +
+ mutation.createRateSheet.mutateAsync(_)} + trigger={ + + } + /> +
+
+ +
+
    +
  • + + Your Accounts + +
  • +
  • + + System Accounts + +
  • +
  • + + Rate Sheets + +
  • +
+
+ + + + ); + }; + + return AuthenticatedPage( + + + {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Connections/system.tsx b/packages/core/modules/Connections/system.tsx similarity index 66% rename from apps/dashboard/src/modules/Connections/system.tsx rename to packages/core/modules/Connections/system.tsx index 7e6fef0ff4..60dda1ca4d 100644 --- a/apps/dashboard/src/modules/Connections/system.tsx +++ b/packages/core/modules/Connections/system.tsx @@ -1,80 +1,96 @@ -import { ConnectProviderModal, ConnectProviderModalContext } from "@karrio/ui/modals/connect-provider-modal"; -import { useCarrierConnectionMutation, useCarrierConnections } from "@karrio/hooks/user-connection"; -import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; -import { Tabs, TabStateContext, TabStateProvider } from "@karrio/ui/components/tabs"; -import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; -import { SystemConnectionList } from "@karrio/ui/forms/system-carrier-list"; -import { UserConnectionList } from "@karrio/ui/forms/user-carrier-list"; -import { useSystemConnections } from "@karrio/hooks/system-connection"; -import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Loading } from "@karrio/ui/components/loader"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useRouter } from "next/dist/client/router"; -import { useContext, useEffect } from "react"; -import Head from "next/head"; -import { AppLink } from "@karrio/ui/components/app-link"; - -export { getServerSideProps } from "@/context/main"; -const ContextProviders = bundleContexts([ - ModalProvider, - ConfirmModal, - ConnectProviderModal, - LabelTemplateEditModalProvider, - RateSheetEditModalProvider, -]); - - -export default function ConnectionsPage(pageProps: any) { - const Component: React.FC = () => { - const router = useRouter(); - - return ( - <> - -
- Carriers -
-
-
- -
-
    -
  • - - Your Accounts - -
  • -
  • - - System Accounts - -
  • -
  • - - Rate Sheets - -
  • -
-
- - - - - ); - }; - - return AuthenticatedPage(( - - {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - - ), pageProps); -} +import { + ConnectProviderModal, + ConnectProviderModalContext, +} from "@karrio/ui/modals/connect-provider-modal"; +import { + useCarrierConnectionMutation, + useCarrierConnections, +} from "@karrio/hooks/user-connection"; +import { LabelTemplateEditModalProvider } from "@karrio/ui/modals/label-template-edit-modal"; +import { + Tabs, + TabStateContext, + TabStateProvider, +} from "@karrio/ui/components/tabs"; +import { RateSheetEditModalProvider } from "@karrio/ui/modals/rate-sheet-edit-modal"; +import { SystemConnectionList } from "@karrio/ui/forms/system-carrier-list"; +import { UserConnectionList } from "@karrio/ui/forms/user-carrier-list"; +import { useSystemConnections } from "@karrio/hooks/system-connection"; +import { RateSheetList } from "@karrio/ui/forms/rate-sheet-list"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Loading } from "@karrio/ui/components/loader"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useRouter } from "next/dist/client/router"; +import { useContext, useEffect } from "react"; +import Head from "next/head"; +import { AppLink } from "@karrio/ui/components/app-link"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ + ModalProvider, + ConfirmModal, + ConnectProviderModal, + LabelTemplateEditModalProvider, + RateSheetEditModalProvider, +]); + +export default function ConnectionsPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + + return ( + <> +
+ Carriers +
+
+ +
+
    +
  • + + Your Accounts + +
  • +
  • + + System Accounts + +
  • +
  • + + Rate Sheets + +
  • +
+
+ + + + ); + }; + + return AuthenticatedPage( + + + {`Carrier Connections - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Developers/apikeys.tsx b/packages/core/modules/Developers/apikeys.tsx similarity index 55% rename from apps/dashboard/src/modules/Developers/apikeys.tsx rename to packages/core/modules/Developers/apikeys.tsx index 00963044e7..fba52ff833 100644 --- a/apps/dashboard/src/modules/Developers/apikeys.tsx +++ b/packages/core/modules/Developers/apikeys.tsx @@ -1,140 +1,186 @@ -import { GenerateAPIModal } from "@karrio/ui/modals/generate-api-dialog"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useContext, useEffect, useRef, useState } from "react"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { Loading } from "@karrio/ui/components/loader"; -import { useAPIToken } from "@karrio/hooks/api-token"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function ApiPage(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - const { setLoading } = useContext(Loading); - const tokenInput = useRef(null); - const [isRevealed, setIsRevealed] = useState(false); - const { query: { data: { token } = {}, ...query } } = useAPIToken(); - - const copy = (_: React.MouseEvent) => { - tokenInput.current?.select(); - document.execCommand("copy"); - }; - - useEffect(() => { setLoading(query.isRefetching); }, [query.isRefetching]); - - return ( - <> - -
- Developers -
-
- -
-
    -
  • - - Overview - -
  • -
  • - - API Keys - -
  • -
  • - - Webhooks - -
  • -
  • - - Events - -
  • -
  • - - Logs - -
  • -
-
- - {/* API Keys */} -
- -
-
-

Private Key

-

- Use this key to authenticate your API calls. - - Learn more - -

-

Warning: must be kept securely and never exposed to a client application.

-
- -
-
-

- -

-

- -

-

- -

-
- - - -
-
- -

- Click - - to revoke old keys and generate a new one. -

-
- -
-
-
- - - ); - }; - - return AuthenticatedPage(( - - {`API Keys - ${references?.APP_NAME}`} - - - ), pageProps); -} +import { GenerateAPIModal } from "@karrio/ui/modals/generate-api-dialog"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useContext, useEffect, useRef, useState } from "react"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Loading } from "@karrio/ui/components/loader"; +import { useAPIToken } from "@karrio/hooks/api-token"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function ApiPage(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + const { setLoading } = useContext(Loading); + const tokenInput = useRef(null); + const [isRevealed, setIsRevealed] = useState(false); + const { + query: { data: { token } = {}, ...query }, + } = useAPIToken(); + + const copy = (_: React.MouseEvent) => { + tokenInput.current?.select(); + document.execCommand("copy"); + }; + + useEffect(() => { + setLoading(query.isRefetching); + }, [query.isRefetching]); + + return ( + <> +
+ Developers +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + API Keys + +
  • +
  • + + Webhooks + +
  • +
  • + + Events + +
  • +
  • + + Logs + +
  • +
+
+ + {/* API Keys */} +
+
+
+

Private Key

+

+ Use this key to authenticate your API calls. + + Learn more + +

+

+ Warning: must be kept securely and never + exposed to a client application. +

+
+ +
+
+

+ +

+

+ +

+

+ +

+
+ + + +
+
+ +

+ Click + + + + to revoke old keys and generate a new one. +

+
+ +
+
+
+ + ); + }; + + return AuthenticatedPage( + + + {`API Keys - ${references?.APP_NAME}`} + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Developers/event.tsx b/packages/core/modules/Developers/event.tsx new file mode 100644 index 0000000000..45f4eb058f --- /dev/null +++ b/packages/core/modules/Developers/event.tsx @@ -0,0 +1,131 @@ +import { formatDateTimeLong, isNone, notEmptyJSON } from "@karrio/lib"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { useRouter } from "next/dist/client/router"; +import json from "highlight.js/lib/languages/json"; +import React, { useEffect, useState } from "react"; +import { useEvent } from "@karrio/hooks/event"; +import hljs from "highlight.js"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +hljs.registerLanguage("json", json); + +export const EventComponent: React.FC<{ eventId?: string }> = ({ eventId }) => { + const router = useRouter(); + const { setLoading } = useLoader(); + const entity_id = eventId || (router.query.id as string); + const [data, setData] = useState(); + const { + query: { data: { event } = {}, ...query }, + } = useEvent(entity_id); + + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if (event !== undefined) { + setData(JSON.stringify(event || {}, null, 2)); + } + }); + + return ( + <> + {!isNone(event?.id) && ( + <> +
+
+ + EVENT + +
+ {event?.type} +
+ +
+

+ +

+ {!isNone(eventId) && ( +

+ + + + + +

+ )} +
+
+ +
+ +
+
+ Date +
+ + {formatDateTimeLong(event?.created_at)} + +
+ +
+ +
+ Source +
+ + Automatic + +
+
+ +

Event data

+
+ + {notEmptyJSON(data) && ( +
+ +
+                
+              
+
+ )} + + )} + + ); +}; + +export default function EventPage(pageProps: any) { + return AuthenticatedPage( + + + {`Event - ${(pageProps as any).metadata?.APP_NAME}`} + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Developers/events.tsx b/packages/core/modules/Developers/events.tsx new file mode 100644 index 0000000000..34997afd19 --- /dev/null +++ b/packages/core/modules/Developers/events.tsx @@ -0,0 +1,202 @@ +import { + formatDateTimeLong, + getURLSearchParams, + isNoneOrEmpty, +} from "@karrio/lib"; +import { + EventPreview, + EventPreviewContext, +} from "../../components/event-preview"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { EventsFilter } from "@karrio/ui/filters/events-filter"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { useLoader } from "@karrio/ui/components/loader"; +import { Spinner } from "@karrio/ui/components/spinner"; +import React, { useContext, useEffect } from "react"; +import { useRouter } from "next/dist/client/router"; +import { useEvents } from "@karrio/hooks/event"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function EventsPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const context = useEvents(); + const { setLoading } = useLoader(); + const { previewEvent } = useContext(EventPreviewContext); + const [initialized, setInitialized] = React.useState(false); + const { + query: { data: { events } = {}, ...query }, + filter, + setFilter, + } = context; + + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + previewEvent(router.query.modal as string); + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+ Developers +
+ +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + API Keys + +
  • +
  • + + Webhooks + +
  • +
  • + + Events + +
  • +
  • + + Logs + +
  • +
+
+ + {!query.isFetched && } + + {query.isFetched && (events?.edges || []).length > 0 && ( + <> +
+ + + + + + + + {(events?.edges || []).map(({ node: event }) => ( + previewEvent(event.id)} + > + + + + ))} + +
+ EVENT +
+ {`${event.type}`} + + + {formatDateTimeLong(event.created_at)} + +
+
+ +
+ + {(events?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (events?.edges || []).length == 0 && ( +
+
+

No API events found.

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Events - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Developers/index.tsx b/packages/core/modules/Developers/index.tsx new file mode 100644 index 0000000000..28dc5b1fea --- /dev/null +++ b/packages/core/modules/Developers/index.tsx @@ -0,0 +1,301 @@ +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { SelectField } from "@karrio/ui/components"; +import { useAPIUsage } from "@karrio/hooks/usage"; +import Head from "next/head"; +import { + Bar, + BarChart, + CartesianGrid, + Line, + LineChart, + ResponsiveContainer, + Tooltip, + XAxis, +} from "recharts"; +import moment from "moment"; + +export { getServerSideProps } from "../../context/main"; + +export default function ApiPage(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + const { + query: { data: { usage } = {} }, + setFilter, + filter, + USAGE_FILTERS, + DAYS_LIST, + currentFilter, + } = useAPIUsage(); + + return ( + <> +
+ Developers +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + API Keys + +
  • +
  • + + Webhooks + +
  • +
  • + + Events + +
  • +
  • + + Logs + +
  • +
+
+ +
+ {/* API usage stats */} +
+
+ Your Integration +
+ setFilter(JSON.parse(e.target.value))} + > + {Object.entries(USAGE_FILTERS).map(([key, value]) => ( + + ))} + +
+
+
+ +
+
+
+
+ + API requests + +
+ +

+ {!!usage ? usage?.total_requests : 0} +

+ +
+ {!!usage?.api_requests && ( + + ({ + name: + i === 0 || + i === + DAYS_LIST[currentFilter() || "15 days"] + .length - + 1 + ? _ + : "", + total: + usage!.api_requests!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + margin={{ top: 5, right: 15, left: 15, bottom: 5 }} + > + + + + + + + )} +
+
+
+ +
+
+
+ API errors +
+ +

+ {!!usage ? usage?.total_errors : 0} +

+ +
+ {!!usage?.api_errors && ( + + ({ + name: + i === 0 || + i === + DAYS_LIST[currentFilter() || "15 days"] + .length - + 1 + ? _ + : "", + total: + usage!.api_errors!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + margin={{ top: 5, right: 15, left: 15, bottom: 5 }} + > + + + + + + + )} +
+
+
+
+
+ +
+ + {/* APIs Overview */} +
+
+ API Details +
+
+
+ +
+
+
+
+
+ + API Version + +
+
+ {references?.VERSION} +
+
+
+
+ + REST API + +
+
+ +
+
+
+
+ + GRAPHQL API + +
+
+ +
+
+
+
+
+
+
+ +
+ + ); + }; + + return AuthenticatedPage( + + + {`API Keys - ${references?.APP_NAME}`} + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Developers/log.tsx b/packages/core/modules/Developers/log.tsx new file mode 100644 index 0000000000..b838509575 --- /dev/null +++ b/packages/core/modules/Developers/log.tsx @@ -0,0 +1,384 @@ +import { + failsafe, + formatDateTimeLong, + groupBy, + isNone, + jsonify, + notEmptyJSON, +} from "@karrio/lib"; +import { Tabs, TabStateProvider } from "@karrio/ui/components/tabs"; +import { StatusCode } from "@karrio/ui/components/status-code-badge"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { useRouter } from "next/dist/client/router"; +import json from "highlight.js/lib/languages/json"; +import { useLog } from "@karrio/hooks/log"; +import hljs from "highlight.js"; +import Head from "next/head"; +import React from "react"; +import moment from "moment"; + +export { getServerSideProps } from "../../context/main"; + +hljs.registerLanguage("json", json); + +export const LogComponent: React.FC<{ logId?: string }> = ({ logId }) => { + const router = useRouter(); + const { setLoading } = useLoader(); + const entity_id = logId || (router.query.id as string); + const [data, setData] = React.useState(); + const [response, setResponse] = React.useState(); + const [query_params, setQueryParams] = React.useState(); + const { + query: { data: { log } = {}, ...query }, + } = useLog(entity_id); + + React.useEffect(() => { + (window as any).moment = moment; + setLoading(query.isFetching); + }, [query.isFetching]); + React.useEffect(() => { + if (log !== undefined) { + setQueryParams(failsafe(() => jsonify(log?.query_params), "{}")); + setResponse(failsafe(() => jsonify(log?.response), "{}")); + setData(failsafe(() => jsonify(log?.data), "{}")); + } + }); + + return ( + <> + {log !== undefined && ( + <> +
+
+ + LOG + +
+ + {log?.method} {log?.path}{" "} + + +
+ {!isNone(logId) && ( +
+ + + + + +
+ )} +
+ +
+ +
+
+
ID
+
{log?.id}
+
+
+
Date
+
+ {formatDateTimeLong(log?.requested_at)} +
+
+
+
Origin
+
{log?.remote_addr}
+
+
+
IP Address
+
{log?.host}
+
+
+ + + + {notEmptyJSON(response) && ( +
+

Response body

+ +
+ +
+                      
+                    
+
+
+ )} + +
+ {notEmptyJSON(query_params) && query_params !== data && ( + <> +

Request query params

+ +
+
+                        
+                      
+
+ +
+ + )} + + {notEmptyJSON(data) && ( + <> +

+ Request {log?.method} body +

+ +
+ +
+                        
+                      
+
+ + )} +
+ +
+ {(log?.records || []).length == 0 && ( +
+ No tracing records... +
+ )} + {(log?.records || []).length > 0 && ( + <> + {Object.values( + groupBy(log!.records, (r: any) => r.record?.request_id), + ).map((records: any, key) => { + const request = records.find( + (r: any) => r.key === "request", + ); + const response = records.find( + (r: any) => r.key !== "request", + ); + const request_data = parseRecordData(request?.record); + const response_data = parseRecordData(response?.record); + const tabs = [ + ...(request ? ["request"] : []), + ...(response ? [response.key] : []), + ]; + + return ( +
+
+

+ + Carrier:{" "} + + {(request || response)?.meta?.carrier_name} + + +

+

+ + Connection:{" "} + + {(request || response)?.meta?.carrier_id} + + +

+

+ + URL:{" "} + + {(request?.record || response?.record)?.url} + + +

+

+ + Request ID:{" "} + + { + (request?.record || response?.record) + ?.request_id + } + + +

+ {request?.timestamp && ( +

+ + Request Timestamp:{" "} + + {moment(request.timestamp * 1000).format( + "LTS", + )} + + +

+ )} + {response?.timestamp && ( +

+ + Response Timestamp:{" "} + + {moment(response.timestamp * 1000).format( + "LTS", + )} + + +

+ )} +
+ + + {request && ( +
+ +
+                                    
+                                  
+
+ )} + + {response_data && ( +
+ <", + ">\n<", + )} + style={{ + position: "absolute", + right: 0, + zIndex: 1, + }} + className="button is-primary is-small m-1" + /> +
+                                    <",
+                                            ">\n<",
+                                          ),
+                                          {
+                                            language:
+                                              response.record?.format || "json",
+                                          },
+                                        ).value,
+                                      }}
+                                    />
+                                  
+
+ )} +
+
+
+ ); + })} + + )} +
+
+
+ + )} + + ); +}; + +export default function LogPage(pageProps: any) { + return AuthenticatedPage( + + + {`Log - ${(pageProps as any).metadata?.APP_NAME}`} + + + , + pageProps, + ); +} + +function parseRecordData(record: any) { + if (!record) return null; + if (record?.format === "xml") { + return record.data || record.response || record.error; + } + + return failsafe( + () => jsonify(record.data || record.response || record.error), + record.data || record.response || record.error, + ); +} diff --git a/packages/core/modules/Developers/logs.tsx b/packages/core/modules/Developers/logs.tsx new file mode 100644 index 0000000000..a1d6133782 --- /dev/null +++ b/packages/core/modules/Developers/logs.tsx @@ -0,0 +1,246 @@ +import { + formatDateTimeLong, + getURLSearchParams, + isNone, + isNoneOrEmpty, +} from "@karrio/lib"; +import { LogPreview, LogPreviewContext } from "../../components/log-preview"; +import { StatusCode } from "@karrio/ui/components/status-code-badge"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { LogsFilter } from "@karrio/ui/filters/logs-filter"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useLoader } from "@karrio/ui/components/loader"; +import { Spinner } from "@karrio/ui/components/spinner"; +import React, { useContext, useEffect } from "react"; +import { useRouter } from "next/dist/client/router"; +import { useLogs } from "@karrio/hooks/log"; +import Head from "next/head"; +import { AppLink } from "@karrio/ui/components/app-link"; + +export { getServerSideProps } from "../../context/main"; + +export default function LogsPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const { setLoading } = useLoader(); + const { previewLog } = useContext(LogPreviewContext); + const [initialized, setInitialized] = React.useState(false); + const context = useLogs(); + const { + query: { data: { logs } = {}, ...query }, + filter, + setFilter, + } = context; + + const updateFilter = (extra: any = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + previewLog(router.query.modal as string); + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+ Developers +
+ +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + API Keys + +
  • +
  • + + Webhooks + +
  • +
  • + + Events + +
  • +
  • + + Logs + +
  • +
+
+ + + + {!query.isFetched && } + + {query.isFetched && (logs?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + {(logs?.edges || []).map(({ node: log }) => ( + previewLog(log.id as any)} + > + + + + + ))} + +
+ STATUS + + DESCRIPTION +
+ + {`${log.method} ${log.path}`} + + {formatDateTimeLong(log.requested_at)} + +
+
+ +
+ + {(logs?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (logs?.edges || []).length == 0 && ( +
+
+

No API logs found.

+

+ Use the API to send shipping requests. +

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Logs - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Developers/webhooks.tsx b/packages/core/modules/Developers/webhooks.tsx new file mode 100644 index 0000000000..4349d54c92 --- /dev/null +++ b/packages/core/modules/Developers/webhooks.tsx @@ -0,0 +1,250 @@ +import { + WebhookTestModal, + useTestWebhookModal, +} from "@karrio/ui/modals/webhook-test-modal"; +import { + WebhookEditModal, + useWebhookModal, +} from "@karrio/ui/modals/webhook-edit-modal"; +import { + ConfirmModal, + ConfirmModalContext, +} from "@karrio/ui/modals/confirm-modal"; +import { useWebhookMutation, useWebhooks } from "@karrio/hooks/webhook"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { NotificationType, WebhookType } from "@karrio/types"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { formatDateTime, isNoneOrEmpty } from "@karrio/lib"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Notify } from "@karrio/ui/components/notifier"; +import { useRouter } from "next/dist/client/router"; +import { useContext, useEffect } from "react"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export default function WebhooksPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const { query } = useWebhooks(); + const mutation = useWebhookMutation(); + const { notify } = useContext(Notify); + const { editWebhook } = useWebhookModal(); + const { testWebhook } = useTestWebhookModal(); + const { confirm: confirmDeletion } = useContext(ConfirmModalContext); + const [initialized, setInitialized] = React.useState(false); + + const remove = (id: string) => async () => { + await mutation.deleteWebhook.mutateAsync({ id }); + }; + const toggle = + ({ disabled, id }: WebhookType) => + async () => { + try { + await mutation.updateWebhook.mutateAsync({ id, disabled: !disabled }); + notify({ + type: NotificationType.success, + message: `webhook ${!disabled ? "activated" : "deactivated"}!`, + }); + } catch (message: any) { + notify({ type: NotificationType.error, message }); + } + }; + + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + const webhook = query.data?.webhooks.edges.find( + (c) => c.node.id === router.query.modal, + ); + webhook && editWebhook({ webhook } as any); + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+ Developers +
+ +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + API Keys + +
  • +
  • + + Webhooks + +
  • +
  • + + Events + +
  • +
  • + + Logs + +
  • +
+
+ + {(query.data?.webhooks.edges || []).length > 0 && ( + <> +
+ + + + + + + + + + {(query.data?.webhooks.edges || []).map( + ({ node: webhook }) => ( + + + + + + + ), + )} + +
URLMODE + LAST EVENT +
editWebhook({ webhook })} + > + + {webhook.url} + + editWebhook({ webhook })} + > + + {webhook.test_mode ? "test" : "live"} + + editWebhook({ webhook })} + > + + {webhook.last_event_at + ? formatDateTime(webhook.last_event_at as any) + : "No recent event"} + + + + + +
+
+ + )} + + {query.isFetched && (query.data?.webhooks.edges || []).length == 0 && ( +
+
+

No webhooks added yet.

+

+ Use the Add Enpoint button above to add +

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + <> + + + {`Webhooks - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Home/index.tsx b/packages/core/modules/Home/index.tsx new file mode 100644 index 0000000000..638b4fa222 --- /dev/null +++ b/packages/core/modules/Home/index.tsx @@ -0,0 +1,492 @@ +import { Bar, BarChart, Tooltip, ResponsiveContainer, XAxis } from "recharts"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { SelectField } from "@karrio/ui/components"; +import { useAppMode } from "@karrio/hooks/app-mode"; +import { useAPIUsage } from "@karrio/hooks/usage"; +import { useUser } from "@karrio/hooks/user"; +import { useRouter } from "next/router"; +import Head from "next/head"; +import moment from "moment"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export default function ApiPage(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + const router = useRouter(); + const { basePath } = useAppMode(); + const { + query: { data: { user } = {} }, + } = useUser(); + const { + query: { data: { usage } = {} }, + setFilter, + filter, + USAGE_FILTERS, + DAYS_LIST, + currentFilter, + } = useAPIUsage(); + + return ( + <> +
+ {`Welcome${!!user?.full_name ? ", " + user.full_name : ""}`} +
+
+ + {/* Usage stats */} +
+ setFilter(JSON.parse(e.target.value))} + > + {Object.entries(USAGE_FILTERS).map(([key, value]) => ( + + ))} + + +
+
+
+
+
+
+ + + + Shipment +
+ +

+ Total shipments +

+

+ {!!usage ? usage?.total_shipments : 0} +

+ +
+ {!!usage?.shipment_count && ( + + ({ + name: _, + total: + usage!.shipment_count!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + > + + + + + + )} +
+
+
+
+
+
+ + + + Tracker +
+ +

+ Total trackers +

+

+ {!!usage ? usage?.total_trackers : 0} +

+ +
+ {!!usage?.tracker_count && ( + + ({ + name: _, + total: + usage!.tracker_count!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + > + + + + + + )} +
+
+
+
+
+ +
+
+
+
+
+ + + + Order +
+ +

+ Fullfilled orders volume +

+

+ + + + + {!!usage ? usage?.order_volume : 0} + +

+ +
+ {!!usage?.order_volumes && ( + + ({ + name: _, + value: + usage!.order_volumes!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + > + + + + + + )} +
+
+
+
+
+
+ + + + Spend +
+ +

+ Estimated shipping spend +

+

+ + + + + {!!usage ? usage?.total_shipping_spend : 0} + +

+ +
+ {!!usage?.shipping_spend && ( + + ({ + name: _, + value: + usage!.shipping_spend!.find( + ({ date }) => + moment(date).format("MMM D") === _, + )?.count || 0, + }), + )} + > + + + + + + )} +
+
+
+
+
+
+
+ + {/* Next actions */} +
Things to do next
+ + {!!usage?.unfulfilled_orders && usage.unfulfilled_orders > 0 && ( +
+ + + + + + {`${usage.unfulfilled_orders} order${usage.unfulfilled_orders > 1 ? "s" : ""} to fulfill`} + + +
+ )} + +
+
+
router.push(`${basePath}/settings/addresses`)} + > +
+ + + +
+

+ Add shipping location address +

+

+ Add one or multiple warehouse locations. +

+
+ + + +
+
+
+ +
+
router.push(`${basePath}/connections`)} + > +
+ + + +
+

+ Set up carrier accounts +

+

+ Connect your carrier accounts to start +

+
+ + + +
+
+
+ +
+
router.push(`/test/create_label?shipment_id=new`)} + > +
+ + + +
+

+ Print a test label +

+

+ Generate a test label for a sample shipment. +

+
+ + + +
+
+
+ +
+
router.push(`${basePath}/trackers?modal=new`)} + > +
+ + + +
+

+ Add a tracking number +

+

+ Add one or multiple shipments to track. +

+
+ + + +
+
+
+ +
+
router.push(`${basePath}/developers/apikeys`)} + > +
+ + + +
+

+ Set up an API connection +

+

+ Retrieve your API key to connect via API. +

+
+ + + +
+
+
+ +
+
router.push(`${basePath}/developers/logs`)} + > +
+ + + +
+

+ Review your API request +

+

+ Audit your API requests logs and system health. +

+
+ + + +
+
+
+
+ + ); + }; + + return AuthenticatedPage( + + + {`Home - ${references?.APP_NAME}`} + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Invitation/accept-invite.tsx b/packages/core/modules/Invitation/accept-invite.tsx similarity index 61% rename from apps/dashboard/src/modules/Invitation/accept-invite.tsx rename to packages/core/modules/Invitation/accept-invite.tsx index 4dad79cb61..7e6db5b3f9 100644 --- a/apps/dashboard/src/modules/Invitation/accept-invite.tsx +++ b/packages/core/modules/Invitation/accept-invite.tsx @@ -1,68 +1,85 @@ -import { useOrganizationInvitation } from "@karrio/hooks/organization"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { SectionLayout } from "@/layouts/section-layout"; -import { useRouter } from "next/dist/client/router"; -import { useSession } from "next-auth/react"; -import React, { useEffect } from "react"; -import { isNone } from "@karrio/lib"; -import Link from "next/link"; -import Head from "next/head"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function Page(pageProps: any) { - const { references } = useAPIMetadata(); - const { data: session } = useSession(); - const router = useRouter(); - const { token } = router.query; - const { query: { data: { organization_invitation } = {}, ...query } } = useOrganizationInvitation(token as string); - - useEffect(() => { - const called = query.isFetched; - const invite = organization_invitation; - - // If there is no active session and invitee doesn't exist, redirect to the signup page - if (called && isNone(session) && invite && !invite?.invitee) { - setTimeout(() => router.push(`/signup?email=${invite?.invitee_identifier}`), 1000); - return; - } - // If there is no active session and invitee exist, redirect to the login page - if (called && isNone(session) && invite && invite?.invitee) { - setTimeout(() => router.push(`/login?email=${invite?.invitee?.email}&next=/?accept_invitation=${token}`), 1000); - return; - } - // If there is an active session, redirect to the dashboard - if (called && !isNone(session) && invite) { - setTimeout(() => router.push(`/?accept_invitation=${token}`), 1000); - return; - } - }, [session, organization_invitation, token, router]); - - return ( - <> - - {`Accept Invitation - ${references.APP_NAME}`} - -
-
- - {!query.isFetched && query.isFetching && } - - {(query.isFetched && (query.error || !organization_invitation)) && -

Error, invalid or expired organization invitation token!

} - - {organization_invitation &&

Redirecting...

} - -
-
- - {query.error &&
- Return to Sign in -
} - -
- - ) -} +import { useOrganizationInvitation } from "@karrio/hooks/organization"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { SectionLayout } from "../../layouts/section-layout"; +import { useRouter } from "next/dist/client/router"; +import { useSession } from "next-auth/react"; +import React, { useEffect } from "react"; +import { isNone } from "@karrio/lib"; +import Link from "next/link"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/metadata"; + +export default function Page(pageProps: any) { + const { references } = useAPIMetadata(); + const { data: session } = useSession(); + const router = useRouter(); + const { token } = router.query; + const { + query: { data: { organization_invitation } = {}, ...query }, + } = useOrganizationInvitation(token as string); + + useEffect(() => { + const called = query.isFetched; + const invite = organization_invitation; + + // If there is no active session and invitee doesn't exist, redirect to the signup page + if (called && isNone(session) && invite && !invite?.invitee) { + setTimeout( + () => router.push(`/signup?email=${invite?.invitee_identifier}`), + 1000, + ); + return; + } + // If there is no active session and invitee exist, redirect to the login page + if (called && isNone(session) && invite && invite?.invitee) { + setTimeout( + () => + router.push( + `/login?email=${invite?.invitee?.email}&next=/?accept_invitation=${token}`, + ), + 1000, + ); + return; + } + // If there is an active session, redirect to the dashboard + if (called && !isNone(session) && invite) { + setTimeout(() => router.push(`/?accept_invitation=${token}`), 1000); + return; + } + }, [session, organization_invitation, token, router]); + + return ( + <> + + + {`Accept Invitation - ${references.APP_NAME}`} + + +
+
+ {!query.isFetched && query.isFetching && } + + {query.isFetched && (query.error || !organization_invitation) && ( +

Error, invalid or expired organization invitation token!

+ )} + + {organization_invitation &&

Redirecting...

} +
+
+ + {query.error && ( +
+ + Return to{" "} + + Sign in + + +
+ )} +
+ + ); +} diff --git a/apps/dashboard/src/modules/Labels/create_labels.tsx b/packages/core/modules/Labels/create_labels.tsx similarity index 98% rename from apps/dashboard/src/modules/Labels/create_labels.tsx rename to packages/core/modules/Labels/create_labels.tsx index edc415b5a7..ff475d7d39 100644 --- a/apps/dashboard/src/modules/Labels/create_labels.tsx +++ b/packages/core/modules/Labels/create_labels.tsx @@ -1,2434 +1,2434 @@ -import { - AddressType, - CURRENCY_OPTIONS, - CommodityType, - CustomsType, - DEFAULT_CUSTOMS_CONTENT, - MetadataObjectTypeEnum, - NotificationType, - OrderType, - PaidByEnum, - ShipmentType, -} from "@karrio/types"; -import { - createShipmentFromOrders, - formatAddressLocationShort, - formatRef, - formatWeight, - getShipmentCommodities, - isNone, - isNoneOrEmpty, - p, - useLocation, -} from "@karrio/lib"; -import { - CheckBoxField, - Dropdown, - InputField, - SelectField, - Spinner, - TextAreaField, -} from "@karrio/ui/components"; -import { - CommodityEditModalProvider, - CommodityStateContext, -} from "@karrio/ui/modals/commodity-edit-modal"; -import { - AddressModalEditor, - CustomsModalEditor, - ParcelModalEditor, -} from "@karrio/ui/modals/form-modals"; -import { - MetadataEditor, - MetadataEditorContext, -} from "@karrio/ui/forms/metadata-editor"; -import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; -import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; -import { CommodityDescription } from "@karrio/ui/components/commodity-description"; -import { MessagesDescription } from "@karrio/ui/components/messages-description"; -import { AddressDescription } from "@karrio/ui/components/address-description"; -import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; -import { ParcelDescription } from "@karrio/ui/components/parcel-description"; -import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; -import { RateDescription } from "@karrio/ui/components/rate-description"; -import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; -import { useCarrierConnections } from "@karrio/hooks/user-connection"; -import { useDefaultTemplates } from "@karrio/hooks/default-template"; -import { useBatchShipmentForm } from "@karrio/hooks/bulk-shipments"; -import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; -import { useConnections } from "@karrio/hooks/carrier-connections"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { closeDropdown } from "@karrio/ui/components/dropdown"; -import { useNotifier } from "@karrio/ui/components/notifier"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { useLoader } from "@karrio/ui/components/loader"; -import { AppLink } from "@karrio/ui/components/app-link"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Dialog, Disclosure } from "@headlessui/react"; -import { useShipments } from "@karrio/hooks/shipment"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useAppMode } from "@karrio/hooks/app-mode"; -import { useOrders } from "@karrio/hooks/order"; -import Image from "next/legacy/image"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ModalProvider]); - -export default function Page(pageProps: any) { - const { ORDERS_MANAGEMENT } = pageProps?.metadata; - - const Component: React.FC = () => { - // General context data ----------------------------------------------------------- - //#region - const loader = useLoader(); - const router = useLocation(); - const notifier = useNotifier(); - const { basePath } = useAppMode(); - const { references } = useAPIMetadata(); - const { carrierOptions } = useConnections(); - const workspace_config = useWorkspaceConfig(); - const { query: defaults } = useDefaultTemplates(); - const { order_ids = "", shipment_ids = "" } = router.query as any; - const [orderIds] = React.useState( - order_ids.split(",").filter((_) => !isNoneOrEmpty(_)), - ); - const [shipmentIds] = React.useState( - shipment_ids.split(",").filter((_) => !isNoneOrEmpty(_)), - ); - - const { - query: { data: { user_connections } = {} }, - } = useCarrierConnections(); - const { - query: { data: { system_connections } = {} }, - } = useSystemCarrierConnections(); - const ordersQuery = useOrders({ - id: orderIds, - isDisabled: orderIds.length === 0, - }); - const shipmentsQuery = useShipments({ - id: shipmentIds, - cacheKey: "labels", - isDisabled: shipmentIds.length === 0, - }); - const { - query: { data: { orders: orderList } = {} }, - } = ordersQuery; - const { - query: { data: { shipments: shipmentList } = {} }, - } = shipmentsQuery; - - const { batch, ...mutation } = useBatchShipmentForm({ - shipmentList: - shipmentIds.length === 0 - ? collectShipments(orderList) - : (shipmentList?.edges.map(({ node }) => node) as ShipmentType[]), - }); - - function collectShipments(current: typeof orderList) { - return (current?.edges || []).map(({ node: order }) => { - const shipment = - order.shipments.find(({ status }) => status === "draft") || - createShipmentFromOrders( - [order] as OrderType[], - defaults, - workspace_config, - ); - - return shipment as ShipmentType; - }); - } - - //#endregion - - // Bulk shipment context data ----------------------------------------------------------- - //#region - - const [ready, setReady] = React.useState(false); - const [allChecked, setAllChecked] = React.useState(false); - const [selection, setSelection] = React.useState([]); - - const _shipmentsOrdersQuery = useOrders({ - order_id: (shipmentList?.edges || []) - .map(({ node }) => - (node.meta?.order_id || node.metadata?.order_ids || "").split(","), - ) - .flat() - .filter((_) => !isNoneOrEmpty(_)), - isDisabled: - orderIds.length === 0 && - (shipmentList?.edges || []) - .map(({ node }) => - (node.meta?.order_id || node.metadata?.order_ids || "").split(","), - ) - .flat() - .filter((_) => !isNoneOrEmpty(_)).length === 0, - }); - const shipmentsOrdersQuery = - orderIds.length > 0 ? ordersQuery : _shipmentsOrdersQuery; - const { - query: { data: { orders: shipmentOrderList } = {} }, - } = shipmentsOrdersQuery; - - const handleSelection = (e: React.ChangeEvent) => { - const { checked, name } = e.target as HTMLInputElement; - if (name === "all") { - setSelection( - !checked ? [] : (batch.shipments || []).map(({ id }) => id as string), - ); - } else { - setSelection( - checked - ? [...selection, name] - : selection.filter((id) => id !== name), - ); - } - }; - const updatedSelection = ( - selectedShipments: string[], - current: typeof batch.shipments, - ) => { - const shipment_ids = (current || []).map(({ id }) => id); - const selection = selectedShipments.filter((id) => - shipment_ids.includes(id), - ); - const selected = - selection.length > 0 && - selection.length === (shipment_ids || []).length; - setAllChecked(selected); - if ( - selectedShipments.filter((id) => !shipment_ids.includes(id)).length > 0 - ) { - setSelection(selection); - } - }; - const retrieveShipment = (shipments: ShipmentType[], selected?: string) => { - const shipment_index = shipments.findIndex( - ({ id }, index) => `${id || index}` === `${selected}`, - ); - const shipment = shipments[shipment_index]; - const ShipmentEditor: React.FC = - !!shipment && !isNone(shipment_index) - ? ( - f: (props: { - shipment: ShipmentType; - shipment_index: number; - }) => any, - ) => f({ shipment, shipment_index }) - : () => <>; - return ShipmentEditor; - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - try { - await mutation.buyLabels.mutateAsync(); - router.push( - `${basePath}${location.pathname.replace(basePath, "").replace("create_labels", "")}`, - ); - } catch (message: any) { - notifier.notify({ type: NotificationType.error, message }); - } - loader.setLoading(false); - }; - - //#endregion - - // Shipment editor context data ----------------------------------------------------------- - //#region - - const [isOpen, setIsOpen] = React.useState(false); - const [keys, setKeys] = React.useState<{ [code: number]: string }>({}); - const [selectedRow, setSelectedRow] = React.useState(); - const [addReturn, setAddReturn] = React.useState(false); - - const requireInfoForRating = (shipment: ShipmentType) => { - return ( - shipment.recipient.address_line1 === undefined || - shipment.shipper.address_line1 === undefined || - shipment.parcels.length === 0 || - shipmentsQuery.query.isFetching === true - ); - }; - const isInternational = (shipment: ShipmentType) => { - return ( - shipment.recipient.country_code !== undefined && - shipment.shipper.country_code !== undefined && - shipment.recipient.country_code !== shipment.shipper.country_code - ); - }; - const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { - const item = getShipmentCommodities(shipment).find( - (item) => - (!!cdt.parent_id && cdt.parent_id === item.parent_id) || - (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || - (!!cdt.sku && cdt.sku === item.sku), - ); - return !!item; - }; - const getCarrier = (rate: ShipmentType["rates"][0]) => - !!rate && - (user_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - ) || - system_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - )); - const toggle = (id: string) => (e: React.MouseEvent) => { - e.preventDefault(); - setSelectedRow(id); - setIsOpen(true); - }; - const onClose = () => { - setSelectedRow(undefined); - setIsOpen(false); - }; - const selectedRate = (shipment) => - (shipment?.rates || []).find( - (_) => _.service === shipment?.options?.preferred_service, - ) || (shipment?.rates || [])[0]; - const getItems = (orders: OrderType[]) => { - return (orders || []).map(({ line_items }) => line_items).flat(); - }; - const getParent = (orders: OrderType[], id: string | null) => { - return getItems(orders).find((item) => item.id === id); - }; - const getOrder = (orders: OrderType[], item_id?: string | null) => { - return (orders || []).find((order) => - order.line_items.find((item) => item.id === item_id), - ); - }; - const getAvailableQuantity = ( - shipment: ShipmentType, - orders: OrderType[], - item: CommodityType, - item_index: number, - ) => { - const parent_quantity = - getParent(orders, item.parent_id)?.unfulfilled_quantity || 0; - const packed_quantity = shipment.parcels - .map(({ items }) => items || []) - .flat() - .filter((_, index) => index !== item_index) - .reduce((acc, { parent_id, quantity }) => { - return parent_id === item.parent_id ? acc + (quantity as number) : 0; - }, 0); - - return parent_quantity - packed_quantity; - }; - const onChange = async ( - shipment_index: number, - shipment: ShipmentType, - changes: Partial, - ) => { - if (changes === undefined) { - return; - } - await mutation.updateShipment(shipment_index)({ - id: shipment.id, - ...changes, - }); - setKeys({ - ...keys, - [shipment_index]: `${shipment.id || shipment_index}-${new Date()}`, - }); - }; - - //#endregion - - React.useEffect(() => { - updatedSelection(selection, batch.shipments); - }, [selection, batch.shipments]); - React.useEffect(() => { - if (ready === true) return; - if (batch.shipments.length === 0) return; - if (workspace_config.query.isLoading) return; - if (orderIds.length > 0 && ordersQuery.query.isLoading) return; - if (shipmentIds.length > 0 && shipmentsQuery.query.isLoading) return; - if (shipmentsOrdersQuery.query.isLoading) return; - - setReady(true); - setKeys( - batch.shipments.reduce( - (acc, shipment, index) => ({ - ...acc, - [index]: `${shipment.id || index}-${new Date()}`, - }), - {}, - ), - ); - }, [ - ready, - orderIds, - shipmentIds, - batch.shipments, - ordersQuery.query.isLoading, - shipmentsQuery.query.isLoading, - workspace_config.query.isLoading, - shipmentsOrdersQuery.query.isLoading, - ]); - - return ( -
-
-
- - - - - - - Create shipping labels - -
-
- -
-
- -
- - {!ready && } - - {ready && Object.keys(keys).length > 0 && ( - <> - {/* Error & messages */} - {batch.shipments.map((_) => _.messages || []).flat().length > 0 && ( - <> -
- {batch.shipments - .filter((_) => (_.messages || []).length > 0) - .map((shipment, index) => ( - - -
- -
- ))} -
- - )} - - {/* Batch labels table */} - <> -
- - - - - - - - - - - - {batch.shipments.map((shipment, shipment_index) => ( - - - - - - - - - ))} - -
e.preventDefault()} - > - - Order #ItemsPackageTotal weightShipping service
- - - - - - - - - - - - {/* Dropdown trigger */} - - - {/* Dropdown content */} -
closeDropdown(e.target)} - id="dropdown-menu" - role="menu" - style={{ maxHeight: "20rem", overflowY: "auto" }} - > - {(shipment.rates || []).map((rate) => ( - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - preferred_service: rate.service, - }, - }) - } - > -
- -
- -
-
-
- ))} -
-
-
-
- - - {/* Label editor */} - <> - - -
- {retrieveShipment( - batch.shipments, - selectedRow, - )(({ shipment, shipment_index }) => { - const shipmentOrderIds = ( - shipment.meta?.order_id || - shipment.metadata?.order_ids || - "" - ) - .split(",") - .filter((_) => !isNoneOrEmpty(_)); - const orders = (shipmentOrderList?.edges || []) - .filter(({ node }) => - shipmentOrderIds.includes(node.order_id), - ) - .map(({ node }) => node) as OrderType[]; - - return ( - - - -
-
- - {`#${shipment.meta?.order_id || shipment.metadata?.order_ids || " - "}`} - -
- - {formatAddressLocationShort( - shipment.recipient as AddressType, - )} - -
-
- -
-
-
- -
- {/* Address section */} -
-
-
- - Customer - -
- - onChange(shipment_index, shipment, { - recipient: address, - }) - } - trigger={ - - } - /> -
-
- - - - {Object.values(shipment.recipient || {}) - .length === 0 && ( - <> -
- Please add a customer address. -
- - )} -
- -
- -
-
- - Ship from - -
- - onChange(shipment_index, shipment, { - shipper: address, - }) - } - trigger={ - - } - /> -
-
- - - - {Object.values(shipment.shipper || {}) - .length === 0 && ( - <> -
- Please specify an address. -
- - )} -
- - {/* Retrun address section */} -
- -
-
-
- { - setAddReturn(e.target.checked); - if ( - !e.target.checked && - !isNone(shipment.return_address) - ) { - onChange( - shipment_index, - shipment, - { - return_address: null, - }, - ); - } - }} - > - - Add a return address (optional) - - -
-
- {(addReturn || - !isNone(shipment.return_address)) && ( - - onChange( - shipment_index, - shipment, - { - return_address: address, - }, - ) - } - trigger={ - - } - /> - )} -
-
- -
- {shipment?.return_address && ( - - )} -
- - {Object.values( - shipment?.return_address || [], - ).length === 0 && ( -
- - Use this to specify an origin address - different from the shipper address - above.
- This address will be used for pickup - and return. -
-
- )} -
- - {/* Billing address section */} -
- -
- - -
- - - -
- - {shipment.payment?.paid_by && - shipment.payment?.paid_by !== - PaidByEnum.sender && ( -
- - onChange( - shipment_index, - shipment, - { - payment: { - ...shipment.payment, - account_number: - e.target.value, - }, - }, - ) - } - /> -
- )} -
- - {(shipment?.billing_address || - shipment.payment?.paid_by === - PaidByEnum.third_party) && ( - <> -
-
- -
- - onChange( - shipment_index, - shipment, - { billing_address: address }, - ) - } - trigger={ - - } - /> -
-
- - {shipment?.billing_address && ( - - )} - - {isNone(shipment?.billing_address) && ( -
- Add shipment billing address. - (optional) -
- )} -
- - )} -
- - {/* Parcel & Items section */} -
-
- - PACKAGES - -
- - Add package - - } - /> -
-
- -
- - {shipment.parcels.map((pkg, pkg_index) => ( - - {pkg_index > 0 && ( -
- )} - -
- {/* Parcel header */} -
-
- - {pkg_index + 1} - - } - /> -
-
- - - - - - } - /> - -
-
- - {/* Items section */} - - ITEMS - - - {(pkg.items || []).map( - (item, item_index) => ( - -
-
-
-

- {item_index + 1}{" "} - {`${item.title || item.description || "Item"}`} -

-

- {isNoneOrEmpty(item.sku) - ? "SKU: 0000000" - : `SKU: ${item.sku}`} - {getOrder( - orders, - item.parent_id, - ) && ( - - {` | ORDER: ${getOrder(orders, item.parent_id)?.order_id}`} - - )} -

-

-
-
-
- - {formatWeight(item)} - -
-

- { - mutation.updateItem( - shipment_index, - )( - pkg_index, - item_index, - pkg.id, - )({ - quantity: - parseInt( - e.target - .value, - ), - } as CommodityType); - }} - className="input is-small" - style={{ - width: "60px", - textAlign: "center", - }} - {...(getParent( - orders, - item.parent_id, - ) - ? { - max: getAvailableQuantity( - shipment, - orders, - item, - item_index, - ), - } - : {})} - /> -

- {getParent( - orders, - item.parent_id, - ) && ( -

- - of{" "} - {getParent( - orders, - item.parent_id, - ) - ?.unfulfilled_quantity || - item.quantity} - -

- )} -
-
- - {({ editCommodity }) => ( - - )} - - -
-
-
- ), - )} - - {(pkg.items || []).length === 0 && ( -
- You can specify content items. -
- )} - -
- - {({ editCommodity }) => ( - - )} - -
-
-
- ))} - - {(shipment.parcels || []).length === 0 && ( -
- Add one or more packages to create a - shipment. -
- )} -
- - {/* Shipping options section */} -
-
- - Shipping options - -
- -
- -
- {/* shipment date */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - shipment_date: e.target.value, - }, - }) - } - /> - - {/* currency */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - currency: e.target.value, - }, - }) - } - > - - {CURRENCY_OPTIONS.map((unit) => ( - - ))} - - - {/* signature confirmation */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - signature_confirmation: - e.target.checked || null, - }, - }) - } - > - Add signature confirmation - - - {/* insurance */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - insurance: - e.target.checked === true - ? "" - : null, - }, - }) - } - > - Add insurance coverage - - -
- - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - insurance: parseFloat( - e.target.value, - ), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Cash on delivery */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - cash_on_delivery: - e.target.checked === true - ? "" - : null, - }, - }) - } - > - Collect on delivery - - -
- - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - cash_on_delivery: parseFloat( - e.target.value, - ), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Declared value */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - declared_value: - e.target.checked === true - ? "" - : null, - }, - }) - } - > - Add package value - - -
- - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - declared_value: parseFloat( - e.target.value, - ), - }, - }) - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* paperless trade */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - paperless_trade: e.target.checked, - }, - }) - } - > - Paperless trade - - - {/* hold at location */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - hold_at_location: e.target.checked, - }, - }) - } - > - Hold at location - - - {/* dangerous good */} - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - dangerous_good: e.target.checked, - }, - }) - } - > - Dangerous good - -
- - {/* CARRIER OPTIONS SECTION */} - {Object.keys(carrierOptions).length > 0 && ( -
- - {({ open }) => ( -
- -
- CARRIER SPECIFIC OPTIONS -
- - {open ? ( - - ) : ( - - )} - -
- - {Object.entries( - carrierOptions, - ).map(([carrier, options]) => ( - - -
- -
- {options.map( - (option, index) => ( - - {references!.options[ - carrier - ][option]?.type === - "boolean" && ( -
- - onChange( - shipment_index, - shipment, - { - options: { - ...shipment.options, - [option]: - e - .target - .checked || - null, - }, - }, - ) - } - > - - {formatRef( - option, - )} - - -
- )} - - {references!.options[ - carrier - ][option]?.type === - "string" && ( - <> - - onChange( - shipment_index, - shipment, - { - options: { - ...shipment.options, - [option]: - e - .target - .value, - }, - }, - ) - } - /> - - )} -
- ), - )} -
- -
-
- ))} -
-
- )} -
-
- )} - -
- -
- - onChange(shipment_index, shipment, { - reference: e.target.value as string, - }) - } - placeholder="shipment reference" - className="is-small" - autoComplete="off" - /> -
-
- - {/* Customs declaration section */} - {isInternational(shipment) && ( -
-
- - CUSTOMS DECLARATION - -
- - Edit customs info - - } - /> -
-
- -
- -
- {!isNone(shipment.customs) && ( - <> - - - {/* Commodities section */} - - COMMODITIES - - - {( - shipment.customs!.commodities || [] - ).map((commodity, index) => ( - -
-
- -
- - {({ editCommodity }) => ( - - )} - - -
-
-
- ))} - - {(shipment.customs!.commodities || []) - .length === 0 && ( -
- You need provide commodity items - for customs purpose. (required) -
- )} - -
- - {({ editCommodity }) => ( - - )} - - {ORDERS_MANAGEMENT && ( - - mutation.addCommodities( - _ as any, - ) - } - /> - )} -
- - {/* Duty Billing address section */} - {(shipment.customs! - .duty_billing_address || - shipment.customs!.duty?.paid_by === - PaidByEnum.third_party) && ( - <> -
- -
-
- -
- - mutation.updateShipment( - shipment_index, - )({ - customs: { - ...shipment! - .customs, - duty_billing_address: - address, - } as any, - }) - } - trigger={ - - } - /> -
-
- - {shipment!.customs! - .duty_billing_address && ( - - )} - - {isNone( - shipment!.customs! - .duty_billing_address, - ) && ( -
- Add customs duty billing - address. (optional) -
- )} -
- - )} - - )} - - {isNone(shipment.customs) && ( -
- Looks like you have an international - shipment. You may need to provide a - customs declaration unless you are - shipping documents only. -
- )} -
-
- )} - - {/* Shipment Summary */} - - - {/* Metadata section */} -
-
- - onChange(shipment_index, shipment, { - metadata, - }) - } - > - - {({ isEditing, editMetadata }) => ( - <> -
- - METADATA - -
- -
-
- - )} -
-
-
-
- - {/* Instructions section */} -
-
- - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - instructions: e.target.value, - }, - }) - } - /> -
-
-
- -
- - {/* Service section */} -
-
-
- - Service - -
-
- -
-
- - {/* Shipping service */} - - {/* Dropdown trigger */} - - - {/* Dropdown content */} -
closeDropdown(e.target)} - id="dropdown-menu" - role="menu" - style={{ - maxHeight: "20rem", - overflowY: "auto", - }} - > - {(shipment.rates || []).map((rate) => ( - - onChange(shipment_index, shipment, { - options: { - ...shipment.options, - preferred_service: rate.service, - }, - }) - } - > -
- - -
-
- ))} -
-
-
-
-
-
- ); - })} -
-
-
- - - )} -
- ); - }; - - return AuthenticatedPage( - <> - - - {`Create labels - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - , - pageProps, - ); -} +import { + AddressType, + CURRENCY_OPTIONS, + CommodityType, + CustomsType, + DEFAULT_CUSTOMS_CONTENT, + MetadataObjectTypeEnum, + NotificationType, + OrderType, + PaidByEnum, + ShipmentType, +} from "@karrio/types"; +import { + createShipmentFromOrders, + formatAddressLocationShort, + formatRef, + formatWeight, + getShipmentCommodities, + isNone, + isNoneOrEmpty, + p, + useLocation, +} from "@karrio/lib"; +import { + CheckBoxField, + Dropdown, + InputField, + SelectField, + Spinner, + TextAreaField, +} from "@karrio/ui/components"; +import { + CommodityEditModalProvider, + CommodityStateContext, +} from "@karrio/ui/modals/commodity-edit-modal"; +import { + AddressModalEditor, + CustomsModalEditor, + ParcelModalEditor, +} from "@karrio/ui/modals/form-modals"; +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { MessagesDescription } from "@karrio/ui/components/messages-description"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { ParcelDescription } from "@karrio/ui/components/parcel-description"; +import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; +import { RateDescription } from "@karrio/ui/components/rate-description"; +import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { useDefaultTemplates } from "@karrio/hooks/default-template"; +import { useBatchShipmentForm } from "@karrio/hooks/bulk-shipments"; +import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; +import { useConnections } from "@karrio/hooks/carrier-connections"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { closeDropdown } from "@karrio/ui/components/dropdown"; +import { useNotifier } from "@karrio/ui/components/notifier"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Dialog, Disclosure } from "@headlessui/react"; +import { useShipments } from "@karrio/hooks/shipment"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useAppMode } from "@karrio/hooks/app-mode"; +import { useOrders } from "@karrio/hooks/order"; +import Image from "next/legacy/image"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ModalProvider]); + +export default function Page(pageProps: any) { + const { ORDERS_MANAGEMENT } = pageProps?.metadata; + + const Component: React.FC = () => { + // General context data ----------------------------------------------------------- + //#region + const loader = useLoader(); + const router = useLocation(); + const notifier = useNotifier(); + const { basePath } = useAppMode(); + const { references } = useAPIMetadata(); + const { carrierOptions } = useConnections(); + const workspace_config = useWorkspaceConfig(); + const { query: defaults } = useDefaultTemplates(); + const { order_ids = "", shipment_ids = "" } = router.query as any; + const [orderIds] = React.useState( + order_ids.split(",").filter((_) => !isNoneOrEmpty(_)), + ); + const [shipmentIds] = React.useState( + shipment_ids.split(",").filter((_) => !isNoneOrEmpty(_)), + ); + + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const ordersQuery = useOrders({ + id: orderIds, + isDisabled: orderIds.length === 0, + }); + const shipmentsQuery = useShipments({ + id: shipmentIds, + cacheKey: "labels", + isDisabled: shipmentIds.length === 0, + }); + const { + query: { data: { orders: orderList } = {} }, + } = ordersQuery; + const { + query: { data: { shipments: shipmentList } = {} }, + } = shipmentsQuery; + + const { batch, ...mutation } = useBatchShipmentForm({ + shipmentList: + shipmentIds.length === 0 + ? collectShipments(orderList) + : (shipmentList?.edges.map(({ node }) => node) as ShipmentType[]), + }); + + function collectShipments(current: typeof orderList) { + return (current?.edges || []).map(({ node: order }) => { + const shipment = + order.shipments.find(({ status }) => status === "draft") || + createShipmentFromOrders( + [order] as OrderType[], + defaults, + workspace_config, + ); + + return shipment as ShipmentType; + }); + } + + //#endregion + + // Bulk shipment context data ----------------------------------------------------------- + //#region + + const [ready, setReady] = React.useState(false); + const [allChecked, setAllChecked] = React.useState(false); + const [selection, setSelection] = React.useState([]); + + const _shipmentsOrdersQuery = useOrders({ + order_id: (shipmentList?.edges || []) + .map(({ node }) => + (node.meta?.order_id || node.metadata?.order_ids || "").split(","), + ) + .flat() + .filter((_) => !isNoneOrEmpty(_)), + isDisabled: + orderIds.length === 0 && + (shipmentList?.edges || []) + .map(({ node }) => + (node.meta?.order_id || node.metadata?.order_ids || "").split(","), + ) + .flat() + .filter((_) => !isNoneOrEmpty(_)).length === 0, + }); + const shipmentsOrdersQuery = + orderIds.length > 0 ? ordersQuery : _shipmentsOrdersQuery; + const { + query: { data: { orders: shipmentOrderList } = {} }, + } = shipmentsOrdersQuery; + + const handleSelection = (e: React.ChangeEvent) => { + const { checked, name } = e.target as HTMLInputElement; + if (name === "all") { + setSelection( + !checked ? [] : (batch.shipments || []).map(({ id }) => id as string), + ); + } else { + setSelection( + checked + ? [...selection, name] + : selection.filter((id) => id !== name), + ); + } + }; + const updatedSelection = ( + selectedShipments: string[], + current: typeof batch.shipments, + ) => { + const shipment_ids = (current || []).map(({ id }) => id); + const selection = selectedShipments.filter((id) => + shipment_ids.includes(id), + ); + const selected = + selection.length > 0 && + selection.length === (shipment_ids || []).length; + setAllChecked(selected); + if ( + selectedShipments.filter((id) => !shipment_ids.includes(id)).length > 0 + ) { + setSelection(selection); + } + }; + const retrieveShipment = (shipments: ShipmentType[], selected?: string) => { + const shipment_index = shipments.findIndex( + ({ id }, index) => `${id || index}` === `${selected}`, + ); + const shipment = shipments[shipment_index]; + const ShipmentEditor: React.FC = + !!shipment && !isNone(shipment_index) + ? ( + f: (props: { + shipment: ShipmentType; + shipment_index: number; + }) => any, + ) => f({ shipment, shipment_index }) + : () => <>; + return ShipmentEditor; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + await mutation.buyLabels.mutateAsync(); + router.push( + `${basePath}${location.pathname.replace(basePath, "").replace("create_labels", "")}`, + ); + } catch (message: any) { + notifier.notify({ type: NotificationType.error, message }); + } + loader.setLoading(false); + }; + + //#endregion + + // Shipment editor context data ----------------------------------------------------------- + //#region + + const [isOpen, setIsOpen] = React.useState(false); + const [keys, setKeys] = React.useState<{ [code: number]: string }>({}); + const [selectedRow, setSelectedRow] = React.useState(); + const [addReturn, setAddReturn] = React.useState(false); + + const requireInfoForRating = (shipment: ShipmentType) => { + return ( + shipment.recipient.address_line1 === undefined || + shipment.shipper.address_line1 === undefined || + shipment.parcels.length === 0 || + shipmentsQuery.query.isFetching === true + ); + }; + const isInternational = (shipment: ShipmentType) => { + return ( + shipment.recipient.country_code !== undefined && + shipment.shipper.country_code !== undefined && + shipment.recipient.country_code !== shipment.shipper.country_code + ); + }; + const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { + const item = getShipmentCommodities(shipment).find( + (item) => + (!!cdt.parent_id && cdt.parent_id === item.parent_id) || + (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || + (!!cdt.sku && cdt.sku === item.sku), + ); + return !!item; + }; + const getCarrier = (rate: ShipmentType["rates"][0]) => + !!rate && + (user_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + )); + const toggle = (id: string) => (e: React.MouseEvent) => { + e.preventDefault(); + setSelectedRow(id); + setIsOpen(true); + }; + const onClose = () => { + setSelectedRow(undefined); + setIsOpen(false); + }; + const selectedRate = (shipment) => + (shipment?.rates || []).find( + (_) => _.service === shipment?.options?.preferred_service, + ) || (shipment?.rates || [])[0]; + const getItems = (orders: OrderType[]) => { + return (orders || []).map(({ line_items }) => line_items).flat(); + }; + const getParent = (orders: OrderType[], id: string | null) => { + return getItems(orders).find((item) => item.id === id); + }; + const getOrder = (orders: OrderType[], item_id?: string | null) => { + return (orders || []).find((order) => + order.line_items.find((item) => item.id === item_id), + ); + }; + const getAvailableQuantity = ( + shipment: ShipmentType, + orders: OrderType[], + item: CommodityType, + item_index: number, + ) => { + const parent_quantity = + getParent(orders, item.parent_id)?.unfulfilled_quantity || 0; + const packed_quantity = shipment.parcels + .map(({ items }) => items || []) + .flat() + .filter((_, index) => index !== item_index) + .reduce((acc, { parent_id, quantity }) => { + return parent_id === item.parent_id ? acc + (quantity as number) : 0; + }, 0); + + return parent_quantity - packed_quantity; + }; + const onChange = async ( + shipment_index: number, + shipment: ShipmentType, + changes: Partial, + ) => { + if (changes === undefined) { + return; + } + await mutation.updateShipment(shipment_index)({ + id: shipment.id, + ...changes, + }); + setKeys({ + ...keys, + [shipment_index]: `${shipment.id || shipment_index}-${new Date()}`, + }); + }; + + //#endregion + + React.useEffect(() => { + updatedSelection(selection, batch.shipments); + }, [selection, batch.shipments]); + React.useEffect(() => { + if (ready === true) return; + if (batch.shipments.length === 0) return; + if (workspace_config.query.isLoading) return; + if (orderIds.length > 0 && ordersQuery.query.isLoading) return; + if (shipmentIds.length > 0 && shipmentsQuery.query.isLoading) return; + if (shipmentsOrdersQuery.query.isLoading) return; + + setReady(true); + setKeys( + batch.shipments.reduce( + (acc, shipment, index) => ({ + ...acc, + [index]: `${shipment.id || index}-${new Date()}`, + }), + {}, + ), + ); + }, [ + ready, + orderIds, + shipmentIds, + batch.shipments, + ordersQuery.query.isLoading, + shipmentsQuery.query.isLoading, + workspace_config.query.isLoading, + shipmentsOrdersQuery.query.isLoading, + ]); + + return ( +
+
+
+ + + + + + + Create shipping labels + +
+
+ +
+
+ +
+ + {!ready && } + + {ready && Object.keys(keys).length > 0 && ( + <> + {/* Error & messages */} + {batch.shipments.map((_) => _.messages || []).flat().length > 0 && ( + <> +
+ {batch.shipments + .filter((_) => (_.messages || []).length > 0) + .map((shipment, index) => ( + + +
+ +
+ ))} +
+ + )} + + {/* Batch labels table */} + <> +
+ + + + + + + + + + + + {batch.shipments.map((shipment, shipment_index) => ( + + + + + + + + + ))} + +
e.preventDefault()} + > + + Order #ItemsPackageTotal weightShipping service
+ + + + + + + + + + + + {/* Dropdown trigger */} + + + {/* Dropdown content */} +
closeDropdown(e.target)} + id="dropdown-menu" + role="menu" + style={{ maxHeight: "20rem", overflowY: "auto" }} + > + {(shipment.rates || []).map((rate) => ( + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + preferred_service: rate.service, + }, + }) + } + > +
+ +
+ +
+
+
+ ))} +
+
+
+
+ + + {/* Label editor */} + <> + + +
+ {retrieveShipment( + batch.shipments, + selectedRow, + )(({ shipment, shipment_index }) => { + const shipmentOrderIds = ( + shipment.meta?.order_id || + shipment.metadata?.order_ids || + "" + ) + .split(",") + .filter((_) => !isNoneOrEmpty(_)); + const orders = (shipmentOrderList?.edges || []) + .filter(({ node }) => + shipmentOrderIds.includes(node.order_id), + ) + .map(({ node }) => node) as OrderType[]; + + return ( + + + +
+
+ + {`#${shipment.meta?.order_id || shipment.metadata?.order_ids || " - "}`} + +
+ + {formatAddressLocationShort( + shipment.recipient as AddressType, + )} + +
+
+ +
+
+
+ +
+ {/* Address section */} +
+
+
+ + Customer + +
+ + onChange(shipment_index, shipment, { + recipient: address, + }) + } + trigger={ + + } + /> +
+
+ + + + {Object.values(shipment.recipient || {}) + .length === 0 && ( + <> +
+ Please add a customer address. +
+ + )} +
+ +
+ +
+
+ + Ship from + +
+ + onChange(shipment_index, shipment, { + shipper: address, + }) + } + trigger={ + + } + /> +
+
+ + + + {Object.values(shipment.shipper || {}) + .length === 0 && ( + <> +
+ Please specify an address. +
+ + )} +
+ + {/* Retrun address section */} +
+ +
+
+
+ { + setAddReturn(e.target.checked); + if ( + !e.target.checked && + !isNone(shipment.return_address) + ) { + onChange( + shipment_index, + shipment, + { + return_address: null, + }, + ); + } + }} + > + + Add a return address (optional) + + +
+
+ {(addReturn || + !isNone(shipment.return_address)) && ( + + onChange( + shipment_index, + shipment, + { + return_address: address, + }, + ) + } + trigger={ + + } + /> + )} +
+
+ +
+ {shipment?.return_address && ( + + )} +
+ + {Object.values( + shipment?.return_address || [], + ).length === 0 && ( +
+ + Use this to specify an origin address + different from the shipper address + above.
+ This address will be used for pickup + and return. +
+
+ )} +
+ + {/* Billing address section */} +
+ +
+ + +
+ + + +
+ + {shipment.payment?.paid_by && + shipment.payment?.paid_by !== + PaidByEnum.sender && ( +
+ + onChange( + shipment_index, + shipment, + { + payment: { + ...shipment.payment, + account_number: + e.target.value, + }, + }, + ) + } + /> +
+ )} +
+ + {(shipment?.billing_address || + shipment.payment?.paid_by === + PaidByEnum.third_party) && ( + <> +
+
+ +
+ + onChange( + shipment_index, + shipment, + { billing_address: address }, + ) + } + trigger={ + + } + /> +
+
+ + {shipment?.billing_address && ( + + )} + + {isNone(shipment?.billing_address) && ( +
+ Add shipment billing address. + (optional) +
+ )} +
+ + )} +
+ + {/* Parcel & Items section */} +
+
+ + PACKAGES + +
+ + Add package + + } + /> +
+
+ +
+ + {shipment.parcels.map((pkg, pkg_index) => ( + + {pkg_index > 0 && ( +
+ )} + +
+ {/* Parcel header */} +
+
+ + {pkg_index + 1} + + } + /> +
+
+ + + + + + } + /> + +
+
+ + {/* Items section */} + + ITEMS + + + {(pkg.items || []).map( + (item, item_index) => ( + +
+
+
+

+ {item_index + 1}{" "} + {`${item.title || item.description || "Item"}`} +

+

+ {isNoneOrEmpty(item.sku) + ? "SKU: 0000000" + : `SKU: ${item.sku}`} + {getOrder( + orders, + item.parent_id, + ) && ( + + {` | ORDER: ${getOrder(orders, item.parent_id)?.order_id}`} + + )} +

+

+
+
+
+ + {formatWeight(item)} + +
+

+ { + mutation.updateItem( + shipment_index, + )( + pkg_index, + item_index, + pkg.id, + )({ + quantity: + parseInt( + e.target + .value, + ), + } as CommodityType); + }} + className="input is-small" + style={{ + width: "60px", + textAlign: "center", + }} + {...(getParent( + orders, + item.parent_id, + ) + ? { + max: getAvailableQuantity( + shipment, + orders, + item, + item_index, + ), + } + : {})} + /> +

+ {getParent( + orders, + item.parent_id, + ) && ( +

+ + of{" "} + {getParent( + orders, + item.parent_id, + ) + ?.unfulfilled_quantity || + item.quantity} + +

+ )} +
+
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ), + )} + + {(pkg.items || []).length === 0 && ( +
+ You can specify content items. +
+ )} + +
+ + {({ editCommodity }) => ( + + )} + +
+
+
+ ))} + + {(shipment.parcels || []).length === 0 && ( +
+ Add one or more packages to create a + shipment. +
+ )} +
+ + {/* Shipping options section */} +
+
+ + Shipping options + +
+ +
+ +
+ {/* shipment date */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + shipment_date: e.target.value, + }, + }) + } + /> + + {/* currency */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + currency: e.target.value, + }, + }) + } + > + + {CURRENCY_OPTIONS.map((unit) => ( + + ))} + + + {/* signature confirmation */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + signature_confirmation: + e.target.checked || null, + }, + }) + } + > + Add signature confirmation + + + {/* insurance */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + insurance: + e.target.checked === true + ? "" + : null, + }, + }) + } + > + Add insurance coverage + + +
+ + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + insurance: parseFloat( + e.target.value, + ), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Cash on delivery */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + cash_on_delivery: + e.target.checked === true + ? "" + : null, + }, + }) + } + > + Collect on delivery + + +
+ + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + cash_on_delivery: parseFloat( + e.target.value, + ), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Declared value */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + declared_value: + e.target.checked === true + ? "" + : null, + }, + }) + } + > + Add package value + + +
+ + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + declared_value: parseFloat( + e.target.value, + ), + }, + }) + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* paperless trade */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + paperless_trade: e.target.checked, + }, + }) + } + > + Paperless trade + + + {/* hold at location */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + hold_at_location: e.target.checked, + }, + }) + } + > + Hold at location + + + {/* dangerous good */} + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + dangerous_good: e.target.checked, + }, + }) + } + > + Dangerous good + +
+ + {/* CARRIER OPTIONS SECTION */} + {Object.keys(carrierOptions).length > 0 && ( +
+ + {({ open }) => ( +
+ +
+ CARRIER SPECIFIC OPTIONS +
+ + {open ? ( + + ) : ( + + )} + +
+ + {Object.entries( + carrierOptions, + ).map(([carrier, options]) => ( + + +
+ +
+ {options.map( + (option, index) => ( + + {references!.options[ + carrier + ][option]?.type === + "boolean" && ( +
+ + onChange( + shipment_index, + shipment, + { + options: { + ...shipment.options, + [option]: + e + .target + .checked || + null, + }, + }, + ) + } + > + + {formatRef( + option, + )} + + +
+ )} + + {references!.options[ + carrier + ][option]?.type === + "string" && ( + <> + + onChange( + shipment_index, + shipment, + { + options: { + ...shipment.options, + [option]: + e + .target + .value, + }, + }, + ) + } + /> + + )} +
+ ), + )} +
+ +
+
+ ))} +
+
+ )} +
+
+ )} + +
+ +
+ + onChange(shipment_index, shipment, { + reference: e.target.value as string, + }) + } + placeholder="shipment reference" + className="is-small" + autoComplete="off" + /> +
+
+ + {/* Customs declaration section */} + {isInternational(shipment) && ( +
+
+ + CUSTOMS DECLARATION + +
+ + Edit customs info + + } + /> +
+
+ +
+ +
+ {!isNone(shipment.customs) && ( + <> + + + {/* Commodities section */} + + COMMODITIES + + + {( + shipment.customs!.commodities || [] + ).map((commodity, index) => ( + +
+
+ +
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ))} + + {(shipment.customs!.commodities || []) + .length === 0 && ( +
+ You need provide commodity items + for customs purpose. (required) +
+ )} + +
+ + {({ editCommodity }) => ( + + )} + + {ORDERS_MANAGEMENT && ( + + mutation.addCommodities( + _ as any, + ) + } + /> + )} +
+ + {/* Duty Billing address section */} + {(shipment.customs! + .duty_billing_address || + shipment.customs!.duty?.paid_by === + PaidByEnum.third_party) && ( + <> +
+ +
+
+ +
+ + mutation.updateShipment( + shipment_index, + )({ + customs: { + ...shipment! + .customs, + duty_billing_address: + address, + } as any, + }) + } + trigger={ + + } + /> +
+
+ + {shipment!.customs! + .duty_billing_address && ( + + )} + + {isNone( + shipment!.customs! + .duty_billing_address, + ) && ( +
+ Add customs duty billing + address. (optional) +
+ )} +
+ + )} + + )} + + {isNone(shipment.customs) && ( +
+ Looks like you have an international + shipment. You may need to provide a + customs declaration unless you are + shipping documents only. +
+ )} +
+
+ )} + + {/* Shipment Summary */} + + + {/* Metadata section */} +
+
+ + onChange(shipment_index, shipment, { + metadata, + }) + } + > + + {({ isEditing, editMetadata }) => ( + <> +
+ + METADATA + +
+ +
+
+ + )} +
+
+
+
+ + {/* Instructions section */} +
+
+ + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + instructions: e.target.value, + }, + }) + } + /> +
+
+
+ +
+ + {/* Service section */} +
+
+
+ + Service + +
+
+ +
+
+ + {/* Shipping service */} + + {/* Dropdown trigger */} + + + {/* Dropdown content */} +
closeDropdown(e.target)} + id="dropdown-menu" + role="menu" + style={{ + maxHeight: "20rem", + overflowY: "auto", + }} + > + {(shipment.rates || []).map((rate) => ( + + onChange(shipment_index, shipment, { + options: { + ...shipment.options, + preferred_service: rate.service, + }, + }) + } + > +
+ + +
+
+ ))} +
+
+
+
+
+
+ ); + })} +
+
+
+ + + )} +
+ ); + }; + + return AuthenticatedPage( + <> + + + {`Create labels - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Manifests/create_manifests.tsx b/packages/core/modules/Manifests/create_manifests.tsx new file mode 100644 index 0000000000..5feb706cf0 --- /dev/null +++ b/packages/core/modules/Manifests/create_manifests.tsx @@ -0,0 +1,425 @@ +import { + formatAddressLocationShort, + formatAddressShort, + formatCarrierSlug, + formatDateTime, + formatRef, + isNone, + preventPropagation, + url$, +} from "@karrio/lib"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { CreateManifestModal } from "@karrio/ui/modals/create-manifest-modal"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { ShipmentMenu } from "@karrio/ui/components/shipment-menu"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AddressType, ShipmentType } from "@karrio/types"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { useShipments } from "@karrio/hooks/shipment"; +import { ManifestData } from "@karrio/types/rest/api"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { Spinner } from "@karrio/ui/components"; +import { useRouter } from "next/router"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ModalProvider, ConfirmModal]); + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + // General context data ----------------------------------------------------------- + //#region + + const router = useRouter(); + const loader = useLoader(); + const { metadata } = useAPIMetadata(); + const [allChecked, setAllChecked] = React.useState(false); + const [selection, setSelection] = React.useState([]); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const context = useShipments({ + status: ["purchased"] as any, + meta_key: "manifest_required", + meta_value: true, + has_manifest: false, + preloadNextPage: true, + }); + const { + query: { data: { shipments } = {}, ...query }, + filter, + setFilter, + } = context; + + //#endregion + + // Helper functions ----------------------------------------------------------- + //#region + + const getRate = (shipment: any) => + shipment.selected_rate || + (shipment?.rates || []).find( + (_) => _.service === shipment?.options?.preferred_service, + ) || + (shipment?.rates || [])[0] || + shipment; + const getCarrier = (rate?: ShipmentType["rates"][0]) => + user_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ); + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...extra, + }; + + setFilter(query); + }; + const updatedSelection = ( + selectedShipments: string[], + current: typeof shipments, + ) => { + const shipment_ids = (current?.edges || []).map( + ({ node: shipment }) => shipment.id, + ); + const selection = selectedShipments.filter((id) => + shipment_ids.includes(id), + ); + const selected = + selection.length > 0 && + selection.length === (shipment_ids || []).length; + setAllChecked(selected); + if ( + selectedShipments.filter((id) => !shipment_ids.includes(id)).length > 0 + ) { + setSelection(selection); + } + }; + const handleSelection = (e: React.ChangeEvent) => { + const { checked, name } = e.target as HTMLInputElement; + if (name === "all") { + setSelection( + !checked + ? [] + : (shipments?.edges || []).map(({ node: { id } }) => id), + ); + } else { + setSelection( + checked + ? [...selection, name] + : selection.filter((id) => id !== name), + ); + } + }; + const compatibleCarrierSelection = (selection: string[]) => { + const carrier_id = shipments?.edges?.find(({ node: shipment }) => + selection.includes(shipment.id), + )?.node?.carrier_id; + return ( + (shipments?.edges || []).filter( + ({ node: shipment }) => + selection.includes(shipment.id) && + shipment.carrier_id == carrier_id, + ).length === selection.length + ); + }; + const computeManifestData = ( + selection: string[], + current: typeof shipments, + ): ManifestData => { + const shipment = (current?.edges || []).find(({ node: shipment }) => + selection.includes(shipment.id), + )?.node; + const { id, ...address } = shipment?.shipper || ({} as any); + + return { + address, + shipment_ids: selection, + reference: shipment?.reference as string, + carrier_name: shipment?.carrier_name as string, + }; + }; + + //#endregion + + React.useEffect(() => { + updateFilter(); + }, [router.query]); + React.useEffect(() => { + loader.setLoading(query.isLoading); + }, [query.isLoading]); + React.useEffect(() => { + updatedSelection(selection, shipments); + }, [selection, shipments]); + + return ( + <> +
+ Manifests +
+
+ +
+ +
+ + {query.isLoading && } + + {query.isLoading === false && (shipments?.edges || []).length > 0 && ( + <> +
+ + + + + + {selection.length > 0 && ( + + )} + + {selection.length === 0 && ( + <> + + + + + + + + )} + + + {(shipments?.edges || []).map(({ node: shipment }) => ( + + + + + + + + + + ))} + +
+ + +
+ + + Create Manifests + + + } + /> +
+
+ SHIPPING SERVICE + + RECIPIENT + + REFERENCE + DATE
+ + +
+ +
+ + {shipment.tracking_number} + +
+ + {formatRef( + ((getRate(shipment).meta as any) + ?.service_name || + getRate(shipment).service) as string, + )} + +
+
+
+ + +
+

+ {formatAddressShort( + shipment.recipient as AddressType, + )} +

+

+ {formatAddressLocationShort( + shipment.recipient as AddressType, + )} +

+
+
+ {shipment.reference || ""} + +

+ {formatDateTime(shipment.created_at)} +

+
+ +
+
+ +
+ + {(shipments?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isLoading === false && (shipments?.edges || []).length == 0 && ( + <> +
+
+

No pending shipment found.

+
+
+ + )} + + ); + }; + + return AuthenticatedPage( + + + {`Manifests - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Manifests/index.tsx b/packages/core/modules/Manifests/index.tsx new file mode 100644 index 0000000000..963b0e8663 --- /dev/null +++ b/packages/core/modules/Manifests/index.tsx @@ -0,0 +1,369 @@ +import { + formatAddressLocationShort, + formatAddressShort, + formatCarrierSlug, + formatDateTime, + isNone, + preventPropagation, + url$, +} from "@karrio/lib"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { MenuComponent } from "@karrio/ui/components/menu"; +import { AddressType, ManifestType } from "@karrio/types"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { useManifests } from "@karrio/hooks/manifests"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { Spinner } from "@karrio/ui/components"; +import { useRouter } from "next/router"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ModalProvider]); + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + // General context data ----------------------------------------------------------- + //#region + + const router = useRouter(); + const loader = useLoader(); + const { metadata } = useAPIMetadata(); + const [allChecked, setAllChecked] = React.useState(false); + const [selection, setSelection] = React.useState([]); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const { + query: { data: { manifests } = {}, ...query }, + filter, + setFilter, + } = useManifests({ + preloadNextPage: true, + }); + + //#endregion + + // Helper functions ----------------------------------------------------------- + //#region + + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...extra, + }; + + setFilter(query); + }; + const updatedSelection = ( + selectedManifests: string[], + current: typeof manifests, + ) => { + const manifest_ids = (current?.edges || []).map( + ({ node: manifest }) => manifest.id, + ); + const selection = selectedManifests.filter((id) => + manifest_ids.includes(id), + ); + const selected = + selection.length > 0 && + selection.length === (manifest_ids || []).length; + setAllChecked(selected); + if ( + selectedManifests.filter((id) => !manifest_ids.includes(id)).length > 0 + ) { + setSelection(selection); + } + }; + const handleSelection = (e: React.ChangeEvent) => { + const { checked, name } = e.target as HTMLInputElement; + if (name === "all") { + setSelection( + !checked + ? [] + : (manifests?.edges || []).map(({ node: { id } }) => id), + ); + } else { + setSelection( + checked + ? [...selection, name] + : selection.filter((id) => id !== name), + ); + } + }; + const getCarrier = (manifest: ManifestType) => + user_connections?.find( + (_) => + _.id === manifest?.meta?.carrier || + _.carrier_id === manifest?.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === manifest?.meta?.carrier || + _.carrier_id === manifest?.carrier_id, + ); + + //#endregion + + React.useEffect(() => { + updateFilter(); + }, [router.query]); + React.useEffect(() => { + loader.setLoading(query.isLoading); + }, [query.isLoading]); + React.useEffect(() => { + updatedSelection(selection, manifests); + }, [selection, manifests]); + + return ( + <> +
+ Manifests +
+
+ +
+
    +
  • + Ready +
  • +
  • + + Pending Shipments + +
  • +
+
+ + {query.isLoading && } + + {query.isLoading === false && (manifests?.edges || []).length > 0 && ( + <> +
+ + + + + + {selection.length > 0 && ( + + )} + + {selection.length === 0 && ( + <> + + + + + + + )} + + + {(manifests?.edges || []).map(({ node: manifest }) => ( + + + + + + + + + ))} + +
+ + + + + CARRIER + + ADDRESS + + REFERENCE + DATE
+ + +
+ +
+ + {manifest.id} + +
+ + {manifest.shipment_identifiers[0] || " - "} + +
+
+
+
+

+ {formatAddressShort( + manifest.address as AddressType, + )} +

+

+ {formatAddressLocationShort( + manifest.address as AddressType, + )} +

+
+
+ {manifest.reference || ""} + +

+ {formatDateTime(manifest.created_at)} +

+
+
+ + + + + + } + > + + Print Manifest + + +
+
+
+ +
+ + {(manifests?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isLoading === false && (manifests?.edges || []).length == 0 && ( + <> +
+
+

No manifest found.

+
+
+ + )} + + ); + }; + + return AuthenticatedPage( + + + {`Manifests - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Orders/create_label.tsx b/packages/core/modules/Orders/create_label.tsx similarity index 97% rename from apps/dashboard/src/modules/Orders/create_label.tsx rename to packages/core/modules/Orders/create_label.tsx index 523a2f019a..f14f39d60d 100644 --- a/apps/dashboard/src/modules/Orders/create_label.tsx +++ b/packages/core/modules/Orders/create_label.tsx @@ -1,1686 +1,1686 @@ -import { - AddressType, - CommodityType, - CURRENCY_OPTIONS, - CustomsType, - DEFAULT_CUSTOMS_CONTENT, - NotificationType, - OrderType, - ShipmentType, -} from "@karrio/types"; -import { - createShipmentFromOrders, - formatRef, - formatWeight, - getShipmentCommodities, - isNone, - isNoneOrEmpty, - useLocation, -} from "@karrio/lib"; -import { - CommodityEditModalProvider, - CommodityStateContext, -} from "@karrio/ui/modals/commodity-edit-modal"; -import { - AddressModalEditor, - CustomsModalEditor, - ParcelModalEditor, -} from "@karrio/ui/modals/form-modals"; -import { - MetadataEditor, - MetadataEditorContext, -} from "@karrio/ui/forms/metadata-editor"; -import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; -import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; -import { CommodityDescription } from "@karrio/ui/components/commodity-description"; -import { - LabelTypeEnum, - MetadataObjectTypeEnum, - PaidByEnum, -} from "@karrio/types"; -import { MessagesDescription } from "@karrio/ui/components/messages-description"; -import { AddressDescription } from "@karrio/ui/components/address-description"; -import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; -import { ParcelDescription } from "@karrio/ui/components/parcel-description"; -import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; -import { RateDescription } from "@karrio/ui/components/rate-description"; -import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; -import { useCarrierConnections } from "@karrio/hooks/user-connection"; -import { useDefaultTemplates } from "@karrio/hooks/default-template"; -import { CheckBoxField } from "@karrio/ui/components/checkbox-field"; -import { TextAreaField } from "@karrio/ui/components/textarea-field"; -import { useConnections } from "@karrio/hooks/carrier-connections"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ButtonField } from "@karrio/ui/components/button-field"; -import { SelectField } from "@karrio/ui/components/select-field"; -import { useLabelDataMutation } from "@karrio/hooks/label-data"; -import { InputField } from "@karrio/ui/components/input-field"; -import { useNotifier } from "@karrio/ui/components/notifier"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { useLoader } from "@karrio/ui/components/loader"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useAppMode } from "@karrio/hooks/app-mode"; -import React, { useEffect, useState } from "react"; -import { useOrders } from "@karrio/hooks/order"; -import { Disclosure } from "@headlessui/react"; -import Head from "next/head"; -import moment from "moment"; -import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - CommodityEditModalProvider, - ModalProvider, -]); - -export default function CreateShipmentPage(pageProps: any) { - const { ORDERS_MANAGEMENT } = pageProps?.metadata || {}; - - const Component: React.FC = () => { - const loader = useLoader(); - const notifier = useNotifier(); - const { basePath } = useAppMode(); - const { references } = useAPIMetadata(); - const { carrierOptions } = useConnections(); - const workspace_config = useWorkspaceConfig(); - const { addUrlParam, ...router } = useLocation(); - const { query: templates } = useDefaultTemplates(); - const [ready, setReady] = useState(false); - const [loading, setLoading] = useState(false); - const { shipment_id = "new", order_id = "" } = router.query as any; - const [key, setKey] = useState(`${shipment_id}-${Date.now()}`); - const [addReturn, setAddReturn] = useState(false); - const { - query: { data: { user_connections } = {} }, - } = useCarrierConnections(); - const { - query: { data: { system_connections } = {} }, - } = useSystemCarrierConnections(); - const { - state: { shipment, query }, - ...mutation - } = useLabelDataMutation(shipment_id); - const [selected_rate, setSelectedRate] = useState< - ShipmentType["rates"][0] | undefined - >( - shipment?.selected_rate_id - ? ({ id: shipment?.selected_rate_id } as any) - : undefined, - ); - const { query: orders } = useOrders({ - first: 20, - status: ["unfulfilled", "partial"] as any, - ...(order_id ? { id: order_id.split(",").map((s) => s.trim()) } : {}), - }); - - const requireInfoForRating = (shipment: ShipmentType) => { - return ( - shipment.recipient.address_line1 === undefined || - shipment.shipper.address_line1 === undefined || - shipment.parcels.length === 0 || - loading === true - ); - }; - const isInternational = (shipment: ShipmentType) => { - return ( - shipment.recipient.country_code !== undefined && - shipment.shipper.country_code !== undefined && - shipment.recipient.country_code !== shipment.shipper.country_code - ); - }; - const getCarrier = (rate: ShipmentType["rates"][0]) => - user_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - ) || - system_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - ); - const getOptions = (): any => { - return (orders.data?.orders.edges || []).reduce( - (acc, { node: { options } }) => ({ ...acc, ...options }), - {}, - ); - }; - const getItems = () => { - return (orders.data?.orders.edges || []) - .map(({ node: { line_items } }) => line_items) - .flat(); - }; - const getParent = (id: string | null) => { - return getItems().find((item) => item.id === id); - }; - const getOrder = (item_id?: string | null) => { - return (orders.data?.orders.edges || []).find(({ node: order }) => - order.line_items.find((item) => item.id === item_id), - )?.node; - }; - const getAvailableQuantity = ( - shipment: ShipmentType, - item: CommodityType, - item_index: number, - ) => { - const parent_quantity = - getParent(item.parent_id)?.unfulfilled_quantity || 0; - const packed_quantity = shipment.parcels - .map(({ items }) => items || []) - .flat() - .filter((_, index) => index !== item_index) - .reduce((acc, { parent_id, quantity }) => { - return parent_id === item.parent_id ? acc + (quantity as number) : 0; - }, 0); - - return parent_quantity - packed_quantity; - }; - const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { - const item = getShipmentCommodities(shipment).find( - (item) => - (!!cdt.parent_id && cdt.parent_id === item.parent_id) || - (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || - (!!cdt.sku && cdt.sku === item.sku), - ); - return !!item; - }; - const setInitialData = () => { - const orderList = orders.data!.orders!.edges.map(({ node }) => node); - - onChange( - createShipmentFromOrders( - orderList as OrderType[], - templates, - workspace_config, - ), - ); - - setReady(true); - }; - const onChange = async (changes: Partial) => { - if (changes === undefined) { - return; - } - await mutation.updateShipment({ id: shipment_id, ...changes }); - setKey(`${shipment_id}-${Date.now()}`); - }; - - useEffect(() => { - setLoading(query.isFetching || loader.loading); - }, [query.isFetching, loader.loading]); - useEffect(() => { - if (isNoneOrEmpty(order_id)) { - notifier.notify({ - type: NotificationType.info, - message: "Select order(s) first! redirecting...", - }); - setTimeout(() => router.push(basePath + "/orders"), 2000); - } - }, [order_id]); - useEffect(() => { - if (ready) return; - if (orders.isLoading) return; - if (templates.isLoading) return; - if (workspace_config.query.isLoading) return; - if (shipment_id === "new") { - setTimeout(() => setInitialData(), 500); - return; - } - if (shipment_id !== "new" && Object.keys(shipment.recipient).length === 0) - return; - - setReady(true); - setKey(`${shipment_id}-${Date.now()}`); - }, [ready, orders.isLoading, templates.isLoading, shipment_id, shipment]); - - return ( - <> - node.order_id, - ), - }} - > -
-
- Create label -
- {ready && ( - - {(orders.data?.orders.edges || [{}]).length > 1 - ? `Multiple Orders` - : `Order #${(orders.data?.orders.edges || [{}])[0]?.node?.order_id}`} - - )} -
-
- - {(shipment.messages || []).length > 0 && ( -
- -
- )} - - {!ready && } - - {ready && ( -
-
- {/* Address section */} -
-
-
- - SHIP TO - -
- - onChange({ recipient: address }) - } - trigger={ - - } - /> -
-
- - -
- -
- -
-
- - SHIP FROM - -
- onChange({ shipper: address })} - trigger={ - - } - /> -
-
- - - - {Object.values(shipment.shipper || {}).length === 0 && ( -
- Please specify the origin address. -
- )} -
- - {/* Retrun address section */} -
- -
-
-
- { - setAddReturn(e.target.checked); - if ( - !e.target.checked && - !isNone(shipment.return_address) - ) { - onChange({ return_address: null }); - } - }} - > - Add a return address (optional) - -
-
- {(addReturn || !isNone(shipment.return_address)) && ( - - onChange({ return_address: address }) - } - trigger={ - - } - /> - )} -
-
- -
- {shipment?.return_address && ( - - )} -
- - {Object.values(shipment?.return_address || []).length === - 0 && ( -
- - Use this to specify an origin address different from - the shipper address above.
- This address will be used for pickup and return. -
-
- )} -
- - {/* Billing address section */} -
- -
- - -
- - - -
- - {shipment.payment?.paid_by && - shipment.payment?.paid_by !== PaidByEnum.sender && ( -
- - mutation.updateShipment({ - payment: { - ...shipment.payment, - account_number: e.target.value, - }, - }) - } - /> -
- )} -
- - {(shipment.billing_address || - shipment.payment?.paid_by === PaidByEnum.third_party) && ( - <> -
-
- -
- - onChange({ billing_address: address }) - } - trigger={ - - } - /> -
-
- - {shipment.billing_address && ( - - )} - - {!shipment.billing_address && ( -
- Add shipment billing address. (optional) -
- )} -
- - )} -
- - {/* Parcel & Items section */} -
-
- - PACKAGES AND ITEMS - -
- - Add package - - } - /> -
-
- -
- - {shipment.parcels.map((pkg, pkg_index) => ( - - {pkg_index > 0 && ( -
- )} - -
- {/* Parcel header */} -
-
- - {pkg_index + 1} - - } - /> -
-
- - - - - - } - /> - -
-
- - {/* Items section */} - - ITEMS - - - {(pkg.items || []).map((item, item_index) => ( - -
-
-
-

- {item_index + 1}{" "} - {`${item.title || item.description || "Item"}`} -

-

- {` ORDER: ${getOrder(item.parent_id)?.order_id}`} - {isNoneOrEmpty(item.sku) - ? " | SKU: 0000000" - : ` | SKU: ${item.sku}`} -

-
-
-
- - {formatWeight(item)} - -
-

- { - mutation.updateItem( - pkg_index, - item_index, - pkg.id, - )({ - quantity: parseInt(e.target.value), - } as CommodityType); - }} - className="input is-small" - style={{ - width: "60px", - textAlign: "center", - }} - /> -

-

- - of{" "} - { - getParent(item.parent_id) - ?.unfulfilled_quantity - } - -

-
-
- -
-
-
- ))} - - {(pkg.items || []).length === 0 && ( -
- You can specify content items. -
- )} - -
- - mutation.addItems(pkg_index, pkg.id)(_ as any) - } - order_ids={order_id.split(",").map((s) => s.trim())} - /> -
-
-
- ))} - - {(shipment.parcels || []).length === 0 && ( -
- Add one or more packages to create a shipment. -
- )} -
- - {/* Shipping options section */} -
-
- - OPTIONS - -
- -
- -
- {/* shipment date */} - - onChange({ - options: { - ...shipment.options, - shipment_date: e.target.value, - }, - }) - } - /> - - {/* currency */} - - onChange({ - options: { - ...shipment.options, - currency: e.target.value, - }, - }) - } - > - - {CURRENCY_OPTIONS.map((unit) => ( - - ))} - - - {/* signature confirmation */} - - onChange({ - options: { - ...shipment.options, - signature_confirmation: e.target.checked, - }, - }) - } - > - Add signature confirmation - - - {/* insurance */} - - onChange({ - options: { - ...shipment.options, - insurance: e.target.checked === true ? "" : null, - }, - }) - } - > - Add insurance coverage - - -
- - onChange({ - options: { - ...shipment.options, - insurance: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Cash on delivery */} - - onChange({ - options: { - ...shipment.options, - cash_on_delivery: - e.target.checked === true ? "" : null, - }, - }) - } - > - Collect on delivery - - -
- - onChange({ - options: { - ...shipment.options, - cash_on_delivery: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Declared value */} - - onChange({ - options: { - ...shipment.options, - declared_value: - e.target.checked === true ? "" : null, - }, - }) - } - > - Add package value - - -
- - onChange({ - options: { - ...shipment.options, - declared_value: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* paperless trade */} - - onChange({ - options: { - ...shipment.options, - paperless_trade: e.target.checked, - }, - }) - } - > - Paperless trade - - - {/* hold at location */} - - onChange({ - options: { - ...shipment.options, - hold_at_location: e.target.checked, - }, - }) - } - > - Hold at location - - - {/* dangerous good */} - - onChange({ - options: { - ...shipment.options, - dangerous_good: e.target.checked, - }, - }) - } - > - Dangerous good - -
- - {/* CARRIER OPTIONS SECTION */} - {Object.keys(carrierOptions).length > 0 && ( -
- - {({ open }) => ( -
- -
- CARRIER SPECIFIC OPTIONS -
- - {open ? ( - - ) : ( - - )} - -
- - {Object.entries(carrierOptions).map( - ([carrier, options]) => ( - - -
- -
- {options.map((option, index) => ( - - {references!.options[carrier][option] - ?.type === "boolean" && ( - <> - - onChange({ - options: { - ...shipment.options, - [option]: - e.target.checked || - null, - }, - }) - } - > - {formatRef(option)} - - - )} - - {references!.options[carrier][option] - ?.type === "string" && ( - <> - - onChange({ - options: { - ...shipment.options, - [option]: e.target.value, - }, - }) - } - /> - - )} - - ))} -
- -
-
- ), - )} -
-
- )} -
-
- )} - -
- -
- - mutation.updateShipment({ reference: e.target.value }) - } - placeholder="shipment reference" - className="is-small" - autoComplete="off" - /> -
-
- - {/* Customs declaration section */} - {isInternational(shipment) && ( -
-
- - CUSTOMS DECLARATION - -
- - Edit customs info - - } - /> -
-
- -
- -
- {!isNone(shipment.customs) && ( - <> - - - {/* Commodities section */} - - COMMODITIES - - - {(shipment.customs!.commodities || []).map( - (commodity, index) => ( - -
-
- -
- - {({ editCommodity }) => ( - - )} - - -
-
-
- ), - )} - - {(shipment.customs!.commodities || []).length === - 0 && ( -
- You need provide commodity items for customs - purpose. (required) -
- )} - -
- - {({ editCommodity }) => ( - - )} - -
- - {/* Duty Billing address section */} - {(shipment.customs!.duty_billing_address || - shipment.customs!.duty?.paid_by === - PaidByEnum.third_party) && ( - <> -
- -
-
- -
- - mutation.updateShipment({ - customs: { - ...shipment!.customs, - duty_billing_address: address, - } as any, - }) - } - trigger={ - - } - /> -
-
- - {shipment!.customs!.duty_billing_address && ( - - )} - - {isNone( - shipment!.customs!.duty_billing_address, - ) && ( -
- Add customs duty billing address. (optional) -
- )} -
- - )} - - )} - - {isNone(shipment.customs) && ( -
- Looks like you have an international shipment. You may - need to provide a customs declaration unless you are - shipping documents only. -
- )} -
-
- )} -
- -
- -
-
- node, - ) as OrderType[] - } - className="card px-0 mb-5" - /> - - {/* Shipping section */} -
-
- - SHIPPING SERVICES - -
- -
-
- -
- - {/* Live rates section */} -
- {loading && ( - - )} - - {!loading && (shipment.rates || []).length === 0 && ( -
- Provide all shipping details to retrieve shipping - rates. -
- )} - - {!loading && (shipment.rates || []).length > 0 && ( - - )} -
- -
- -
-
- - -
-
- - - mutation.buyLabel.mutateAsync(selected_rate as any) - } - fieldClass="has-text-centered py-1 px-6 m-0" - className="is-success is-fullwidth" - disabled={ - (shipment.rates || []).filter( - (r) => r.id === selected_rate?.id, - ).length === 0 || - mutation.buyLabel.isLoading || - loading - } - > - Buy shipping label - - -
- - {!(!!shipment.id && shipment.id !== "new") && ( - mutation.saveDraft.mutateAsync({})} - fieldClass="has-text-centered py-1 px-6 m-0" - className="is-default is-fullwidth" - disabled={ - query.isFetching || mutation.saveDraft.isLoading - } - > - Save draft - - )} - -
-
- - {/* Metadata section */} -
-
- onChange({ metadata })} - > - - {({ isEditing, editMetadata }) => ( - <> -
- - METADATA - -
- -
-
- - )} -
-
-
-
- - {/* Instructions section */} -
-
- - onChange({ - options: { - ...shipment.options, - instructions: e.target.value, - }, - }) - } - /> -
-
-
-
-
- )} -
- - ); - }; - - return AuthenticatedPage( - - - - {`Create shipment - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - , - pageProps, - ); -} +import { + AddressType, + CommodityType, + CURRENCY_OPTIONS, + CustomsType, + DEFAULT_CUSTOMS_CONTENT, + NotificationType, + OrderType, + ShipmentType, +} from "@karrio/types"; +import { + createShipmentFromOrders, + formatRef, + formatWeight, + getShipmentCommodities, + isNone, + isNoneOrEmpty, + useLocation, +} from "@karrio/lib"; +import { + CommodityEditModalProvider, + CommodityStateContext, +} from "@karrio/ui/modals/commodity-edit-modal"; +import { + AddressModalEditor, + CustomsModalEditor, + ParcelModalEditor, +} from "@karrio/ui/modals/form-modals"; +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { + LabelTypeEnum, + MetadataObjectTypeEnum, + PaidByEnum, +} from "@karrio/types"; +import { MessagesDescription } from "@karrio/ui/components/messages-description"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { ParcelDescription } from "@karrio/ui/components/parcel-description"; +import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; +import { RateDescription } from "@karrio/ui/components/rate-description"; +import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { useDefaultTemplates } from "@karrio/hooks/default-template"; +import { CheckBoxField } from "@karrio/ui/components/checkbox-field"; +import { TextAreaField } from "@karrio/ui/components/textarea-field"; +import { useConnections } from "@karrio/hooks/carrier-connections"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ButtonField } from "@karrio/ui/components/button-field"; +import { SelectField } from "@karrio/ui/components/select-field"; +import { useLabelDataMutation } from "@karrio/hooks/label-data"; +import { InputField } from "@karrio/ui/components/input-field"; +import { useNotifier } from "@karrio/ui/components/notifier"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useLoader } from "@karrio/ui/components/loader"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useAppMode } from "@karrio/hooks/app-mode"; +import React, { useEffect, useState } from "react"; +import { useOrders } from "@karrio/hooks/order"; +import { Disclosure } from "@headlessui/react"; +import Head from "next/head"; +import moment from "moment"; +import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ + CommodityEditModalProvider, + ModalProvider, +]); + +export default function CreateShipmentPage(pageProps: any) { + const { ORDERS_MANAGEMENT } = pageProps?.metadata || {}; + + const Component: React.FC = () => { + const loader = useLoader(); + const notifier = useNotifier(); + const { basePath } = useAppMode(); + const { references } = useAPIMetadata(); + const { carrierOptions } = useConnections(); + const workspace_config = useWorkspaceConfig(); + const { addUrlParam, ...router } = useLocation(); + const { query: templates } = useDefaultTemplates(); + const [ready, setReady] = useState(false); + const [loading, setLoading] = useState(false); + const { shipment_id = "new", order_id = "" } = router.query as any; + const [key, setKey] = useState(`${shipment_id}-${Date.now()}`); + const [addReturn, setAddReturn] = useState(false); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const { + state: { shipment, query }, + ...mutation + } = useLabelDataMutation(shipment_id); + const [selected_rate, setSelectedRate] = useState< + ShipmentType["rates"][0] | undefined + >( + shipment?.selected_rate_id + ? ({ id: shipment?.selected_rate_id } as any) + : undefined, + ); + const { query: orders } = useOrders({ + first: 20, + status: ["unfulfilled", "partial"] as any, + ...(order_id ? { id: order_id.split(",").map((s) => s.trim()) } : {}), + }); + + const requireInfoForRating = (shipment: ShipmentType) => { + return ( + shipment.recipient.address_line1 === undefined || + shipment.shipper.address_line1 === undefined || + shipment.parcels.length === 0 || + loading === true + ); + }; + const isInternational = (shipment: ShipmentType) => { + return ( + shipment.recipient.country_code !== undefined && + shipment.shipper.country_code !== undefined && + shipment.recipient.country_code !== shipment.shipper.country_code + ); + }; + const getCarrier = (rate: ShipmentType["rates"][0]) => + user_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + ); + const getOptions = (): any => { + return (orders.data?.orders.edges || []).reduce( + (acc, { node: { options } }) => ({ ...acc, ...options }), + {}, + ); + }; + const getItems = () => { + return (orders.data?.orders.edges || []) + .map(({ node: { line_items } }) => line_items) + .flat(); + }; + const getParent = (id: string | null) => { + return getItems().find((item) => item.id === id); + }; + const getOrder = (item_id?: string | null) => { + return (orders.data?.orders.edges || []).find(({ node: order }) => + order.line_items.find((item) => item.id === item_id), + )?.node; + }; + const getAvailableQuantity = ( + shipment: ShipmentType, + item: CommodityType, + item_index: number, + ) => { + const parent_quantity = + getParent(item.parent_id)?.unfulfilled_quantity || 0; + const packed_quantity = shipment.parcels + .map(({ items }) => items || []) + .flat() + .filter((_, index) => index !== item_index) + .reduce((acc, { parent_id, quantity }) => { + return parent_id === item.parent_id ? acc + (quantity as number) : 0; + }, 0); + + return parent_quantity - packed_quantity; + }; + const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { + const item = getShipmentCommodities(shipment).find( + (item) => + (!!cdt.parent_id && cdt.parent_id === item.parent_id) || + (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || + (!!cdt.sku && cdt.sku === item.sku), + ); + return !!item; + }; + const setInitialData = () => { + const orderList = orders.data!.orders!.edges.map(({ node }) => node); + + onChange( + createShipmentFromOrders( + orderList as OrderType[], + templates, + workspace_config, + ), + ); + + setReady(true); + }; + const onChange = async (changes: Partial) => { + if (changes === undefined) { + return; + } + await mutation.updateShipment({ id: shipment_id, ...changes }); + setKey(`${shipment_id}-${Date.now()}`); + }; + + useEffect(() => { + setLoading(query.isFetching || loader.loading); + }, [query.isFetching, loader.loading]); + useEffect(() => { + if (isNoneOrEmpty(order_id)) { + notifier.notify({ + type: NotificationType.info, + message: "Select order(s) first! redirecting...", + }); + setTimeout(() => router.push(basePath + "/orders"), 2000); + } + }, [order_id]); + useEffect(() => { + if (ready) return; + if (orders.isLoading) return; + if (templates.isLoading) return; + if (workspace_config.query.isLoading) return; + if (shipment_id === "new") { + setTimeout(() => setInitialData(), 500); + return; + } + if (shipment_id !== "new" && Object.keys(shipment.recipient).length === 0) + return; + + setReady(true); + setKey(`${shipment_id}-${Date.now()}`); + }, [ready, orders.isLoading, templates.isLoading, shipment_id, shipment]); + + return ( + <> + node.order_id, + ), + }} + > +
+
+ Create label +
+ {ready && ( + + {(orders.data?.orders.edges || [{}]).length > 1 + ? `Multiple Orders` + : `Order #${(orders.data?.orders.edges || [{}])[0]?.node?.order_id}`} + + )} +
+
+ + {(shipment.messages || []).length > 0 && ( +
+ +
+ )} + + {!ready && } + + {ready && ( +
+
+ {/* Address section */} +
+
+
+ + SHIP TO + +
+ + onChange({ recipient: address }) + } + trigger={ + + } + /> +
+
+ + +
+ +
+ +
+
+ + SHIP FROM + +
+ onChange({ shipper: address })} + trigger={ + + } + /> +
+
+ + + + {Object.values(shipment.shipper || {}).length === 0 && ( +
+ Please specify the origin address. +
+ )} +
+ + {/* Retrun address section */} +
+ +
+
+
+ { + setAddReturn(e.target.checked); + if ( + !e.target.checked && + !isNone(shipment.return_address) + ) { + onChange({ return_address: null }); + } + }} + > + Add a return address (optional) + +
+
+ {(addReturn || !isNone(shipment.return_address)) && ( + + onChange({ return_address: address }) + } + trigger={ + + } + /> + )} +
+
+ +
+ {shipment?.return_address && ( + + )} +
+ + {Object.values(shipment?.return_address || []).length === + 0 && ( +
+ + Use this to specify an origin address different from + the shipper address above.
+ This address will be used for pickup and return. +
+
+ )} +
+ + {/* Billing address section */} +
+ +
+ + +
+ + + +
+ + {shipment.payment?.paid_by && + shipment.payment?.paid_by !== PaidByEnum.sender && ( +
+ + mutation.updateShipment({ + payment: { + ...shipment.payment, + account_number: e.target.value, + }, + }) + } + /> +
+ )} +
+ + {(shipment.billing_address || + shipment.payment?.paid_by === PaidByEnum.third_party) && ( + <> +
+
+ +
+ + onChange({ billing_address: address }) + } + trigger={ + + } + /> +
+
+ + {shipment.billing_address && ( + + )} + + {!shipment.billing_address && ( +
+ Add shipment billing address. (optional) +
+ )} +
+ + )} +
+ + {/* Parcel & Items section */} +
+
+ + PACKAGES AND ITEMS + +
+ + Add package + + } + /> +
+
+ +
+ + {shipment.parcels.map((pkg, pkg_index) => ( + + {pkg_index > 0 && ( +
+ )} + +
+ {/* Parcel header */} +
+
+ + {pkg_index + 1} + + } + /> +
+
+ + + + + + } + /> + +
+
+ + {/* Items section */} + + ITEMS + + + {(pkg.items || []).map((item, item_index) => ( + +
+
+
+

+ {item_index + 1}{" "} + {`${item.title || item.description || "Item"}`} +

+

+ {` ORDER: ${getOrder(item.parent_id)?.order_id}`} + {isNoneOrEmpty(item.sku) + ? " | SKU: 0000000" + : ` | SKU: ${item.sku}`} +

+
+
+
+ + {formatWeight(item)} + +
+

+ { + mutation.updateItem( + pkg_index, + item_index, + pkg.id, + )({ + quantity: parseInt(e.target.value), + } as CommodityType); + }} + className="input is-small" + style={{ + width: "60px", + textAlign: "center", + }} + /> +

+

+ + of{" "} + { + getParent(item.parent_id) + ?.unfulfilled_quantity + } + +

+
+
+ +
+
+
+ ))} + + {(pkg.items || []).length === 0 && ( +
+ You can specify content items. +
+ )} + +
+ + mutation.addItems(pkg_index, pkg.id)(_ as any) + } + order_ids={order_id.split(",").map((s) => s.trim())} + /> +
+
+
+ ))} + + {(shipment.parcels || []).length === 0 && ( +
+ Add one or more packages to create a shipment. +
+ )} +
+ + {/* Shipping options section */} +
+
+ + OPTIONS + +
+ +
+ +
+ {/* shipment date */} + + onChange({ + options: { + ...shipment.options, + shipment_date: e.target.value, + }, + }) + } + /> + + {/* currency */} + + onChange({ + options: { + ...shipment.options, + currency: e.target.value, + }, + }) + } + > + + {CURRENCY_OPTIONS.map((unit) => ( + + ))} + + + {/* signature confirmation */} + + onChange({ + options: { + ...shipment.options, + signature_confirmation: e.target.checked, + }, + }) + } + > + Add signature confirmation + + + {/* insurance */} + + onChange({ + options: { + ...shipment.options, + insurance: e.target.checked === true ? "" : null, + }, + }) + } + > + Add insurance coverage + + +
+ + onChange({ + options: { + ...shipment.options, + insurance: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Cash on delivery */} + + onChange({ + options: { + ...shipment.options, + cash_on_delivery: + e.target.checked === true ? "" : null, + }, + }) + } + > + Collect on delivery + + +
+ + onChange({ + options: { + ...shipment.options, + cash_on_delivery: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Declared value */} + + onChange({ + options: { + ...shipment.options, + declared_value: + e.target.checked === true ? "" : null, + }, + }) + } + > + Add package value + + +
+ + onChange({ + options: { + ...shipment.options, + declared_value: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* paperless trade */} + + onChange({ + options: { + ...shipment.options, + paperless_trade: e.target.checked, + }, + }) + } + > + Paperless trade + + + {/* hold at location */} + + onChange({ + options: { + ...shipment.options, + hold_at_location: e.target.checked, + }, + }) + } + > + Hold at location + + + {/* dangerous good */} + + onChange({ + options: { + ...shipment.options, + dangerous_good: e.target.checked, + }, + }) + } + > + Dangerous good + +
+ + {/* CARRIER OPTIONS SECTION */} + {Object.keys(carrierOptions).length > 0 && ( +
+ + {({ open }) => ( +
+ +
+ CARRIER SPECIFIC OPTIONS +
+ + {open ? ( + + ) : ( + + )} + +
+ + {Object.entries(carrierOptions).map( + ([carrier, options]) => ( + + +
+ +
+ {options.map((option, index) => ( + + {references!.options[carrier][option] + ?.type === "boolean" && ( + <> + + onChange({ + options: { + ...shipment.options, + [option]: + e.target.checked || + null, + }, + }) + } + > + {formatRef(option)} + + + )} + + {references!.options[carrier][option] + ?.type === "string" && ( + <> + + onChange({ + options: { + ...shipment.options, + [option]: e.target.value, + }, + }) + } + /> + + )} + + ))} +
+ +
+
+ ), + )} +
+
+ )} +
+
+ )} + +
+ +
+ + mutation.updateShipment({ reference: e.target.value }) + } + placeholder="shipment reference" + className="is-small" + autoComplete="off" + /> +
+
+ + {/* Customs declaration section */} + {isInternational(shipment) && ( +
+
+ + CUSTOMS DECLARATION + +
+ + Edit customs info + + } + /> +
+
+ +
+ +
+ {!isNone(shipment.customs) && ( + <> + + + {/* Commodities section */} + + COMMODITIES + + + {(shipment.customs!.commodities || []).map( + (commodity, index) => ( + +
+
+ +
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ), + )} + + {(shipment.customs!.commodities || []).length === + 0 && ( +
+ You need provide commodity items for customs + purpose. (required) +
+ )} + +
+ + {({ editCommodity }) => ( + + )} + +
+ + {/* Duty Billing address section */} + {(shipment.customs!.duty_billing_address || + shipment.customs!.duty?.paid_by === + PaidByEnum.third_party) && ( + <> +
+ +
+
+ +
+ + mutation.updateShipment({ + customs: { + ...shipment!.customs, + duty_billing_address: address, + } as any, + }) + } + trigger={ + + } + /> +
+
+ + {shipment!.customs!.duty_billing_address && ( + + )} + + {isNone( + shipment!.customs!.duty_billing_address, + ) && ( +
+ Add customs duty billing address. (optional) +
+ )} +
+ + )} + + )} + + {isNone(shipment.customs) && ( +
+ Looks like you have an international shipment. You may + need to provide a customs declaration unless you are + shipping documents only. +
+ )} +
+
+ )} +
+ +
+ +
+
+ node, + ) as OrderType[] + } + className="card px-0 mb-5" + /> + + {/* Shipping section */} +
+
+ + SHIPPING SERVICES + +
+ +
+
+ +
+ + {/* Live rates section */} +
+ {loading && ( + + )} + + {!loading && (shipment.rates || []).length === 0 && ( +
+ Provide all shipping details to retrieve shipping + rates. +
+ )} + + {!loading && (shipment.rates || []).length > 0 && ( + + )} +
+ +
+ +
+
+ + +
+
+ + + mutation.buyLabel.mutateAsync(selected_rate as any) + } + fieldClass="has-text-centered py-1 px-6 m-0" + className="is-success is-fullwidth" + disabled={ + (shipment.rates || []).filter( + (r) => r.id === selected_rate?.id, + ).length === 0 || + mutation.buyLabel.isLoading || + loading + } + > + Buy shipping label + + +
+ + {!(!!shipment.id && shipment.id !== "new") && ( + mutation.saveDraft.mutateAsync({})} + fieldClass="has-text-centered py-1 px-6 m-0" + className="is-default is-fullwidth" + disabled={ + query.isFetching || mutation.saveDraft.isLoading + } + > + Save draft + + )} + +
+
+ + {/* Metadata section */} +
+
+ onChange({ metadata })} + > + + {({ isEditing, editMetadata }) => ( + <> +
+ + METADATA + +
+ +
+
+ + )} +
+
+
+
+ + {/* Instructions section */} +
+
+ + onChange({ + options: { + ...shipment.options, + instructions: e.target.value, + }, + }) + } + /> +
+
+
+
+
+ )} +
+ + ); + }; + + return AuthenticatedPage( + + + + {`Create shipment - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Orders/draft_order.tsx b/packages/core/modules/Orders/draft_order.tsx new file mode 100644 index 0000000000..49a7793975 --- /dev/null +++ b/packages/core/modules/Orders/draft_order.tsx @@ -0,0 +1,569 @@ +import { + CommodityEditModalProvider, + CommodityStateContext, +} from "@karrio/ui/modals/commodity-edit-modal"; +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { + formatRef, + isEqual, + isNone, + isNoneOrEmpty, + useLocation, +} from "@karrio/lib"; +import { MetadataObjectTypeEnum, PaidByEnum } from "@karrio/types"; +import { AddressModalEditor } from "@karrio/ui/modals/form-modals"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { InputField } from "@karrio/ui/components/input-field"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useLoader } from "@karrio/ui/components/loader"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useOrderForm } from "@karrio/hooks/order"; +import React, { useEffect, useState } from "react"; +import { AddressType } from "@karrio/types"; +import Head from "next/head"; +import { Spinner } from "@karrio/ui/components"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ + CommodityEditModalProvider, + ModalProvider, +]); + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + const loader = useLoader(); + const router = useLocation(); + const { id } = router.query; + const [ready, setReady] = useState(false); + const [loading, setLoading] = useState(false); + const [key, setKey] = useState(`order-${Date.now()}`); + const { order, current, isNew, DEFAULT_STATE, query, ...mutation } = + useOrderForm({ id }); + + const handleChange = async (changes?: Partial) => { + if (changes === undefined) { + return; + } + await mutation.updateOrder({ id, ...changes }); + setKey(`${id}-${Date.now()}`); + }; + useEffect(() => { + if ( + !ready && + query.isFetched && + id === "new" && + !isNone(order.line_items) + ) { + setTimeout(() => setReady(true), 1000); + } + if ( + !ready && + query.isFetched && + !isNoneOrEmpty(id) && + id !== "new" && + !isNone(order.line_items) + ) { + setReady(true); + } + }, [query.isFetched, id]); + useEffect(() => { + if (ready) { + setKey(`order-${id}-${Date.now()}`); + } + }, [ready]); + + return ( + <> + +
+ {`${id === "new" ? "Create" : "Edit"} order`} +
+ +
+
+ + {!ready && } + + {ready && ( +
+
+ {/* Line Items */} +
+
+ + LINE ITEMS + +
+ + {({ editCommodity }) => ( + + )} + +
+
+ +
+ +
+ {(order.line_items || []).map((item, index) => ( + + {index > 0 && ( +
+ )} +
+ +
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ))} + + {(order.line_items || []).length === 0 && ( +
+ Add one or more product to create a order. +
+ )} +
+
+ + {/* Order options section */} +
+
+ + OPTIONS + +
+ +
+ +
+ {/* order date */} + + handleChange({ order_date: e.target.value }) + } + /> + + {/* invoice */} + + handleChange({ + options: { + ...order.options, + invoice_number: e.target.value, + }, + }) + } + /> + + {/* invoice date */} + + handleChange({ + options: { + ...order.options, + invoice_date: e.target.value, + }, + }) + } + /> +
+ +
+ +
+ + +
+ + + +
+ + {order.options?.paid_by && + order.options?.paid_by !== PaidByEnum.sender && ( +
+ + handleChange({ + options: { + ...order.options, + account_number: e.target.value, + }, + }) + } + /> +
+ )} +
+
+
+ +
+ +
+
+ {/* Summary section */} + {!isNone(order.line_items) && ( +
+
+ + SUMMARY + +
+ +
+

+ {`ITEMS (${order.line_items.reduce((_, { quantity }) => _ + (isNone(quantity) ? 1 : (quantity as any)), 0)})`} +

+ +
+ {order.line_items.map((item, index) => ( + +
+ +
+ ))} +
+
+ +
+

+ TOTAL:{" "} + { + + {order.line_items.reduce( + (_, { quantity, value_amount }) => + _ + + (isNone(quantity) ? 1 : (quantity as any)) * + (isNone(value_amount) + ? 1.0 + : (value_amount as any)), + 0.0, + )}{" "} + {order.line_items[0]?.value_currency} + + } +

+

+ TOTAL WEIGHT:{" "} + { + + {order.line_items.reduce( + (_, { quantity, weight }) => + _ + + (isNone(quantity) ? 1 : (quantity as any)) * + (isNone(weight) ? 1.0 : (weight as any)), + 0.0, + )}{" "} + {order.line_items[0]?.weight_unit} + + } +

+
+
+ )} + + {/* Address section */} +
+
+
+ + Customer + +
+ + handleChange({ shipping_to: address }) + } + trigger={ + + } + /> +
+
+ + {Object.values(order.shipping_to || {}).length > 0 && ( + + )} + + {Object.values(order.shipping_to || {}).length === 0 && ( +
+ Please specify the customer address. +
+ )} +
+ +
+ +
+
+ + Billing Address + +
+ + handleChange({ billing_address: address }) + } + trigger={ + + } + /> +
+
+ + {Object.values(order.billing_address || {}).length > + 0 && ( + + )} + + {Object.values(order.billing_address || {}).length === + 0 && ( +
+ Same as shipping address. +
+ )} +
+
+ + {/* Metadata section */} +
+
+ handleChange({ metadata })} + > + + {({ isEditing, editMetadata }) => ( + <> +
+ + METADATA + +
+ +
+
+ + )} +
+
+
+
+
+
+
+ )} +
+ + ); + }; + + return AuthenticatedPage( + + + + {`Draft order - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Orders/index.tsx b/packages/core/modules/Orders/index.tsx new file mode 100644 index 0000000000..f7bbb6523e --- /dev/null +++ b/packages/core/modules/Orders/index.tsx @@ -0,0 +1,662 @@ +import { + formatAddressLocationShort, + formatAddressShort, + formatCarrierSlug, + formatDateTime, + formatRef, + getURLSearchParams, + isListEqual, + isNone, + isNoneOrEmpty, + url$, +} from "@karrio/lib"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { + OrderPreview, + OrderPreviewContext, +} from "../../components/order-preview"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { useDocumentTemplates } from "@karrio/hooks/document-template"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import React, { ChangeEvent, useContext, useEffect } from "react"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { OrdersFilter } from "@karrio/ui/filters/orders-filter"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { OrderMenu } from "@karrio/ui/components/order-menu"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AddressType, ShipmentType } from "@karrio/types"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useRouter } from "next/dist/client/router"; +import { useOrders } from "@karrio/hooks/order"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ + OrderPreview, + ConfirmModal, + ModalProvider, +]); + +export default function OrdersPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const { setLoading } = useLoader(); + const { references } = useAPIMetadata(); + const { previewOrder } = useContext(OrderPreviewContext); + const [allChecked, setAllChecked] = React.useState(false); + const [initialized, setInitialized] = React.useState(false); + const [selection, setSelection] = React.useState([]); + const context = useOrders({ + setVariablesToURL: true, + preloadNextPage: true, + }); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const { + query: { data: { orders } = {}, ...query }, + filter, + setFilter, + } = context; + const { + query: { data: { document_templates } = {} }, + } = useDocumentTemplates({ + related_object: "order" as any, + }); + + const preventPropagation = (e: React.MouseEvent) => e.stopPropagation(); + const getRate = (shipment: any, default_rate?: any) => + default_rate || + shipment?.selected_rate || + (shipment?.rates || []).find( + (_) => _.service === shipment?.options?.preferred_service, + ) || + (shipment?.rates || [])[0] || + shipment; + const getCarrier = (rate?: ShipmentType["rates"][0]) => + user_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ); + const updatedSelection = ( + selectedOrders: string[], + current: typeof orders, + ) => { + const order_ids = (current?.edges || []).map( + ({ node: order }) => order.id, + ); + const selection = selectedOrders.filter((id) => order_ids.includes(id)); + const selected = + selection.length > 0 && selection.length === (order_ids || []).length; + setAllChecked(selected); + if (selectedOrders.filter((id) => !order_ids.includes(id)).length > 0) { + setSelection(selection); + } + }; + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + const handleSelection = (e: ChangeEvent) => { + const { checked, name } = e.target as HTMLInputElement; + if (name === "all") { + setSelection( + !checked ? [] : (orders?.edges || []).map(({ node: { id } }) => id), + ); + } else { + setSelection( + checked + ? [...selection, name] + : selection.filter((id) => id !== name), + ); + } + }; + const unfulfilledSelection = (selection: string[]) => { + return ( + (orders?.edges || []).filter( + ({ node: order }) => + selection.includes(order.id) && + !["cancelled", "fulfilled"].includes(order.status), + ).length === selection.length + ); + }; + const computeDocFormat = (selection: string[]): string | null => { + const _order = (orders?.edges || []).find( + ({ node: order }) => order.id == selection[0], + ); + return (_order?.node?.shipments || [])[0]?.label_type; + }; + const compatibleTypeSelection = (selection: string[]) => { + const format = computeDocFormat(selection); + return ( + (orders?.edges || []).filter( + ({ node: order }) => + selection.includes(order.id) && + !!order.shipments.find(({ label_type }) => label_type === format), + ).length === selection.length + ); + }; + const computeOrderService = (order: any) => { + const shipment = + order.shipments.find( + ({ status, tracking_number }) => + !!tracking_number && !["cancelled", "draft"].includes(status), + ) || order.shipments.find(({ status }) => ["draft"].includes(status)); + const rate = getRate(shipment); + + if (!shipment) { + const _shipment = + order.shipments.find( + ({ status }) => !["cancelled", "draft"].includes(status), + ) || + order.shipments.find(({ status }) => !["draft"].includes(status)); + const _rate = getRate(_shipment); + + return ( + <> + +
+ + {` - `} + +
+ + {!isNone(_rate?.carrier_name) && + formatRef( + (_rate.meta?.service_name || _rate.service) as string, + )} + {isNone(_rate?.carrier_name) && "UNFULFILLED"} + +
+ + ); + } + + return ( + <> + +
+ + {!isNone(shipment.carrier_name) && ( + {shipment.tracking_number} + )} + {isNone(shipment.carrier_name) && {` - `}} + +
+ + {!isNone(rate.carrier_name) && + formatRef((rate.meta?.service_name || rate.service) as string)} + {isNone(rate.carrier_name) && "UNFULFILLED"} + +
+ + ); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + updatedSelection(selection, orders); + }, [selection, orders]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + previewOrder(router.query.modal as string); + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+
+ Orders +
+
+ + Create order + + + Manage manifests + + +
+
+ + + + {!query.isFetched && } + + {query.isFetched && (orders?.edges || []).length > 0 && ( + <> +
+ + + + + + {selection.length > 0 && ( + + )} + + {selection.length === 0 && ( + <> + + + + + + + + + + )} + + + {(orders?.edges || []).map(({ node: order }) => ( + + + + + + + + + + + + ))} + +
+ + +
+ + + Create labels + + + + + Print Labels + + + + + Print Invoices + + + {(document_templates?.edges || []).map( + ({ node: template }) => ( + + + Print {template.name} + + + ), + )} +
+
+ ORDER # + + ITEMS + + SHIP TO + TOTALDATE + SHIPPING SERVICE +
+ + previewOrder(order.id)} + > +
+

+ {order.order_id} +

+

+ {order.source} +

+
+
previewOrder(order.id)} + > + + previewOrder(order.id)} + > +
+

+ {((items: number): any => + `${items} item${items === 1 ? "" : "s"}`)( + order.line_items.reduce( + (acc, item) => + acc + (item.quantity as number) || 1, + 0, + ), + )} +

+

+ {order.line_items.length > 1 + ? "(Multiple)" + : order.line_items[0].title || + order.line_items[0].description || + order.line_items[0].sku} +

+
+
previewOrder(order.id)} + > +
+

+ {formatAddressShort( + order.shipping_to as AddressType, + )} +

+

+ {formatAddressLocationShort( + order.shipping_to as AddressType, + )} +

+
+
previewOrder(order.id)} + > +

+ {order.line_items.reduce( + (acc, item) => + acc + + (item.quantity as number) * + (item.value_amount as number), + 0, + )} + {` `} + {order.options.currency || + order.line_items[0].value_currency} +

+
previewOrder(order.id)} + > +

+ {formatDateTime(order.created_at)} +

+
+
+ {computeOrderService(order)} +
+
+ +
+
+ +
+ + {(orders?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (orders?.edges || []).length == 0 && ( +
+
+

No order found.

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + + {`Orders - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Orders/order.tsx b/packages/core/modules/Orders/order.tsx new file mode 100644 index 0000000000..5c73ebd6a4 --- /dev/null +++ b/packages/core/modules/Orders/order.tsx @@ -0,0 +1,417 @@ +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { + formatAddressLocation, + formatDateTime, + formatRef, + isNone, +} from "@karrio/lib"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { StatusCode } from "@karrio/ui/components/status-code-badge"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { OrderMenu } from "@karrio/ui/components/order-menu"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { MetadataObjectTypeEnum } from "@karrio/types"; +import { useRouter } from "next/dist/client/router"; +import { useEvents } from "@karrio/hooks/event"; +import { useOrder } from "@karrio/hooks/order"; +import { useLogs } from "@karrio/hooks/log"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export const OrderComponent: React.FC<{ orderId?: string }> = ({ orderId }) => { + const router = useRouter(); + const { setLoading } = useLoader(); + const entity_id = orderId || (router.query.id as string); + const { query: logs } = useLogs({ entity_id }); + const { query: events } = useEvents({ entity_id }); + const { + query: { data: { order } = {}, ...query }, + } = useOrder(entity_id); + + React.useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + + return ( + <> + {!query.isFetched && query.isFetching && } + + {order && ( + <> + {/* Header section */} +
+
+ + ORDER + +
+ {order?.order_id} + +
+ +
+
+ +
+
+ {!isNone(orderId) && ( + + + + + + )} + +
+ +
+
+
+
+ + {/* Reference and highlights section */} +
+ +
+
+ Date +
+ + {formatDateTime(order?.created_at)} + +
+ + {!isNone(order?.source) && ( + <> +
+
+ Source +
+ + {order?.source} + +
+ + )} +
+ +

Order Details

+
+ +
+ {/* address and line items section */} +
+ {/* Shipping Address section */} +
+

+ ADDRESS +

+ +

+ {order?.shipping_to.person_name} +

+

+ {order?.shipping_to.company_name} +

+

+ {order?.shipping_to.email} +

+

+ {order?.shipping_to.phone_number} +

+

+ {order?.shipping_to.address_line1} + {!isNone(order?.shipping_to.address_line2) && ( + {order?.shipping_to.address_line2} + )} +

+

+ {formatAddressLocation(order?.shipping_to)} +

+
+ + {/* Line Items section */} +
+

+ LINE ITEMS ( + {order?.line_items.reduce( + (_, { quantity }) => _ + (quantity || 1), + 0, + )} + ) +

+ +
+ {order?.line_items.map((item, index) => ( + +
+ +
+ ))} +
+
+
+ + {/* Options section */} +
+ {Object.values(order?.options as object).length > 0 && ( +
+

+ ORDER OPTIONS +

+ + {Object.entries(order?.options).map( + ([key, value]: any, index) => ( + +

+ + {formatRef(key).toLowerCase()}:{" "} + {String(value)} + +

+
+ ), + )} +
+ )} +
+ + {/* Billing address section */} +
+ {order?.billing_address && ( +
+

+ BILL TO +

+ + +
+ )} +
+
+ + {/* Metadata section */} + + + {({ isEditing, editMetadata }) => ( + <> +
+

Metadata

+ + +
+ +
+ + )} +
+
+ +
+ + {/* Shipments section */} +

Shipments

+ + {(order?.shipments || []).length == 0 &&
No shipments
} + + {(order?.shipments || []).length > 0 && ( +
+ + + {(order?.shipments || []).map((shipment) => ( + + + + + + ))} + +
+ + + + + + {shipment.id} + {shipment.tracking_number && + ` - ${shipment.tracking_number}`} + + + + {formatDateTime(shipment.created_at)} + +
+
+ )} + +
+ + {/* Logs section */} +

Logs

+ + {logs.isFetching && } + + {logs.isFetched && (logs.data?.logs.edges || []).length == 0 && ( +
No logs
+ )} + + {logs.isFetched && (logs.data?.logs.edges || []).length > 0 && ( +
+ + + {(logs.data?.logs.edges || []).map(({ node: log }) => ( + + + + + + ))} + +
+ + + + + + {`${log.method} ${log.path}`} + + + + {formatDateTime(log.requested_at)} + +
+
+ )} + +
+ + {/* Events section */} +

Events

+ + {events.isFetching && } + + {events.isFetched && + (events.data?.events.edges || []).length == 0 && ( +
No events
+ )} + + {events.isFetched && (events.data?.events.edges || []).length > 0 && ( +
+ + + {(events.data?.events.edges || []).map(({ node: event }) => ( + + + + + ))} + +
+ + {`${event.type}`} + + + + {formatDateTime(event.created_at)} + +
+
+ )} + + )} + + {query.isFetched && isNone(order) && ( +
+
+

Uh Oh!

+

{"We couldn't find any order with that reference"}

+
+
+ )} + + ); +}; + +export default function OrderPage(pageProps: any) { + return AuthenticatedPage( + + + {`Order - ${(pageProps as any).metadata?.APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Password/reset/done.tsx b/packages/core/modules/Password/reset/done.tsx similarity index 61% rename from apps/dashboard/src/modules/Password/reset/done.tsx rename to packages/core/modules/Password/reset/done.tsx index 9bfba6daca..6b0a82908b 100644 --- a/apps/dashboard/src/modules/Password/reset/done.tsx +++ b/packages/core/modules/Password/reset/done.tsx @@ -1,32 +1,32 @@ -import { SectionLayout } from "@/layouts/section-layout"; -import Head from "next/head"; -import Link from "next/link"; -import React from "react"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function Page(pageProps: any) { - return ( - <> - - {`Password Reset Complete - ${pageProps.metadata?.APP_NAME}`} - -
-
-

Password Reset Complete

- -

Your password has been set.

-

You may go ahead and log in now.

- -
-
- -
- Sign in -
- -
- - ); -} +import { SectionLayout } from "../../../layouts/section-layout"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../../context/metadata"; + +export default function Page(pageProps: any) { + return ( + <> + + + {`Password Reset Complete - ${pageProps.metadata?.APP_NAME}`} + + +
+
+

Password Reset Complete

+ +

Your password has been set.

+

You may go ahead and log in now.

+
+
+ +
+ + Sign in + +
+
+ + ); +} diff --git a/packages/core/modules/Password/reset/index.tsx b/packages/core/modules/Password/reset/index.tsx new file mode 100644 index 0000000000..becd2f01c1 --- /dev/null +++ b/packages/core/modules/Password/reset/index.tsx @@ -0,0 +1,166 @@ +import React, { FormEvent, useContext, useEffect, useReducer } from "react"; +import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; +import { ConfirmPasswordResetMutationInput } from "@karrio/types"; +import { ButtonField } from "@karrio/ui/components/button-field"; +import { InputField } from "@karrio/ui/components/input-field"; +import { SectionLayout } from "../../../layouts/section-layout"; +import { useUserMutation } from "@karrio/hooks/user"; +import { useRouter } from "next/dist/client/router"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../../context/metadata"; + +const DEFAULT_VALUE: Partial = { + new_password1: "", + new_password2: "", +}; + +function reducer( + state: Partial, + { name, value }: { name: string; value: string | object }, +) { + switch (name) { + case "full": + return { ...(value as object) }; + case "partial": + return { ...state, ...(value as object) }; + default: + return { ...state, [name]: value }; + } +} + +const Component: React.FC<{}> = () => { + const router = useRouter(); + const { uidb64, token } = router.query; + const { loading, setLoading } = useContext(Loading); + const [data, dispatch] = useReducer( + reducer, + DEFAULT_VALUE, + () => DEFAULT_VALUE, + ); + const { + confirmPasswordReset: { error, mutateAsync }, + } = useUserMutation(); + + const handleChange = (event: React.ChangeEvent) => { + const value: string = event.target.value; + const name: string = event.target.name; + + dispatch({ name, value }); + }; + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + try { + setLoading(true); + await mutateAsync(data as ConfirmPasswordResetMutationInput); + router.push("/password/reset/done"); + } catch (error: any) { + console.log(error); + } + setLoading(false); + }; + const renderFieldError = (check: CallableFunction, errorData: any) => { + const validation = (errorData?.data?.errors || [])[0]?.validation || {}; + return ( + <> + {Object.entries(validation) + .filter(([key, _]) => check(key)) + .map(([_, messages]: any) => + messages.map((message: string, index: number) => ( +

+ {message} +

+ )), + )} + + ); + }; + + useEffect(() => { + dispatch({ name: "partial", value: { uid: uidb64, token } }); + }, [uidb64, token]); + + return ( + <> +
+
+

New Password

+

+ Enter your new email and password. +

+ + {((error as any)?.data?.errors || []).map((_: any, index: number) => ( + <> +

+ {_.message} +

+ + ))} + +
+ + {renderFieldError((_: string) => _ === "new_password1", error)} + + + + {renderFieldError((_: string) => _ === "new_password2", error)} + + + + Change my password + +
+
+
+ +
+ + Return to{" "} + + Sign in + + +
+ + ); +}; + +export default function Page(pageProps: any) { + return ( + <> + + + {`Password Reset - ${pageProps.metadata?.APP_NAME}`} + + + + + + + + ); +} diff --git a/apps/dashboard/src/modules/Password/reset/request.tsx b/packages/core/modules/Password/reset/request.tsx similarity index 50% rename from apps/dashboard/src/modules/Password/reset/request.tsx rename to packages/core/modules/Password/reset/request.tsx index c3138a4c13..a3233d4758 100644 --- a/apps/dashboard/src/modules/Password/reset/request.tsx +++ b/packages/core/modules/Password/reset/request.tsx @@ -1,93 +1,119 @@ -import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; -import { ButtonField } from "@karrio/ui/components/button-field"; -import { SectionLayout } from "@/layouts/section-layout"; -import { useUserMutation } from "@karrio/hooks/user"; -import { useRouter } from "next/dist/client/router"; -import React, { FormEvent, useRef } from "react"; -import { p, isNone } from "@karrio/lib"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function Page(pageProps: any) { - - const Component: React.FC<{}> = () => { - const router = useRouter(); - const email = useRef(null); - const [errors, setErrors] = React.useState([]); - const { loading, setLoading } = React.useContext(Loading); - const { requestPasswordReset } = useUserMutation(); - - const onSubmit = async (e: FormEvent) => { - e.preventDefault(); - try { - setLoading(true); - await requestPasswordReset.mutateAsync({ - email: email.current?.value as string, - redirect_url: location.origin + p`/password/reset` - }); - router.push(`/password/reset/sent`) - } catch (error: any) { - const _error = error.data?.errors || error; - setErrors(Array.isArray(_error) ? _error : [_error]); - } - setLoading(false); - }; - - return ( - <> -
-
-

Forgotten your password?

-

Enter your email address below, and we’ll email instructions for setting a new one.

- - {(errors as any[]).filter(error => isNone(error.field)).map(({ message }, index) => ( -

{message}

- ))} - -
- -
-
- -
-
- - {errors.filter(error => error.field === 'email').map(({ messages }) => ( - messages.map((message: any, index: number) =>

{message}

) - ))} - - - Reset my password - - -
- -
-
- -
- Return to Sign in -
- - ) - }; - - return ( - <> - - {`Password Reset - ${pageProps.metadata?.APP_NAME}`} - - - - - - - - ) -} +import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; +import { ButtonField } from "@karrio/ui/components/button-field"; +import { SectionLayout } from "../../../layouts/section-layout"; +import { useUserMutation } from "@karrio/hooks/user"; +import { useRouter } from "next/dist/client/router"; +import React, { FormEvent, useRef } from "react"; +import { p, isNone } from "@karrio/lib"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../../context/metadata"; + +export default function Page(pageProps: any) { + const Component: React.FC<{}> = () => { + const router = useRouter(); + const email = useRef(null); + const [errors, setErrors] = React.useState([]); + const { loading, setLoading } = React.useContext(Loading); + const { requestPasswordReset } = useUserMutation(); + + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + try { + setLoading(true); + await requestPasswordReset.mutateAsync({ + email: email.current?.value as string, + redirect_url: location.origin + p`/password/reset`, + }); + router.push(`/password/reset/sent`); + } catch (error: any) { + const _error = error.data?.errors || error; + setErrors(Array.isArray(_error) ? _error : [_error]); + } + setLoading(false); + }; + + return ( + <> +
+
+

+ Forgotten your password? +

+

+ Enter your email address below, and we’ll email instructions for + setting a new one. +

+ + {(errors as any[]) + .filter((error) => isNone(error.field)) + .map(({ message }, index) => ( +

+ {message} +

+ ))} + +
+
+
+ +
+
+ + {errors + .filter((error) => error.field === "email") + .map(({ messages }) => + messages.map((message: any, index: number) => ( +

+ {message} +

+ )), + )} + + + Reset my password + +
+
+
+ +
+ + Return to Sign in + +
+ + ); + }; + + return ( + <> + + + {`Password Reset - ${pageProps.metadata?.APP_NAME}`} + + + + + + + + ); +} diff --git a/packages/core/modules/Password/reset/sent.tsx b/packages/core/modules/Password/reset/sent.tsx new file mode 100644 index 0000000000..ec97d7e1b7 --- /dev/null +++ b/packages/core/modules/Password/reset/sent.tsx @@ -0,0 +1,40 @@ +import { SectionLayout } from "../../../layouts/section-layout"; +import Head from "next/head"; +import Link from "next/link"; +import React from "react"; + +export { getServerSideProps } from "../../../context/metadata"; + +export default function Page(pageProps: any) { + return ( + <> + + + {`Password Reset Sent - ${pageProps.metadata?.APP_NAME}`} + + +
+
+

Password Reset Sent

+ +

+ We’ve emailed you instructions for setting your password, if an + account exists with the email you entered. You should receive them + shortly. +

+

+ If you don’t receive an email, please make sure you’ve entered the + address you registered with, and check your spam folder. +

+
+
+ +
+ + Sign in + +
+
+ + ); +} diff --git a/apps/dashboard/src/modules/Registration/confirm_email.tsx b/packages/core/modules/Registration/confirm_email.tsx similarity index 53% rename from apps/dashboard/src/modules/Registration/confirm_email.tsx rename to packages/core/modules/Registration/confirm_email.tsx index 622fa52a3d..4b82f60a4d 100644 --- a/apps/dashboard/src/modules/Registration/confirm_email.tsx +++ b/packages/core/modules/Registration/confirm_email.tsx @@ -1,46 +1,52 @@ -import { SectionLayout } from "@/layouts/section-layout"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { useUserMutation } from "@karrio/hooks/user"; -import { useRouter } from "next/dist/client/router"; -import React, { useEffect } from "react"; -import { isNone } from "@karrio/lib"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function Page(pageProps: any) { - const router = useRouter(); - const { token } = router.query as { token: string }; - const { confirmEmail: { isLoading, data, mutateAsync } } = useUserMutation(); - - useEffect(() => { !isNone(token) && mutateAsync({ token }) }, [token]); - - return ( - <> - - {`Sign Up Confirmation - ${pageProps.metadata?.APP_NAME}`} - -
-
- - {isLoading && } - - {(data?.confirm_email?.success === true) && -

Your account is verified!

} - - {(!isLoading && !data?.confirm_email?.success) && -

Error, invalid or expired account activation token!

} - -
-
- -
- Sign in -
- -
- - ) -} +import { SectionLayout } from "../../layouts/section-layout"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { useUserMutation } from "@karrio/hooks/user"; +import { useRouter } from "next/dist/client/router"; +import React, { useEffect } from "react"; +import { isNone } from "@karrio/lib"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../context/metadata"; + +export default function Page(pageProps: any) { + const router = useRouter(); + const { token } = router.query as { token: string }; + const { + confirmEmail: { isLoading, data, mutateAsync }, + } = useUserMutation(); + + useEffect(() => { + !isNone(token) && mutateAsync({ token }); + }, [token]); + + return ( + <> + + + {`Sign Up Confirmation - ${pageProps.metadata?.APP_NAME}`} + + +
+
+ {isLoading && } + + {data?.confirm_email?.success === true && ( +

Your account is verified!

+ )} + + {!isLoading && !data?.confirm_email?.success && ( +

Error, invalid or expired account activation token!

+ )} +
+
+ +
+ + Sign in + +
+
+ + ); +} diff --git a/apps/dashboard/src/modules/Registration/confirm_email_change.tsx b/packages/core/modules/Registration/confirm_email_change.tsx similarity index 60% rename from apps/dashboard/src/modules/Registration/confirm_email_change.tsx rename to packages/core/modules/Registration/confirm_email_change.tsx index fe0075252d..fb5433cfa9 100644 --- a/apps/dashboard/src/modules/Registration/confirm_email_change.tsx +++ b/packages/core/modules/Registration/confirm_email_change.tsx @@ -1,73 +1,82 @@ -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { SectionLayout } from "@/layouts/section-layout"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { useUserMutation } from "@karrio/hooks/user"; -import { useRouter } from "next/dist/client/router"; -import React, { useEffect } from "react"; -import { isNone } from "@karrio/lib"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/main'; - - -export default function Page(pageProps: any) { - const { references } = useAPIMetadata(); - - const Component: React.FC = () => { - const router = useRouter(); - const { token } = router.query; - const { confirmEmailChange } = useUserMutation(); - const [loading, setLoading] = React.useState(false); - const [success, setSuccess] = React.useState(false); - const [email, setEmail] = React.useState(''); - - const confirm = async () => { - setLoading(true); - try { - const { confirm_email_change } = await confirmEmailChange.mutateAsync({ token: token as string }); - const email = confirm_email_change.user?.email; - setSuccess(!isNone(email)); - setEmail(email || ''); - } catch (e) { - setSuccess(false); - } - setLoading(false); - }; - - useEffect(() => { - if (!isNone(token)) { - confirm(); - } - }, [token]); - - return ( - <> -
-
- - {loading && } - - {(!loading && success === true) &&

Your email has been changed to {email}!

} - {(!loading && success === false) &&

Error, invalid or expired email change token!

} - -
-
- -
- Return Home -
- - ); - }; - - return AuthenticatedPage(( - - {`Email change confirmation - ${references.APP_NAME}`} - - - - - ), pageProps); -} +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { SectionLayout } from "../../layouts/section-layout"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { useUserMutation } from "@karrio/hooks/user"; +import { useRouter } from "next/dist/client/router"; +import React, { useEffect } from "react"; +import { isNone } from "@karrio/lib"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../context/main"; + +export default function Page(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + const router = useRouter(); + const { token } = router.query; + const { confirmEmailChange } = useUserMutation(); + const [loading, setLoading] = React.useState(false); + const [success, setSuccess] = React.useState(false); + const [email, setEmail] = React.useState(""); + + const confirm = async () => { + setLoading(true); + try { + const { confirm_email_change } = await confirmEmailChange.mutateAsync({ + token: token as string, + }); + const email = confirm_email_change.user?.email; + setSuccess(!isNone(email)); + setEmail(email || ""); + } catch (e) { + setSuccess(false); + } + setLoading(false); + }; + + useEffect(() => { + if (!isNone(token)) { + confirm(); + } + }, [token]); + + return ( + <> +
+
+ {loading && } + + {!loading && success === true && ( +

+ Your email has been changed to {email}! +

+ )} + {!loading && success === false && ( +

Error, invalid or expired email change token!

+ )} +
+
+ +
+ + Return Home + +
+ + ); + }; + + return AuthenticatedPage( + + + {`Email change confirmation - ${references.APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Registration/login.tsx b/packages/core/modules/Registration/login.tsx similarity index 58% rename from apps/dashboard/src/modules/Registration/login.tsx rename to packages/core/modules/Registration/login.tsx index 7ba4904d4a..361d972f92 100644 --- a/apps/dashboard/src/modules/Registration/login.tsx +++ b/packages/core/modules/Registration/login.tsx @@ -1,108 +1,136 @@ -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { SectionLayout } from "@/layouts/section-layout"; -import { useRouter } from "next/dist/client/router"; -import React, { FormEvent, useRef } from "react"; -import { getCookie, isNone } from "@karrio/lib"; -import { signIn } from "next-auth/react"; -import { p } from "@karrio/lib"; -import Head from "next/head"; -import Link from "next/link"; - -export { getServerSideProps } from '@/context/metadata'; - - -export default function LoginPage(pageProps: any) { - const router = useRouter(); - const { references } = useAPIMetadata(); - const email = useRef(null); - const password = useRef(null); - const [showError, setShowError] = React.useState(false); - const [isLoading, setIsLoading] = React.useState(false); - - const onSubmit = async (e: FormEvent) => { - e.preventDefault(); - setShowError(false); - setIsLoading(true); - - const orgId = getCookie('orgId'); - const response: any = await signIn('credentials', { - redirect: false, - email: email.current?.value, - password: password.current?.value, - ...(!!orgId ? { orgId } : {}), - }); - - if (response.ok) { - setTimeout(() => window.location.replace(p`${(new URLSearchParams(location.search)).get('next') || '/'}`), 500); - } else { - setShowError(true); - setTimeout(() => setIsLoading(false), 1000); - } - }; - - return ( - <> - - {`Login - ${references.APP_NAME}`} - -
-
-

Sign in to your account

- - {showError &&

- Please enter a correct email address and password.
- Note that both fields may be case-sensitive. -

} - -
- -
- -
- -
-
- -
- - -
- -
-
- -
-
- -
-
- -
-
-
- - {pageProps?.metadata?.ALLOW_SIGNUP &&
- Dont have an account? Sign Up -
} -
- - ) -} +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { SectionLayout } from "../../layouts/section-layout"; +import { useRouter } from "next/dist/client/router"; +import React, { FormEvent, useRef } from "react"; +import { getCookie, isNone } from "@karrio/lib"; +import { signIn } from "next-auth/react"; +import { p } from "@karrio/lib"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../context/metadata"; + +export default function LoginPage(pageProps: any) { + const router = useRouter(); + const { references } = useAPIMetadata(); + const email = useRef(null); + const password = useRef(null); + const [showError, setShowError] = React.useState(false); + const [isLoading, setIsLoading] = React.useState(false); + + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + setShowError(false); + setIsLoading(true); + + const orgId = getCookie("orgId"); + const response: any = await signIn("credentials", { + redirect: false, + email: email.current?.value, + password: password.current?.value, + ...(!!orgId ? { orgId } : {}), + }); + + if (response.ok) { + setTimeout( + () => + window.location.replace( + p`${new URLSearchParams(location.search).get("next") || "/"}`, + ), + 500, + ); + } else { + setShowError(true); + setTimeout(() => setIsLoading(false), 1000); + } + }; + + return ( + <> + + + {`Login - ${references.APP_NAME}`} + + +
+
+

+ Sign in to your account +

+ + {showError && ( +

+ Please enter a correct email address and password.
+ Note that both fields may be case-sensitive. +

+ )} + +
+
+ +
+ +
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
+ + {pageProps?.metadata?.ALLOW_SIGNUP && ( +
+ Dont have an account?{" "} + + Sign Up + +
+ )} +
+ + ); +} diff --git a/packages/core/modules/Registration/signup.tsx b/packages/core/modules/Registration/signup.tsx new file mode 100644 index 0000000000..b31260df3e --- /dev/null +++ b/packages/core/modules/Registration/signup.tsx @@ -0,0 +1,227 @@ +import { + RegisterUserMutationInput, + register_user_register_user_errors, +} from "@karrio/types"; +import React, { + FormEvent, + useContext, + useEffect, + useReducer, + useState, +} from "react"; +import { LoadingProvider, Loading } from "@karrio/ui/components/loader"; +import { ButtonField } from "@karrio/ui/components/button-field"; +import { InputField } from "@karrio/ui/components/input-field"; +import { SectionLayout } from "../../layouts/section-layout"; +import { isNone, isNoneOrEmpty } from "@karrio/lib"; +import { useRouter } from "next/dist/client/router"; +import { useUserMutation } from "@karrio/hooks/user"; +import { p } from "@karrio/lib"; +import Head from "next/head"; +import Link from "next/link"; + +export { getServerSideProps } from "../../context/metadata"; + +const DEFAULT_VALUE: Partial = { + email: "", + full_name: "", + password1: "", + password2: "", +}; + +function reducer( + state: Partial, + { name, value }: { name: string; value: string | object }, +) { + switch (name) { + case "full": + return { ...(value as object) }; + case "partial": + return { ...state, ...(value as object) }; + default: + return { ...state, [name]: value }; + } +} + +const Component: React.FC = () => { + const router = useRouter(); + const { email } = router.query; + const mutation = useUserMutation(); + const { loading, setLoading } = useContext(Loading); + const [user, dispatch] = useReducer( + reducer, + DEFAULT_VALUE, + () => DEFAULT_VALUE, + ); + const [errors, setErrors] = useState( + [], + ); + + const handleChange = (event: React.ChangeEvent) => { + const value: string = event.target.value; + const name: string = event.target.name; + + dispatch({ name, value }); + }; + const onSubmit = async (e: FormEvent) => { + e.preventDefault(); + try { + setLoading(true); + await mutation.registerUser.mutateAsync({ + ...user, + redirect_url: location.origin + p`/email`, + } as RegisterUserMutationInput); + router.push(p`/signup/success`); + } catch (error: any) { + setErrors(Array.isArray(error) ? error : [error]); + } + setLoading(false); + }; + + useEffect(() => { + if (!isNoneOrEmpty(email)) { + dispatch({ name: "email", value: email as string }); + } + }, [email]); + + return ( + <> +
+
+

+ Sign up with credentials +

+ + {(errors as any[]) + .filter((error) => isNone(error.field)) + .map(({ message }, index) => ( +

+ {message} +

+ ))} + +
+ + {errors + .filter((error) => error.field === "email") + .map(({ messages }) => + messages.map((message, index) => ( +

+ {message} +

+ )), + )} +
+ + + {errors + .filter((error) => error.field === "full_name") + .map(({ messages }) => + messages.map((message, index) => ( +

+ {message} +

+ )), + )} +
+ + + {errors + .filter((error) => error.field === "password1") + .map(({ messages }) => + messages.map((message, index) => ( +

+ {message} +

+ )), + )} +
+ + + {errors + .filter((error) => error.field === "password2") + .map(({ messages }) => + messages.map((message, index) => ( +

+ {message} +

+ )), + )} +
+ + + Create account + +
+
+
+ +
+ + Have an account?{" "} + + Sign in + + +
+ + ); +}; + +function SignUp(pageProps: any) { + return ( + <> + + + {`Sign Up - ${pageProps.metadata?.APP_NAME}`} + + + + + + + + ); +} + +export default SignUp; diff --git a/apps/dashboard/src/modules/Registration/signup_success.tsx b/packages/core/modules/Registration/signup_success.tsx similarity index 50% rename from apps/dashboard/src/modules/Registration/signup_success.tsx rename to packages/core/modules/Registration/signup_success.tsx index 107171d71f..f37df09b60 100644 --- a/apps/dashboard/src/modules/Registration/signup_success.tsx +++ b/packages/core/modules/Registration/signup_success.tsx @@ -1,34 +1,36 @@ -import { SectionLayout } from "@/layouts/section-layout"; -import Head from "next/head"; -import Link from "next/link"; -import React from "react"; - -export { getServerSideProps } from '@/context/metadata'; - - -function SignUpSuccess(pageProps: any) { - return ( - <> - - {`Sign Up Success - ${pageProps.metadata?.APP_NAME}`} - -
-
- -

Your account has been created.

-

Check your registration email inbox to verify the address and activate your account.

- -
-
- -
- Sign in -
- -
- - ) -}; - - -export default SignUpSuccess; +import { SectionLayout } from "../../layouts/section-layout"; +import Head from "next/head"; +import Link from "next/link"; +import React from "react"; + +export { getServerSideProps } from "../../context/metadata"; + +function SignUpSuccess(pageProps: any) { + return ( + <> + + + {`Sign Up Success - ${pageProps.metadata?.APP_NAME}`} + + +
+
+

Your account has been created.

+

+ Check your registration email inbox to verify the address and + activate your account. +

+
+
+ +
+ + Sign in + +
+
+ + ); +} + +export default SignUpSuccess; diff --git a/packages/core/modules/Resources/graphiql.tsx b/packages/core/modules/Resources/graphiql.tsx new file mode 100644 index 0000000000..921c1e7ce1 --- /dev/null +++ b/packages/core/modules/Resources/graphiql.tsx @@ -0,0 +1,54 @@ +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { createGraphiQLFetcher } from "@graphiql/toolkit"; +import { useSyncedSession } from "@karrio/hooks/session"; +import { EmbedLayout } from "../../layouts/embed-layout"; +import { GraphiQL } from "graphiql"; +import Head from "next/head"; +import React from "react"; + +import "graphiql/graphiql.css"; + +export { getServerSideProps } from "../../context/main"; + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + const { metadata } = useAPIMetadata(); + const { + query: { data: session }, + } = useSyncedSession(); + + const fetcher = React.useMemo(() => { + return createGraphiQLFetcher({ + url: metadata?.GRAPHQL, + headers: { + ...(!!session?.orgId ? { "x-org-id": session.orgId } : {}), + ...(!!session?.testMode ? { "x-test-mode": session.testMode } : {}), + ...(!!session?.accessToken + ? { authorization: `Bearer ${session.accessToken}` } + : {}), + }, + }); + }, [session.accessToken]); + + return ( +
+ +
+ ); + }; + + return AuthenticatedPage( + + + {`GraphiQL - ${(pageProps as any).metadata?.APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Resources/reference.tsx b/packages/core/modules/Resources/reference.tsx similarity index 58% rename from apps/dashboard/src/modules/Resources/reference.tsx rename to packages/core/modules/Resources/reference.tsx index af4d9e3140..fc1241f751 100644 --- a/apps/dashboard/src/modules/Resources/reference.tsx +++ b/packages/core/modules/Resources/reference.tsx @@ -1,38 +1,38 @@ -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { useSyncedSession } from "@karrio/hooks/session"; -import { EmbedLayout } from "@/layouts/embed-layout"; -import { RedocStandalone } from 'redoc'; -import { url$ } from "@karrio/lib"; -import Head from "next/head"; -import React from "react"; - -export { getServerSideProps } from "@/context/main"; - -export default function Page(pageProps: any) { - const Component: React.FC = () => { - const { metadata } = useAPIMetadata(); - - return ( - <> - - - - - ); - }; - - return AuthenticatedPage(( - - {`API Reference - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - ), pageProps) -} +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useSyncedSession } from "@karrio/hooks/session"; +import { EmbedLayout } from "../../layouts/embed-layout"; +import { RedocStandalone } from "redoc"; +import { url$ } from "@karrio/lib"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + const { metadata } = useAPIMetadata(); + + return ( + <> + + + ); + }; + + return AuthenticatedPage( + + + {`API Reference - ${(pageProps as any).metadata?.APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Settings/account.tsx b/packages/core/modules/Settings/account.tsx similarity index 58% rename from apps/dashboard/src/modules/Settings/account.tsx rename to packages/core/modules/Settings/account.tsx index 6220ee4a2a..a8e7ddc21a 100644 --- a/apps/dashboard/src/modules/Settings/account.tsx +++ b/packages/core/modules/Settings/account.tsx @@ -1,98 +1,122 @@ -import { WorkspaceConfigForm } from "@karrio/ui/forms/workspace-config-form"; -import { CloseAccountAction } from "@karrio/ui/forms/close-account-action"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function AccountPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> - -
- Settings -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- -
- - {/* General preferences section */} - - -
- - {/* Close account section */} -
-
-

Close Account

-

- Warning: You will lose access to your {APP_NAME} services -

-
- -
- - Close this account... - -
-
- -
- - ); - }; - - return AuthenticatedPage(( - - {`Account Settings - ${APP_NAME}`} - - - - - - - ), pageProps) -} +import { WorkspaceConfigForm } from "@karrio/ui/forms/workspace-config-form"; +import { CloseAccountAction } from "@karrio/ui/forms/close-account-action"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function AccountPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ Settings +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ +
+ {/* General preferences section */} + + +
+ + {/* Close account section */} +
+
+

Close Account

+

+ Warning: You will lose access to your{" "} + {APP_NAME} services +

+
+ +
+ + Close this account... + +
+
+
+ + ); + }; + + return AuthenticatedPage( + + + {`Account Settings - ${APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Settings/addresses.tsx b/packages/core/modules/Settings/addresses.tsx new file mode 100644 index 0000000000..50bdf8470e --- /dev/null +++ b/packages/core/modules/Settings/addresses.tsx @@ -0,0 +1,312 @@ +import { + formatAddressLocationShort, + formatAddressShort, + getURLSearchParams, + isNoneOrEmpty, +} from "@karrio/lib"; +import { + AddressEditModal, + AddressEditContext, +} from "@karrio/ui/modals/address-edit-modal"; +import { + useAddressTemplateMutation, + useAddressTemplates, +} from "@karrio/hooks/address"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { + ConfirmModal, + ConfirmModalContext, +} from "@karrio/ui/modals/confirm-modal"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Loading } from "@karrio/ui/components/loader"; +import React, { useContext, useEffect } from "react"; +import { useRouter } from "next/dist/client/router"; +import { AddressType } from "@karrio/types"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function AddressPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + const router = useRouter(); + const { setLoading } = useContext(Loading); + const { confirm } = useContext(ConfirmModalContext); + const { editAddress } = useContext(AddressEditContext); + const { deleteAddressTemplate } = useAddressTemplateMutation(); + const [initialized, setInitialized] = React.useState(false); + const { + query: { data: { address_templates } = {}, ...query }, + filter, + setFilter, + } = useAddressTemplates({ + setVariablesToURL: true, + }); + + const remove = (id: string) => async () => { + await deleteAddressTemplate.mutateAsync({ id }); + }; + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + const templates = address_templates?.edges || []; + const addressTemplate: any = templates.find( + (c) => c.node.id === router.query.modal, + )?.node; + if (addressTemplate || router.query.modal === "new") { + editAddress({ addressTemplate }); + } + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+ Settings +
+ +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ + {(address_templates?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + + + {(address_templates?.edges || []).map( + ({ node: template }) => ( + + + + + + + + ), + )} + +
LABELADDRESSEMAIL
+ + {template.label} + + + + {formatAddressShort( + template.address as AddressType, + )} + +
+ + {formatAddressLocationShort( + template.address as AddressType, + )} + +
+ + {template.address.email} + + + {template.is_default && ( + + + + {" "} + default + + )} + +
+ + +
+
+
+ +
+ + {(address_templates?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (address_templates?.edges || []).length == 0 && ( +
+
+

{`There aren't any results for that query.`}

+

{`Create a new address`}

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + + {`Addresses Settings - ${APP_NAME}`} + + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Settings/organization.tsx b/packages/core/modules/Settings/organization.tsx new file mode 100644 index 0000000000..841bb59e77 --- /dev/null +++ b/packages/core/modules/Settings/organization.tsx @@ -0,0 +1,108 @@ +import { OrganizationManagement } from "@karrio/ui/forms/organization-management"; +import { InviteMemberProvider } from "@karrio/ui/modals/invite-member-modal"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function AccountPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ Settings +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ + {MULTI_ORGANIZATIONS && ( +
+ + + +
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Organization Settings - ${APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Settings/parcels.tsx b/packages/core/modules/Settings/parcels.tsx new file mode 100644 index 0000000000..8ad5f1e9e0 --- /dev/null +++ b/packages/core/modules/Settings/parcels.tsx @@ -0,0 +1,277 @@ +import { + ParcelEditModal, + ParcelEditContext, +} from "@karrio/ui/modals/parcel-edit-modal"; +import { + useParcelTemplateMutation, + useParcelTemplates, +} from "@karrio/hooks/parcel"; +import { + ConfirmModal, + ConfirmModalContext, +} from "@karrio/ui/modals/confirm-modal"; +import { ParcelDescription } from "@karrio/ui/components/parcel-description"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Loading } from "@karrio/ui/components/loader"; +import { useRouter } from "next/dist/client/router"; +import { useContext, useEffect } from "react"; +import { getURLSearchParams, isNoneOrEmpty } from "@karrio/lib"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export default function ParcelsPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + const router = useRouter(); + const { setLoading } = useContext(Loading); + const mutation = useParcelTemplateMutation(); + const { editParcel } = useContext(ParcelEditContext); + const [initialized, setInitialized] = React.useState(false); + const { confirm: confirmDeletion } = useContext(ConfirmModalContext); + const { + query: { data: { parcel_templates } = {}, ...query }, + filter, + setFilter, + } = useParcelTemplates({ + setVariablesToURL: true, + }); + + const remove = (id: string) => async () => { + await mutation.deleteParcelTemplate.mutateAsync({ id }); + }; + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + const parcelTemplate = (parcel_templates?.edges || []).find( + (c) => c.node.id === router.query.modal, + )?.node; + if (parcelTemplate || router.query.modal === "new") { + editParcel({ parcelTemplate } as any); + } + setInitialized(true); + } + query.isFetched && setInitialized(true); + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+ Settings +
+ +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ + {(parcel_templates?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + + {(parcel_templates?.edges || []).map(({ node: template }) => ( + + + + + + + ))} + +
LABELPARCEL
+ + {template.label} + + + + + {template.is_default && ( + + + + {" "} + default + + )} + +
+ + +
+
+
+ +
+ + {(parcel_templates?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (parcel_templates?.edges || [])?.length == 0 && ( +
+
+

{`There aren't any results for that query.`}

+

{`Create a new parcel`}

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Parcels Settings - ${APP_NAME}`} + + + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Settings/profile.tsx b/packages/core/modules/Settings/profile.tsx similarity index 56% rename from apps/dashboard/src/modules/Settings/profile.tsx rename to packages/core/modules/Settings/profile.tsx index a8554eb610..2877c2b629 100644 --- a/apps/dashboard/src/modules/Settings/profile.tsx +++ b/packages/core/modules/Settings/profile.tsx @@ -1,105 +1,136 @@ -import { CloseAccountAction } from "@karrio/ui/forms/close-account-action"; -import { ProfileUpdateInput } from "@karrio/ui/forms/profile-update-input"; -import { PasswordManagement } from "@karrio/ui/forms/password-management"; -import { EmailManagement } from "@karrio/ui/forms/email-management"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { AppLink } from "@karrio/ui/components/app-link"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - - -export default function AccountPage(pageProps: any) { - const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; - - const Component: React.FC = () => { - - return ( - <> - -
- Settings -
-
- -
-
    -
  • - - Account - -
  • -
  • - - Profile - -
  • - {MULTI_ORGANIZATIONS &&
  • - - Organization - -
  • } -
  • - - Addresses - -
  • -
  • - - Parcels - -
  • -
  • - - Templates - -
  • -
-
- - -
-
-
-

Profile

-

Your email address is your identity on {APP_NAME} and is used to log in.

-
- -
- - -
- -
-
- -
- -
-
-

Password

-

You can change your password.

-
- - - -
-
-
- - ); - }; - - return AuthenticatedPage(( - - {`Profile Settings - ${APP_NAME}`} - - - - - - - ), pageProps) -} +import { CloseAccountAction } from "@karrio/ui/forms/close-account-action"; +import { ProfileUpdateInput } from "@karrio/ui/forms/profile-update-input"; +import { PasswordManagement } from "@karrio/ui/forms/password-management"; +import { EmailManagement } from "@karrio/ui/forms/email-management"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function AccountPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + return ( + <> +
+ Settings +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ +
+
+
+

Profile

+

+ Your email address is your identity on {APP_NAME} and is used to + log in. +

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

Password

+

You can change your password.

+
+ + + +
+
+
+ + ); + }; + + return AuthenticatedPage( + + + {`Profile Settings - ${APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Settings/template.tsx b/packages/core/modules/Settings/template.tsx new file mode 100644 index 0000000000..cb3dc85a20 --- /dev/null +++ b/packages/core/modules/Settings/template.tsx @@ -0,0 +1,300 @@ +import { + DocumentTemplateType, + DOCUMENT_RELATED_OBJECTS, + NotificationType, + TemplateType, +} from "@karrio/types"; +import { + isEqual, + isNoneOrEmpty, + url$, + useLocation, + validationMessage, + validityCheck, +} from "@karrio/lib"; +import { + useDocumentTemplate, + useDocumentTemplateMutation, +} from "@karrio/hooks/document-template"; +import { TextAreaField } from "@karrio/ui/components/textarea-field"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import React, { useEffect, useReducer, useState } from "react"; +import { InputField } from "@karrio/ui/components/input-field"; +import { DEFAULT_DOCUMENT_TEMPLATE } from "@karrio/lib/sample"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useNotifier } from "@karrio/ui/components/notifier"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import CodeMirror from "@uiw/react-codemirror"; +import { html } from "@codemirror/lang-html"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +type stateValue = string | boolean | string[] | Partial; +const DEFAULT_STATE = { + related_object: "order", + template: DEFAULT_DOCUMENT_TEMPLATE, +}; + +function reducer( + state: any, + { name, value }: { name: string; value: stateValue }, +) { + switch (name) { + case "partial": + return { ...(value as TemplateType) }; + default: + return { ...state, [name]: value }; + } +} + +export default function DocumentTemplatePage(pageProps: any) { + const Component: React.FC = () => { + const loader = useLoader(); + const router = useLocation(); + const { id } = router.query; + const notifier = useNotifier(); + const { references } = useAPIMetadata(); + const [isNew, setIsNew] = useState(); + const mutation = useDocumentTemplateMutation(); + const [template, dispatch] = useReducer( + reducer, + DEFAULT_STATE, + () => DEFAULT_STATE, + ); + const { + query: { data: { document_template } = {}, ...query }, + docId, + setDocId, + } = useDocumentTemplate({ + setVariablesToURL: true, + id: id as string, + }); + + const computeParams = (template: DocumentTemplateType) => { + if (isNoneOrEmpty(template.related_object)) { + return ""; + } + return `?${template.related_object}s=sample`; + }; + const handleChange = (event: React.ChangeEvent) => { + const target = event.target; + let value = target.type === "checkbox" ? target.checked : target.value; + let name: string = target.name; + + if (target.multiple === true) { + value = Array.from(target.selectedOptions).map((o: any) => o.value); + } + + dispatch({ name, value }); + }; + const handleSubmit = async (evt: React.FormEvent) => { + evt.preventDefault(); + loader.setLoading(true); + const { updated_at, ...data } = template; + + try { + if (isNew) { + const { create_document_template } = + await mutation.createDocumentTemplate.mutateAsync(data); + notifier.notify({ + type: NotificationType.success, + message: `Document template created successfully`, + }); + loader.setLoading(false); + + setDocId(create_document_template.template?.id as string); + } else { + await mutation.updateDocumentTemplate.mutateAsync(data); + query.refetch(); + notifier.notify({ + type: NotificationType.success, + message: `Document template updated successfully`, + }); + loader.setLoading(false); + } + } catch (message: any) { + notifier.notify({ type: NotificationType.error, message }); + loader.setLoading(false); + } + }; + + useEffect(() => { + setIsNew(docId === "new"); + }, [docId]); + useEffect(() => { + if (docId !== "new") { + dispatch({ name: "partial", value: document_template as any }); + } + }, [document_template]); + + return ( +
+
+
+ + + + + + + Edit document template + +
+
+ + Preview Template + + +
+
+ +
+ +
+
+ + + + +
+ + +
+
+ +
+
+
+ + + +
+
+

+ Editing your template +

+

+ To edit your template, use HTML, CSS, and{" "} + + Jinja variables + + {" "} + for documents. +

+

+ The template can be styled with{" "} + + bulma css framework + + {" "} + . +

+
+
+
+ +
+ +
+
+ dispatch({ name: "template", value })} + /> +
+
+
+
+ ); + }; + + return AuthenticatedPage( + <> + + {`Template - ${(pageProps as any).metadata?.APP_NAME}`} + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Settings/templates.tsx b/packages/core/modules/Settings/templates.tsx new file mode 100644 index 0000000000..b4bc3623d8 --- /dev/null +++ b/packages/core/modules/Settings/templates.tsx @@ -0,0 +1,242 @@ +import { + useDocumentTemplateMutation, + useDocumentTemplates, +} from "@karrio/hooks/document-template"; +import { + ConfirmModal, + ConfirmModalContext, +} from "@karrio/ui/modals/confirm-modal"; +import { TemplateDescription } from "@karrio/ui/components/template-description"; +import { DocumentTemplateType, NotificationType } from "@karrio/types"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Notify } from "@karrio/ui/components/notifier"; +import { useContext } from "react"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +export default function TemplatesPage(pageProps: any) { + const { APP_NAME, MULTI_ORGANIZATIONS } = (pageProps as any).metadata || {}; + + const Component: React.FC = () => { + const { notify } = useContext(Notify); + const mutation = useDocumentTemplateMutation(); + const { confirm: confirmDeletion } = useContext(ConfirmModalContext); + const { + query: { data: { document_templates } = {}, ...query }, + filter, + setFilter, + } = useDocumentTemplates(); + + const remove = (id: string) => async () => { + await mutation.deleteDocumentTemplate.mutateAsync({ id }); + }; + const toggle = + ({ active, id }: DocumentTemplateType) => + async () => { + try { + await mutation.updateDocumentTemplate.mutateAsync({ + id, + active: !active, + }); + notify({ + type: NotificationType.success, + message: `template ${!active ? "enabled" : "disabled"}!`, + }); + } catch (message: any) { + notify({ type: NotificationType.error, message }); + } + }; + + return ( + <> +
+ Settings +
+ + Create template + +
+
+ +
+
    +
  • + + Account + +
  • +
  • + + Profile + +
  • + {MULTI_ORGANIZATIONS && ( +
  • + + Organization + +
  • + )} +
  • + + Addresses + +
  • +
  • + + Parcels + +
  • +
  • + + Templates + +
  • +
+
+ + {(document_templates?.edges || [])?.length > 0 && ( + <> +
+ + + + + + + + {(document_templates?.edges || []).map( + ({ node: template }) => ( + + + + + ), + )} + +
DOCUMENT TEMPLATES
+ + +
+ + + + + + + +
+
+
+ +
+ + {(document_templates?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (document_templates?.edges || [])?.length == 0 && ( +
+
+

{`There aren't any results for that query.`}

+

{`Create a new template`}

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Templates Settings - ${APP_NAME}`} + + + + + , + pageProps, + ); +} diff --git a/apps/dashboard/src/modules/Shipments/create_label.tsx b/packages/core/modules/Shipments/create_label.tsx similarity index 97% rename from apps/dashboard/src/modules/Shipments/create_label.tsx rename to packages/core/modules/Shipments/create_label.tsx index 34dc554ebd..25ce1ef344 100644 --- a/apps/dashboard/src/modules/Shipments/create_label.tsx +++ b/packages/core/modules/Shipments/create_label.tsx @@ -1,1794 +1,1794 @@ -import { - AddressType, - CommodityType, - CURRENCY_OPTIONS, - CustomsType, - NotificationType, - OrderType, - ShipmentType, - get_orders_orders_edges, - LabelTypeEnum, - MetadataObjectTypeEnum, - PaidByEnum, - ShipmentStatusEnum, - DEFAULT_PARCEL_CONTENT, - DEFAULT_CUSTOMS_CONTENT, -} from "@karrio/types"; -import { - formatRef, - formatWeight, - getShipmentCommodities, - isNone, - isNoneOrEmpty, - useLocation, -} from "@karrio/lib"; -import { - AddressModalEditor, - CustomsModalEditor, - ParcelModalEditor, -} from "@karrio/ui/modals/form-modals"; -import { - CommodityEditModalProvider, - CommodityStateContext, -} from "@karrio/ui/modals/commodity-edit-modal"; -import { - MetadataEditor, - MetadataEditorContext, -} from "@karrio/ui/forms/metadata-editor"; -import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; -import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; -import { CommodityDescription } from "@karrio/ui/components/commodity-description"; -import { MessagesDescription } from "@karrio/ui/components/messages-description"; -import { AddressDescription } from "@karrio/ui/components/address-description"; -import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; -import { ParcelDescription } from "@karrio/ui/components/parcel-description"; -import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; -import { RateDescription } from "@karrio/ui/components/rate-description"; -import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; -import { useCarrierConnections } from "@karrio/hooks/user-connection"; -import { useDefaultTemplates } from "@karrio/hooks/default-template"; -import { CheckBoxField } from "@karrio/ui/components/checkbox-field"; -import { TextAreaField } from "@karrio/ui/components/textarea-field"; -import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; -import { useConnections } from "@karrio/hooks/carrier-connections"; -import { CarrierImage } from "@karrio/ui/components/carrier-image"; -import { AuthenticatedPage } from "@/layouts/authenticated-page"; -import { ButtonField } from "@karrio/ui/components/button-field"; -import { SelectField } from "@karrio/ui/components/select-field"; -import { useLabelDataMutation } from "@karrio/hooks/label-data"; -import { InputField } from "@karrio/ui/components/input-field"; -import { DashboardLayout } from "@/layouts/dashboard-layout"; -import { useNotifier } from "@karrio/ui/components/notifier"; -import { useAPIMetadata } from "@karrio/hooks/api-metadata"; -import { ModalProvider } from "@karrio/ui/modals/modal"; -import { Spinner } from "@karrio/ui/components/spinner"; -import { bundleContexts } from "@karrio/hooks/utils"; -import { useAppMode } from "@karrio/hooks/app-mode"; -import React, { useEffect, useState } from "react"; -import { useOrders } from "@karrio/hooks/order"; -import { Disclosure } from "@headlessui/react"; -import Head from "next/head"; - -export { getServerSideProps } from "@/context/main"; - -const ContextProviders = bundleContexts([ - CommodityEditModalProvider, - ModalProvider, -]); - -export default function CreateLabelPage(pageProps: any) { - const { ORDERS_MANAGEMENT } = pageProps?.metadata || {}; - - const Component: React.FC = () => { - const notifier = useNotifier(); - const { basePath } = useAppMode(); - const { references } = useAPIMetadata(); - const { carrierOptions } = useConnections(); - const workspace_config = useWorkspaceConfig(); - const { addUrlParam, ...router } = useLocation(); - const { query: templates } = useDefaultTemplates(); - const [ready, setReady] = useState(false); - const { shipment_id = "new" } = router.query as any; - const [key, setKey] = useState(`${shipment_id}-${Date.now()}`); - const [addReturn, setAddReturn] = useState(false); - const { - query: { data: { user_connections } = {} }, - } = useCarrierConnections(); - const { - query: { data: { system_connections } = {} }, - } = useSystemCarrierConnections(); - const { - state: { shipment, query }, - ...mutation - } = useLabelDataMutation(shipment_id); - const { query: orders } = useOrders({ - first: 10, - status: ["unfulfilled", "partial"] as any, - }); - const [selected_rate, setSelectedRate] = useState< - ShipmentType["rates"][0] | undefined - >( - shipment?.selected_rate_id - ? ({ id: shipment?.selected_rate_id } as any) - : undefined, - ); - - const requireInfoForRating = (shipment: ShipmentType) => { - return ( - shipment.recipient.address_line1 === undefined || - shipment.shipper.address_line1 === undefined || - shipment.parcels.length === 0 || - query.isFetching === true - ); - }; - const isInternational = (shipment: ShipmentType) => { - return ( - shipment.recipient.country_code !== undefined && - shipment.shipper.country_code !== undefined && - shipment.recipient.country_code !== shipment.shipper.country_code - ); - }; - const getCarrier = (rate: ShipmentType["rates"][0]) => - user_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - ) || - system_connections?.find( - (_) => - _.id === rate.meta.carrier_connection_id || - _.carrier_id === rate.carrier_id, - ); - const getItems = () => { - return (orders.data?.orders.edges || []) - .map(({ node: { line_items } }) => line_items) - .flat(); - }; - const getParent = (id: string | null) => { - return getItems().find((item) => item.id === id); - }; - const getOrder = (item_id?: string | null) => { - return (orders.data?.orders.edges || []).find(({ node: order }) => - order.line_items.find((item) => item.id === item_id), - )?.node; - }; - const getLinkedOrders = ( - orderList?: get_orders_orders_edges[], - shipment?: ShipmentType, - ) => { - if (shipment && orderList) { - const parents = Array.from( - new Set( - getShipmentCommodities(shipment) - .filter(({ parent_id }) => !!parent_id) - .map(({ parent_id }) => parent_id), - ), - ); - return (orderList || []) - .map(({ node }) => node) - .filter( - ({ line_items = [] }) => - line_items.filter(({ id }) => parents.includes(id)).length > 0, - ) as OrderType[]; - } - - return []; - }; - const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { - const item = getShipmentCommodities(shipment).find( - (item) => - (!!cdt.parent_id && cdt.parent_id === item.parent_id) || - (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || - (!!cdt.sku && cdt.sku === item.sku), - ); - return !!item; - }; - const getAvailableQuantity = ( - shipment: ShipmentType, - item: CommodityType, - item_index: number, - ) => { - const parent_quantity = - getParent(item.parent_id)?.unfulfilled_quantity || 0; - const packed_quantity = shipment.parcels - .map(({ items }) => items || []) - .flat() - .filter((_, index) => index !== item_index) - .reduce((acc, { parent_id, quantity }) => { - return parent_id === item.parent_id ? acc + (quantity as number) : 0; - }, 0); - - return parent_quantity - packed_quantity; - }; - const setInitialData = () => { - const shipper = - templates.data?.default_templates.default_address?.address || - ({} as any); - const parcel = { - ...(templates.data?.default_templates.default_parcel?.parcel || - DEFAULT_PARCEL_CONTENT), - }; - - if ( - !!workspace_config.query.data?.workspace_config?.federal_tax_id && - !shipper.federal_tax_id - ) { - shipper.federal_tax_id = - workspace_config.query.data?.workspace_config?.federal_tax_id; - } - if ( - !!workspace_config.query.data?.workspace_config?.state_tax_id && - !shipper.state_tax_id - ) { - shipper.state_tax_id = - workspace_config.query.data?.workspace_config?.state_tax_id; - } - - onChange({ - ...(shipper - ? { shipper: shipper as (typeof shipment)["shipper"] } - : {}), - ...(parcel - ? { parcels: [parcel] as (typeof shipment)["parcels"] } - : {}), - label_type: LabelTypeEnum.PDF, - }); - - setReady(true); - }; - const onChange = async (changes: Partial) => { - if (changes === undefined) { - return; - } - await mutation.updateShipment({ id: shipment_id, ...changes }); - setKey(`${shipment_id}-${Date.now()}`); - }; - - useEffect(() => { - if (shipment.status && shipment.status !== ShipmentStatusEnum.draft) { - notifier.notify({ - type: NotificationType.info, - message: "Label already purchased! redirecting...", - }); - setTimeout(() => router.push(basePath), 2000); - } - }, [shipment]); - useEffect(() => { - if (ready) return; - const orders_called = (ORDERS_MANAGEMENT && !orders.isLoading) || true; - - if ( - !ready && - !templates.isLoading && - !workspace_config.query.isLoading && - shipment_id === "new" && - orders_called - ) { - setTimeout(() => setInitialData(), 1000); - } - if ( - !ready && - query.isLoading && - !isNoneOrEmpty(shipment_id) && - shipment_id !== "new" && - orders_called - ) { - setReady(true); - } - }, [ - ready, - templates.isLoading, - orders.isLoading, - query.isLoading, - workspace_config.query.isLoading, - shipment_id, - shipment, - ]); - - return ( - <> - -
- Create label -
- - {(shipment.messages || []).length > 0 && ( -
- -
- )} - - {!ready && } - - {/* Shipment details section */} - {ready && ( -
-
- {/* Address section */} -
-
-
- - RECIPIENT - -
- - onChange({ recipient: address }) - } - trigger={ - - } - /> -
-
- - - - {Object.values(shipment.recipient || {}).length === 0 && ( -
- Please add a recipient address. -
- )} -
- -
- -
-
- - SHIPPER - -
- onChange({ shipper: address })} - trigger={ - - } - /> -
-
- - - - {Object.values(shipment.shipper || {}).length === 0 && ( -
- Please specify the shipper address. -
- )} -
- - {/* Retrun address section */} -
- -
-
-
- { - setAddReturn(e.target.checked); - if ( - !e.target.checked && - !isNone(shipment.return_address) - ) { - onChange({ return_address: null }); - } - }} - > - Add a return address (optional) - -
-
- {(addReturn || !isNone(shipment.return_address)) && ( - - onChange({ return_address: address }) - } - trigger={ - - } - /> - )} -
-
- -
- {shipment?.return_address && ( - - )} -
- - {Object.values(shipment?.return_address || []).length === - 0 && ( -
- - Use this to specify an origin address different from - the shipper address above.
- This address will be used for pickup and return. -
-
- )} -
- - {/* Billing address section */} -
- -
- - -
- - - -
- - {shipment.payment?.paid_by && - shipment.payment?.paid_by !== PaidByEnum.sender && ( -
- - mutation.updateShipment({ - payment: { - ...shipment.payment, - account_number: e.target.value, - }, - }) - } - /> -
- )} -
- - {(shipment?.billing_address || - shipment.payment?.paid_by === PaidByEnum.third_party) && ( - <> -
-
- -
- - onChange({ billing_address: address }) - } - trigger={ - - } - /> -
-
- - {shipment?.billing_address && ( - - )} - - {isNone(shipment?.billing_address) && ( -
- Add shipment billing address. (optional) -
- )} -
- - )} -
- - {/* Parcel & Items section */} -
-
- - PACKAGES - -
- - Add package - - } - /> -
-
- -
- - {shipment.parcels.map((pkg, pkg_index) => ( - - {pkg_index > 0 && ( -
- )} - -
- {/* Parcel header */} -
-
- - {pkg_index + 1} - - } - /> -
-
- - - - - - } - /> - -
-
- - {/* Items section */} - - ITEMS - - - {(pkg.items || []).map((item, item_index) => ( - -
-
-
-

- {item_index + 1}{" "} - {`${item.title || item.description || "Item"}`} -

-

- {isNoneOrEmpty(item.sku) - ? "SKU: 0000000" - : `SKU: ${item.sku}`} - {getOrder(item.parent_id) && ( - - {` | ORDER: ${getOrder(item.parent_id)?.order_id}`} - - )} -

-

-
-
-
- - {formatWeight(item)} - -
-

- { - mutation.updateItem( - pkg_index, - item_index, - pkg.id, - )({ - quantity: parseInt(e.target.value), - } as CommodityType); - }} - className="input is-small" - style={{ - width: "60px", - textAlign: "center", - }} - {...(getParent(item.parent_id) - ? { - max: getAvailableQuantity( - shipment, - item, - item_index, - ), - } - : {})} - /> -

- {getParent(item.parent_id) && ( -

- - of{" "} - {getParent(item.parent_id) - ?.unfulfilled_quantity || - item.quantity} - -

- )} -
-
- - {({ editCommodity }) => ( - - )} - - -
-
-
- ))} - - {(pkg.items || []).length === 0 && ( -
- You can specify content items. -
- )} - -
- - {({ editCommodity }) => ( - - )} - - {ORDERS_MANAGEMENT && ( - - mutation.addItems(pkg_index, pkg.id)(_ as any) - } - /> - )} -
-
-
- ))} - - {(shipment.parcels || []).length === 0 && ( -
- Add one or more packages to create a shipment. -
- )} -
- - {/* Shipping options section */} -
-
- - OPTIONS - -
- -
- -
- {/* shipment date */} - - onChange({ - options: { - ...shipment.options, - shipment_date: e.target.value, - }, - }) - } - /> - - {/* currency */} - - onChange({ - options: { - ...shipment.options, - currency: e.target.value, - }, - }) - } - > - - {CURRENCY_OPTIONS.map((unit) => ( - - ))} - - - {/* signature confirmation */} - - onChange({ - options: { - ...shipment.options, - signature_confirmation: e.target.checked || null, - }, - }) - } - > - Add signature confirmation - - - {/* insurance */} - - onChange({ - options: { - ...shipment.options, - insurance: e.target.checked === true ? "" : null, - }, - }) - } - > - Add insurance coverage - - -
- - onChange({ - options: { - ...shipment.options, - insurance: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Cash on delivery */} - - onChange({ - options: { - ...shipment.options, - cash_on_delivery: - e.target.checked === true ? "" : null, - }, - }) - } - > - Collect on delivery - - -
- - onChange({ - options: { - ...shipment.options, - cash_on_delivery: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* Declared value */} - - onChange({ - options: { - ...shipment.options, - declared_value: - e.target.checked === true ? "" : null, - }, - }) - } - > - Add package value - - -
- - onChange({ - options: { - ...shipment.options, - declared_value: parseFloat(e.target.value), - }, - }) - } - iconLeft={ - - - - } - iconRight={ - - {shipment.options?.currency} - - } - /> -
- - {/* paperless trade */} - - onChange({ - options: { - ...shipment.options, - paperless_trade: e.target.checked, - }, - }) - } - > - Paperless trade - - - {/* hold at location */} - - onChange({ - options: { - ...shipment.options, - hold_at_location: e.target.checked, - }, - }) - } - > - Hold at location - - - {/* dangerous good */} - - onChange({ - options: { - ...shipment.options, - dangerous_good: e.target.checked, - }, - }) - } - > - Dangerous good - -
- - {/* CARRIER OPTIONS SECTION */} - {Object.keys(carrierOptions).length > 0 && ( -
- - {({ open }) => ( -
- -
- CARRIER SPECIFIC OPTIONS -
- - {open ? ( - - ) : ( - - )} - -
- - {Object.entries(carrierOptions).map( - ([carrier, options]) => ( - - -
- -
- {options.map((option, index) => ( - - {references!.options[carrier][option] - ?.type === "boolean" && ( - <> - - onChange({ - options: { - ...shipment.options, - [option]: - e.target.checked || - null, - }, - }) - } - > - {formatRef(option)} - - - )} - - {references!.options[carrier][option] - ?.type === "string" && ( - <> - - onChange({ - options: { - ...shipment.options, - [option]: e.target.value, - }, - }) - } - /> - - )} - - ))} -
- -
-
- ), - )} -
-
- )} -
-
- )} - -
- -
- - mutation.updateShipment({ - reference: e.target.value as string, - }) - } - placeholder="shipment reference" - className="is-small" - autoComplete="off" - /> -
-
- - {/* Customs declaration section */} - {isInternational(shipment) && ( -
-
- - CUSTOMS DECLARATION - -
- - Edit customs info - - } - /> -
-
- -
- -
- {!isNone(shipment.customs) && ( - <> - - - {/* Commodities section */} - - COMMODITIES - - - {(shipment.customs!.commodities || []).map( - (commodity, index) => ( - -
-
- -
- - {({ editCommodity }) => ( - - )} - - -
-
-
- ), - )} - - {(shipment.customs!.commodities || []).length === - 0 && ( -
- You need provide commodity items for customs - purpose. (required) -
- )} - -
- - {({ editCommodity }) => ( - - )} - - {ORDERS_MANAGEMENT && ( - - mutation.addCommodities(_ as any) - } - /> - )} -
- - {/* Duty Billing address section */} - {(shipment.customs!.duty_billing_address || - shipment.customs!.duty?.paid_by === - PaidByEnum.third_party) && ( - <> -
- -
-
- -
- - mutation.updateShipment({ - customs: { - ...shipment!.customs, - duty_billing_address: address, - } as any, - }) - } - trigger={ - - } - /> -
-
- - {shipment!.customs!.duty_billing_address && ( - - )} - - {isNone( - shipment!.customs!.duty_billing_address, - ) && ( -
- Add customs duty billing address. (optional) -
- )} -
- - )} - - )} - - {isNone(shipment.customs) && ( -
- Looks like you have an international shipment. You may - need to provide a customs declaration unless you are - shipping documents only. -
- )} -
-
- )} -
- -
- - {/* Shipment details section */} -
-
- {/* Shipment Summary */} - - - {/* Purchase shipment section */} -
-
- - SHIPPING SERVICES - -
- -
-
- -
- - {/* Live rates section */} -
- {!query.isFetched && - query.isFetching && - (shipment.rates || []).length === 0 && ( - - )} - - {query.isFetched && - !query.isFetching && - (shipment.rates || []).length === 0 && ( -
- Provide all shipping details to retrieve shipping - rates. -
- )} - - {query.isFetched && (shipment.rates || []).length > 0 && ( - - )} -
- -
- -
-
- - -
-
- - - mutation.buyLabel.mutateAsync(selected_rate as any) - } - fieldClass="has-text-centered py-1 px-6 m-0" - className="is-success is-fullwidth" - disabled={ - (shipment.rates || []).filter( - (r) => r.id === selected_rate?.id, - ).length === 0 || - mutation.buyLabel.isLoading || - query.isFetching - } - > - Buy shipping label - - -
- - {!(!!shipment.id && shipment.id !== "new") && ( - mutation.saveDraft.mutateAsync({})} - fieldClass="has-text-centered py-1 px-6 m-0" - className="is-default is-fullwidth" - disabled={ - query.isFetching || mutation.saveDraft.isLoading - } - > - Save draft - - )} - -
-
- - {/* Metadata section */} -
-
- onChange({ metadata })} - > - - {({ isEditing, editMetadata }) => ( - <> -
- - METADATA - -
- -
-
- - )} -
-
-
-
- - {/* Instructions section */} -
-
- - onChange({ - options: { - ...shipment.options, - instructions: e.target.value, - }, - }) - } - /> -
-
-
-
-
- )} -
- - ); - }; - - return AuthenticatedPage( - - - - {`Create label - ${(pageProps as any).metadata?.APP_NAME}`} - - - - - - , - pageProps, - ); -} +import { + AddressType, + CommodityType, + CURRENCY_OPTIONS, + CustomsType, + NotificationType, + OrderType, + ShipmentType, + get_orders_orders_edges, + LabelTypeEnum, + MetadataObjectTypeEnum, + PaidByEnum, + ShipmentStatusEnum, + DEFAULT_PARCEL_CONTENT, + DEFAULT_CUSTOMS_CONTENT, +} from "@karrio/types"; +import { + formatRef, + formatWeight, + getShipmentCommodities, + isNone, + isNoneOrEmpty, + useLocation, +} from "@karrio/lib"; +import { + AddressModalEditor, + CustomsModalEditor, + ParcelModalEditor, +} from "@karrio/ui/modals/form-modals"; +import { + CommodityEditModalProvider, + CommodityStateContext, +} from "@karrio/ui/modals/commodity-edit-modal"; +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; +import { GoogleGeocodingScript } from "@karrio/ui/components/google-geocoding-script"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { MessagesDescription } from "@karrio/ui/components/messages-description"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { ParcelDescription } from "@karrio/ui/components/parcel-description"; +import { CommoditySummary } from "@karrio/ui/components/commodity-summary"; +import { RateDescription } from "@karrio/ui/components/rate-description"; +import { LineItemSelector } from "@karrio/ui/forms/line-item-selector"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { useDefaultTemplates } from "@karrio/hooks/default-template"; +import { CheckBoxField } from "@karrio/ui/components/checkbox-field"; +import { TextAreaField } from "@karrio/ui/components/textarea-field"; +import { useWorkspaceConfig } from "@karrio/hooks/workspace-config"; +import { useConnections } from "@karrio/hooks/carrier-connections"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { ButtonField } from "@karrio/ui/components/button-field"; +import { SelectField } from "@karrio/ui/components/select-field"; +import { useLabelDataMutation } from "@karrio/hooks/label-data"; +import { InputField } from "@karrio/ui/components/input-field"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useNotifier } from "@karrio/ui/components/notifier"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { useAppMode } from "@karrio/hooks/app-mode"; +import React, { useEffect, useState } from "react"; +import { useOrders } from "@karrio/hooks/order"; +import { Disclosure } from "@headlessui/react"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +const ContextProviders = bundleContexts([ + CommodityEditModalProvider, + ModalProvider, +]); + +export default function CreateLabelPage(pageProps: any) { + const { ORDERS_MANAGEMENT } = pageProps?.metadata || {}; + + const Component: React.FC = () => { + const notifier = useNotifier(); + const { basePath } = useAppMode(); + const { references } = useAPIMetadata(); + const { carrierOptions } = useConnections(); + const workspace_config = useWorkspaceConfig(); + const { addUrlParam, ...router } = useLocation(); + const { query: templates } = useDefaultTemplates(); + const [ready, setReady] = useState(false); + const { shipment_id = "new" } = router.query as any; + const [key, setKey] = useState(`${shipment_id}-${Date.now()}`); + const [addReturn, setAddReturn] = useState(false); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const { + state: { shipment, query }, + ...mutation + } = useLabelDataMutation(shipment_id); + const { query: orders } = useOrders({ + first: 10, + status: ["unfulfilled", "partial"] as any, + }); + const [selected_rate, setSelectedRate] = useState< + ShipmentType["rates"][0] | undefined + >( + shipment?.selected_rate_id + ? ({ id: shipment?.selected_rate_id } as any) + : undefined, + ); + + const requireInfoForRating = (shipment: ShipmentType) => { + return ( + shipment.recipient.address_line1 === undefined || + shipment.shipper.address_line1 === undefined || + shipment.parcels.length === 0 || + query.isFetching === true + ); + }; + const isInternational = (shipment: ShipmentType) => { + return ( + shipment.recipient.country_code !== undefined && + shipment.shipper.country_code !== undefined && + shipment.recipient.country_code !== shipment.shipper.country_code + ); + }; + const getCarrier = (rate: ShipmentType["rates"][0]) => + user_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate.meta.carrier_connection_id || + _.carrier_id === rate.carrier_id, + ); + const getItems = () => { + return (orders.data?.orders.edges || []) + .map(({ node: { line_items } }) => line_items) + .flat(); + }; + const getParent = (id: string | null) => { + return getItems().find((item) => item.id === id); + }; + const getOrder = (item_id?: string | null) => { + return (orders.data?.orders.edges || []).find(({ node: order }) => + order.line_items.find((item) => item.id === item_id), + )?.node; + }; + const getLinkedOrders = ( + orderList?: get_orders_orders_edges[], + shipment?: ShipmentType, + ) => { + if (shipment && orderList) { + const parents = Array.from( + new Set( + getShipmentCommodities(shipment) + .filter(({ parent_id }) => !!parent_id) + .map(({ parent_id }) => parent_id), + ), + ); + return (orderList || []) + .map(({ node }) => node) + .filter( + ({ line_items = [] }) => + line_items.filter(({ id }) => parents.includes(id)).length > 0, + ) as OrderType[]; + } + + return []; + }; + const isPackedItem = (cdt: CommodityType, shipment: ShipmentType) => { + const item = getShipmentCommodities(shipment).find( + (item) => + (!!cdt.parent_id && cdt.parent_id === item.parent_id) || + (!!cdt.hs_code && cdt.hs_code === cdt.hs_code) || + (!!cdt.sku && cdt.sku === item.sku), + ); + return !!item; + }; + const getAvailableQuantity = ( + shipment: ShipmentType, + item: CommodityType, + item_index: number, + ) => { + const parent_quantity = + getParent(item.parent_id)?.unfulfilled_quantity || 0; + const packed_quantity = shipment.parcels + .map(({ items }) => items || []) + .flat() + .filter((_, index) => index !== item_index) + .reduce((acc, { parent_id, quantity }) => { + return parent_id === item.parent_id ? acc + (quantity as number) : 0; + }, 0); + + return parent_quantity - packed_quantity; + }; + const setInitialData = () => { + const shipper = + templates.data?.default_templates.default_address?.address || + ({} as any); + const parcel = { + ...(templates.data?.default_templates.default_parcel?.parcel || + DEFAULT_PARCEL_CONTENT), + }; + + if ( + !!workspace_config.query.data?.workspace_config?.federal_tax_id && + !shipper.federal_tax_id + ) { + shipper.federal_tax_id = + workspace_config.query.data?.workspace_config?.federal_tax_id; + } + if ( + !!workspace_config.query.data?.workspace_config?.state_tax_id && + !shipper.state_tax_id + ) { + shipper.state_tax_id = + workspace_config.query.data?.workspace_config?.state_tax_id; + } + + onChange({ + ...(shipper + ? { shipper: shipper as (typeof shipment)["shipper"] } + : {}), + ...(parcel + ? { parcels: [parcel] as (typeof shipment)["parcels"] } + : {}), + label_type: LabelTypeEnum.PDF, + }); + + setReady(true); + }; + const onChange = async (changes: Partial) => { + if (changes === undefined) { + return; + } + await mutation.updateShipment({ id: shipment_id, ...changes }); + setKey(`${shipment_id}-${Date.now()}`); + }; + + useEffect(() => { + if (shipment.status && shipment.status !== ShipmentStatusEnum.draft) { + notifier.notify({ + type: NotificationType.info, + message: "Label already purchased! redirecting...", + }); + setTimeout(() => router.push(basePath), 2000); + } + }, [shipment]); + useEffect(() => { + if (ready) return; + const orders_called = (ORDERS_MANAGEMENT && !orders.isLoading) || true; + + if ( + !ready && + !templates.isLoading && + !workspace_config.query.isLoading && + shipment_id === "new" && + orders_called + ) { + setTimeout(() => setInitialData(), 1000); + } + if ( + !ready && + query.isLoading && + !isNoneOrEmpty(shipment_id) && + shipment_id !== "new" && + orders_called + ) { + setReady(true); + } + }, [ + ready, + templates.isLoading, + orders.isLoading, + query.isLoading, + workspace_config.query.isLoading, + shipment_id, + shipment, + ]); + + return ( + <> + +
+ Create label +
+ + {(shipment.messages || []).length > 0 && ( +
+ +
+ )} + + {!ready && } + + {/* Shipment details section */} + {ready && ( +
+
+ {/* Address section */} +
+
+
+ + RECIPIENT + +
+ + onChange({ recipient: address }) + } + trigger={ + + } + /> +
+
+ + + + {Object.values(shipment.recipient || {}).length === 0 && ( +
+ Please add a recipient address. +
+ )} +
+ +
+ +
+
+ + SHIPPER + +
+ onChange({ shipper: address })} + trigger={ + + } + /> +
+
+ + + + {Object.values(shipment.shipper || {}).length === 0 && ( +
+ Please specify the shipper address. +
+ )} +
+ + {/* Retrun address section */} +
+ +
+
+
+ { + setAddReturn(e.target.checked); + if ( + !e.target.checked && + !isNone(shipment.return_address) + ) { + onChange({ return_address: null }); + } + }} + > + Add a return address (optional) + +
+
+ {(addReturn || !isNone(shipment.return_address)) && ( + + onChange({ return_address: address }) + } + trigger={ + + } + /> + )} +
+
+ +
+ {shipment?.return_address && ( + + )} +
+ + {Object.values(shipment?.return_address || []).length === + 0 && ( +
+ + Use this to specify an origin address different from + the shipper address above.
+ This address will be used for pickup and return. +
+
+ )} +
+ + {/* Billing address section */} +
+ +
+ + +
+ + + +
+ + {shipment.payment?.paid_by && + shipment.payment?.paid_by !== PaidByEnum.sender && ( +
+ + mutation.updateShipment({ + payment: { + ...shipment.payment, + account_number: e.target.value, + }, + }) + } + /> +
+ )} +
+ + {(shipment?.billing_address || + shipment.payment?.paid_by === PaidByEnum.third_party) && ( + <> +
+
+ +
+ + onChange({ billing_address: address }) + } + trigger={ + + } + /> +
+
+ + {shipment?.billing_address && ( + + )} + + {isNone(shipment?.billing_address) && ( +
+ Add shipment billing address. (optional) +
+ )} +
+ + )} +
+ + {/* Parcel & Items section */} +
+
+ + PACKAGES + +
+ + Add package + + } + /> +
+
+ +
+ + {shipment.parcels.map((pkg, pkg_index) => ( + + {pkg_index > 0 && ( +
+ )} + +
+ {/* Parcel header */} +
+
+ + {pkg_index + 1} + + } + /> +
+
+ + + + + + } + /> + +
+
+ + {/* Items section */} + + ITEMS + + + {(pkg.items || []).map((item, item_index) => ( + +
+
+
+

+ {item_index + 1}{" "} + {`${item.title || item.description || "Item"}`} +

+

+ {isNoneOrEmpty(item.sku) + ? "SKU: 0000000" + : `SKU: ${item.sku}`} + {getOrder(item.parent_id) && ( + + {` | ORDER: ${getOrder(item.parent_id)?.order_id}`} + + )} +

+

+
+
+
+ + {formatWeight(item)} + +
+

+ { + mutation.updateItem( + pkg_index, + item_index, + pkg.id, + )({ + quantity: parseInt(e.target.value), + } as CommodityType); + }} + className="input is-small" + style={{ + width: "60px", + textAlign: "center", + }} + {...(getParent(item.parent_id) + ? { + max: getAvailableQuantity( + shipment, + item, + item_index, + ), + } + : {})} + /> +

+ {getParent(item.parent_id) && ( +

+ + of{" "} + {getParent(item.parent_id) + ?.unfulfilled_quantity || + item.quantity} + +

+ )} +
+
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ))} + + {(pkg.items || []).length === 0 && ( +
+ You can specify content items. +
+ )} + +
+ + {({ editCommodity }) => ( + + )} + + {ORDERS_MANAGEMENT && ( + + mutation.addItems(pkg_index, pkg.id)(_ as any) + } + /> + )} +
+
+
+ ))} + + {(shipment.parcels || []).length === 0 && ( +
+ Add one or more packages to create a shipment. +
+ )} +
+ + {/* Shipping options section */} +
+
+ + OPTIONS + +
+ +
+ +
+ {/* shipment date */} + + onChange({ + options: { + ...shipment.options, + shipment_date: e.target.value, + }, + }) + } + /> + + {/* currency */} + + onChange({ + options: { + ...shipment.options, + currency: e.target.value, + }, + }) + } + > + + {CURRENCY_OPTIONS.map((unit) => ( + + ))} + + + {/* signature confirmation */} + + onChange({ + options: { + ...shipment.options, + signature_confirmation: e.target.checked || null, + }, + }) + } + > + Add signature confirmation + + + {/* insurance */} + + onChange({ + options: { + ...shipment.options, + insurance: e.target.checked === true ? "" : null, + }, + }) + } + > + Add insurance coverage + + +
+ + onChange({ + options: { + ...shipment.options, + insurance: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Cash on delivery */} + + onChange({ + options: { + ...shipment.options, + cash_on_delivery: + e.target.checked === true ? "" : null, + }, + }) + } + > + Collect on delivery + + +
+ + onChange({ + options: { + ...shipment.options, + cash_on_delivery: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* Declared value */} + + onChange({ + options: { + ...shipment.options, + declared_value: + e.target.checked === true ? "" : null, + }, + }) + } + > + Add package value + + +
+ + onChange({ + options: { + ...shipment.options, + declared_value: parseFloat(e.target.value), + }, + }) + } + iconLeft={ + + + + } + iconRight={ + + {shipment.options?.currency} + + } + /> +
+ + {/* paperless trade */} + + onChange({ + options: { + ...shipment.options, + paperless_trade: e.target.checked, + }, + }) + } + > + Paperless trade + + + {/* hold at location */} + + onChange({ + options: { + ...shipment.options, + hold_at_location: e.target.checked, + }, + }) + } + > + Hold at location + + + {/* dangerous good */} + + onChange({ + options: { + ...shipment.options, + dangerous_good: e.target.checked, + }, + }) + } + > + Dangerous good + +
+ + {/* CARRIER OPTIONS SECTION */} + {Object.keys(carrierOptions).length > 0 && ( +
+ + {({ open }) => ( +
+ +
+ CARRIER SPECIFIC OPTIONS +
+ + {open ? ( + + ) : ( + + )} + +
+ + {Object.entries(carrierOptions).map( + ([carrier, options]) => ( + + +
+ +
+ {options.map((option, index) => ( + + {references!.options[carrier][option] + ?.type === "boolean" && ( + <> + + onChange({ + options: { + ...shipment.options, + [option]: + e.target.checked || + null, + }, + }) + } + > + {formatRef(option)} + + + )} + + {references!.options[carrier][option] + ?.type === "string" && ( + <> + + onChange({ + options: { + ...shipment.options, + [option]: e.target.value, + }, + }) + } + /> + + )} + + ))} +
+ +
+
+ ), + )} +
+
+ )} +
+
+ )} + +
+ +
+ + mutation.updateShipment({ + reference: e.target.value as string, + }) + } + placeholder="shipment reference" + className="is-small" + autoComplete="off" + /> +
+
+ + {/* Customs declaration section */} + {isInternational(shipment) && ( +
+
+ + CUSTOMS DECLARATION + +
+ + Edit customs info + + } + /> +
+
+ +
+ +
+ {!isNone(shipment.customs) && ( + <> + + + {/* Commodities section */} + + COMMODITIES + + + {(shipment.customs!.commodities || []).map( + (commodity, index) => ( + +
+
+ +
+ + {({ editCommodity }) => ( + + )} + + +
+
+
+ ), + )} + + {(shipment.customs!.commodities || []).length === + 0 && ( +
+ You need provide commodity items for customs + purpose. (required) +
+ )} + +
+ + {({ editCommodity }) => ( + + )} + + {ORDERS_MANAGEMENT && ( + + mutation.addCommodities(_ as any) + } + /> + )} +
+ + {/* Duty Billing address section */} + {(shipment.customs!.duty_billing_address || + shipment.customs!.duty?.paid_by === + PaidByEnum.third_party) && ( + <> +
+ +
+
+ +
+ + mutation.updateShipment({ + customs: { + ...shipment!.customs, + duty_billing_address: address, + } as any, + }) + } + trigger={ + + } + /> +
+
+ + {shipment!.customs!.duty_billing_address && ( + + )} + + {isNone( + shipment!.customs!.duty_billing_address, + ) && ( +
+ Add customs duty billing address. (optional) +
+ )} +
+ + )} + + )} + + {isNone(shipment.customs) && ( +
+ Looks like you have an international shipment. You may + need to provide a customs declaration unless you are + shipping documents only. +
+ )} +
+
+ )} +
+ +
+ + {/* Shipment details section */} +
+
+ {/* Shipment Summary */} + + + {/* Purchase shipment section */} +
+
+ + SHIPPING SERVICES + +
+ +
+
+ +
+ + {/* Live rates section */} +
+ {!query.isFetched && + query.isFetching && + (shipment.rates || []).length === 0 && ( + + )} + + {query.isFetched && + !query.isFetching && + (shipment.rates || []).length === 0 && ( +
+ Provide all shipping details to retrieve shipping + rates. +
+ )} + + {query.isFetched && (shipment.rates || []).length > 0 && ( + + )} +
+ +
+ +
+
+ + +
+
+ + + mutation.buyLabel.mutateAsync(selected_rate as any) + } + fieldClass="has-text-centered py-1 px-6 m-0" + className="is-success is-fullwidth" + disabled={ + (shipment.rates || []).filter( + (r) => r.id === selected_rate?.id, + ).length === 0 || + mutation.buyLabel.isLoading || + query.isFetching + } + > + Buy shipping label + + +
+ + {!(!!shipment.id && shipment.id !== "new") && ( + mutation.saveDraft.mutateAsync({})} + fieldClass="has-text-centered py-1 px-6 m-0" + className="is-default is-fullwidth" + disabled={ + query.isFetching || mutation.saveDraft.isLoading + } + > + Save draft + + )} + +
+
+ + {/* Metadata section */} +
+
+ onChange({ metadata })} + > + + {({ isEditing, editMetadata }) => ( + <> +
+ + METADATA + +
+ +
+
+ + )} +
+
+
+
+ + {/* Instructions section */} +
+
+ + onChange({ + options: { + ...shipment.options, + instructions: e.target.value, + }, + }) + } + /> +
+
+
+
+
+ )} +
+ + ); + }; + + return AuthenticatedPage( + + + + {`Create label - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Shipments/index.tsx b/packages/core/modules/Shipments/index.tsx new file mode 100644 index 0000000000..c8e7702471 --- /dev/null +++ b/packages/core/modules/Shipments/index.tsx @@ -0,0 +1,581 @@ +import { + formatAddressShort, + formatAddressLocationShort, + formatDateTime, + formatRef, + getURLSearchParams, + isListEqual, + isNone, + isNoneOrEmpty, + formatCarrierSlug, + url$, + preventPropagation, +} from "@karrio/lib"; +import { + ShipmentPreview, + ShipmentPreviewContext, +} from "../../components/shipment-preview"; +import { useSystemCarrierConnections } from "@karrio/hooks/admin/connections"; +import { useDocumentTemplates } from "@karrio/hooks/document-template"; +import { useCarrierConnections } from "@karrio/hooks/user-connection"; +import { ShipmentsFilter } from "@karrio/ui/filters/shipments-filter"; +import { ShipmentMenu } from "@karrio/ui/components/shipment-menu"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AddressType, ShipmentType } from "@karrio/types"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { useShipments } from "@karrio/hooks/shipment"; +import React, { useContext, useEffect } from "react"; +import { useRouter } from "next/dist/client/router"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function ShipmentsPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const { setLoading } = useLoader(); + const { metadata } = useAPIMetadata(); + const [allChecked, setAllChecked] = React.useState(false); + const [initialized, setInitialized] = React.useState(false); + const [selection, setSelection] = React.useState([]); + const { previewShipment } = useContext(ShipmentPreviewContext); + const { + query: { data: { user_connections } = {} }, + } = useCarrierConnections(); + const { + query: { data: { system_connections } = {} }, + } = useSystemCarrierConnections(); + const context = useShipments({ + status: [ + "purchased", + "delivered", + "in_transit", + "cancelled", + "needs_attention", + "out_for_delivery", + "delivery_failed", + ] as any, + setVariablesToURL: true, + preloadNextPage: true, + }); + const { + query: { data: { shipments } = {}, ...query }, + filter, + setFilter, + } = context; + const { + query: { data: { document_templates } = {} }, + } = useDocumentTemplates({ + related_object: "shipment" as any, + active: true, + }); + + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + const updatedSelection = ( + selectedShipments: string[], + current: typeof shipments, + ) => { + const shipment_ids = (current?.edges || []).map( + ({ node: shipment }) => shipment.id, + ); + const selection = selectedShipments.filter((id) => + shipment_ids.includes(id), + ); + const selected = + selection.length > 0 && + selection.length === (shipment_ids || []).length; + setAllChecked(selected); + if ( + selectedShipments.filter((id) => !shipment_ids.includes(id)).length > 0 + ) { + setSelection(selection); + } + }; + const handleSelection = (e: React.ChangeEvent) => { + const { checked, name } = e.target as HTMLInputElement; + if (name === "all") { + setSelection( + !checked + ? [] + : (shipments?.edges || []).map(({ node: { id } }) => id), + ); + } else { + setSelection( + checked + ? [...selection, name] + : selection.filter((id) => id !== name), + ); + } + }; + const computeDocFormat = (selection: string[]) => { + const _shipment = (shipments?.edges || []).find( + ({ node: shipment }) => shipment.id == selection[0], + ); + return (_shipment?.node || {}).label_type; + }; + const compatibleTypeSelection = (selection: string[]) => { + const format = computeDocFormat(selection); + return ( + (shipments?.edges || []).filter( + ({ node: shipment }) => + selection.includes(shipment.id) && shipment.label_type == format, + ).length === selection.length + ); + }; + const draftSelection = (selection: string[]) => { + return ( + (shipments?.edges || []).filter( + ({ node: shipment }) => + selection.includes(shipment.id) && shipment.status == "draft", + ).length === selection.length + ); + }; + const getRate = (shipment: any) => + shipment.selected_rate || + (shipment?.rates || []).find( + (_) => _.service === shipment?.options?.preferred_service, + ) || + (shipment?.rates || [])[0] || + shipment; + const getCarrier = (rate?: ShipmentType["rates"][0]) => + user_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ) || + system_connections?.find( + (_) => + _.id === rate?.meta?.carrier_connection_id || + _.carrier_id === rate?.carrier_id, + ); + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + updatedSelection(selection, shipments); + }, [selection, shipments]); + useEffect(() => { + if ( + query.isFetched && + !initialized && + !isNoneOrEmpty(router.query.modal) + ) { + previewShipment(router.query.modal as string); + setInitialized(true); + } + }, [router.query.modal, query.isFetched]); + + return ( + <> +
+
+ Shipments +
+
+ + Create Label + + + Manage manifests + + +
+
+ + + + {!query.isFetched && } + + {query.isFetched && (shipments?.edges || []).length > 0 && ( + <> +
+ + + + + + {selection.length > 0 && ( + + )} + + {selection.length === 0 && ( + <> + + + + + + + + )} + + + {(shipments?.edges || []).map(({ node: shipment }) => ( + + + + + + + + + + ))} + +
+ + +
+ + + Create labels + + + + + Print Labels + + + + + Print Invoices + + + {(document_templates?.edges || []).map( + ({ node: template }) => ( + + + Print {template.name} + + + ), + )} +
+
+ SHIPPING SERVICE + + RECIPIENT + + REFERENCE + DATE
+ + previewShipment(shipment.id)} + title={ + isNone(getRate(shipment)) + ? "UNFULFILLED" + : formatRef( + ((shipment.meta as any)?.service_name || + getRate(shipment).service) as string, + ) + } + > +
+ +
+ + {!isNone(shipment.tracking_number) && ( + {shipment.tracking_number} + )} + {isNone(shipment.tracking_number) && ( + - + )} + +
+ + {!isNone(getRate(shipment).carrier_name) && + formatRef( + ((getRate(shipment).meta as any) + ?.service_name || + getRate(shipment).service) as string, + )} + {isNone(getRate(shipment).carrier_name) && + "UNFULFILLED"} + +
+
+
previewShipment(shipment.id)} + > + + previewShipment(shipment.id)} + > +
+

+ {formatAddressShort( + shipment.recipient as AddressType, + )} +

+

+ {formatAddressLocationShort( + shipment.recipient as AddressType, + )} +

+
+
previewShipment(shipment.id)} + > + {shipment.reference || ""} + previewShipment(shipment.id)} + > +

+ {formatDateTime(shipment.created_at)} +

+
+ +
+
+ +
+ + {(shipments?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (shipments?.edges || []).length == 0 && ( +
+
+

No shipment found.

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Shipments - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Shipments/shipment.tsx b/packages/core/modules/Shipments/shipment.tsx new file mode 100644 index 0000000000..f0c45d1037 --- /dev/null +++ b/packages/core/modules/Shipments/shipment.tsx @@ -0,0 +1,773 @@ +import { CustomsInfoDescription } from "@karrio/ui/components/customs-info-description"; +import { + MetadataEditor, + MetadataEditorContext, +} from "@karrio/ui/forms/metadata-editor"; +import { + useUploadRecordMutation, + useUploadRecords, +} from "@karrio/hooks/upload-record"; +import { CommodityDescription } from "@karrio/ui/components/commodity-description"; +import { OptionsDescription } from "@karrio/ui/components/options-description"; +import { formatDateTime, formatDayDate, formatRef, isNone } from "@karrio/lib"; +import { AddressDescription } from "@karrio/ui/components/address-description"; +import { ParcelDescription } from "@karrio/ui/components/parcel-description"; +import { CustomsType, NotificationType, ParcelType } from "@karrio/types"; +import { StatusCode } from "@karrio/ui/components/status-code-badge"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { CarrierBadge } from "@karrio/ui/components/carrier-badge"; +import { ShipmentMenu } from "@karrio/ui/components/shipment-menu"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { SelectField } from "@karrio/ui/components/select-field"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { InputField } from "@karrio/ui/components/input-field"; +import { ConfirmModal } from "@karrio/ui/modals/confirm-modal"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useNotifier } from "@karrio/ui/components/notifier"; +import { DocumentUploadData } from "@karrio/types/rest/api"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { MetadataObjectTypeEnum } from "@karrio/types"; +import { useShipment } from "@karrio/hooks/shipment"; +import { useRouter } from "next/dist/client/router"; +import { useEvents } from "@karrio/hooks/event"; +import { useLogs } from "@karrio/hooks/log"; +import Head from "next/head"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +type FileDataType = DocumentUploadData["document_files"][0]; + +export const ShipmentComponent: React.FC<{ shipmentId?: string }> = ({ + shipmentId, +}) => { + const router = useRouter(); + const notifier = useNotifier(); + const { setLoading } = useLoader(); + const $fileInput = React.useRef(); + const $fileSelectInput = React.useRef(); + const { + references: { carrier_capabilities = {} }, + } = useAPIMetadata(); + const entity_id = shipmentId || (router.query.id as string); + const { query: logs } = useLogs({ entity_id }); + const { query: events } = useEvents({ entity_id }); + const { + query: { data: { shipment } = {}, ...query }, + } = useShipment(entity_id); + const { uploadDocument } = useUploadRecordMutation(); + const { + query: { data: { results: uploads } = {}, ...documents }, + } = useUploadRecords({ shipmentId: entity_id }); + const [fileData, setFileData] = React.useState( + {} as FileDataType, + ); + + const handleFileChange = (e: React.ChangeEvent) => { + e.preventDefault(); + try { + if (!!e.target.files && !!e.target.files[0]) { + let file = e.target.files[0]; + let reader = new FileReader(); + reader.readAsDataURL(file); + reader.onloadend = () => { + let sections = (reader.result as string).split(","); + let doc_file = sections[sections.length - 1]; + let doc_name = file.name; + setFileData({ ...fileData, doc_name, doc_file }); + }; + } else { + setFileData({ doc_file: fileData.doc_file } as FileDataType); + } + } catch (_) { + setFileData({ doc_file: fileData.doc_file } as FileDataType); + } + }; + const uploadCustomsDocument = async () => { + try { + await uploadDocument.mutateAsync({ + shipment_id: entity_id, + document_files: [fileData], + }); + notifier.notify({ + type: NotificationType.success, + message: `document updloaded successfully`, + }); + if (!!$fileInput.current) $fileInput.current.value = ""; + if (!!$fileSelectInput.current) $fileSelectInput.current.value = "other"; + } catch (message: any) { + notifier.notify({ type: NotificationType.error, message }); + } + }; + + React.useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + + return ( + <> + {!query.isFetched && query.isFetching && } + + {shipment && ( + <> + {/* Header Section */} +
+
+ + SHIPMENT + +
+ + {shipment.tracking_number || "UNFULFILLED"} + + +
+ +
+
+ +
+
+ {!isNone(shipmentId) && ( + + + + + + )} + +
+ +
+
+
+
+ +
+ + {/* Reference and highlights section */} +
+
+ Date +
+ + {formatDateTime(shipment.created_at)} + +
+ + {!isNone(shipment.service) && ( + <> +
+
+ Courier +
+ +
+ +
+
+ Service Level +
+ + {formatRef( + ((shipment.meta as any)?.service_name || + shipment.service) as string, + )} + +
+ + )} + + {!isNone(shipment.reference) && ( + <> +
+
+ Reference +
+ + {shipment.reference} + +
+ + )} +
+ + {!isNone(shipment.selected_rate) && ( + <> +

Service Details

+
+ +
+
+
+
+
Service
+
+ {formatRef( + ((shipment.meta as any)?.service_name || + shipment.service) as string, + )} +
+
+
+
Courier
+
+ {formatRef(shipment.meta.carrier as string)} +
+
+
+
Rate
+
+ + {shipment.selected_rate?.total_charge} + + {shipment.selected_rate?.currency} +
+
+
+
+ Rate Provider +
+
+ {formatRef(shipment.meta.ext as string)} +
+
+
+
+ Tracking Number +
+
+ + {shipment.tracking_number as string} + +
+
+
+ + {(shipment.selected_rate?.extra_charges || []).length > 0 && ( + <> +
+

+ CHARGES +

+
+ + {(shipment.selected_rate?.extra_charges || []).map( + (charge, index) => ( +
+
+ + {charge?.name?.toLocaleLowerCase()} + +
+
+ {charge?.amount} + {!isNone(charge?.currency) && ( + {charge?.currency} + )} +
+
+ ), + )} +
+ + )} +
+
+ + )} + + {!isNone(shipment.tracker) && ( + <> +

+ Tracking Details + + + + + +

+
+
+
+
+ {!isNone(shipment.tracker?.estimated_delivery) && ( +
+
+ {shipment.tracker?.delivered + ? "Delivered" + : "Estimated Delivery"} +
+
+ {formatDayDate( + shipment.tracker!.estimated_delivery as string, + )} +
+
+ )} +
+
+ Last event +
+
+

+ {formatDayDate( + (shipment.tracker?.events || [])[0]?.date as string, + )}{" "} + + {(shipment.tracker?.events || [])[0]?.time} + +

+
+
+ {!isNone((shipment.tracker?.events || [])[0]?.location) && ( +
+
+
+ {(shipment.tracker?.events || [])[0]?.location} +
+
+ )} + {!isNone( + (shipment.tracker?.events || [])[0]?.description, + ) && ( +
+
+
+ {(shipment.tracker?.events || [])[0]?.description} +
+
+ )} +
+
+
+ + )} + + {/* Shipment details section */} +

Shipment Details

+
+ +
+
+ {/* Recipient Address section */} +
+

+ ADDRESS +

+ + +
+ + {/* Options section */} + {Object.values(shipment.options as object).length > 0 && ( +
+

+ OPTIONS +

+ + +
+ )} +
+ + {/* Parcels section */} +
+

+ PARCEL{shipment.parcels.length > 1 && "S"} +

+ + {shipment.parcels.map((parcel: ParcelType, index) => ( + +
+ +
+ {/* Parcel details */} +
+ +
+ + {/* Parcel items */} + {(parcel.items || []).length > 0 && ( +
+

+ ITEMS{" "} + + ( + {(parcel.items || []).reduce( + (acc, { quantity }) => acc + (quantity || 0), + 0, + )} + ) + +

+ +
+ {(parcel.items || []).map((item, index) => ( + +
+ +
+ ))} +
+
+ )} +
+
+ ))} +
+ + {/* Customs section */} +
+ {/* Customs details */} + {!isNone(shipment.customs) && ( +
+

+ CUSTOMS DECLARATION +

+ + +
+ )} + + {/* Customs commodities */} + {!isNone(shipment.customs) && + (shipment.customs?.commodities || []).length > 0 && ( +
+

+ COMMODITIES{" "} + + ( + {(shipment.customs?.commodities || []).reduce( + (acc, { quantity }) => acc + (quantity || 0), + 0, + )} + ) + +

+ + {(shipment.customs?.commodities || []).map( + (commodity, index) => ( + +
+ +
+ ), + )} +
+ )} +
+
+ + {/* Document section */} + {( + (carrier_capabilities[shipment.carrier_name as string] || []) as any + ).includes("paperless") && + "paperless_trade" in shipment.options && ( + <> +

Paperless Trade Documents

+ + {!documents.isFetched && documents.isFetching && } + + {documents.isFetched && + !documents.isFetching && + [...(uploads || []), ...(shipment.options.doc_files || [])] + .length == 0 && ( + <> +
+
No documents uploaded
+ + )} + + {documents.isFetched && + [...(uploads || []), ...(shipment.options.doc_files || [])] + .length > 0 && ( +
+ + + {(uploads || []).map((upload) => ( + + {(upload.documents || []).map((doc) => ( + + + + + ))} + + ))} + {(shipment.options.doc_files || []).map( + (doc: any, idx: number) => ( + + + + + ), + )} + +
+ {doc.file_name} + + + uploaded + +
+ {doc.doc_name} + + + uploaded + +
+
+ )} + +
+
+ + setFileData({ ...fileData, doc_type: e.target.value }) + } + defaultValue="other" + className="is-small is-fullwidth" + > + + + + + + + +
+ + +
+ +
+ + )} + + {/* Metadata section */} + + + {({ isEditing, editMetadata }) => ( + <> +
+

Metadata

+ + +
+ +
+ + )} +
+
+ +
+ + {/* Logs section */} +

Logs

+ + {!logs.isFetched && logs.isFetching && ( + + )} + + {logs.isFetched && (logs.data?.logs.edges || []).length == 0 && ( +
No logs
+ )} + + {logs.isFetched && (logs.data?.logs.edges || []).length > 0 && ( +
+ + + {(logs.data?.logs.edges || []).map(({ node: log }) => ( + + + + + + ))} + +
+ + + + + + {`${log.method} ${log.path}`} + + + + {formatDateTime(log.requested_at)} + +
+
+ )} + +
+ + {/* Events section */} +

Events

+ + {!events.isFetched && events.isFetching && ( + + )} + + {events.isFetched && + (events.data?.events.edges || []).length == 0 && ( +
No events
+ )} + + {events.isFetched && (events.data?.events.edges || []).length > 0 && ( +
+ + + {(events.data?.events.edges || []).map(({ node: event }) => ( + + + + + ))} + +
+ + {`${event.type}`} + + + + {formatDateTime(event.created_at)} + +
+
+ )} + + )} + + {query.isFetched && isNone(shipment) && ( +
+
+

Uh Oh!

+

{"We couldn't find any shipment with that reference"}

+
+
+ )} + + ); +}; + +export default function ShipmentPage(pageProps: any) { + return AuthenticatedPage( + + + {`Shipment - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Trackers/index.tsx b/packages/core/modules/Trackers/index.tsx new file mode 100644 index 0000000000..66c2e3d004 --- /dev/null +++ b/packages/core/modules/Trackers/index.tsx @@ -0,0 +1,369 @@ +import { + TrackerModalProvider, + TrackerModalContext, +} from "@karrio/ui/modals/track-shipment-modal"; +import { + TrackingPreview, + TrackingPreviewContext, +} from "../../components/tracking-preview"; +import { + ConfirmModal, + ConfirmModalContext, +} from "@karrio/ui/modals/confirm-modal"; +import { + formatRef, + getURLSearchParams, + isNone, + isNoneOrEmpty, +} from "@karrio/lib"; +import { useTrackerMutation, useTrackers } from "@karrio/hooks/tracker"; +import { TrackersFilter } from "@karrio/ui/filters/trackers-filter"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useLoader } from "@karrio/ui/components/loader"; +import { Spinner } from "@karrio/ui/components/spinner"; +import { TrackingEvent } from "@karrio/types/rest/api"; +import React, { useContext, useEffect } from "react"; +import { useRouter } from "next/dist/client/router"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; + +export default function TrackersPage(pageProps: any) { + const Component: React.FC = () => { + const router = useRouter(); + const { modal } = router.query; + const { setLoading } = useLoader(); + const mutation = useTrackerMutation(); + const { addTracker } = useContext(TrackerModalContext); + const { previewTracker } = useContext(TrackingPreviewContext); + const { confirm: confirmDeletion } = useContext(ConfirmModalContext); + const [initialized, setInitialized] = React.useState(false); + const context = useTrackers({ + setVariablesToURL: true, + preloadNextPage: true, + }); + const { + query: { data: { trackers } = {}, ...query }, + filter, + setFilter, + } = context; + + const remove = (id: string) => async () => { + await mutation.deleteTracker.mutateAsync({ idOrTrackingNumber: id }); + }; + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + useEffect(() => { + updateFilter(); + }, [router.query]); + useEffect(() => { + setLoading(query.isFetching); + }, [query.isFetching]); + useEffect(() => { + if (query.isFetching && !initialized && !isNoneOrEmpty(modal)) { + const tracker = (trackers?.edges || []).find( + (t) => t.node.id === modal, + )?.node; + modal === "new" && addTracker({ onChange: updateFilter }); + tracker && previewTracker(tracker); + setInitialized(true); + } + }, [modal, query.isFetched]); + + return ( + <> +
+ Trackers +
+ + +
+
+ +
+ +
+ + {!query.isFetched && query.isFetching && } + + {query.isFetched && (trackers?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + + + {(trackers?.edges || []).map(({ node: tracker }) => ( + previewTracker(tracker)} + > + + + + + + + ))} + +
SHIPPING SERVICELAST EVENT
+
+ +
+ + {tracker.tracking_number} + +
+ + {formatRef( + tracker.info?.shipment_service || + tracker.shipment?.meta?.service_name || + tracker.shipment?.service || + `SERVICE UNKNOWN`, + )} + +
+
+
+ + + + {isNoneOrEmpty(tracker?.events) + ? "" + : formatEventDescription( + (tracker?.events as TrackingEvent[])[0], + )} + + +

+ {isNoneOrEmpty(tracker?.events) + ? "" + : formatEventDate( + (tracker?.events as TrackingEvent[])[0], + )} +

+
+ +
+
+ +
+ + {(trackers?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (trackers?.edges || []).length == 0 && ( +
+
+

No shipment trackers found.

+
+
+ )} + + ); + }; + + return AuthenticatedPage( + + + {`Trackers - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + + + + , + pageProps, + ); +} + +function formatEventDescription(last_event?: TrackingEvent): string { + return last_event?.description || ""; +} + +function formatEventDate(last_event?: TrackingEvent): string { + if (isNone(last_event)) return ""; + + return [last_event?.date, last_event?.time] + .filter((a) => !isNone(a) && a !== "") + .join(" "); +} diff --git a/packages/core/modules/Trackers/tracking-page.tsx b/packages/core/modules/Trackers/tracking-page.tsx new file mode 100644 index 0000000000..bc838770af --- /dev/null +++ b/packages/core/modules/Trackers/tracking-page.tsx @@ -0,0 +1,157 @@ +import { TrackingEvent, TrackingStatus } from "@karrio/types/rest/api"; +import { CarrierImage } from "@karrio/ui/components/carrier-image"; +import { formatDayDate, isNone } from "@karrio/lib"; +import { Metadata } from "@karrio/types"; +import { NextPage } from "next"; +import Head from "next/head"; +import Link from "next/link"; +import React from "react"; + +export { getServerSideProps } from "../../context/tracker"; + +type DayEvents = { [k: string]: TrackingEvent[] }; + +const Tracking: NextPage<{ + id: string; + metadata: Metadata; + tracker?: TrackingStatus; + message?: string; +}> = ({ metadata, id, tracker, message }) => { + const computeEvents = (tracker: TrackingStatus): DayEvents => { + return (tracker?.events || []).reduce((days, event: TrackingEvent) => { + const daydate = formatDayDate(event.date as string); + return { ...days, [daydate]: [...(days[daydate] || []), event] }; + }, {} as DayEvents); + }; + + return ( + <> + + {`Tracking - ${tracker?.tracking_number || id} - ${metadata?.APP_NAME}`} + + +
+
+
+ + + {metadata?.APP_NAME} + + +
+ + {!isNone(tracker) && ( + <> +
+
+
+ +
+ +

+ Tracking ID{" "} + {tracker?.tracking_number} +

+ + {!isNone(tracker?.estimated_delivery) && ( +

+ + {tracker?.delivered + ? "Delivered" + : "Estimated Delivery"} + {" "} + + {formatDayDate(tracker!.estimated_delivery as string)} + +

+ )} +
+ +
+ {tracker?.status === "delivered" && ( +

+ Delivered +

+ )} + + {tracker?.status === "in_transit" && ( +

+ In-Transit +

+ )} + + {tracker?.status !== "delivered" && + tracker?.status !== "in_transit" && ( +

+ Pending +

+ )} +
+
+ +
+ +
+ +
+ + )} + + {!isNone(message) && ( +
+
+

{message}

+
+
+ )} +
+ +
+ +
+
+

+ + Powered by © {metadata.APP_NAME} + +

+
+
+
+ + ); +}; + +export default Tracking; diff --git a/packages/core/modules/Workflows/event.tsx b/packages/core/modules/Workflows/event.tsx new file mode 100644 index 0000000000..652e136950 --- /dev/null +++ b/packages/core/modules/Workflows/event.tsx @@ -0,0 +1,252 @@ +import { failsafe, formatDateTimeLong, isNone, jsonify } from "@karrio/lib"; +import { Tabs, TabStateProvider } from "@karrio/ui/components/tabs"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { useWorkflowEvent } from "@karrio/hooks/workflow-events"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { useRouter } from "next/dist/client/router"; +import json from "highlight.js/lib/languages/json"; +import hljs from "highlight.js"; +import Head from "next/head"; +import moment from "moment"; +import React from "react"; + +export { getServerSideProps } from "../../context/main"; + +hljs.registerLanguage("json", json); + +export const Component: React.FC<{ eventId?: string }> = ({ eventId }) => { + const router = useRouter(); + const { setLoading } = useLoader(); + const entity_id = eventId || (router.query.id as string); + const { + query: { data: { workflow_event } = {}, ...query }, + } = useWorkflowEvent(entity_id); + + React.useEffect(() => { + (window as any).moment = moment; + setLoading(query.isFetching); + }, [query.isFetching]); + + return ( + <> + {workflow_event !== undefined && ( + <> +
+
+ + WORKFLOW EVENT + +
+
+ {workflow_event?.event_type} + +
+
+ {!isNone(eventId) && ( +
+ + + + + +
+ )} +
+ +
+ +
+
+
ID
+
{workflow_event?.id}
+
+
+
Workflow ID
+
+ {workflow_event?.workflow?.id} +
+
+
+
Workflow name
+
+ {workflow_event?.workflow?.name} +
+
+
+
Date
+
+ {formatDateTimeLong(workflow_event?.created_at)} +
+
+
+ + + +
+

Event parameters

+ + {Object.keys(workflow_event?.parameters || {}).length == 0 && ( +
+ No parameters provided... +
+ )} + + {Object.keys(workflow_event?.parameters || {}).length > 0 && ( + <> +
+ +
+                        
+                      
+
+ + )} +
+ +
+ {(workflow_event?.records || []).length == 0 && ( +
+ No tracing records... +
+ )} + + {(workflow_event?.records || []).length > 0 && ( + <> + {workflow_event!.records.map((trace) => { + return ( +
+
+

+ + Record type: {trace.key} + +

+ {!!trace.record.url && ( +

+ + URL: {trace.record.url} + +

+ )} + {!!trace.record.request_id && ( +

+ + Request ID:{" "} + {trace.record.request_id} + +

+ )} + {!!trace.record.action_name && ( +

+ + Action:{" "} + {trace.record.action_name} + +

+ )} + {trace?.timestamp && ( +

+ + Request Timestamp:{" "} + + {moment(trace.timestamp * 1000).format( + "LTS", + )} + + +

+ )} +
+ +
+ +
+                              
+                            
+
+
+ ); + })} + + )} +
+
+
+ + )} + + ); +}; + +export default function Page(pageProps: any) { + return AuthenticatedPage( + + + {`Workflow Event - ${(pageProps as any).metadata?.APP_NAME}`} + + + , + pageProps, + ); +} + +export function parseWorkflowEventRecordData(record: any) { + if (!record) return null; + if (record?.format === "xml") { + return record.data || record.response || record.error; + } + + return failsafe( + () => + jsonify( + record.data || record.response || record.error || record.parameters, + ), + record.data || record.response || record.error, + ); +} diff --git a/packages/core/modules/Workflows/events.tsx b/packages/core/modules/Workflows/events.tsx new file mode 100644 index 0000000000..dab2bc6c74 --- /dev/null +++ b/packages/core/modules/Workflows/events.tsx @@ -0,0 +1,201 @@ +import { WorkflowPreviewModal } from "../../components/workflow-event-preview"; +import { formatDateTimeLong, getURLSearchParams } from "@karrio/lib"; +import { useWorkflowEvents } from "@karrio/hooks/workflow-events"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { StatusBadge } from "@karrio/ui/components/status-badge"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { Spinner } from "@karrio/ui/components"; +import { useRouter } from "next/router"; +import Head from "next/head"; +import React from "react"; +import { WorkflowEventFilter } from "@karrio/types/graphql/ee"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ModalProvider]); + +export const WorkflowEventList: React.FC<{ + defaultFilter?: WorkflowEventFilter; +}> = ({ defaultFilter }) => { + const router = useRouter(); + const loader = useLoader(); + const { + query: { data: { workflow_events } = {}, ...query }, + filter, + setFilter, + } = useWorkflowEvents({ + setVariablesToURL: true, + ...(defaultFilter || {}), + }); + + const updateFilter = (extra: Partial = {}) => { + const query = { + ...filter, + ...getURLSearchParams(), + ...extra, + }; + + setFilter(query); + }; + + React.useEffect(() => { + updateFilter(); + }, [router.query]); + React.useEffect(() => { + loader.setLoading(query.isFetching); + }, [query.isFetching]); + React.useEffect(() => { + // if (query.isFetched && !initialized && !isNoneOrEmpty(router.query.modal)) { + // previewEvent(router.query.modal as string); + // setInitialized(true); + // } + }, [router.query.modal, query.isFetched]); + + return ( + <> + {/* APIs Overview */} + {!query.isFetched && } + + {query.isFetched && (workflow_events?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + {(workflow_events?.edges || []).map(({ node: event }) => ( + + + + + + } + /> + ))} + +
+ EVENT + DATE
+ + + + {`${event.event_type} trigger of ${event.workflow.name}`} + + + + {formatDateTimeLong(event.created_at)} + +
+
+ +
+ + {(workflow_events?.edges || []).length} results + + +
+ + +
+
+ + )} + + {query.isFetched && (workflow_events?.edges || []).length == 0 && ( +
+
+

No Workflow events found.

+
+
+ )} + + ); +}; + +export default function Page(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + return ( + <> +
+
+ Workflows + + BETA + +
+
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + Event history + +
  • +
+
+ + + + ); + }; + + return AuthenticatedPage( + + + {`Workflows - ${references?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Workflows/index.tsx b/packages/core/modules/Workflows/index.tsx new file mode 100644 index 0000000000..f50dad9a89 --- /dev/null +++ b/packages/core/modules/Workflows/index.tsx @@ -0,0 +1,123 @@ +import { WorkflowMenu } from "@karrio/ui/components/workflow-menu"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { DashboardLayout } from "../../layouts/dashboard-layout"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import { useWorkflows } from "@karrio/hooks/workflows"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { Spinner } from "@karrio/ui/components"; +import Head from "next/head"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ModalProvider]); + +export default function Page(pageProps: any) { + const { references } = useAPIMetadata(); + + const Component: React.FC = () => { + const { + query: { data: { workflows } = {}, ...query }, + } = useWorkflows(); + + return ( + <> +
+
+ Workflows + + BETA + +
+
+ + Create Workflow + +
+
+ +
+
    +
  • + + Overview + +
  • +
  • + + Event history + +
  • +
+
+ + {!query.isFetched && } + + {query.isFetched && (workflows?.edges || []).length > 0 && ( + <> + + + )} + + {query.isFetched && (workflows?.edges || []).length == 0 && ( + <> +
+
+

No workflow found.

+
+
+ + )} + + ); + }; + + return AuthenticatedPage( + + + {`Workflows - ${references?.APP_NAME}`} + + + + + + , + pageProps, + ); +} diff --git a/packages/core/modules/Workflows/workflow.tsx b/packages/core/modules/Workflows/workflow.tsx new file mode 100644 index 0000000000..d7ff65eed0 --- /dev/null +++ b/packages/core/modules/Workflows/workflow.tsx @@ -0,0 +1,1103 @@ +import { + ActionNodeInput, + AutomationActionType, + AutomationAuthType, + AutomationEventStatus, + AutomationTriggerType, +} from "@karrio/types/graphql/ee"; +import { ConnectionModalEditor } from "@karrio/ui/modals/workflow-connection-edit-modal"; +import { ActionModalEditor } from "@karrio/ui/modals/workflow-action-edit-modal"; +import { isEqual, isNone, isNoneOrEmpty, url$, useLocation } from "@karrio/lib"; +import { TextAreaField } from "@karrio/ui/components/textarea-field"; +import { TabStateProvider, Tabs } from "@karrio/ui/components/tabs"; +import { WorkflowActionType } from "@karrio/hooks/workflow-actions"; +import { ConfirmModalWrapper } from "@karrio/ui/modals/form-modals"; +import { CopiableLink } from "@karrio/ui/components/copiable-link"; +import { AuthenticatedPage } from "../../layouts/authenticated-page"; +import { InputField } from "@karrio/ui/components/input-field"; +import { useAPIMetadata } from "@karrio/hooks/api-metadata"; +import { useWorkflowForm } from "@karrio/hooks/workflows"; +import { useLoader } from "@karrio/ui/components/loader"; +import { AppLink } from "@karrio/ui/components/app-link"; +import { ModalProvider } from "@karrio/ui/modals/modal"; +import django from "highlight.js/lib/languages/django"; +import { parseWorkflowEventRecordData } from "./event"; +import { bundleContexts } from "@karrio/hooks/utils"; +import { jsonLanguage } from "@codemirror/lang-json"; +import { htmlLanguage } from "@codemirror/lang-html"; +import { SelectField } from "@karrio/ui/components"; +import json from "highlight.js/lib/languages/json"; +import { Disclosure } from "@headlessui/react"; +import CodeMirror from "@uiw/react-codemirror"; +import { WorkflowEventList } from "./events"; +import React, { useState } from "react"; +import hljs from "highlight.js"; +import Head from "next/head"; +import moment from "moment"; + +export { getServerSideProps } from "../../context/main"; +const ContextProviders = bundleContexts([ModalProvider]); +hljs.registerLanguage("django", django); +hljs.registerLanguage("json", json); + +export default function Page(pageProps: any) { + const Component: React.FC = () => { + const loader = useLoader(); + const router = useLocation(); + const { id } = router.query; + const { metadata } = useAPIMetadata(); + const [key, setKey] = useState(`workflow-${Date.now()}`); + const { + workflow, + current, + isNew, + DEFAULT_STATE, + query, + zipActionWithNode, + debug_event, + ...mutation + } = useWorkflowForm({ id: id as string }); + + const handleChange = async (changes?: Partial) => { + if (changes === undefined) { + return; + } + await mutation.updateWorkflow({ id, ...changes }); + setKey(`${id}-${Date.now()}`); + }; + + const NextIndicator = () => ( +
+ + + +
+ ); + + return ( +
+
+
+ + + + + + + Edit workflow + +
+
+ +
+
+ + + <> + {query.isFetched && !!workflow.actions && ( +
+ {/* Workflow fields section */} +
+ handleChange({ name: e.target.value })} + placeholder="ERP orders sync" + className="is-small" + required + /> + + + handleChange({ description: e.target.value }) + } + placeholder="Automate ERP orders syncing for fulfillment" + className="is-small" + /> +
+ +
+ +
+
+ {/* Workflow related objects section */} +
+ {/* Trigger section */} + + +
+ + Trigger + +

+ How the workflow is tiggered +

+
+
+ +
+ +
+ {/* trigger type */} + + handleChange({ + trigger: { + ...workflow.trigger, + trigger_type: e.target.value, + }, + }) + } + > + {Array.from( + new Set(Object.values(AutomationTriggerType)), + ).map((unit) => ( + + ))} + + + {/* trigger schedule */} +
+ + handleChange({ + trigger: { + ...workflow.trigger, + schedule: e.target.value, + }, + }) + } + /> +
+ + {/* webhook options */} +
+ + handleChange({ + trigger: { + ...workflow.trigger, + secret: e.target.value, + }, + }) + } + /> + + + handleChange({ + trigger: { + ...workflow.trigger, + secret_key: e.target.value, + }, + }) + } + /> +
+
+ +
+ + {/* webhook URL */} + + + + } + readOnly + /> +
+
+ + + + {/* Actions section */} + {zipActionWithNode( + workflow.actions as WorkflowActionType[], + workflow.action_nodes as ActionNodeInput[], + ).map(([action, node], index) => ( + + + +
+ + Action + +

+ {action.name || "An action to perform"} +

+
+
+ + + + + + } + /> + + + + + + } + /> +
+
+ +
+ + + +
+
+ + mutation.updateAction( + index, + action?.id, + )({ parameters_template: value }) + } + /> +
+
+ +
+ {(debug_event?.records || []).filter( + (_) => + _.meta.workflow_action_slug === + action.slug && + _.key === "action-input", + ).length == 0 && ( +
+ No input data sample... +
+ )} + + {(debug_event?.records || []).length > + 0 && ( + <> + {debug_event!.records + .filter( + (_) => + _.meta.workflow_action_slug === + action.slug && + _.key === "action-input", + ) + .map((trace) => { + return ( +
+ +
+ ); + })} + + )} +
+ +
+ {(debug_event?.records || []).filter( + (_) => + _.meta.workflow_action_slug === + action.slug && + _.key === "action-output", + ).length == 0 && ( +
+ No output data sample... +
+ )} + + {(debug_event?.records || []).length > + 0 && ( + <> + {debug_event!.records + .filter( + (_) => + _.meta.workflow_action_slug === + action.slug && + _.key === "action-output", + ) + .map((trace) => { + return ( +
+ +
+ ); + })} + + )} +
+ +
+ {(debug_event?.records || []).length == + 0 && ( +
+ No tracing records... +
+ )} + + {(debug_event?.records || []).length > + 0 && ( + <> + {debug_event!.records + .filter( + (_) => + _.meta.workflow_action_id == + action.id, + ) + .map((trace) => { + return ( +
+
+

+ + Record type:{" "} + + {trace.key} + + +

+ {!!trace.record.url && ( +

+ + URL:{" "} + + {trace.record.url} + + +

+ )} + {!!trace.record + .request_id && ( +

+ + Request id:{" "} + + { + trace.record + .request_id + } + + +

+ )} + {trace?.timestamp && ( +

+ + Request timestamp:{" "} + + {moment( + trace.timestamp * + 1000, + ).format("LTS")} + + +

+ )} + {!!trace.record.status && ( +

+ + Step status:{" "} + + {trace.record.status} + + +

+ )} +
+ +
+ +
+                                                    
+                                                  
+
+
+ ); + })} + + )} +
+ +
+
+ {/* action type */} +
+
+ + Action type + +
+
+ {action.action_type} +
+
+ + {/* action description */} + {!!action.description && ( +
+
+ + description + +
+
+ {action.description} +
+
+ )} + + {/* data mapping options */} + {action.action_type == + AutomationActionType.data_mapping && ( + <> + {/* action parameters type */} +
+
+ + format + +
+
+ {action.content_type} +
+
+ + )} + + {/* http request options */} + {action.action_type == + AutomationActionType.http_request && ( + <> + {/* action method */} +
+
+ + Method + +
+
+ + {action.method?.toLocaleUpperCase()} + +
+
+ + {/* action host */} +
+
+ + Host + +
+
+ {action.host} +
+
+ + {/* host port */} + {!isNone(action.port) && ( +
+
+ + Port + +
+
+ {action.port} +
+
+ )} + + {/* action endpoint */} + {!isNoneOrEmpty(action.endpoint) && ( +
+
+ + Endpoint + +
+
+ {action.endpoint} +
+
+ )} + + {/* action content type */} +
+
+ + Content Type + +
+
+ {action.content_type} +
+
+ + {/* action parameters type */} +
+
+ + Parameters Type + +
+
+ + {action.parameters_type} + +
+
+ + )} +
+ + + +
+ + Connection + +

+ {action.connection?.name || + "A connection for the action"} +

+
+
+ + + + + + } + /> + + + + + + } + /> +
+
+ +
+ + {!action.connection && ( +
+
+ + No connection defined! + +
+
+ )} + + {!!action.connection && ( +
+ {/* auth type */} +
+
+ + Auth type + +
+
+ + {action.connection.auth_type} + +
+
+ + {/* connection description */} + {!!action.connection + .description && ( +
+
+ + description + +
+
+ + { + action.connection + .description + } + +
+
+ )} + + {/* http request options */} + {[ + AutomationAuthType.oauth2, + AutomationAuthType.jwt, + ].includes( + action.connection + .auth_type as any, + ) && ( + <> + {/* connection host */} +
+
+ + Host + +
+
+ + {action.connection.host} + +
+
+ + {/* host port */} + {!isNone( + action.connection.port, + ) && ( +
+
+ + Port + +
+
+ + {action.connection.port} + +
+
+ )} + + {/* endpoint */} + {!isNoneOrEmpty( + action.connection.endpoint, + ) && ( +
+
+ + Endpoint + +
+
+ + { + action.connection + .endpoint + } + +
+
+ )} + + )} + + {/* connection auth template */} +
+
+ + Auth template + +
+
+
+                                                  
+                                                
+
+
+ + {[ + AutomationAuthType.oauth2, + AutomationAuthType.jwt, + ].includes( + action.connection + .auth_type as any, + ) && ( + <> + {/* parameters template */} +
+
+ + Template + +
+
+
+                                                      
+                                                    
+
+
+ + )} +
+ )} +
+
+
+
+
+
+
+ + +
+ ))} + + {/* Add action button */} + + +
+ } + /> +
+ +
+ +
+
+
+
+ )} + + + <>{!!id && } + + + ); + }; + + return AuthenticatedPage( + <> + + {`Workflow - ${(pageProps as any).metadata?.APP_NAME}`} + + + + + + + + , + pageProps, + ); +} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000000..c8de9a66f1 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,26 @@ +{ + "name": "@karrio/core", + "version": "0.0.0", + "main": "./index.tsx", + "types": "./index.tsx", + "license": "Apache-2.0", + "scripts": { + "lint": "eslint ." + }, + "dependencies": { + "@karrio/hooks": "*", + "@karrio/lib": "*", + "@karrio/types": "*", + "@karrio/ui": "*" + }, + "devDependencies": { + "@karrio/eslint-config-custom": "*", + "@karrio/tsconfig": "*", + "@turbo/gen": "^1.10.12", + "@types/node": "^20.5.2", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "react": "^18.2.0", + "typescript": "^4.5.2" + } +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000000..b6ca0aff48 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig/react-library.json", + "include": [ + ".", + "./**/*.ts", + "./**/*.tsx", + ], + "exclude": [ + "dist", + "build", + "node_modules" + ] +} \ No newline at end of file diff --git a/packages/ui/admin/rate-sheet-management.tsx b/packages/ui/admin/rate-sheet-management.tsx index 86d97d3e4b..a8f858e07e 100644 --- a/packages/ui/admin/rate-sheet-management.tsx +++ b/packages/ui/admin/rate-sheet-management.tsx @@ -1,86 +1,96 @@ -import { useRateSheets } from "@karrio/hooks/admin/rate-sheets"; -import { Spinner } from "../components"; - - -interface RateSheetManagementComponent { } - -export const RateSheetManagement: React.FC = () => { - const { query: { data: { rate_sheets } = {}, ...query } } = useRateSheets(); - - return ( - <> -
-
- Rate sheets -
- -
-
- -
- -
- - {(query.isFetching && !query.isFetched) && } - - {(query.isFetched && (rate_sheets?.edges || []).length > 0) && <> - -
- - - - - - - - - - - {(rate_sheets?.edges || []).map(({ node: sheet }) => ( - - - - - - - ))} - - -
MEMBERROLELAST LOGIN
- - -
- -
- -
- {(rate_sheets?.edges || []).length} results - -
- - -
-
-
- } - - {(query.isFetched && (rate_sheets?.edges || []).length == 0) &&
- -
-

No rate sheets found.

-

Use the New sheet button above to add one

-
- -
} - -
-
- - ) -}; +import { useRateSheets } from "@karrio/hooks/admin/rate-sheets"; +import { Spinner } from "../components"; + +interface RateSheetManagementComponent {} + +export const RateSheetManagement: React.FC< + RateSheetManagementComponent +> = () => { + const { + query: { data: { rate_sheets } = {}, ...query }, + } = useRateSheets(); + + return ( + <> +
+
+ + Rate sheets + +
+ +
+
+ +
+ +
+ {query.isFetching && !query.isFetched && } + + {query.isFetched && (rate_sheets?.edges || []).length > 0 && ( + <> +
+ + + + + + + + + + {(rate_sheets?.edges || []).map(({ node: sheet }) => ( + + + + + ))} + +
MEMBERROLELAST LOGIN
+ +
+ +
+ + {(rate_sheets?.edges || []).length} results + + +
+ + +
+
+
+ + )} + + {query.isFetched && (rate_sheets?.edges || []).length == 0 && ( +
+
+

No rate sheets found.

+

+ Use the New sheet button above to add one +

+
+
+ )} +
+
+ + ); +}; diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index b6ca0aff48..3b7ece3260 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -1,13 +1,13 @@ -{ - "extends": "../tsconfig/react-library.json", - "include": [ - ".", - "./**/*.ts", - "./**/*.tsx", - ], - "exclude": [ - "dist", - "build", - "node_modules" - ] -} \ No newline at end of file +{ + "extends": "../tsconfig/react-library.json", + "include": [ + ".", + "./**/*.ts", + "./**/*.tsx", + ], + "exclude": [ + "dist", + "build", + "node_modules" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 5e4187ebe9..1f08c1b0be 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,3 @@ -{ - "extends": "./packages/tsconfig/base.json" -} +{ + "extends": "./packages/tsconfig/base.json" +}