@rovela-ai/sdk 0.3.34 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/{styles/admin-theme.css → admin-theme.css} +1 -46
- package/dist/checkout/index.d.ts +1 -1
- package/dist/checkout/index.d.ts.map +1 -1
- package/dist/checkout/server/create-checkout-session.d.ts +10 -1
- package/dist/checkout/server/create-checkout-session.d.ts.map +1 -1
- package/dist/checkout/server/create-checkout-session.js +28 -17
- package/dist/checkout/server/create-checkout-session.js.map +1 -1
- package/dist/checkout/server/handle-webhook.d.ts.map +1 -1
- package/dist/checkout/server/handle-webhook.js +24 -26
- package/dist/checkout/server/handle-webhook.js.map +1 -1
- package/dist/checkout/server/order-service.d.ts +8 -3
- package/dist/checkout/server/order-service.d.ts.map +1 -1
- package/dist/checkout/server/order-service.js +60 -14
- package/dist/checkout/server/order-service.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -2
- package/dist/admin/api/accept-invite.d.ts +0 -65
- package/dist/admin/api/accept-invite.d.ts.map +0 -1
- package/dist/admin/api/accept-invite.js +0 -115
- package/dist/admin/api/accept-invite.js.map +0 -1
- package/dist/admin/api/auth.d.ts +0 -32
- package/dist/admin/api/auth.d.ts.map +0 -1
- package/dist/admin/api/auth.js +0 -37
- package/dist/admin/api/auth.js.map +0 -1
- package/dist/admin/api/categories.d.ts +0 -68
- package/dist/admin/api/categories.d.ts.map +0 -1
- package/dist/admin/api/categories.js +0 -266
- package/dist/admin/api/categories.js.map +0 -1
- package/dist/admin/api/check.d.ts +0 -38
- package/dist/admin/api/check.d.ts.map +0 -1
- package/dist/admin/api/check.js +0 -49
- package/dist/admin/api/check.js.map +0 -1
- package/dist/admin/api/customers.d.ts +0 -77
- package/dist/admin/api/customers.d.ts.map +0 -1
- package/dist/admin/api/customers.js +0 -219
- package/dist/admin/api/customers.js.map +0 -1
- package/dist/admin/api/forgot-password.d.ts +0 -39
- package/dist/admin/api/forgot-password.d.ts.map +0 -1
- package/dist/admin/api/forgot-password.js +0 -66
- package/dist/admin/api/forgot-password.js.map +0 -1
- package/dist/admin/api/index.d.ts +0 -107
- package/dist/admin/api/index.d.ts.map +0 -1
- package/dist/admin/api/index.js +0 -127
- package/dist/admin/api/index.js.map +0 -1
- package/dist/admin/api/linked-customer.d.ts +0 -32
- package/dist/admin/api/linked-customer.d.ts.map +0 -1
- package/dist/admin/api/linked-customer.js +0 -45
- package/dist/admin/api/linked-customer.js.map +0 -1
- package/dist/admin/api/me.d.ts +0 -72
- package/dist/admin/api/me.d.ts.map +0 -1
- package/dist/admin/api/me.js +0 -177
- package/dist/admin/api/me.js.map +0 -1
- package/dist/admin/api/orders.d.ts +0 -91
- package/dist/admin/api/orders.d.ts.map +0 -1
- package/dist/admin/api/orders.js +0 -390
- package/dist/admin/api/orders.js.map +0 -1
- package/dist/admin/api/products-bulk.d.ts +0 -38
- package/dist/admin/api/products-bulk.d.ts.map +0 -1
- package/dist/admin/api/products-bulk.js +0 -135
- package/dist/admin/api/products-bulk.js.map +0 -1
- package/dist/admin/api/products-stats.d.ts +0 -34
- package/dist/admin/api/products-stats.d.ts.map +0 -1
- package/dist/admin/api/products-stats.js +0 -43
- package/dist/admin/api/products-stats.js.map +0 -1
- package/dist/admin/api/products.d.ts +0 -104
- package/dist/admin/api/products.d.ts.map +0 -1
- package/dist/admin/api/products.js +0 -491
- package/dist/admin/api/products.js.map +0 -1
- package/dist/admin/api/refund.d.ts +0 -29
- package/dist/admin/api/refund.d.ts.map +0 -1
- package/dist/admin/api/refund.js +0 -142
- package/dist/admin/api/refund.js.map +0 -1
- package/dist/admin/api/reset-password.d.ts +0 -49
- package/dist/admin/api/reset-password.d.ts.map +0 -1
- package/dist/admin/api/reset-password.js +0 -99
- package/dist/admin/api/reset-password.js.map +0 -1
- package/dist/admin/api/return.d.ts +0 -47
- package/dist/admin/api/return.d.ts.map +0 -1
- package/dist/admin/api/return.js +0 -186
- package/dist/admin/api/return.js.map +0 -1
- package/dist/admin/api/settings.d.ts +0 -49
- package/dist/admin/api/settings.d.ts.map +0 -1
- package/dist/admin/api/settings.js +0 -201
- package/dist/admin/api/settings.js.map +0 -1
- package/dist/admin/api/setup-guide.d.ts +0 -78
- package/dist/admin/api/setup-guide.d.ts.map +0 -1
- package/dist/admin/api/setup-guide.js +0 -235
- package/dist/admin/api/setup-guide.js.map +0 -1
- package/dist/admin/api/setup.d.ts +0 -60
- package/dist/admin/api/setup.d.ts.map +0 -1
- package/dist/admin/api/setup.js +0 -126
- package/dist/admin/api/setup.js.map +0 -1
- package/dist/admin/api/shipping.d.ts +0 -287
- package/dist/admin/api/shipping.d.ts.map +0 -1
- package/dist/admin/api/shipping.js +0 -746
- package/dist/admin/api/shipping.js.map +0 -1
- package/dist/admin/api/stats.d.ts +0 -43
- package/dist/admin/api/stats.d.ts.map +0 -1
- package/dist/admin/api/stats.js +0 -92
- package/dist/admin/api/stats.js.map +0 -1
- package/dist/admin/api/stripe-status.d.ts +0 -39
- package/dist/admin/api/stripe-status.d.ts.map +0 -1
- package/dist/admin/api/stripe-status.js +0 -99
- package/dist/admin/api/stripe-status.js.map +0 -1
- package/dist/admin/api/tax-zones.d.ts +0 -97
- package/dist/admin/api/tax-zones.d.ts.map +0 -1
- package/dist/admin/api/tax-zones.js +0 -265
- package/dist/admin/api/tax-zones.js.map +0 -1
- package/dist/admin/api/users.d.ts +0 -142
- package/dist/admin/api/users.d.ts.map +0 -1
- package/dist/admin/api/users.js +0 -356
- package/dist/admin/api/users.js.map +0 -1
- package/dist/admin/components/AdminAcceptInviteForm.d.ts +0 -3
- package/dist/admin/components/AdminAcceptInviteForm.d.ts.map +0 -1
- package/dist/admin/components/AdminAcceptInviteForm.js +0 -137
- package/dist/admin/components/AdminAcceptInviteForm.js.map +0 -1
- package/dist/admin/components/AdminAccountPage.d.ts +0 -10
- package/dist/admin/components/AdminAccountPage.d.ts.map +0 -1
- package/dist/admin/components/AdminAccountPage.js +0 -126
- package/dist/admin/components/AdminAccountPage.js.map +0 -1
- package/dist/admin/components/AdminBarBanner.d.ts +0 -2
- package/dist/admin/components/AdminBarBanner.d.ts.map +0 -1
- package/dist/admin/components/AdminBarBanner.js +0 -266
- package/dist/admin/components/AdminBarBanner.js.map +0 -1
- package/dist/admin/components/AdminForgotPasswordForm.d.ts +0 -8
- package/dist/admin/components/AdminForgotPasswordForm.d.ts.map +0 -1
- package/dist/admin/components/AdminForgotPasswordForm.js +0 -59
- package/dist/admin/components/AdminForgotPasswordForm.js.map +0 -1
- package/dist/admin/components/AdminGuard.d.ts +0 -40
- package/dist/admin/components/AdminGuard.d.ts.map +0 -1
- package/dist/admin/components/AdminGuard.js +0 -94
- package/dist/admin/components/AdminGuard.js.map +0 -1
- package/dist/admin/components/AdminLayout.d.ts +0 -40
- package/dist/admin/components/AdminLayout.d.ts.map +0 -1
- package/dist/admin/components/AdminLayout.js +0 -39
- package/dist/admin/components/AdminLayout.js.map +0 -1
- package/dist/admin/components/AdminLoginForm.d.ts +0 -18
- package/dist/admin/components/AdminLoginForm.d.ts.map +0 -1
- package/dist/admin/components/AdminLoginForm.js +0 -61
- package/dist/admin/components/AdminLoginForm.js.map +0 -1
- package/dist/admin/components/AdminNav.d.ts +0 -44
- package/dist/admin/components/AdminNav.d.ts.map +0 -1
- package/dist/admin/components/AdminNav.js +0 -175
- package/dist/admin/components/AdminNav.js.map +0 -1
- package/dist/admin/components/AdminResetPasswordForm.d.ts +0 -12
- package/dist/admin/components/AdminResetPasswordForm.d.ts.map +0 -1
- package/dist/admin/components/AdminResetPasswordForm.js +0 -134
- package/dist/admin/components/AdminResetPasswordForm.js.map +0 -1
- package/dist/admin/components/AdminSelect.d.ts +0 -47
- package/dist/admin/components/AdminSelect.d.ts.map +0 -1
- package/dist/admin/components/AdminSelect.js +0 -71
- package/dist/admin/components/AdminSelect.js.map +0 -1
- package/dist/admin/components/AdminSetupForm.d.ts +0 -28
- package/dist/admin/components/AdminSetupForm.d.ts.map +0 -1
- package/dist/admin/components/AdminSetupForm.js +0 -85
- package/dist/admin/components/AdminSetupForm.js.map +0 -1
- package/dist/admin/components/AdminToast.d.ts +0 -31
- package/dist/admin/components/AdminToast.d.ts.map +0 -1
- package/dist/admin/components/AdminToast.js +0 -83
- package/dist/admin/components/AdminToast.js.map +0 -1
- package/dist/admin/components/AdminUserMenu.d.ts +0 -14
- package/dist/admin/components/AdminUserMenu.d.ts.map +0 -1
- package/dist/admin/components/AdminUserMenu.js +0 -34
- package/dist/admin/components/AdminUserMenu.js.map +0 -1
- package/dist/admin/components/CategoryForm.d.ts +0 -30
- package/dist/admin/components/CategoryForm.d.ts.map +0 -1
- package/dist/admin/components/CategoryForm.js +0 -152
- package/dist/admin/components/CategoryForm.js.map +0 -1
- package/dist/admin/components/CategorySelect.d.ts +0 -32
- package/dist/admin/components/CategorySelect.d.ts.map +0 -1
- package/dist/admin/components/CategorySelect.js +0 -139
- package/dist/admin/components/CategorySelect.js.map +0 -1
- package/dist/admin/components/CustomerDetails.d.ts +0 -15
- package/dist/admin/components/CustomerDetails.d.ts.map +0 -1
- package/dist/admin/components/CustomerDetails.js +0 -177
- package/dist/admin/components/CustomerDetails.js.map +0 -1
- package/dist/admin/components/CustomerTable.d.ts +0 -13
- package/dist/admin/components/CustomerTable.d.ts.map +0 -1
- package/dist/admin/components/CustomerTable.js +0 -112
- package/dist/admin/components/CustomerTable.js.map +0 -1
- package/dist/admin/components/DeleteConfirmDialog.d.ts +0 -57
- package/dist/admin/components/DeleteConfirmDialog.d.ts.map +0 -1
- package/dist/admin/components/DeleteConfirmDialog.js +0 -46
- package/dist/admin/components/DeleteConfirmDialog.js.map +0 -1
- package/dist/admin/components/ExampleContentBanner.d.ts +0 -2
- package/dist/admin/components/ExampleContentBanner.d.ts.map +0 -1
- package/dist/admin/components/ExampleContentBanner.js +0 -153
- package/dist/admin/components/ExampleContentBanner.js.map +0 -1
- package/dist/admin/components/InventoryEditor.d.ts +0 -15
- package/dist/admin/components/InventoryEditor.d.ts.map +0 -1
- package/dist/admin/components/InventoryEditor.js +0 -86
- package/dist/admin/components/InventoryEditor.js.map +0 -1
- package/dist/admin/components/InviteUserDialog.d.ts +0 -3
- package/dist/admin/components/InviteUserDialog.d.ts.map +0 -1
- package/dist/admin/components/InviteUserDialog.js +0 -127
- package/dist/admin/components/InviteUserDialog.js.map +0 -1
- package/dist/admin/components/LogoUpload.d.ts +0 -22
- package/dist/admin/components/LogoUpload.d.ts.map +0 -1
- package/dist/admin/components/LogoUpload.js +0 -210
- package/dist/admin/components/LogoUpload.js.map +0 -1
- package/dist/admin/components/LowStockAlert.d.ts +0 -11
- package/dist/admin/components/LowStockAlert.d.ts.map +0 -1
- package/dist/admin/components/LowStockAlert.js +0 -33
- package/dist/admin/components/LowStockAlert.js.map +0 -1
- package/dist/admin/components/OrderDetails.d.ts +0 -14
- package/dist/admin/components/OrderDetails.d.ts.map +0 -1
- package/dist/admin/components/OrderDetails.js +0 -210
- package/dist/admin/components/OrderDetails.js.map +0 -1
- package/dist/admin/components/OrderStatusChart.d.ts +0 -21
- package/dist/admin/components/OrderStatusChart.d.ts.map +0 -1
- package/dist/admin/components/OrderStatusChart.js +0 -61
- package/dist/admin/components/OrderStatusChart.js.map +0 -1
- package/dist/admin/components/OrderTable.d.ts +0 -13
- package/dist/admin/components/OrderTable.d.ts.map +0 -1
- package/dist/admin/components/OrderTable.js +0 -117
- package/dist/admin/components/OrderTable.js.map +0 -1
- package/dist/admin/components/PaymentSettings.d.ts +0 -13
- package/dist/admin/components/PaymentSettings.d.ts.map +0 -1
- package/dist/admin/components/PaymentSettings.js +0 -120
- package/dist/admin/components/PaymentSettings.js.map +0 -1
- package/dist/admin/components/PeriodSelector.d.ts +0 -9
- package/dist/admin/components/PeriodSelector.d.ts.map +0 -1
- package/dist/admin/components/PeriodSelector.js +0 -19
- package/dist/admin/components/PeriodSelector.js.map +0 -1
- package/dist/admin/components/PermissionsMatrix.d.ts +0 -8
- package/dist/admin/components/PermissionsMatrix.d.ts.map +0 -1
- package/dist/admin/components/PermissionsMatrix.js +0 -70
- package/dist/admin/components/PermissionsMatrix.js.map +0 -1
- package/dist/admin/components/PrimaryMetricsRow.d.ts +0 -11
- package/dist/admin/components/PrimaryMetricsRow.d.ts.map +0 -1
- package/dist/admin/components/PrimaryMetricsRow.js +0 -73
- package/dist/admin/components/PrimaryMetricsRow.js.map +0 -1
- package/dist/admin/components/ProductForm.d.ts +0 -18
- package/dist/admin/components/ProductForm.d.ts.map +0 -1
- package/dist/admin/components/ProductForm.js +0 -261
- package/dist/admin/components/ProductForm.js.map +0 -1
- package/dist/admin/components/ProductTable.d.ts +0 -14
- package/dist/admin/components/ProductTable.d.ts.map +0 -1
- package/dist/admin/components/ProductTable.js +0 -384
- package/dist/admin/components/ProductTable.js.map +0 -1
- package/dist/admin/components/RecentOrders.d.ts +0 -11
- package/dist/admin/components/RecentOrders.d.ts.map +0 -1
- package/dist/admin/components/RecentOrders.js +0 -63
- package/dist/admin/components/RecentOrders.js.map +0 -1
- package/dist/admin/components/RefundDialog.d.ts +0 -17
- package/dist/admin/components/RefundDialog.d.ts.map +0 -1
- package/dist/admin/components/RefundDialog.js +0 -90
- package/dist/admin/components/RefundDialog.js.map +0 -1
- package/dist/admin/components/RevenueChart.d.ts +0 -23
- package/dist/admin/components/RevenueChart.d.ts.map +0 -1
- package/dist/admin/components/RevenueChart.js +0 -75
- package/dist/admin/components/RevenueChart.js.map +0 -1
- package/dist/admin/components/SEOPreview.d.ts +0 -33
- package/dist/admin/components/SEOPreview.d.ts.map +0 -1
- package/dist/admin/components/SEOPreview.js +0 -30
- package/dist/admin/components/SEOPreview.js.map +0 -1
- package/dist/admin/components/SecondaryMetricsRow.d.ts +0 -14
- package/dist/admin/components/SecondaryMetricsRow.d.ts.map +0 -1
- package/dist/admin/components/SecondaryMetricsRow.js +0 -45
- package/dist/admin/components/SecondaryMetricsRow.js.map +0 -1
- package/dist/admin/components/SetupGuide.d.ts +0 -4
- package/dist/admin/components/SetupGuide.d.ts.map +0 -1
- package/dist/admin/components/SetupGuide.js +0 -244
- package/dist/admin/components/SetupGuide.js.map +0 -1
- package/dist/admin/components/ShippingSettings.d.ts +0 -3
- package/dist/admin/components/ShippingSettings.d.ts.map +0 -1
- package/dist/admin/components/ShippingSettings.js +0 -553
- package/dist/admin/components/ShippingSettings.js.map +0 -1
- package/dist/admin/components/StatsCards.d.ts +0 -18
- package/dist/admin/components/StatsCards.d.ts.map +0 -1
- package/dist/admin/components/StatsCards.js +0 -71
- package/dist/admin/components/StatsCards.js.map +0 -1
- package/dist/admin/components/StoreSettings.d.ts +0 -19
- package/dist/admin/components/StoreSettings.d.ts.map +0 -1
- package/dist/admin/components/StoreSettings.js +0 -149
- package/dist/admin/components/StoreSettings.js.map +0 -1
- package/dist/admin/components/TagInput.d.ts +0 -29
- package/dist/admin/components/TagInput.d.ts.map +0 -1
- package/dist/admin/components/TagInput.js +0 -69
- package/dist/admin/components/TagInput.js.map +0 -1
- package/dist/admin/components/TaxSettings.d.ts +0 -12
- package/dist/admin/components/TaxSettings.d.ts.map +0 -1
- package/dist/admin/components/TaxSettings.js +0 -272
- package/dist/admin/components/TaxSettings.js.map +0 -1
- package/dist/admin/components/UsersTable.d.ts +0 -3
- package/dist/admin/components/UsersTable.d.ts.map +0 -1
- package/dist/admin/components/UsersTable.js +0 -393
- package/dist/admin/components/UsersTable.js.map +0 -1
- package/dist/admin/components/VariantManager.d.ts +0 -44
- package/dist/admin/components/VariantManager.d.ts.map +0 -1
- package/dist/admin/components/VariantManager.js +0 -325
- package/dist/admin/components/VariantManager.js.map +0 -1
- package/dist/admin/components/index.d.ts +0 -71
- package/dist/admin/components/index.d.ts.map +0 -1
- package/dist/admin/components/index.js +0 -87
- package/dist/admin/components/index.js.map +0 -1
- package/dist/admin/config.d.ts +0 -71
- package/dist/admin/config.d.ts.map +0 -1
- package/dist/admin/config.js +0 -253
- package/dist/admin/config.js.map +0 -1
- package/dist/admin/hooks/fetchAdminApi.d.ts +0 -65
- package/dist/admin/hooks/fetchAdminApi.d.ts.map +0 -1
- package/dist/admin/hooks/fetchAdminApi.js +0 -96
- package/dist/admin/hooks/fetchAdminApi.js.map +0 -1
- package/dist/admin/hooks/index.d.ts +0 -24
- package/dist/admin/hooks/index.d.ts.map +0 -1
- package/dist/admin/hooks/index.js +0 -19
- package/dist/admin/hooks/index.js.map +0 -1
- package/dist/admin/hooks/useAdminAuth.d.ts +0 -25
- package/dist/admin/hooks/useAdminAuth.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminAuth.js +0 -183
- package/dist/admin/hooks/useAdminAuth.js.map +0 -1
- package/dist/admin/hooks/useAdminCategories.d.ts +0 -9
- package/dist/admin/hooks/useAdminCategories.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminCategories.js +0 -112
- package/dist/admin/hooks/useAdminCategories.js.map +0 -1
- package/dist/admin/hooks/useAdminCustomers.d.ts +0 -3
- package/dist/admin/hooks/useAdminCustomers.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminCustomers.js +0 -110
- package/dist/admin/hooks/useAdminCustomers.js.map +0 -1
- package/dist/admin/hooks/useAdminMe.d.ts +0 -31
- package/dist/admin/hooks/useAdminMe.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminMe.js +0 -78
- package/dist/admin/hooks/useAdminMe.js.map +0 -1
- package/dist/admin/hooks/useAdminOrders.d.ts +0 -3
- package/dist/admin/hooks/useAdminOrders.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminOrders.js +0 -118
- package/dist/admin/hooks/useAdminOrders.js.map +0 -1
- package/dist/admin/hooks/useAdminPermissions.d.ts +0 -3
- package/dist/admin/hooks/useAdminPermissions.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminPermissions.js +0 -51
- package/dist/admin/hooks/useAdminPermissions.js.map +0 -1
- package/dist/admin/hooks/useAdminProductMetrics.d.ts +0 -3
- package/dist/admin/hooks/useAdminProductMetrics.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminProductMetrics.js +0 -32
- package/dist/admin/hooks/useAdminProductMetrics.js.map +0 -1
- package/dist/admin/hooks/useAdminProducts.d.ts +0 -3
- package/dist/admin/hooks/useAdminProducts.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminProducts.js +0 -132
- package/dist/admin/hooks/useAdminProducts.js.map +0 -1
- package/dist/admin/hooks/useAdminSession.d.ts +0 -23
- package/dist/admin/hooks/useAdminSession.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminSession.js +0 -117
- package/dist/admin/hooks/useAdminSession.js.map +0 -1
- package/dist/admin/hooks/useAdminStats.d.ts +0 -47
- package/dist/admin/hooks/useAdminStats.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminStats.js +0 -128
- package/dist/admin/hooks/useAdminStats.js.map +0 -1
- package/dist/admin/hooks/useAdminUsers.d.ts +0 -3
- package/dist/admin/hooks/useAdminUsers.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminUsers.js +0 -177
- package/dist/admin/hooks/useAdminUsers.js.map +0 -1
- package/dist/admin/hooks/useLinkedCustomerStatus.d.ts +0 -3
- package/dist/admin/hooks/useLinkedCustomerStatus.d.ts.map +0 -1
- package/dist/admin/hooks/useLinkedCustomerStatus.js +0 -48
- package/dist/admin/hooks/useLinkedCustomerStatus.js.map +0 -1
- package/dist/admin/hooks/useSetupGuide.d.ts +0 -45
- package/dist/admin/hooks/useSetupGuide.d.ts.map +0 -1
- package/dist/admin/hooks/useSetupGuide.js +0 -60
- package/dist/admin/hooks/useSetupGuide.js.map +0 -1
- package/dist/admin/index.d.ts +0 -66
- package/dist/admin/index.d.ts.map +0 -1
- package/dist/admin/index.js +0 -144
- package/dist/admin/index.js.map +0 -1
- package/dist/admin/permissions.d.ts +0 -92
- package/dist/admin/permissions.d.ts.map +0 -1
- package/dist/admin/permissions.js +0 -201
- package/dist/admin/permissions.js.map +0 -1
- package/dist/admin/server/admin-invite.d.ts +0 -122
- package/dist/admin/server/admin-invite.d.ts.map +0 -1
- package/dist/admin/server/admin-invite.js +0 -235
- package/dist/admin/server/admin-invite.js.map +0 -1
- package/dist/admin/server/admin-password-reset.d.ts +0 -87
- package/dist/admin/server/admin-password-reset.d.ts.map +0 -1
- package/dist/admin/server/admin-password-reset.js +0 -220
- package/dist/admin/server/admin-password-reset.js.map +0 -1
- package/dist/admin/server/admin-self-service.d.ts +0 -86
- package/dist/admin/server/admin-self-service.d.ts.map +0 -1
- package/dist/admin/server/admin-self-service.js +0 -188
- package/dist/admin/server/admin-self-service.js.map +0 -1
- package/dist/admin/server/admin-service.d.ts +0 -130
- package/dist/admin/server/admin-service.d.ts.map +0 -1
- package/dist/admin/server/admin-service.js +0 -278
- package/dist/admin/server/admin-service.js.map +0 -1
- package/dist/admin/server/admin-session.d.ts +0 -173
- package/dist/admin/server/admin-session.d.ts.map +0 -1
- package/dist/admin/server/admin-session.js +0 -272
- package/dist/admin/server/admin-session.js.map +0 -1
- package/dist/admin/server/index.d.ts +0 -17
- package/dist/admin/server/index.d.ts.map +0 -1
- package/dist/admin/server/index.js +0 -39
- package/dist/admin/server/index.js.map +0 -1
- package/dist/admin/server/user-management.d.ts +0 -223
- package/dist/admin/server/user-management.d.ts.map +0 -1
- package/dist/admin/server/user-management.js +0 -846
- package/dist/admin/server/user-management.js.map +0 -1
- package/dist/admin/types.d.ts +0 -1172
- package/dist/admin/types.d.ts.map +0 -1
- package/dist/admin/types.js +0 -10
- package/dist/admin/types.js.map +0 -1
- package/dist/auth/api/auth.d.ts +0 -54
- package/dist/auth/api/auth.d.ts.map +0 -1
- package/dist/auth/api/auth.js +0 -59
- package/dist/auth/api/auth.js.map +0 -1
- package/dist/auth/api/forgot-password.d.ts +0 -41
- package/dist/auth/api/forgot-password.d.ts.map +0 -1
- package/dist/auth/api/forgot-password.js +0 -65
- package/dist/auth/api/forgot-password.js.map +0 -1
- package/dist/auth/api/index.d.ts +0 -36
- package/dist/auth/api/index.d.ts.map +0 -1
- package/dist/auth/api/index.js +0 -44
- package/dist/auth/api/index.js.map +0 -1
- package/dist/auth/api/register.d.ts +0 -41
- package/dist/auth/api/register.d.ts.map +0 -1
- package/dist/auth/api/register.js +0 -94
- package/dist/auth/api/register.js.map +0 -1
- package/dist/auth/api/request-refund.d.ts +0 -38
- package/dist/auth/api/request-refund.d.ts.map +0 -1
- package/dist/auth/api/request-refund.js +0 -142
- package/dist/auth/api/request-refund.js.map +0 -1
- package/dist/auth/api/request-return.d.ts +0 -39
- package/dist/auth/api/request-return.d.ts.map +0 -1
- package/dist/auth/api/request-return.js +0 -109
- package/dist/auth/api/request-return.js.map +0 -1
- package/dist/auth/api/resend-verification.d.ts +0 -41
- package/dist/auth/api/resend-verification.d.ts.map +0 -1
- package/dist/auth/api/resend-verification.js +0 -68
- package/dist/auth/api/resend-verification.js.map +0 -1
- package/dist/auth/api/reset-password.d.ts +0 -67
- package/dist/auth/api/reset-password.d.ts.map +0 -1
- package/dist/auth/api/reset-password.js +0 -106
- package/dist/auth/api/reset-password.js.map +0 -1
- package/dist/auth/api/verify-email.d.ts +0 -47
- package/dist/auth/api/verify-email.d.ts.map +0 -1
- package/dist/auth/api/verify-email.js +0 -90
- package/dist/auth/api/verify-email.js.map +0 -1
- package/dist/auth/components/AuthGuard.d.ts +0 -52
- package/dist/auth/components/AuthGuard.d.ts.map +0 -1
- package/dist/auth/components/AuthGuard.js +0 -109
- package/dist/auth/components/AuthGuard.js.map +0 -1
- package/dist/auth/components/ForgotPasswordForm.d.ts +0 -15
- package/dist/auth/components/ForgotPasswordForm.d.ts.map +0 -1
- package/dist/auth/components/ForgotPasswordForm.js +0 -43
- package/dist/auth/components/ForgotPasswordForm.js.map +0 -1
- package/dist/auth/components/Label.d.ts +0 -19
- package/dist/auth/components/Label.d.ts.map +0 -1
- package/dist/auth/components/Label.js +0 -18
- package/dist/auth/components/Label.js.map +0 -1
- package/dist/auth/components/ResetPasswordForm.d.ts +0 -18
- package/dist/auth/components/ResetPasswordForm.d.ts.map +0 -1
- package/dist/auth/components/ResetPasswordForm.js +0 -87
- package/dist/auth/components/ResetPasswordForm.js.map +0 -1
- package/dist/auth/components/SignInForm.d.ts +0 -21
- package/dist/auth/components/SignInForm.d.ts.map +0 -1
- package/dist/auth/components/SignInForm.js +0 -61
- package/dist/auth/components/SignInForm.js.map +0 -1
- package/dist/auth/components/SignUpForm.d.ts +0 -18
- package/dist/auth/components/SignUpForm.d.ts.map +0 -1
- package/dist/auth/components/SignUpForm.js +0 -78
- package/dist/auth/components/SignUpForm.js.map +0 -1
- package/dist/auth/components/UserMenu.d.ts +0 -18
- package/dist/auth/components/UserMenu.d.ts.map +0 -1
- package/dist/auth/components/UserMenu.js +0 -73
- package/dist/auth/components/UserMenu.js.map +0 -1
- package/dist/auth/components/VerifyEmailNotice.d.ts +0 -20
- package/dist/auth/components/VerifyEmailNotice.d.ts.map +0 -1
- package/dist/auth/components/VerifyEmailNotice.js +0 -57
- package/dist/auth/components/VerifyEmailNotice.js.map +0 -1
- package/dist/auth/components/index.d.ts +0 -15
- package/dist/auth/components/index.d.ts.map +0 -1
- package/dist/auth/components/index.js +0 -14
- package/dist/auth/components/index.js.map +0 -1
- package/dist/auth/config.d.ts +0 -59
- package/dist/auth/config.d.ts.map +0 -1
- package/dist/auth/config.js +0 -255
- package/dist/auth/config.js.map +0 -1
- package/dist/auth/hooks/index.d.ts +0 -7
- package/dist/auth/hooks/index.d.ts.map +0 -1
- package/dist/auth/hooks/index.js +0 -7
- package/dist/auth/hooks/index.js.map +0 -1
- package/dist/auth/hooks/useAuth.d.ts +0 -30
- package/dist/auth/hooks/useAuth.d.ts.map +0 -1
- package/dist/auth/hooks/useAuth.js +0 -261
- package/dist/auth/hooks/useAuth.js.map +0 -1
- package/dist/auth/index.d.ts +0 -55
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -67
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/server/customer-service.d.ts +0 -140
- package/dist/auth/server/customer-service.d.ts.map +0 -1
- package/dist/auth/server/customer-service.js +0 -266
- package/dist/auth/server/customer-service.js.map +0 -1
- package/dist/auth/server/customer-session.d.ts +0 -81
- package/dist/auth/server/customer-session.d.ts.map +0 -1
- package/dist/auth/server/customer-session.js +0 -115
- package/dist/auth/server/customer-session.js.map +0 -1
- package/dist/auth/server/email-sender.d.ts +0 -64
- package/dist/auth/server/email-sender.d.ts.map +0 -1
- package/dist/auth/server/email-sender.js +0 -106
- package/dist/auth/server/email-sender.js.map +0 -1
- package/dist/auth/server/index.d.ts +0 -15
- package/dist/auth/server/index.d.ts.map +0 -1
- package/dist/auth/server/index.js +0 -19
- package/dist/auth/server/index.js.map +0 -1
- package/dist/auth/server/password-reset-service.d.ts +0 -87
- package/dist/auth/server/password-reset-service.d.ts.map +0 -1
- package/dist/auth/server/password-reset-service.js +0 -198
- package/dist/auth/server/password-reset-service.js.map +0 -1
- package/dist/auth/server/password.d.ts +0 -58
- package/dist/auth/server/password.d.ts.map +0 -1
- package/dist/auth/server/password.js +0 -85
- package/dist/auth/server/password.js.map +0 -1
- package/dist/auth/server/verification-service.d.ts +0 -88
- package/dist/auth/server/verification-service.d.ts.map +0 -1
- package/dist/auth/server/verification-service.js +0 -224
- package/dist/auth/server/verification-service.js.map +0 -1
- package/dist/auth/types.d.ts +0 -311
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -7
- package/dist/auth/types.js.map +0 -1
- package/dist/cart/CartProvider.d.ts +0 -65
- package/dist/cart/CartProvider.d.ts.map +0 -1
- package/dist/cart/CartProvider.js +0 -100
- package/dist/cart/CartProvider.js.map +0 -1
- package/dist/cart/components/AddToCartButton.d.ts +0 -77
- package/dist/cart/components/AddToCartButton.d.ts.map +0 -1
- package/dist/cart/components/AddToCartButton.js +0 -122
- package/dist/cart/components/AddToCartButton.js.map +0 -1
- package/dist/cart/components/CartDrawer.d.ts +0 -71
- package/dist/cart/components/CartDrawer.d.ts.map +0 -1
- package/dist/cart/components/CartDrawer.js +0 -117
- package/dist/cart/components/CartDrawer.js.map +0 -1
- package/dist/cart/components/CartIcon.d.ts +0 -36
- package/dist/cart/components/CartIcon.d.ts.map +0 -1
- package/dist/cart/components/CartIcon.js +0 -68
- package/dist/cart/components/CartIcon.js.map +0 -1
- package/dist/cart/components/CartItem.d.ts +0 -52
- package/dist/cart/components/CartItem.d.ts.map +0 -1
- package/dist/cart/components/CartItem.js +0 -55
- package/dist/cart/components/CartItem.js.map +0 -1
- package/dist/cart/components/CartSummary.d.ts +0 -43
- package/dist/cart/components/CartSummary.d.ts.map +0 -1
- package/dist/cart/components/CartSummary.js +0 -60
- package/dist/cart/components/CartSummary.js.map +0 -1
- package/dist/cart/components/QuantitySelector.d.ts +0 -39
- package/dist/cart/components/QuantitySelector.d.ts.map +0 -1
- package/dist/cart/components/QuantitySelector.js +0 -85
- package/dist/cart/components/QuantitySelector.js.map +0 -1
- package/dist/cart/components/index.d.ts +0 -18
- package/dist/cart/components/index.d.ts.map +0 -1
- package/dist/cart/components/index.js +0 -12
- package/dist/cart/components/index.js.map +0 -1
- package/dist/cart/index.d.ts +0 -109
- package/dist/cart/index.d.ts.map +0 -1
- package/dist/cart/index.js +0 -115
- package/dist/cart/index.js.map +0 -1
- package/dist/cart/store.d.ts +0 -150
- package/dist/cart/store.d.ts.map +0 -1
- package/dist/cart/store.js +0 -315
- package/dist/cart/store.js.map +0 -1
- package/dist/checkout/api/checkout.js +0 -112
- package/dist/checkout/api/checkout.js.map +0 -1
- package/dist/checkout/api/countries.d.ts +0 -17
- package/dist/checkout/api/countries.d.ts.map +0 -1
- package/dist/checkout/api/countries.js +0 -38
- package/dist/checkout/api/countries.js.map +0 -1
- package/dist/checkout/api/index.js +0 -11
- package/dist/checkout/api/index.js.map +0 -1
- package/dist/checkout/api/payment-status.d.ts +0 -45
- package/dist/checkout/api/payment-status.d.ts.map +0 -1
- package/dist/checkout/api/payment-status.js +0 -104
- package/dist/checkout/api/payment-status.js.map +0 -1
- package/dist/checkout/api/shipping.d.ts +0 -34
- package/dist/checkout/api/shipping.d.ts.map +0 -1
- package/dist/checkout/api/shipping.js +0 -247
- package/dist/checkout/api/shipping.js.map +0 -1
- package/dist/checkout/api/webhook.js +0 -55
- package/dist/checkout/api/webhook.js.map +0 -1
- package/dist/checkout/components/CheckoutButton.d.ts +0 -55
- package/dist/checkout/components/CheckoutButton.d.ts.map +0 -1
- package/dist/checkout/components/CheckoutButton.js +0 -132
- package/dist/checkout/components/CheckoutButton.js.map +0 -1
- package/dist/checkout/components/CheckoutFlow.d.ts +0 -53
- package/dist/checkout/components/CheckoutFlow.d.ts.map +0 -1
- package/dist/checkout/components/CheckoutFlow.js +0 -286
- package/dist/checkout/components/CheckoutFlow.js.map +0 -1
- package/dist/checkout/components/CheckoutSuccess.d.ts +0 -29
- package/dist/checkout/components/CheckoutSuccess.d.ts.map +0 -1
- package/dist/checkout/components/CheckoutSuccess.js +0 -49
- package/dist/checkout/components/CheckoutSuccess.js.map +0 -1
- package/dist/checkout/components/OrderSummary.d.ts +0 -37
- package/dist/checkout/components/OrderSummary.d.ts.map +0 -1
- package/dist/checkout/components/OrderSummary.js +0 -71
- package/dist/checkout/components/OrderSummary.js.map +0 -1
- package/dist/checkout/components/ShippingForm.d.ts +0 -30
- package/dist/checkout/components/ShippingForm.d.ts.map +0 -1
- package/dist/checkout/components/ShippingForm.js +0 -184
- package/dist/checkout/components/ShippingForm.js.map +0 -1
- package/dist/checkout/components/ShippingOptions.d.ts +0 -51
- package/dist/checkout/components/ShippingOptions.d.ts.map +0 -1
- package/dist/checkout/components/ShippingOptions.js +0 -93
- package/dist/checkout/components/ShippingOptions.js.map +0 -1
- package/dist/checkout/components/index.d.ts +0 -12
- package/dist/checkout/components/index.d.ts.map +0 -1
- package/dist/checkout/components/index.js +0 -12
- package/dist/checkout/components/index.js.map +0 -1
- package/dist/checkout/constants.d.ts +0 -244
- package/dist/checkout/constants.d.ts.map +0 -1
- package/dist/checkout/constants.js +0 -119
- package/dist/checkout/constants.js.map +0 -1
- package/dist/checkout/hooks/index.d.ts +0 -9
- package/dist/checkout/hooks/index.d.ts.map +0 -1
- package/dist/checkout/hooks/index.js +0 -8
- package/dist/checkout/hooks/index.js.map +0 -1
- package/dist/checkout/hooks/useCheckout.d.ts +0 -61
- package/dist/checkout/hooks/useCheckout.d.ts.map +0 -1
- package/dist/checkout/hooks/useCheckout.js +0 -172
- package/dist/checkout/hooks/useCheckout.js.map +0 -1
- package/dist/checkout/hooks/usePaymentStatus.d.ts +0 -42
- package/dist/checkout/hooks/usePaymentStatus.d.ts.map +0 -1
- package/dist/checkout/hooks/usePaymentStatus.js +0 -71
- package/dist/checkout/hooks/usePaymentStatus.js.map +0 -1
- package/dist/checkout/index.js +0 -135
- package/dist/checkout/index.js.map +0 -1
- package/dist/checkout/server/coupons.d.ts +0 -91
- package/dist/checkout/server/coupons.d.ts.map +0 -1
- package/dist/checkout/server/coupons.js +0 -192
- package/dist/checkout/server/coupons.js.map +0 -1
- package/dist/checkout/server/index.js +0 -10
- package/dist/checkout/server/index.js.map +0 -1
- package/dist/checkout/stripe/client.d.ts +0 -74
- package/dist/checkout/stripe/client.d.ts.map +0 -1
- package/dist/checkout/stripe/client.js +0 -175
- package/dist/checkout/stripe/client.js.map +0 -1
- package/dist/checkout/stripe/index.d.ts +0 -7
- package/dist/checkout/stripe/index.d.ts.map +0 -1
- package/dist/checkout/stripe/index.js +0 -7
- package/dist/checkout/stripe/index.js.map +0 -1
- package/dist/checkout/types.d.ts +0 -489
- package/dist/checkout/types.d.ts.map +0 -1
- package/dist/checkout/types.js +0 -8
- package/dist/checkout/types.js.map +0 -1
- package/dist/core/StoreSettingsProvider.d.ts +0 -107
- package/dist/core/StoreSettingsProvider.d.ts.map +0 -1
- package/dist/core/StoreSettingsProvider.js +0 -200
- package/dist/core/StoreSettingsProvider.js.map +0 -1
- package/dist/core/api/index.d.ts +0 -7
- package/dist/core/api/index.d.ts.map +0 -1
- package/dist/core/api/index.js +0 -7
- package/dist/core/api/index.js.map +0 -1
- package/dist/core/api/settings.d.ts +0 -42
- package/dist/core/api/settings.d.ts.map +0 -1
- package/dist/core/api/settings.js +0 -74
- package/dist/core/api/settings.js.map +0 -1
- package/dist/core/config.d.ts +0 -296
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/config.js +0 -117
- package/dist/core/config.js.map +0 -1
- package/dist/core/cookie-consent/CookieBanner.d.ts +0 -2
- package/dist/core/cookie-consent/CookieBanner.d.ts.map +0 -1
- package/dist/core/cookie-consent/CookieBanner.js +0 -243
- package/dist/core/cookie-consent/CookieBanner.js.map +0 -1
- package/dist/core/cookie-consent/CookieConsentProvider.d.ts +0 -53
- package/dist/core/cookie-consent/CookieConsentProvider.d.ts.map +0 -1
- package/dist/core/cookie-consent/CookieConsentProvider.js +0 -162
- package/dist/core/cookie-consent/CookieConsentProvider.js.map +0 -1
- package/dist/core/cookie-consent/CookiePreferencesLink.d.ts +0 -15
- package/dist/core/cookie-consent/CookiePreferencesLink.d.ts.map +0 -1
- package/dist/core/cookie-consent/CookiePreferencesLink.js +0 -12
- package/dist/core/cookie-consent/CookiePreferencesLink.js.map +0 -1
- package/dist/core/cookie-consent/index.d.ts +0 -17
- package/dist/core/cookie-consent/index.d.ts.map +0 -1
- package/dist/core/cookie-consent/index.js +0 -16
- package/dist/core/cookie-consent/index.js.map +0 -1
- package/dist/core/cookie-consent/types.d.ts +0 -31
- package/dist/core/cookie-consent/types.d.ts.map +0 -1
- package/dist/core/cookie-consent/types.js +0 -10
- package/dist/core/cookie-consent/types.js.map +0 -1
- package/dist/core/cookie-consent/useCookieConsent.d.ts +0 -14
- package/dist/core/cookie-consent/useCookieConsent.d.ts.map +0 -1
- package/dist/core/cookie-consent/useCookieConsent.js +0 -25
- package/dist/core/cookie-consent/useCookieConsent.js.map +0 -1
- package/dist/core/db/client.d.ts +0 -39
- package/dist/core/db/client.d.ts.map +0 -1
- package/dist/core/db/client.js +0 -86
- package/dist/core/db/client.js.map +0 -1
- package/dist/core/db/index.d.ts +0 -11
- package/dist/core/db/index.d.ts.map +0 -1
- package/dist/core/db/index.js +0 -36
- package/dist/core/db/index.js.map +0 -1
- package/dist/core/db/queries.d.ts +0 -1427
- package/dist/core/db/queries.d.ts.map +0 -1
- package/dist/core/db/queries.js +0 -1932
- package/dist/core/db/queries.js.map +0 -1
- package/dist/core/db/schema.d.ts +0 -3462
- package/dist/core/db/schema.d.ts.map +0 -1
- package/dist/core/db/schema.js +0 -494
- package/dist/core/db/schema.js.map +0 -1
- package/dist/core/index.d.ts +0 -22
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -43
- package/dist/core/index.js.map +0 -1
- package/dist/core/server/index.d.ts +0 -18
- package/dist/core/server/index.d.ts.map +0 -1
- package/dist/core/server/index.js +0 -47
- package/dist/core/server/index.js.map +0 -1
- package/dist/core/types.d.ts +0 -283
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -8
- package/dist/core/types.js.map +0 -1
- package/dist/core/utils.d.ts +0 -167
- package/dist/core/utils.d.ts.map +0 -1
- package/dist/core/utils.js +0 -351
- package/dist/core/utils.js.map +0 -1
- package/dist/emails/config.d.ts +0 -69
- package/dist/emails/config.d.ts.map +0 -1
- package/dist/emails/config.js +0 -147
- package/dist/emails/config.js.map +0 -1
- package/dist/emails/index.d.ts +0 -85
- package/dist/emails/index.d.ts.map +0 -1
- package/dist/emails/index.js +0 -110
- package/dist/emails/index.js.map +0 -1
- package/dist/emails/send/admin-auth.d.ts +0 -94
- package/dist/emails/send/admin-auth.d.ts.map +0 -1
- package/dist/emails/send/admin-auth.js +0 -118
- package/dist/emails/send/admin-auth.js.map +0 -1
- package/dist/emails/send/auth.d.ts +0 -91
- package/dist/emails/send/auth.d.ts.map +0 -1
- package/dist/emails/send/auth.js +0 -130
- package/dist/emails/send/auth.js.map +0 -1
- package/dist/emails/send/index.d.ts +0 -12
- package/dist/emails/send/index.d.ts.map +0 -1
- package/dist/emails/send/index.js +0 -18
- package/dist/emails/send/index.js.map +0 -1
- package/dist/emails/send/orders.d.ts +0 -186
- package/dist/emails/send/orders.d.ts.map +0 -1
- package/dist/emails/send/orders.js +0 -258
- package/dist/emails/send/orders.js.map +0 -1
- package/dist/emails/sender.d.ts +0 -72
- package/dist/emails/sender.d.ts.map +0 -1
- package/dist/emails/sender.js +0 -116
- package/dist/emails/sender.js.map +0 -1
- package/dist/emails/templates/admin-invite.d.ts +0 -40
- package/dist/emails/templates/admin-invite.d.ts.map +0 -1
- package/dist/emails/templates/admin-invite.js +0 -62
- package/dist/emails/templates/admin-invite.js.map +0 -1
- package/dist/emails/templates/base.d.ts +0 -109
- package/dist/emails/templates/base.d.ts.map +0 -1
- package/dist/emails/templates/base.js +0 -334
- package/dist/emails/templates/base.js.map +0 -1
- package/dist/emails/templates/email-verification.d.ts +0 -28
- package/dist/emails/templates/email-verification.d.ts.map +0 -1
- package/dist/emails/templates/email-verification.js +0 -52
- package/dist/emails/templates/email-verification.js.map +0 -1
- package/dist/emails/templates/index.d.ts +0 -16
- package/dist/emails/templates/index.d.ts.map +0 -1
- package/dist/emails/templates/index.js +0 -28
- package/dist/emails/templates/index.js.map +0 -1
- package/dist/emails/templates/order-cancelled.d.ts +0 -30
- package/dist/emails/templates/order-cancelled.d.ts.map +0 -1
- package/dist/emails/templates/order-cancelled.js +0 -83
- package/dist/emails/templates/order-cancelled.js.map +0 -1
- package/dist/emails/templates/order-confirmation.d.ts +0 -36
- package/dist/emails/templates/order-confirmation.d.ts.map +0 -1
- package/dist/emails/templates/order-confirmation.js +0 -174
- package/dist/emails/templates/order-confirmation.js.map +0 -1
- package/dist/emails/templates/order-delivered.d.ts +0 -31
- package/dist/emails/templates/order-delivered.d.ts.map +0 -1
- package/dist/emails/templates/order-delivered.js +0 -100
- package/dist/emails/templates/order-delivered.js.map +0 -1
- package/dist/emails/templates/order-shipped.d.ts +0 -32
- package/dist/emails/templates/order-shipped.d.ts.map +0 -1
- package/dist/emails/templates/order-shipped.js +0 -83
- package/dist/emails/templates/order-shipped.js.map +0 -1
- package/dist/emails/templates/password-reset.d.ts +0 -27
- package/dist/emails/templates/password-reset.d.ts.map +0 -1
- package/dist/emails/templates/password-reset.js +0 -51
- package/dist/emails/templates/password-reset.js.map +0 -1
- package/dist/emails/templates/refund-processed.d.ts +0 -32
- package/dist/emails/templates/refund-processed.d.ts.map +0 -1
- package/dist/emails/templates/refund-processed.js +0 -92
- package/dist/emails/templates/refund-processed.js.map +0 -1
- package/dist/emails/templates/welcome.d.ts +0 -27
- package/dist/emails/templates/welcome.d.ts.map +0 -1
- package/dist/emails/templates/welcome.js +0 -52
- package/dist/emails/templates/welcome.js.map +0 -1
- package/dist/emails/types.d.ts +0 -229
- package/dist/emails/types.d.ts.map +0 -1
- package/dist/emails/types.js +0 -7
- package/dist/emails/types.js.map +0 -1
- package/dist/emails/utils.d.ts +0 -94
- package/dist/emails/utils.d.ts.map +0 -1
- package/dist/emails/utils.js +0 -218
- package/dist/emails/utils.js.map +0 -1
- package/dist/index.js +0 -73
- package/dist/index.js.map +0 -1
- package/dist/media/api/delete.d.ts +0 -43
- package/dist/media/api/delete.d.ts.map +0 -1
- package/dist/media/api/delete.js +0 -124
- package/dist/media/api/delete.js.map +0 -1
- package/dist/media/api/index.d.ts +0 -17
- package/dist/media/api/index.d.ts.map +0 -1
- package/dist/media/api/index.js +0 -17
- package/dist/media/api/index.js.map +0 -1
- package/dist/media/api/presign.d.ts +0 -38
- package/dist/media/api/presign.d.ts.map +0 -1
- package/dist/media/api/presign.js +0 -130
- package/dist/media/api/presign.js.map +0 -1
- package/dist/media/components/DropZone.d.ts +0 -18
- package/dist/media/components/DropZone.d.ts.map +0 -1
- package/dist/media/components/DropZone.js +0 -107
- package/dist/media/components/DropZone.js.map +0 -1
- package/dist/media/components/ImageGalleryUpload.d.ts +0 -21
- package/dist/media/components/ImageGalleryUpload.d.ts.map +0 -1
- package/dist/media/components/ImageGalleryUpload.js +0 -195
- package/dist/media/components/ImageGalleryUpload.js.map +0 -1
- package/dist/media/components/ImageUpload.d.ts +0 -17
- package/dist/media/components/ImageUpload.d.ts.map +0 -1
- package/dist/media/components/ImageUpload.js +0 -89
- package/dist/media/components/ImageUpload.js.map +0 -1
- package/dist/media/components/index.d.ts +0 -10
- package/dist/media/components/index.d.ts.map +0 -1
- package/dist/media/components/index.js +0 -9
- package/dist/media/components/index.js.map +0 -1
- package/dist/media/config.d.ts +0 -83
- package/dist/media/config.d.ts.map +0 -1
- package/dist/media/config.js +0 -189
- package/dist/media/config.js.map +0 -1
- package/dist/media/hooks/index.d.ts +0 -8
- package/dist/media/hooks/index.d.ts.map +0 -1
- package/dist/media/hooks/index.js +0 -7
- package/dist/media/hooks/index.js.map +0 -1
- package/dist/media/hooks/useUpload.d.ts +0 -32
- package/dist/media/hooks/useUpload.d.ts.map +0 -1
- package/dist/media/hooks/useUpload.js +0 -260
- package/dist/media/hooks/useUpload.js.map +0 -1
- package/dist/media/index.d.ts +0 -57
- package/dist/media/index.d.ts.map +0 -1
- package/dist/media/index.js +0 -68
- package/dist/media/index.js.map +0 -1
- package/dist/media/server/delete.d.ts +0 -59
- package/dist/media/server/delete.d.ts.map +0 -1
- package/dist/media/server/delete.js +0 -176
- package/dist/media/server/delete.js.map +0 -1
- package/dist/media/server/index.d.ts +0 -10
- package/dist/media/server/index.d.ts.map +0 -1
- package/dist/media/server/index.js +0 -13
- package/dist/media/server/index.js.map +0 -1
- package/dist/media/server/presign.d.ts +0 -57
- package/dist/media/server/presign.d.ts.map +0 -1
- package/dist/media/server/presign.js +0 -112
- package/dist/media/server/presign.js.map +0 -1
- package/dist/media/server/r2-client.d.ts +0 -30
- package/dist/media/server/r2-client.d.ts.map +0 -1
- package/dist/media/server/r2-client.js +0 -76
- package/dist/media/server/r2-client.js.map +0 -1
- package/dist/media/types.d.ts +0 -317
- package/dist/media/types.d.ts.map +0 -1
- package/dist/media/types.js +0 -95
- package/dist/media/types.js.map +0 -1
- package/dist/products/api/categories.d.ts +0 -29
- package/dist/products/api/categories.d.ts.map +0 -1
- package/dist/products/api/categories.js +0 -46
- package/dist/products/api/categories.js.map +0 -1
- package/dist/products/api/index.d.ts +0 -24
- package/dist/products/api/index.d.ts.map +0 -1
- package/dist/products/api/index.js +0 -24
- package/dist/products/api/index.js.map +0 -1
- package/dist/products/api/product.d.ts +0 -36
- package/dist/products/api/product.d.ts.map +0 -1
- package/dist/products/api/product.js +0 -67
- package/dist/products/api/product.js.map +0 -1
- package/dist/products/api/products.d.ts +0 -41
- package/dist/products/api/products.d.ts.map +0 -1
- package/dist/products/api/products.js +0 -99
- package/dist/products/api/products.js.map +0 -1
- package/dist/products/components/CategoryNav.d.ts +0 -51
- package/dist/products/components/CategoryNav.d.ts.map +0 -1
- package/dist/products/components/CategoryNav.js +0 -110
- package/dist/products/components/CategoryNav.js.map +0 -1
- package/dist/products/components/ProductBreadcrumb.d.ts +0 -52
- package/dist/products/components/ProductBreadcrumb.d.ts.map +0 -1
- package/dist/products/components/ProductBreadcrumb.js +0 -73
- package/dist/products/components/ProductBreadcrumb.js.map +0 -1
- package/dist/products/components/ProductCard.d.ts +0 -54
- package/dist/products/components/ProductCard.d.ts.map +0 -1
- package/dist/products/components/ProductCard.js +0 -72
- package/dist/products/components/ProductCard.js.map +0 -1
- package/dist/products/components/ProductDetails.d.ts +0 -63
- package/dist/products/components/ProductDetails.d.ts.map +0 -1
- package/dist/products/components/ProductDetails.js +0 -137
- package/dist/products/components/ProductDetails.js.map +0 -1
- package/dist/products/components/ProductFilters.d.ts +0 -70
- package/dist/products/components/ProductFilters.d.ts.map +0 -1
- package/dist/products/components/ProductFilters.js +0 -125
- package/dist/products/components/ProductFilters.js.map +0 -1
- package/dist/products/components/ProductGallery.d.ts +0 -42
- package/dist/products/components/ProductGallery.d.ts.map +0 -1
- package/dist/products/components/ProductGallery.js +0 -103
- package/dist/products/components/ProductGallery.js.map +0 -1
- package/dist/products/components/ProductGrid.d.ts +0 -50
- package/dist/products/components/ProductGrid.d.ts.map +0 -1
- package/dist/products/components/ProductGrid.js +0 -81
- package/dist/products/components/ProductGrid.js.map +0 -1
- package/dist/products/components/ProductSearch.d.ts +0 -43
- package/dist/products/components/ProductSearch.d.ts.map +0 -1
- package/dist/products/components/ProductSearch.js +0 -97
- package/dist/products/components/ProductSearch.js.map +0 -1
- package/dist/products/components/ProductSort.d.ts +0 -43
- package/dist/products/components/ProductSort.d.ts.map +0 -1
- package/dist/products/components/ProductSort.js +0 -59
- package/dist/products/components/ProductSort.js.map +0 -1
- package/dist/products/components/VariantSelector.d.ts +0 -43
- package/dist/products/components/VariantSelector.d.ts.map +0 -1
- package/dist/products/components/VariantSelector.js +0 -147
- package/dist/products/components/VariantSelector.js.map +0 -1
- package/dist/products/components/index.d.ts +0 -26
- package/dist/products/components/index.d.ts.map +0 -1
- package/dist/products/components/index.js +0 -19
- package/dist/products/components/index.js.map +0 -1
- package/dist/products/hooks/index.d.ts +0 -14
- package/dist/products/hooks/index.d.ts.map +0 -1
- package/dist/products/hooks/index.js +0 -10
- package/dist/products/hooks/index.js.map +0 -1
- package/dist/products/hooks/useCategories.d.ts +0 -56
- package/dist/products/hooks/useCategories.d.ts.map +0 -1
- package/dist/products/hooks/useCategories.js +0 -126
- package/dist/products/hooks/useCategories.js.map +0 -1
- package/dist/products/hooks/useProduct.d.ts +0 -44
- package/dist/products/hooks/useProduct.d.ts.map +0 -1
- package/dist/products/hooks/useProduct.js +0 -87
- package/dist/products/hooks/useProduct.js.map +0 -1
- package/dist/products/hooks/useProductAttributes.d.ts +0 -59
- package/dist/products/hooks/useProductAttributes.d.ts.map +0 -1
- package/dist/products/hooks/useProductAttributes.js +0 -146
- package/dist/products/hooks/useProductAttributes.js.map +0 -1
- package/dist/products/hooks/useProducts.d.ts +0 -67
- package/dist/products/hooks/useProducts.d.ts.map +0 -1
- package/dist/products/hooks/useProducts.js +0 -131
- package/dist/products/hooks/useProducts.js.map +0 -1
- package/dist/products/index.d.ts +0 -94
- package/dist/products/index.d.ts.map +0 -1
- package/dist/products/index.js +0 -106
- package/dist/products/index.js.map +0 -1
- package/dist/shipping/index.d.ts +0 -8
- package/dist/shipping/index.d.ts.map +0 -1
- package/dist/shipping/index.js +0 -8
- package/dist/shipping/index.js.map +0 -1
- package/dist/shipping/shippo.d.ts +0 -85
- package/dist/shipping/shippo.d.ts.map +0 -1
- package/dist/shipping/shippo.js +0 -298
- package/dist/shipping/shippo.js.map +0 -1
- package/dist/theme/ThemeProvider.d.ts +0 -70
- package/dist/theme/ThemeProvider.d.ts.map +0 -1
- package/dist/theme/ThemeProvider.js +0 -75
- package/dist/theme/ThemeProvider.js.map +0 -1
- package/dist/theme/colors.d.ts +0 -148
- package/dist/theme/colors.d.ts.map +0 -1
- package/dist/theme/colors.js +0 -214
- package/dist/theme/colors.js.map +0 -1
- package/dist/theme/defaults.d.ts +0 -212
- package/dist/theme/defaults.d.ts.map +0 -1
- package/dist/theme/defaults.js +0 -275
- package/dist/theme/defaults.js.map +0 -1
- package/dist/theme/fonts.d.ts +0 -59
- package/dist/theme/fonts.d.ts.map +0 -1
- package/dist/theme/fonts.js +0 -196
- package/dist/theme/fonts.js.map +0 -1
- package/dist/theme/generator.d.ts +0 -50
- package/dist/theme/generator.d.ts.map +0 -1
- package/dist/theme/generator.js +0 -316
- package/dist/theme/generator.js.map +0 -1
- package/dist/theme/hooks.d.ts +0 -110
- package/dist/theme/hooks.d.ts.map +0 -1
- package/dist/theme/hooks.js +0 -101
- package/dist/theme/hooks.js.map +0 -1
- package/dist/theme/index.d.ts +0 -37
- package/dist/theme/index.d.ts.map +0 -1
- package/dist/theme/index.js +0 -49
- package/dist/theme/index.js.map +0 -1
package/dist/core/db/queries.js
DELETED
|
@@ -1,1932 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @rovela/sdk/core/db/queries
|
|
3
|
-
*
|
|
4
|
-
* Type-safe query helpers for e-commerce stores
|
|
5
|
-
* Each store has its own database (via Neon branches) - no tenant filtering needed
|
|
6
|
-
*/
|
|
7
|
-
import { eq, and, or, desc, asc, ilike, sql, inArray, isNull, isNotNull } from 'drizzle-orm';
|
|
8
|
-
import { getDb } from './client';
|
|
9
|
-
import * as schema from './schema';
|
|
10
|
-
/**
|
|
11
|
-
* Find products with filtering, sorting, and pagination
|
|
12
|
-
*/
|
|
13
|
-
export async function findProducts(options = {}) {
|
|
14
|
-
const db = getDb();
|
|
15
|
-
const conditions = [];
|
|
16
|
-
if (options.categoryId) {
|
|
17
|
-
conditions.push(eq(schema.products.categoryId, options.categoryId));
|
|
18
|
-
}
|
|
19
|
-
if (options.status) {
|
|
20
|
-
conditions.push(eq(schema.products.status, options.status));
|
|
21
|
-
}
|
|
22
|
-
if (options.search) {
|
|
23
|
-
conditions.push(ilike(schema.products.name, `%${options.search}%`));
|
|
24
|
-
}
|
|
25
|
-
if (options.outOfStock) {
|
|
26
|
-
conditions.push(buildOutOfStockCondition());
|
|
27
|
-
}
|
|
28
|
-
if (options.onSale) {
|
|
29
|
-
conditions.push(sql `${schema.products.comparePrice} IS NOT NULL AND ${schema.products.comparePrice} > ${schema.products.price}`);
|
|
30
|
-
}
|
|
31
|
-
if (options.tags && options.tags.length > 0) {
|
|
32
|
-
// JSONB array-overlap check: `tags ?| array['t1','t2']` returns true when
|
|
33
|
-
// ANY tag in the column matches ANY of the provided tags. Powered by the
|
|
34
|
-
// default GIN index on jsonb columns when present.
|
|
35
|
-
conditions.push(sql `${schema.products.tags} ?| ${options.tags}`);
|
|
36
|
-
}
|
|
37
|
-
// Determine sort order
|
|
38
|
-
let orderBy;
|
|
39
|
-
switch (options.sort) {
|
|
40
|
-
case 'price-asc':
|
|
41
|
-
orderBy = asc(schema.products.price);
|
|
42
|
-
break;
|
|
43
|
-
case 'price-desc':
|
|
44
|
-
orderBy = desc(schema.products.price);
|
|
45
|
-
break;
|
|
46
|
-
case 'name':
|
|
47
|
-
orderBy = asc(schema.products.name);
|
|
48
|
-
break;
|
|
49
|
-
case 'newest':
|
|
50
|
-
default:
|
|
51
|
-
orderBy = desc(schema.products.createdAt);
|
|
52
|
-
}
|
|
53
|
-
const query = db
|
|
54
|
-
.select()
|
|
55
|
-
.from(schema.products)
|
|
56
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
57
|
-
.orderBy(orderBy);
|
|
58
|
-
if (options.limit) {
|
|
59
|
-
query.limit(options.limit);
|
|
60
|
-
}
|
|
61
|
-
if (options.offset) {
|
|
62
|
-
query.offset(options.offset);
|
|
63
|
-
}
|
|
64
|
-
return query;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Find a single product by slug
|
|
68
|
-
*/
|
|
69
|
-
export async function findProductBySlug(slug) {
|
|
70
|
-
const db = getDb();
|
|
71
|
-
const result = await db
|
|
72
|
-
.select()
|
|
73
|
-
.from(schema.products)
|
|
74
|
-
.where(eq(schema.products.slug, slug))
|
|
75
|
-
.limit(1);
|
|
76
|
-
return result[0] || null;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Find a single product by ID
|
|
80
|
-
*/
|
|
81
|
-
export async function findProductById(id) {
|
|
82
|
-
const db = getDb();
|
|
83
|
-
const result = await db
|
|
84
|
-
.select()
|
|
85
|
-
.from(schema.products)
|
|
86
|
-
.where(eq(schema.products.id, id))
|
|
87
|
-
.limit(1);
|
|
88
|
-
return result[0] || null;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Get product variants for a product
|
|
92
|
-
*/
|
|
93
|
-
export async function findProductVariants(productId) {
|
|
94
|
-
const db = getDb();
|
|
95
|
-
return db
|
|
96
|
-
.select()
|
|
97
|
-
.from(schema.productVariants)
|
|
98
|
-
.where(eq(schema.productVariants.productId, productId));
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Count total products matching filters
|
|
102
|
-
*/
|
|
103
|
-
export async function countProducts(options = {}) {
|
|
104
|
-
const db = getDb();
|
|
105
|
-
const conditions = [];
|
|
106
|
-
if (options.categoryId) {
|
|
107
|
-
conditions.push(eq(schema.products.categoryId, options.categoryId));
|
|
108
|
-
}
|
|
109
|
-
if (options.status) {
|
|
110
|
-
conditions.push(eq(schema.products.status, options.status));
|
|
111
|
-
}
|
|
112
|
-
if (options.search) {
|
|
113
|
-
conditions.push(ilike(schema.products.name, `%${options.search}%`));
|
|
114
|
-
}
|
|
115
|
-
if (options.outOfStock) {
|
|
116
|
-
conditions.push(buildOutOfStockCondition());
|
|
117
|
-
}
|
|
118
|
-
const result = await db
|
|
119
|
-
.select({ count: sql `count(*)` })
|
|
120
|
-
.from(schema.products)
|
|
121
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined);
|
|
122
|
-
return Number(result[0]?.count || 0);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Build the SQL condition for "effectively out of stock":
|
|
126
|
-
* - non-variant products: trackInventory=true AND inventory <= 0
|
|
127
|
-
* - variant products: no variant exists with inventory > 0
|
|
128
|
-
*
|
|
129
|
-
* Reused by both `findProducts` (the OOS chip filter) and the OOS metric card.
|
|
130
|
-
* The variant subquery uses `product_variants_product_id_idx` (already in schema)
|
|
131
|
-
* so it's an index lookup, not a scan.
|
|
132
|
-
*/
|
|
133
|
-
function buildOutOfStockCondition() {
|
|
134
|
-
return or(and(eq(schema.products.hasVariants, false), eq(schema.products.trackInventory, true), sql `${schema.products.inventory} <= 0`), and(eq(schema.products.hasVariants, true), sql `NOT EXISTS (
|
|
135
|
-
SELECT 1 FROM ${schema.productVariants} pv
|
|
136
|
-
WHERE pv.product_id = ${schema.products.id} AND pv.inventory > 0
|
|
137
|
-
)`));
|
|
138
|
-
}
|
|
139
|
-
// =============================================================================
|
|
140
|
-
// Product Metrics & Inventory Aggregation (admin product list page)
|
|
141
|
-
// =============================================================================
|
|
142
|
-
/**
|
|
143
|
-
* For products with `hasVariants=true`, the parent `products.inventory` column
|
|
144
|
-
* is unused — inventory lives on `product_variants`. This helper returns a
|
|
145
|
-
* Map of `productId → effectiveInventory` for an arbitrary set of variant
|
|
146
|
-
* product IDs. The page-list handler calls this for the products it just
|
|
147
|
-
* returned and merges the sums into each row before responding to the client.
|
|
148
|
-
*
|
|
149
|
-
* Empty input → empty map. Single SQL query, GROUP BY product_id.
|
|
150
|
-
*/
|
|
151
|
-
export async function getInventoryByProductIds(productIds) {
|
|
152
|
-
if (productIds.length === 0)
|
|
153
|
-
return new Map();
|
|
154
|
-
const db = getDb();
|
|
155
|
-
const rows = await db
|
|
156
|
-
.select({
|
|
157
|
-
productId: schema.productVariants.productId,
|
|
158
|
-
total: sql `COALESCE(SUM(${schema.productVariants.inventory}), 0)`,
|
|
159
|
-
})
|
|
160
|
-
.from(schema.productVariants)
|
|
161
|
-
.where(inArray(schema.productVariants.productId, productIds))
|
|
162
|
-
.groupBy(schema.productVariants.productId);
|
|
163
|
-
const result = new Map();
|
|
164
|
-
for (const r of rows) {
|
|
165
|
-
result.set(r.productId, Number(r.total) || 0);
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Count of products in `status='active'`. Drives the "Active products"
|
|
171
|
-
* metric card on the admin product list page.
|
|
172
|
-
*/
|
|
173
|
-
export async function getActiveProductCount() {
|
|
174
|
-
const db = getDb();
|
|
175
|
-
const result = await db
|
|
176
|
-
.select({ count: sql `count(*)` })
|
|
177
|
-
.from(schema.products)
|
|
178
|
-
.where(eq(schema.products.status, 'active'));
|
|
179
|
-
return Number(result[0]?.count || 0);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Count of *active* products that are effectively out of stock. Excludes
|
|
183
|
-
* drafts / archived (they're irrelevant noise on the product list view).
|
|
184
|
-
*
|
|
185
|
-
* Uses the same OOS condition as the chip filter (single source of truth).
|
|
186
|
-
*/
|
|
187
|
-
export async function getOutOfStockProductCount() {
|
|
188
|
-
const db = getDb();
|
|
189
|
-
const result = await db
|
|
190
|
-
.select({ count: sql `count(*)` })
|
|
191
|
-
.from(schema.products)
|
|
192
|
-
.where(and(eq(schema.products.status, 'active'), buildOutOfStockCondition()));
|
|
193
|
-
return Number(result[0]?.count || 0);
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Best-selling product over the last `days` window, by units sold.
|
|
197
|
-
*
|
|
198
|
-
* Counts only orders that actually moved revenue: `paid`, `shipped`,
|
|
199
|
-
* `delivered`. Excludes pending (might fail), cancelled, refunded, and
|
|
200
|
-
* return_requested (not yet finalized).
|
|
201
|
-
*
|
|
202
|
-
* Returns null when no qualifying orders exist in the window — the UI
|
|
203
|
-
* renders an empty state rather than a broken card.
|
|
204
|
-
*/
|
|
205
|
-
export async function getBestSellerProduct(days = 30) {
|
|
206
|
-
const db = getDb();
|
|
207
|
-
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
208
|
-
const rows = await db
|
|
209
|
-
.select({
|
|
210
|
-
id: schema.products.id,
|
|
211
|
-
name: schema.products.name,
|
|
212
|
-
unitsSold: sql `SUM(${schema.orderItems.quantity})`,
|
|
213
|
-
revenue: sql `SUM(${schema.orderItems.quantity} * ${schema.orderItems.price})`,
|
|
214
|
-
})
|
|
215
|
-
.from(schema.orderItems)
|
|
216
|
-
.innerJoin(schema.orders, eq(schema.orders.id, schema.orderItems.orderId))
|
|
217
|
-
.innerJoin(schema.products, eq(schema.products.id, schema.orderItems.productId))
|
|
218
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), sql `${schema.orders.createdAt} >= ${since}`))
|
|
219
|
-
.groupBy(schema.products.id, schema.products.name)
|
|
220
|
-
.orderBy(desc(sql `SUM(${schema.orderItems.quantity})`))
|
|
221
|
-
.limit(1);
|
|
222
|
-
const row = rows[0];
|
|
223
|
-
if (!row)
|
|
224
|
-
return null;
|
|
225
|
-
return {
|
|
226
|
-
id: row.id,
|
|
227
|
-
name: row.name,
|
|
228
|
-
unitsSold: Number(row.unitsSold) || 0,
|
|
229
|
-
revenue: String(row.revenue ?? '0'),
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Best-selling products in the last N days, capped to `limit`.
|
|
234
|
-
*
|
|
235
|
-
* Same SQL shape as `getBestSellerProduct` (paid+shipped+delivered window,
|
|
236
|
-
* ordered by units sold desc) but returns up to `limit` rows. Used by the
|
|
237
|
-
* Best Sellers homepage section card. Returns an empty array when no
|
|
238
|
-
* qualifying orders exist — callers MUST hide their UI when length === 0
|
|
239
|
-
* (don't render an empty rail).
|
|
240
|
-
*
|
|
241
|
-
* `order_items_product_id_idx` covers the JOIN; cost is the same as the
|
|
242
|
-
* existing single-row helper.
|
|
243
|
-
*/
|
|
244
|
-
export async function getBestSellers(days = 30, limit = 8) {
|
|
245
|
-
const db = getDb();
|
|
246
|
-
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
247
|
-
const rows = await db
|
|
248
|
-
.select({
|
|
249
|
-
id: schema.products.id,
|
|
250
|
-
slug: schema.products.slug,
|
|
251
|
-
name: schema.products.name,
|
|
252
|
-
price: schema.products.price,
|
|
253
|
-
comparePrice: schema.products.comparePrice,
|
|
254
|
-
images: schema.products.images,
|
|
255
|
-
unitsSold: sql `SUM(${schema.orderItems.quantity})`,
|
|
256
|
-
})
|
|
257
|
-
.from(schema.orderItems)
|
|
258
|
-
.innerJoin(schema.orders, eq(schema.orders.id, schema.orderItems.orderId))
|
|
259
|
-
.innerJoin(schema.products, eq(schema.products.id, schema.orderItems.productId))
|
|
260
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), eq(schema.products.status, 'active'), sql `${schema.orders.createdAt} >= ${since}`))
|
|
261
|
-
.groupBy(schema.products.id, schema.products.slug, schema.products.name, schema.products.price, schema.products.comparePrice, schema.products.images)
|
|
262
|
-
.orderBy(desc(sql `SUM(${schema.orderItems.quantity})`))
|
|
263
|
-
.limit(limit);
|
|
264
|
-
return rows.map((row) => ({
|
|
265
|
-
id: row.id,
|
|
266
|
-
slug: row.slug,
|
|
267
|
-
name: row.name,
|
|
268
|
-
price: row.price,
|
|
269
|
-
comparePrice: row.comparePrice,
|
|
270
|
-
images: (row.images ?? []),
|
|
271
|
-
unitsSold: Number(row.unitsSold) || 0,
|
|
272
|
-
}));
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Most-popular products by distinct buyer count (last N days), capped.
|
|
276
|
-
*
|
|
277
|
-
* Different from best sellers — best sellers is units sold (revenue lens);
|
|
278
|
-
* this is buyer reach (how many distinct customers bought it). Used by the
|
|
279
|
-
* Most Popular homepage section card. Same JOIN path; same status window.
|
|
280
|
-
*
|
|
281
|
-
* Excludes guest orders (`customer_id IS NULL`) so we count real distinct
|
|
282
|
-
* buyers, not anonymous sessions. Returns empty array when no data — UI
|
|
283
|
-
* must hide.
|
|
284
|
-
*/
|
|
285
|
-
export async function getMostPopularProducts(days = 30, limit = 8) {
|
|
286
|
-
const db = getDb();
|
|
287
|
-
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
288
|
-
const rows = await db
|
|
289
|
-
.select({
|
|
290
|
-
id: schema.products.id,
|
|
291
|
-
slug: schema.products.slug,
|
|
292
|
-
name: schema.products.name,
|
|
293
|
-
price: schema.products.price,
|
|
294
|
-
comparePrice: schema.products.comparePrice,
|
|
295
|
-
images: schema.products.images,
|
|
296
|
-
buyerCount: sql `COUNT(DISTINCT ${schema.orders.customerId})`,
|
|
297
|
-
})
|
|
298
|
-
.from(schema.orderItems)
|
|
299
|
-
.innerJoin(schema.orders, eq(schema.orders.id, schema.orderItems.orderId))
|
|
300
|
-
.innerJoin(schema.products, eq(schema.products.id, schema.orderItems.productId))
|
|
301
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), eq(schema.products.status, 'active'), isNotNull(schema.orders.customerId), sql `${schema.orders.createdAt} >= ${since}`))
|
|
302
|
-
.groupBy(schema.products.id, schema.products.slug, schema.products.name, schema.products.price, schema.products.comparePrice, schema.products.images)
|
|
303
|
-
.orderBy(desc(sql `COUNT(DISTINCT ${schema.orders.customerId})`))
|
|
304
|
-
.limit(limit);
|
|
305
|
-
return rows.map((row) => ({
|
|
306
|
-
id: row.id,
|
|
307
|
-
slug: row.slug,
|
|
308
|
-
name: row.name,
|
|
309
|
-
price: row.price,
|
|
310
|
-
comparePrice: row.comparePrice,
|
|
311
|
-
images: (row.images ?? []),
|
|
312
|
-
buyerCount: Number(row.buyerCount) || 0,
|
|
313
|
-
}));
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Distinct-buyer count for a single product within the last N hours.
|
|
317
|
-
*
|
|
318
|
-
* Powers the "X people bought this in the last 24h" social-proof card on
|
|
319
|
-
* PDPs. Server-only — caller MUST cache via Server Component + ISR
|
|
320
|
-
* (`revalidate: 300`); calling this per page view is a perf trap.
|
|
321
|
-
*
|
|
322
|
-
* Returns 0 when no qualifying orders exist. UI gates on a `minThreshold`
|
|
323
|
-
* (default 5) — never render "1 person bought this" (awkward + reads as
|
|
324
|
-
* dead store).
|
|
325
|
-
*/
|
|
326
|
-
export async function getProductRecentBuyerCount(productId, hours = 24) {
|
|
327
|
-
const db = getDb();
|
|
328
|
-
const since = new Date(Date.now() - hours * 60 * 60 * 1000);
|
|
329
|
-
const rows = await db
|
|
330
|
-
.select({
|
|
331
|
-
count: sql `COUNT(DISTINCT ${schema.orders.customerId})`,
|
|
332
|
-
})
|
|
333
|
-
.from(schema.orderItems)
|
|
334
|
-
.innerJoin(schema.orders, eq(schema.orders.id, schema.orderItems.orderId))
|
|
335
|
-
.where(and(eq(schema.orderItems.productId, productId), inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), isNotNull(schema.orders.customerId), sql `${schema.orders.createdAt} >= ${since}`));
|
|
336
|
-
return Number(rows[0]?.count ?? 0);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Check whether a customer has purchased a specific product.
|
|
340
|
-
*
|
|
341
|
-
* Powers the verified-purchase badge on the C2 reviews card. Returns true
|
|
342
|
-
* iff the customer has at least one paid+shipped+delivered order containing
|
|
343
|
-
* the product (variant-agnostic — buying any variant counts).
|
|
344
|
-
*
|
|
345
|
-
* Uses EXISTS for short-circuit evaluation; cheap even on large order
|
|
346
|
-
* histories. Index `order_items_product_id_idx` covers the inner lookup;
|
|
347
|
-
* `orders_customer_id_idx` covers the join.
|
|
348
|
-
*/
|
|
349
|
-
export async function hasCustomerPurchasedProduct(customerId, productId) {
|
|
350
|
-
const db = getDb();
|
|
351
|
-
const rows = await db
|
|
352
|
-
.select({
|
|
353
|
-
hit: sql `1`,
|
|
354
|
-
})
|
|
355
|
-
.from(schema.orderItems)
|
|
356
|
-
.innerJoin(schema.orders, eq(schema.orders.id, schema.orderItems.orderId))
|
|
357
|
-
.where(and(eq(schema.orderItems.productId, productId), eq(schema.orders.customerId, customerId), inArray(schema.orders.status, ['paid', 'shipped', 'delivered'])))
|
|
358
|
-
.limit(1);
|
|
359
|
-
return rows.length > 0;
|
|
360
|
-
}
|
|
361
|
-
// =============================================================================
|
|
362
|
-
// Categories
|
|
363
|
-
// =============================================================================
|
|
364
|
-
/**
|
|
365
|
-
* Find all categories
|
|
366
|
-
*/
|
|
367
|
-
export async function findCategories() {
|
|
368
|
-
const db = getDb();
|
|
369
|
-
return db
|
|
370
|
-
.select()
|
|
371
|
-
.from(schema.categories)
|
|
372
|
-
.orderBy(asc(schema.categories.order), asc(schema.categories.name));
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Find a category by slug
|
|
376
|
-
*/
|
|
377
|
-
export async function findCategoryBySlug(slug) {
|
|
378
|
-
const db = getDb();
|
|
379
|
-
const result = await db
|
|
380
|
-
.select()
|
|
381
|
-
.from(schema.categories)
|
|
382
|
-
.where(eq(schema.categories.slug, slug))
|
|
383
|
-
.limit(1);
|
|
384
|
-
return result[0] || null;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Find orders with filtering and pagination
|
|
388
|
-
*/
|
|
389
|
-
export async function findOrders(options = {}) {
|
|
390
|
-
const db = getDb();
|
|
391
|
-
const conditions = [];
|
|
392
|
-
if (options.status) {
|
|
393
|
-
conditions.push(eq(schema.orders.status, options.status));
|
|
394
|
-
}
|
|
395
|
-
if (options.customerId) {
|
|
396
|
-
conditions.push(eq(schema.orders.customerId, options.customerId));
|
|
397
|
-
}
|
|
398
|
-
const query = db
|
|
399
|
-
.select()
|
|
400
|
-
.from(schema.orders)
|
|
401
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
402
|
-
.orderBy(desc(schema.orders.createdAt));
|
|
403
|
-
if (options.limit) {
|
|
404
|
-
query.limit(options.limit);
|
|
405
|
-
}
|
|
406
|
-
if (options.offset) {
|
|
407
|
-
query.offset(options.offset);
|
|
408
|
-
}
|
|
409
|
-
return query;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Find order by ID with items (includes product images)
|
|
413
|
-
*/
|
|
414
|
-
export async function findOrderById(id) {
|
|
415
|
-
const db = getDb();
|
|
416
|
-
const order = await db
|
|
417
|
-
.select()
|
|
418
|
-
.from(schema.orders)
|
|
419
|
-
.where(eq(schema.orders.id, id))
|
|
420
|
-
.limit(1);
|
|
421
|
-
if (!order[0])
|
|
422
|
-
return null;
|
|
423
|
-
// Join order_items with products to get images
|
|
424
|
-
const itemsWithProducts = await db
|
|
425
|
-
.select({
|
|
426
|
-
id: schema.orderItems.id,
|
|
427
|
-
orderId: schema.orderItems.orderId,
|
|
428
|
-
productId: schema.orderItems.productId,
|
|
429
|
-
variantId: schema.orderItems.variantId,
|
|
430
|
-
name: schema.orderItems.name,
|
|
431
|
-
price: schema.orderItems.price,
|
|
432
|
-
quantity: schema.orderItems.quantity,
|
|
433
|
-
attributes: schema.orderItems.attributes,
|
|
434
|
-
createdAt: schema.orderItems.createdAt,
|
|
435
|
-
// Get product images (JSONB array)
|
|
436
|
-
productImages: schema.products.images,
|
|
437
|
-
})
|
|
438
|
-
.from(schema.orderItems)
|
|
439
|
-
.leftJoin(schema.products, eq(schema.orderItems.productId, schema.products.id))
|
|
440
|
-
.where(eq(schema.orderItems.orderId, id));
|
|
441
|
-
// Map items to include the first product image
|
|
442
|
-
const items = itemsWithProducts.map((item) => {
|
|
443
|
-
const images = item.productImages;
|
|
444
|
-
return {
|
|
445
|
-
id: item.id,
|
|
446
|
-
orderId: item.orderId,
|
|
447
|
-
productId: item.productId,
|
|
448
|
-
variantId: item.variantId,
|
|
449
|
-
name: item.name,
|
|
450
|
-
price: item.price,
|
|
451
|
-
quantity: item.quantity,
|
|
452
|
-
attributes: item.attributes,
|
|
453
|
-
createdAt: item.createdAt,
|
|
454
|
-
// Add first image from product
|
|
455
|
-
image: images && images.length > 0 ? images[0] : null,
|
|
456
|
-
};
|
|
457
|
-
});
|
|
458
|
-
return {
|
|
459
|
-
...order[0],
|
|
460
|
-
items,
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Get order items for an order
|
|
465
|
-
*/
|
|
466
|
-
export async function findOrderItems(orderId) {
|
|
467
|
-
const db = getDb();
|
|
468
|
-
return db
|
|
469
|
-
.select()
|
|
470
|
-
.from(schema.orderItems)
|
|
471
|
-
.where(eq(schema.orderItems.orderId, orderId));
|
|
472
|
-
}
|
|
473
|
-
// =============================================================================
|
|
474
|
-
// Customers
|
|
475
|
-
// =============================================================================
|
|
476
|
-
/**
|
|
477
|
-
* Find customer by email
|
|
478
|
-
*/
|
|
479
|
-
export async function findCustomerByEmail(email) {
|
|
480
|
-
const db = getDb();
|
|
481
|
-
const result = await db
|
|
482
|
-
.select()
|
|
483
|
-
.from(schema.customers)
|
|
484
|
-
.where(eq(schema.customers.email, email.toLowerCase()))
|
|
485
|
-
.limit(1);
|
|
486
|
-
return result[0] || null;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Find customer by ID
|
|
490
|
-
*/
|
|
491
|
-
export async function findCustomerById(id) {
|
|
492
|
-
const db = getDb();
|
|
493
|
-
const result = await db
|
|
494
|
-
.select()
|
|
495
|
-
.from(schema.customers)
|
|
496
|
-
.where(eq(schema.customers.id, id))
|
|
497
|
-
.limit(1);
|
|
498
|
-
return result[0] || null;
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Find all customers with pagination
|
|
502
|
-
*/
|
|
503
|
-
export async function findCustomers(options = {}) {
|
|
504
|
-
const db = getDb();
|
|
505
|
-
const conditions = [];
|
|
506
|
-
if (options.search) {
|
|
507
|
-
conditions.push(ilike(schema.customers.email, `%${options.search}%`));
|
|
508
|
-
}
|
|
509
|
-
const query = db
|
|
510
|
-
.select()
|
|
511
|
-
.from(schema.customers)
|
|
512
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
513
|
-
.orderBy(desc(schema.customers.createdAt));
|
|
514
|
-
if (options.limit) {
|
|
515
|
-
query.limit(options.limit);
|
|
516
|
-
}
|
|
517
|
-
if (options.offset) {
|
|
518
|
-
query.offset(options.offset);
|
|
519
|
-
}
|
|
520
|
-
return query;
|
|
521
|
-
}
|
|
522
|
-
// =============================================================================
|
|
523
|
-
// Store Admins
|
|
524
|
-
// =============================================================================
|
|
525
|
-
/**
|
|
526
|
-
* Find admin by email
|
|
527
|
-
*/
|
|
528
|
-
export async function findAdminByEmail(email) {
|
|
529
|
-
const db = getDb();
|
|
530
|
-
const result = await db
|
|
531
|
-
.select()
|
|
532
|
-
.from(schema.storeAdmins)
|
|
533
|
-
.where(eq(schema.storeAdmins.email, email.toLowerCase()))
|
|
534
|
-
.limit(1);
|
|
535
|
-
return result[0] || null;
|
|
536
|
-
}
|
|
537
|
-
// =============================================================================
|
|
538
|
-
// Admin Stats
|
|
539
|
-
// =============================================================================
|
|
540
|
-
/**
|
|
541
|
-
* Get store statistics for admin dashboard
|
|
542
|
-
*/
|
|
543
|
-
export async function getStoreStats() {
|
|
544
|
-
const db = getDb();
|
|
545
|
-
const [productCount, orderCount, customerCount, revenue] = await Promise.all([
|
|
546
|
-
// Total products
|
|
547
|
-
db
|
|
548
|
-
.select({ count: sql `count(*)` })
|
|
549
|
-
.from(schema.products)
|
|
550
|
-
.where(eq(schema.products.status, 'active')),
|
|
551
|
-
// Total orders — same status filter as revenue so the two metrics tell
|
|
552
|
-
// the same story. Counting cancelled/refunded/pending here would inflate
|
|
553
|
-
// the number relative to revenue and confuse merchants.
|
|
554
|
-
db
|
|
555
|
-
.select({ count: sql `count(*)` })
|
|
556
|
-
.from(schema.orders)
|
|
557
|
-
.where(inArray(schema.orders.status, ['paid', 'shipped', 'delivered'])),
|
|
558
|
-
// Total customers
|
|
559
|
-
db
|
|
560
|
-
.select({ count: sql `count(*)` })
|
|
561
|
-
.from(schema.customers),
|
|
562
|
-
// Total revenue (paid orders only)
|
|
563
|
-
db
|
|
564
|
-
.select({ total: sql `COALESCE(SUM(total), 0)` })
|
|
565
|
-
.from(schema.orders)
|
|
566
|
-
.where(inArray(schema.orders.status, ['paid', 'shipped', 'delivered'])),
|
|
567
|
-
]);
|
|
568
|
-
return {
|
|
569
|
-
products: Number(productCount[0]?.count || 0),
|
|
570
|
-
orders: Number(orderCount[0]?.count || 0),
|
|
571
|
-
customers: Number(customerCount[0]?.count || 0),
|
|
572
|
-
revenue: Number(revenue[0]?.total || 0),
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
// =============================================================================
|
|
576
|
-
// Period-scoped store stats (for time-window selector)
|
|
577
|
-
// =============================================================================
|
|
578
|
-
/**
|
|
579
|
-
* Same shape as `getStoreStats()` but scoped to a window of `days` ago to now.
|
|
580
|
-
* Powers the "Today / 7d / 30d / 90d" selector on the admin dashboard. Both
|
|
581
|
-
* Orders and Revenue use the same status filter (paid + shipped + delivered)
|
|
582
|
-
* so they tell the same story.
|
|
583
|
-
*
|
|
584
|
-
* Note: `products` and `customers` here mean *new* products created in the
|
|
585
|
-
* window and *new* customers registered in the window — this is what makes
|
|
586
|
-
* the period-vs-prior-period delta meaningful. For the absolute "how many
|
|
587
|
-
* active products do I have" use `getStoreStats()`.
|
|
588
|
-
*/
|
|
589
|
-
export async function getStoreStatsForPeriod(startDate, endDate) {
|
|
590
|
-
const db = getDb();
|
|
591
|
-
const [orderRow, revenueRow, productRow, customerRow] = await Promise.all([
|
|
592
|
-
db
|
|
593
|
-
.select({ count: sql `count(*)` })
|
|
594
|
-
.from(schema.orders)
|
|
595
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), sql `${schema.orders.createdAt} >= ${startDate.toISOString()}`, sql `${schema.orders.createdAt} < ${endDate.toISOString()}`)),
|
|
596
|
-
db
|
|
597
|
-
.select({ total: sql `COALESCE(SUM(total), 0)` })
|
|
598
|
-
.from(schema.orders)
|
|
599
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), sql `${schema.orders.createdAt} >= ${startDate.toISOString()}`, sql `${schema.orders.createdAt} < ${endDate.toISOString()}`)),
|
|
600
|
-
db
|
|
601
|
-
.select({ count: sql `count(*)` })
|
|
602
|
-
.from(schema.products)
|
|
603
|
-
.where(and(eq(schema.products.status, 'active'), sql `${schema.products.createdAt} >= ${startDate.toISOString()}`, sql `${schema.products.createdAt} < ${endDate.toISOString()}`)),
|
|
604
|
-
db
|
|
605
|
-
.select({ count: sql `count(*)` })
|
|
606
|
-
.from(schema.customers)
|
|
607
|
-
.where(and(sql `${schema.customers.createdAt} >= ${startDate.toISOString()}`, sql `${schema.customers.createdAt} < ${endDate.toISOString()}`)),
|
|
608
|
-
]);
|
|
609
|
-
return {
|
|
610
|
-
orders: Number(orderRow[0]?.count || 0),
|
|
611
|
-
revenue: Number(revenueRow[0]?.total || 0),
|
|
612
|
-
newProducts: Number(productRow[0]?.count || 0),
|
|
613
|
-
newCustomers: Number(customerRow[0]?.count || 0),
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Count of refunded orders in the window. Used to compute refund rate.
|
|
618
|
-
*/
|
|
619
|
-
export async function getRefundedOrdersCount(startDate, endDate) {
|
|
620
|
-
const db = getDb();
|
|
621
|
-
const result = await db
|
|
622
|
-
.select({ count: sql `count(*)` })
|
|
623
|
-
.from(schema.orders)
|
|
624
|
-
.where(and(eq(schema.orders.status, 'refunded'), sql `${schema.orders.createdAt} >= ${startDate.toISOString()}`, sql `${schema.orders.createdAt} < ${endDate.toISOString()}`));
|
|
625
|
-
return Number(result[0]?.count || 0);
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Count of all orders in the window (any status, used as the denominator
|
|
629
|
-
* for refund rate so the rate isn't artificially inflated by excluding
|
|
630
|
-
* pending/cancelled). Cancelled orders DO count here — they represent
|
|
631
|
-
* customer behavior even if they don't represent revenue.
|
|
632
|
-
*/
|
|
633
|
-
async function getTotalOrdersInPeriod(startDate, endDate) {
|
|
634
|
-
const db = getDb();
|
|
635
|
-
const result = await db
|
|
636
|
-
.select({ count: sql `count(*)` })
|
|
637
|
-
.from(schema.orders)
|
|
638
|
-
.where(and(sql `${schema.orders.createdAt} >= ${startDate.toISOString()}`, sql `${schema.orders.createdAt} < ${endDate.toISOString()}`));
|
|
639
|
-
return Number(result[0]?.count || 0);
|
|
640
|
-
}
|
|
641
|
-
function pctDelta(current, previous) {
|
|
642
|
-
if (previous === 0)
|
|
643
|
-
return null;
|
|
644
|
-
return (current - previous) / previous;
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* Build the full dashboard summary in a single round-trip. Runs eight
|
|
648
|
-
* parallel queries (current period + previous period × four metrics) and
|
|
649
|
-
* computes derived ratios + deltas in-process. Total wall-clock ~30–50ms
|
|
650
|
-
* on a warm Neon connection.
|
|
651
|
-
*/
|
|
652
|
-
export async function getDashboardSummary(periodDays = 30) {
|
|
653
|
-
// Anchor windows to UTC midnight so day boundaries are stable.
|
|
654
|
-
const now = new Date();
|
|
655
|
-
const todayUtc = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
656
|
-
// `endDate` is the start of tomorrow (exclusive upper bound) so today's
|
|
657
|
-
// orders are included.
|
|
658
|
-
const endDate = new Date(todayUtc);
|
|
659
|
-
endDate.setUTCDate(endDate.getUTCDate() + 1);
|
|
660
|
-
const currentStart = new Date(todayUtc);
|
|
661
|
-
currentStart.setUTCDate(currentStart.getUTCDate() - (periodDays - 1));
|
|
662
|
-
const previousEnd = new Date(currentStart);
|
|
663
|
-
const previousStart = new Date(currentStart);
|
|
664
|
-
previousStart.setUTCDate(previousStart.getUTCDate() - periodDays);
|
|
665
|
-
const [currentStats, previousStats, currentTotalOrders, previousTotalOrders, currentRefunded, previousRefunded, bestSeller,] = await Promise.all([
|
|
666
|
-
getStoreStatsForPeriod(currentStart, endDate),
|
|
667
|
-
getStoreStatsForPeriod(previousStart, previousEnd),
|
|
668
|
-
getTotalOrdersInPeriod(currentStart, endDate),
|
|
669
|
-
getTotalOrdersInPeriod(previousStart, previousEnd),
|
|
670
|
-
getRefundedOrdersCount(currentStart, endDate),
|
|
671
|
-
getRefundedOrdersCount(previousStart, previousEnd),
|
|
672
|
-
getBestSellerProduct(periodDays),
|
|
673
|
-
]);
|
|
674
|
-
const currentAov = currentStats.orders > 0 ? currentStats.revenue / currentStats.orders : 0;
|
|
675
|
-
const previousAov = previousStats.orders > 0 ? previousStats.revenue / previousStats.orders : 0;
|
|
676
|
-
const currentRefundRate = currentTotalOrders > 0 ? currentRefunded / currentTotalOrders : 0;
|
|
677
|
-
const previousRefundRate = previousTotalOrders > 0 ? previousRefunded / previousTotalOrders : 0;
|
|
678
|
-
return {
|
|
679
|
-
periodDays,
|
|
680
|
-
current: {
|
|
681
|
-
revenue: currentStats.revenue,
|
|
682
|
-
orders: currentStats.orders,
|
|
683
|
-
newCustomers: currentStats.newCustomers,
|
|
684
|
-
newProducts: currentStats.newProducts,
|
|
685
|
-
aov: currentAov,
|
|
686
|
-
refundRate: currentRefundRate,
|
|
687
|
-
bestSeller,
|
|
688
|
-
},
|
|
689
|
-
previous: {
|
|
690
|
-
revenue: previousStats.revenue,
|
|
691
|
-
orders: previousStats.orders,
|
|
692
|
-
newCustomers: previousStats.newCustomers,
|
|
693
|
-
newProducts: previousStats.newProducts,
|
|
694
|
-
aov: previousAov,
|
|
695
|
-
refundRate: previousRefundRate,
|
|
696
|
-
},
|
|
697
|
-
deltas: {
|
|
698
|
-
revenue: pctDelta(currentStats.revenue, previousStats.revenue),
|
|
699
|
-
orders: pctDelta(currentStats.orders, previousStats.orders),
|
|
700
|
-
aov: pctDelta(currentAov, previousAov),
|
|
701
|
-
refundRate: pctDelta(currentRefundRate, previousRefundRate),
|
|
702
|
-
customers: pctDelta(currentStats.newCustomers, previousStats.newCustomers),
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Get low stock products (inventory < threshold)
|
|
708
|
-
*/
|
|
709
|
-
export async function getLowStockProducts(threshold = 10) {
|
|
710
|
-
const db = getDb();
|
|
711
|
-
// Get products with variants that have low stock
|
|
712
|
-
const lowStockVariants = await db
|
|
713
|
-
.select({
|
|
714
|
-
product: schema.products,
|
|
715
|
-
variant: schema.productVariants,
|
|
716
|
-
})
|
|
717
|
-
.from(schema.productVariants)
|
|
718
|
-
.innerJoin(schema.products, eq(schema.productVariants.productId, schema.products.id))
|
|
719
|
-
.where(and(eq(schema.products.status, 'active'), sql `${schema.productVariants.inventory} < ${threshold}`))
|
|
720
|
-
.orderBy(asc(schema.productVariants.inventory));
|
|
721
|
-
return lowStockVariants;
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Get recent orders
|
|
725
|
-
*/
|
|
726
|
-
export async function getRecentOrders(limit = 5) {
|
|
727
|
-
const db = getDb();
|
|
728
|
-
return db
|
|
729
|
-
.select()
|
|
730
|
-
.from(schema.orders)
|
|
731
|
-
.orderBy(desc(schema.orders.createdAt))
|
|
732
|
-
.limit(limit);
|
|
733
|
-
}
|
|
734
|
-
// =============================================================================
|
|
735
|
-
// Order Mutations (for checkout module)
|
|
736
|
-
// =============================================================================
|
|
737
|
-
/**
|
|
738
|
-
* Create a new order
|
|
739
|
-
*/
|
|
740
|
-
export async function createOrder(data) {
|
|
741
|
-
const db = getDb();
|
|
742
|
-
const [order] = await db
|
|
743
|
-
.insert(schema.orders)
|
|
744
|
-
.values(data)
|
|
745
|
-
.returning();
|
|
746
|
-
return order;
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Create order items for an order
|
|
750
|
-
*/
|
|
751
|
-
export async function createOrderItems(items) {
|
|
752
|
-
const db = getDb();
|
|
753
|
-
if (items.length === 0) {
|
|
754
|
-
return [];
|
|
755
|
-
}
|
|
756
|
-
return db.insert(schema.orderItems).values(items).returning();
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Update order status
|
|
760
|
-
*/
|
|
761
|
-
export async function updateOrderStatus(orderId, status) {
|
|
762
|
-
const db = getDb();
|
|
763
|
-
const [order] = await db
|
|
764
|
-
.update(schema.orders)
|
|
765
|
-
.set({ status, updatedAt: new Date() })
|
|
766
|
-
.where(eq(schema.orders.id, orderId))
|
|
767
|
-
.returning();
|
|
768
|
-
return order || null;
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* Update order shipping information (for Shippo integration)
|
|
772
|
-
*/
|
|
773
|
-
export async function updateOrderShipping(orderId, shippingData) {
|
|
774
|
-
const db = getDb();
|
|
775
|
-
const [order] = await db
|
|
776
|
-
.update(schema.orders)
|
|
777
|
-
.set({
|
|
778
|
-
...shippingData,
|
|
779
|
-
updatedAt: new Date(),
|
|
780
|
-
})
|
|
781
|
-
.where(eq(schema.orders.id, orderId))
|
|
782
|
-
.returning();
|
|
783
|
-
return order || null;
|
|
784
|
-
}
|
|
785
|
-
/**
|
|
786
|
-
* Find order by Stripe Payment Intent ID
|
|
787
|
-
* Used for idempotency in webhook handling
|
|
788
|
-
*/
|
|
789
|
-
export async function findOrderByPaymentIntent(paymentIntentId) {
|
|
790
|
-
const db = getDb();
|
|
791
|
-
const [order] = await db
|
|
792
|
-
.select()
|
|
793
|
-
.from(schema.orders)
|
|
794
|
-
.where(eq(schema.orders.stripePaymentIntentId, paymentIntentId))
|
|
795
|
-
.limit(1);
|
|
796
|
-
return order || null;
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Update order refund amount (for partial refund tracking)
|
|
800
|
-
* Adds the refund amount to the existing refund total
|
|
801
|
-
*/
|
|
802
|
-
export async function updateOrderRefundAmount(orderId, refundAmountCents) {
|
|
803
|
-
const db = getDb();
|
|
804
|
-
const refundAmountDecimal = (refundAmountCents / 100).toFixed(2);
|
|
805
|
-
const [order] = await db
|
|
806
|
-
.update(schema.orders)
|
|
807
|
-
.set({
|
|
808
|
-
refundAmount: sql `COALESCE(${schema.orders.refundAmount}, 0) + ${refundAmountDecimal}`,
|
|
809
|
-
updatedAt: new Date(),
|
|
810
|
-
})
|
|
811
|
-
.where(eq(schema.orders.id, orderId))
|
|
812
|
-
.returning();
|
|
813
|
-
return order || null;
|
|
814
|
-
}
|
|
815
|
-
/**
|
|
816
|
-
* Update order receipt information (for Stripe receipt URL)
|
|
817
|
-
*/
|
|
818
|
-
export async function updateOrderReceipt(orderId, receiptData) {
|
|
819
|
-
const db = getDb();
|
|
820
|
-
const [order] = await db
|
|
821
|
-
.update(schema.orders)
|
|
822
|
-
.set({
|
|
823
|
-
...receiptData,
|
|
824
|
-
updatedAt: new Date(),
|
|
825
|
-
})
|
|
826
|
-
.where(eq(schema.orders.id, orderId))
|
|
827
|
-
.returning();
|
|
828
|
-
return order || null;
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Update order with return request
|
|
832
|
-
* Sets status to 'return_requested' and stores the return reason
|
|
833
|
-
*/
|
|
834
|
-
export async function updateOrderReturnRequest(orderId, returnReason) {
|
|
835
|
-
const db = getDb();
|
|
836
|
-
const [order] = await db
|
|
837
|
-
.update(schema.orders)
|
|
838
|
-
.set({
|
|
839
|
-
status: 'return_requested',
|
|
840
|
-
returnReason,
|
|
841
|
-
updatedAt: new Date(),
|
|
842
|
-
})
|
|
843
|
-
.where(eq(schema.orders.id, orderId))
|
|
844
|
-
.returning();
|
|
845
|
-
return order || null;
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* Clear order return request
|
|
849
|
-
* Sets status back to 'delivered' and clears the return reason
|
|
850
|
-
*/
|
|
851
|
-
export async function clearOrderReturnRequest(orderId) {
|
|
852
|
-
const db = getDb();
|
|
853
|
-
const [order] = await db
|
|
854
|
-
.update(schema.orders)
|
|
855
|
-
.set({
|
|
856
|
-
status: 'delivered',
|
|
857
|
-
returnReason: null,
|
|
858
|
-
updatedAt: new Date(),
|
|
859
|
-
})
|
|
860
|
-
.where(eq(schema.orders.id, orderId))
|
|
861
|
-
.returning();
|
|
862
|
-
return order || null;
|
|
863
|
-
}
|
|
864
|
-
// =============================================================================
|
|
865
|
-
// Product CRUD (for admin module)
|
|
866
|
-
// =============================================================================
|
|
867
|
-
/**
|
|
868
|
-
* Create a new product
|
|
869
|
-
*/
|
|
870
|
-
export async function createProduct(data) {
|
|
871
|
-
const db = getDb();
|
|
872
|
-
const [product] = await db
|
|
873
|
-
.insert(schema.products)
|
|
874
|
-
.values(data)
|
|
875
|
-
.returning();
|
|
876
|
-
return product;
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* Update a product by ID
|
|
880
|
-
*/
|
|
881
|
-
export async function updateProduct(id, data) {
|
|
882
|
-
const db = getDb();
|
|
883
|
-
const [product] = await db
|
|
884
|
-
.update(schema.products)
|
|
885
|
-
.set({ ...data, updatedAt: new Date() })
|
|
886
|
-
.where(eq(schema.products.id, id))
|
|
887
|
-
.returning();
|
|
888
|
-
return product || null;
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Archive a product by ID (soft delete).
|
|
892
|
-
* Sets status to 'archived' instead of deleting to preserve order history.
|
|
893
|
-
* Use hardDeleteProduct() for permanent deletion if needed.
|
|
894
|
-
*/
|
|
895
|
-
export async function deleteProduct(id) {
|
|
896
|
-
const result = await updateProduct(id, { status: 'archived' });
|
|
897
|
-
return result !== null;
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* Permanently delete a product by ID.
|
|
901
|
-
* WARNING: This will fail if there are order_items referencing this product.
|
|
902
|
-
* Use deleteProduct() (soft delete) for most cases.
|
|
903
|
-
*/
|
|
904
|
-
export async function hardDeleteProduct(id) {
|
|
905
|
-
const db = getDb();
|
|
906
|
-
const result = await db
|
|
907
|
-
.delete(schema.products)
|
|
908
|
-
.where(eq(schema.products.id, id))
|
|
909
|
-
.returning({ id: schema.products.id });
|
|
910
|
-
return result.length > 0;
|
|
911
|
-
}
|
|
912
|
-
// =============================================================================
|
|
913
|
-
// Product Variant CRUD (for admin module)
|
|
914
|
-
// =============================================================================
|
|
915
|
-
/**
|
|
916
|
-
* Create a new product variant
|
|
917
|
-
*/
|
|
918
|
-
export async function createProductVariant(data) {
|
|
919
|
-
const db = getDb();
|
|
920
|
-
const [variant] = await db
|
|
921
|
-
.insert(schema.productVariants)
|
|
922
|
-
.values(data)
|
|
923
|
-
.returning();
|
|
924
|
-
return variant;
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Update a product variant by ID
|
|
928
|
-
*/
|
|
929
|
-
export async function updateProductVariant(id, data) {
|
|
930
|
-
const db = getDb();
|
|
931
|
-
const [variant] = await db
|
|
932
|
-
.update(schema.productVariants)
|
|
933
|
-
.set(data)
|
|
934
|
-
.where(eq(schema.productVariants.id, id))
|
|
935
|
-
.returning();
|
|
936
|
-
return variant || null;
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Delete a product variant by ID
|
|
940
|
-
*/
|
|
941
|
-
export async function deleteProductVariant(id) {
|
|
942
|
-
const db = getDb();
|
|
943
|
-
const result = await db
|
|
944
|
-
.delete(schema.productVariants)
|
|
945
|
-
.where(eq(schema.productVariants.id, id))
|
|
946
|
-
.returning({ id: schema.productVariants.id });
|
|
947
|
-
return result.length > 0;
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
* Update variant inventory
|
|
951
|
-
*/
|
|
952
|
-
export async function updateVariantInventory(id, inventory) {
|
|
953
|
-
const db = getDb();
|
|
954
|
-
await db
|
|
955
|
-
.update(schema.productVariants)
|
|
956
|
-
.set({ inventory })
|
|
957
|
-
.where(eq(schema.productVariants.id, id));
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* Find a variant by ID
|
|
961
|
-
*/
|
|
962
|
-
export async function findVariantById(id) {
|
|
963
|
-
const db = getDb();
|
|
964
|
-
const [variant] = await db
|
|
965
|
-
.select()
|
|
966
|
-
.from(schema.productVariants)
|
|
967
|
-
.where(eq(schema.productVariants.id, id))
|
|
968
|
-
.limit(1);
|
|
969
|
-
return variant || null;
|
|
970
|
-
}
|
|
971
|
-
// =============================================================================
|
|
972
|
-
// Category CRUD (for admin module)
|
|
973
|
-
// =============================================================================
|
|
974
|
-
/**
|
|
975
|
-
* Find a category by ID
|
|
976
|
-
*/
|
|
977
|
-
export async function findCategoryById(id) {
|
|
978
|
-
const db = getDb();
|
|
979
|
-
const [category] = await db
|
|
980
|
-
.select()
|
|
981
|
-
.from(schema.categories)
|
|
982
|
-
.where(eq(schema.categories.id, id))
|
|
983
|
-
.limit(1);
|
|
984
|
-
return category || null;
|
|
985
|
-
}
|
|
986
|
-
/**
|
|
987
|
-
* Create a new category
|
|
988
|
-
*/
|
|
989
|
-
export async function createCategory(data) {
|
|
990
|
-
const db = getDb();
|
|
991
|
-
const [category] = await db
|
|
992
|
-
.insert(schema.categories)
|
|
993
|
-
.values(data)
|
|
994
|
-
.returning();
|
|
995
|
-
return category;
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Update a category by ID
|
|
999
|
-
*/
|
|
1000
|
-
export async function updateCategory(id, data) {
|
|
1001
|
-
const db = getDb();
|
|
1002
|
-
const [category] = await db
|
|
1003
|
-
.update(schema.categories)
|
|
1004
|
-
.set(data)
|
|
1005
|
-
.where(eq(schema.categories.id, id))
|
|
1006
|
-
.returning();
|
|
1007
|
-
return category || null;
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Delete a category by ID
|
|
1011
|
-
*/
|
|
1012
|
-
export async function deleteCategory(id) {
|
|
1013
|
-
const db = getDb();
|
|
1014
|
-
const result = await db
|
|
1015
|
-
.delete(schema.categories)
|
|
1016
|
-
.where(eq(schema.categories.id, id))
|
|
1017
|
-
.returning({ id: schema.categories.id });
|
|
1018
|
-
return result.length > 0;
|
|
1019
|
-
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Count categories
|
|
1022
|
-
*/
|
|
1023
|
-
export async function countCategories() {
|
|
1024
|
-
const db = getDb();
|
|
1025
|
-
const result = await db
|
|
1026
|
-
.select({ count: sql `count(*)` })
|
|
1027
|
-
.from(schema.categories);
|
|
1028
|
-
return Number(result[0]?.count || 0);
|
|
1029
|
-
}
|
|
1030
|
-
// =============================================================================
|
|
1031
|
-
// Customer Queries (for admin module)
|
|
1032
|
-
// =============================================================================
|
|
1033
|
-
/**
|
|
1034
|
-
* Count customers with optional search
|
|
1035
|
-
*/
|
|
1036
|
-
export async function countCustomers(options = {}) {
|
|
1037
|
-
const db = getDb();
|
|
1038
|
-
const conditions = [];
|
|
1039
|
-
if (options.search) {
|
|
1040
|
-
conditions.push(ilike(schema.customers.email, `%${options.search}%`));
|
|
1041
|
-
}
|
|
1042
|
-
const result = await db
|
|
1043
|
-
.select({ count: sql `count(*)` })
|
|
1044
|
-
.from(schema.customers)
|
|
1045
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined);
|
|
1046
|
-
return Number(result[0]?.count || 0);
|
|
1047
|
-
}
|
|
1048
|
-
/**
|
|
1049
|
-
* Find orders for a specific customer
|
|
1050
|
-
*/
|
|
1051
|
-
export async function findCustomerOrders(customerId) {
|
|
1052
|
-
const db = getDb();
|
|
1053
|
-
return db
|
|
1054
|
-
.select()
|
|
1055
|
-
.from(schema.orders)
|
|
1056
|
-
.where(eq(schema.orders.customerId, customerId))
|
|
1057
|
-
.orderBy(desc(schema.orders.createdAt));
|
|
1058
|
-
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Claim guest orders by email when a customer registers.
|
|
1061
|
-
* Updates all orders with matching email and null customerId to the new customer.
|
|
1062
|
-
*
|
|
1063
|
-
* @param email - Email address (will be lowercased)
|
|
1064
|
-
* @param customerId - New customer ID to assign
|
|
1065
|
-
* @returns Number of orders claimed
|
|
1066
|
-
*/
|
|
1067
|
-
export async function claimGuestOrders(email, customerId) {
|
|
1068
|
-
const db = getDb();
|
|
1069
|
-
const result = await db
|
|
1070
|
-
.update(schema.orders)
|
|
1071
|
-
.set({ customerId, updatedAt: new Date() })
|
|
1072
|
-
.where(and(eq(schema.orders.email, email.toLowerCase().trim()), isNull(schema.orders.customerId)))
|
|
1073
|
-
.returning({ id: schema.orders.id });
|
|
1074
|
-
return result.length;
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Delete (anonymize) a customer by ID.
|
|
1078
|
-
*
|
|
1079
|
-
* This is a SOFT DELETE that anonymizes customer data instead of removing the record.
|
|
1080
|
-
* This approach:
|
|
1081
|
-
* - Preserves order history integrity (orders reference customerId)
|
|
1082
|
-
* - Complies with GDPR "right to erasure" through anonymization
|
|
1083
|
-
* - Prevents login by invalidating passwordHash
|
|
1084
|
-
* - Keeps the record for audit trail purposes
|
|
1085
|
-
*
|
|
1086
|
-
* Related tokens (verification, password reset) are auto-deleted via CASCADE.
|
|
1087
|
-
*
|
|
1088
|
-
* @param id - Customer UUID
|
|
1089
|
-
* @returns true if customer was found and anonymized, false if not found
|
|
1090
|
-
*/
|
|
1091
|
-
export async function deleteCustomer(id) {
|
|
1092
|
-
const db = getDb();
|
|
1093
|
-
// Anonymize customer data instead of hard delete
|
|
1094
|
-
const result = await db
|
|
1095
|
-
.update(schema.customers)
|
|
1096
|
-
.set({
|
|
1097
|
-
email: `deleted_${Date.now()}@anonymized.local`,
|
|
1098
|
-
name: null,
|
|
1099
|
-
passwordHash: 'DELETED', // Prevents login
|
|
1100
|
-
emailVerified: null,
|
|
1101
|
-
stripeCustomerId: null,
|
|
1102
|
-
})
|
|
1103
|
-
.where(eq(schema.customers.id, id))
|
|
1104
|
-
.returning({ id: schema.customers.id });
|
|
1105
|
-
return result.length > 0;
|
|
1106
|
-
}
|
|
1107
|
-
// =============================================================================
|
|
1108
|
-
// Order Queries (for admin module)
|
|
1109
|
-
// =============================================================================
|
|
1110
|
-
/**
|
|
1111
|
-
* Count orders with optional status filter
|
|
1112
|
-
*/
|
|
1113
|
-
export async function countOrders(options = {}) {
|
|
1114
|
-
const db = getDb();
|
|
1115
|
-
const conditions = [];
|
|
1116
|
-
if (options.status) {
|
|
1117
|
-
conditions.push(eq(schema.orders.status, options.status));
|
|
1118
|
-
}
|
|
1119
|
-
const result = await db
|
|
1120
|
-
.select({ count: sql `count(*)` })
|
|
1121
|
-
.from(schema.orders)
|
|
1122
|
-
.where(conditions.length > 0 ? and(...conditions) : undefined);
|
|
1123
|
-
return Number(result[0]?.count || 0);
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Get revenue by period (for analytics).
|
|
1127
|
-
*
|
|
1128
|
-
* Returns one row per calendar day in the window, **including days with zero
|
|
1129
|
-
* revenue**. The SQL GROUP BY only emits rows where orders exist, which made
|
|
1130
|
-
* the chart skip empty days and look discontinuous (e.g., a Friday-only seller
|
|
1131
|
-
* showed 5 floating points instead of 30). Fill happens in-process, so the
|
|
1132
|
-
* chart can render a continuous line without the consumer thinking about it.
|
|
1133
|
-
*
|
|
1134
|
-
* Date format is `YYYY-MM-DD` in UTC (matches Postgres `DATE()` output).
|
|
1135
|
-
*/
|
|
1136
|
-
export async function getRevenueByPeriod(days) {
|
|
1137
|
-
const db = getDb();
|
|
1138
|
-
// Normalize to UTC midnight so the day boundaries match Postgres DATE()
|
|
1139
|
-
// and our in-process fill loop produces stable keys.
|
|
1140
|
-
const now = new Date();
|
|
1141
|
-
const todayUtc = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
|
|
1142
|
-
const startDate = new Date(todayUtc);
|
|
1143
|
-
startDate.setUTCDate(startDate.getUTCDate() - days);
|
|
1144
|
-
const result = await db
|
|
1145
|
-
.select({
|
|
1146
|
-
date: sql `DATE(${schema.orders.createdAt})`,
|
|
1147
|
-
revenue: sql `COALESCE(SUM(${schema.orders.total}), 0)`,
|
|
1148
|
-
})
|
|
1149
|
-
.from(schema.orders)
|
|
1150
|
-
.where(and(inArray(schema.orders.status, ['paid', 'shipped', 'delivered']), sql `${schema.orders.createdAt} >= ${startDate.toISOString()}`))
|
|
1151
|
-
.groupBy(sql `DATE(${schema.orders.createdAt})`)
|
|
1152
|
-
.orderBy(sql `DATE(${schema.orders.createdAt})`);
|
|
1153
|
-
// Index actual revenue rows by date for O(1) lookup during fill.
|
|
1154
|
-
const revenueByDate = new Map();
|
|
1155
|
-
for (const row of result) {
|
|
1156
|
-
revenueByDate.set(String(row.date), Number(row.revenue));
|
|
1157
|
-
}
|
|
1158
|
-
// Walk every day from startDate through today and emit a row for each —
|
|
1159
|
-
// empty days get revenue=0 so the chart renders a continuous line.
|
|
1160
|
-
const filled = [];
|
|
1161
|
-
const cursor = new Date(startDate);
|
|
1162
|
-
while (cursor <= todayUtc) {
|
|
1163
|
-
const key = cursor.toISOString().slice(0, 10);
|
|
1164
|
-
filled.push({ date: key, revenue: revenueByDate.get(key) ?? 0 });
|
|
1165
|
-
cursor.setUTCDate(cursor.getUTCDate() + 1);
|
|
1166
|
-
}
|
|
1167
|
-
return filled;
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Get orders by status count (for analytics)
|
|
1171
|
-
*/
|
|
1172
|
-
export async function getOrdersByStatus() {
|
|
1173
|
-
const db = getDb();
|
|
1174
|
-
const result = await db
|
|
1175
|
-
.select({
|
|
1176
|
-
status: schema.orders.status,
|
|
1177
|
-
count: sql `count(*)`,
|
|
1178
|
-
})
|
|
1179
|
-
.from(schema.orders)
|
|
1180
|
-
.groupBy(schema.orders.status);
|
|
1181
|
-
return result.map((r) => ({
|
|
1182
|
-
status: r.status,
|
|
1183
|
-
count: Number(r.count),
|
|
1184
|
-
}));
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Find store settings.
|
|
1188
|
-
* Returns null if no settings exist (first time access).
|
|
1189
|
-
* Each store has only one settings row.
|
|
1190
|
-
*/
|
|
1191
|
-
export async function findSettings() {
|
|
1192
|
-
const db = getDb();
|
|
1193
|
-
const [settings] = await db
|
|
1194
|
-
.select()
|
|
1195
|
-
.from(schema.storeSettings)
|
|
1196
|
-
.limit(1);
|
|
1197
|
-
if (!settings)
|
|
1198
|
-
return null;
|
|
1199
|
-
return {
|
|
1200
|
-
storeName: settings.storeName,
|
|
1201
|
-
storeEmail: settings.storeEmail,
|
|
1202
|
-
logoUrl: settings.logoUrl,
|
|
1203
|
-
storeCurrency: settings.storeCurrency,
|
|
1204
|
-
storeTimezone: settings.storeTimezone,
|
|
1205
|
-
taxIncludedInPrices: settings.taxIncludedInPrices,
|
|
1206
|
-
shippingEnabled: settings.shippingEnabled,
|
|
1207
|
-
freeShippingThreshold: settings.freeShippingThreshold
|
|
1208
|
-
? Number(settings.freeShippingThreshold)
|
|
1209
|
-
: null,
|
|
1210
|
-
shippingCountries: settings.shippingCountries,
|
|
1211
|
-
// Auth configuration
|
|
1212
|
-
guestCheckout: settings.guestCheckout,
|
|
1213
|
-
customerAccounts: settings.customerAccounts,
|
|
1214
|
-
// Shippo fields
|
|
1215
|
-
shippingMode: settings.shippingMode,
|
|
1216
|
-
shippoApiKey: settings.shippoApiKey,
|
|
1217
|
-
shippoTestMode: settings.shippoTestMode,
|
|
1218
|
-
shippoFromAddress: settings.shippoFromAddress,
|
|
1219
|
-
shippoDefaultParcel: settings.shippoDefaultParcel,
|
|
1220
|
-
// Discount/promotion configuration
|
|
1221
|
-
promotionCodesEnabled: settings.promotionCodesEnabled,
|
|
1222
|
-
// Cookie consent banner (R021)
|
|
1223
|
-
cookieBannerEnabled: settings.cookieBannerEnabled,
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Upsert (create or update) store settings.
|
|
1228
|
-
* Each store has only one settings row.
|
|
1229
|
-
*/
|
|
1230
|
-
export async function upsertSettings(data) {
|
|
1231
|
-
const db = getDb();
|
|
1232
|
-
// Check if settings exist
|
|
1233
|
-
const existing = await db
|
|
1234
|
-
.select({ id: schema.storeSettings.id })
|
|
1235
|
-
.from(schema.storeSettings)
|
|
1236
|
-
.limit(1);
|
|
1237
|
-
const values = {
|
|
1238
|
-
storeName: data.storeName ?? undefined,
|
|
1239
|
-
storeEmail: data.storeEmail ?? undefined,
|
|
1240
|
-
logoUrl: data.logoUrl ?? undefined,
|
|
1241
|
-
storeCurrency: data.storeCurrency ?? undefined,
|
|
1242
|
-
storeTimezone: data.storeTimezone ?? undefined,
|
|
1243
|
-
taxIncludedInPrices: data.taxIncludedInPrices ?? undefined,
|
|
1244
|
-
shippingEnabled: data.shippingEnabled ?? undefined,
|
|
1245
|
-
freeShippingThreshold: data.freeShippingThreshold !== null && data.freeShippingThreshold !== undefined
|
|
1246
|
-
? String(data.freeShippingThreshold)
|
|
1247
|
-
: undefined,
|
|
1248
|
-
shippingCountries: data.shippingCountries ?? undefined,
|
|
1249
|
-
// Auth configuration
|
|
1250
|
-
guestCheckout: data.guestCheckout ?? undefined,
|
|
1251
|
-
customerAccounts: data.customerAccounts ?? undefined,
|
|
1252
|
-
// Shippo carrier integration
|
|
1253
|
-
shippingMode: data.shippingMode ?? undefined,
|
|
1254
|
-
shippoApiKey: data.shippoApiKey ?? undefined,
|
|
1255
|
-
shippoTestMode: data.shippoTestMode ?? undefined,
|
|
1256
|
-
shippoFromAddress: data.shippoFromAddress ?? undefined,
|
|
1257
|
-
shippoDefaultParcel: data.shippoDefaultParcel ?? undefined,
|
|
1258
|
-
// Discount/promotion configuration
|
|
1259
|
-
promotionCodesEnabled: data.promotionCodesEnabled ?? undefined,
|
|
1260
|
-
// Cookie consent banner (R021)
|
|
1261
|
-
cookieBannerEnabled: data.cookieBannerEnabled ?? undefined,
|
|
1262
|
-
updatedAt: new Date(),
|
|
1263
|
-
};
|
|
1264
|
-
let result;
|
|
1265
|
-
if (existing.length > 0) {
|
|
1266
|
-
// Update existing
|
|
1267
|
-
[result] = await db
|
|
1268
|
-
.update(schema.storeSettings)
|
|
1269
|
-
.set(values)
|
|
1270
|
-
.where(eq(schema.storeSettings.id, existing[0].id))
|
|
1271
|
-
.returning();
|
|
1272
|
-
}
|
|
1273
|
-
else {
|
|
1274
|
-
// Insert new
|
|
1275
|
-
[result] = await db
|
|
1276
|
-
.insert(schema.storeSettings)
|
|
1277
|
-
.values(values)
|
|
1278
|
-
.returning();
|
|
1279
|
-
}
|
|
1280
|
-
return {
|
|
1281
|
-
storeName: result.storeName,
|
|
1282
|
-
storeEmail: result.storeEmail,
|
|
1283
|
-
logoUrl: result.logoUrl,
|
|
1284
|
-
storeCurrency: result.storeCurrency,
|
|
1285
|
-
storeTimezone: result.storeTimezone,
|
|
1286
|
-
taxIncludedInPrices: result.taxIncludedInPrices,
|
|
1287
|
-
shippingEnabled: result.shippingEnabled,
|
|
1288
|
-
freeShippingThreshold: result.freeShippingThreshold
|
|
1289
|
-
? Number(result.freeShippingThreshold)
|
|
1290
|
-
: null,
|
|
1291
|
-
shippingCountries: result.shippingCountries,
|
|
1292
|
-
// Auth configuration
|
|
1293
|
-
guestCheckout: result.guestCheckout,
|
|
1294
|
-
customerAccounts: result.customerAccounts,
|
|
1295
|
-
// Shippo fields
|
|
1296
|
-
shippingMode: result.shippingMode,
|
|
1297
|
-
shippoApiKey: result.shippoApiKey,
|
|
1298
|
-
shippoTestMode: result.shippoTestMode,
|
|
1299
|
-
shippoFromAddress: result.shippoFromAddress,
|
|
1300
|
-
shippoDefaultParcel: result.shippoDefaultParcel,
|
|
1301
|
-
// Discount/promotion configuration
|
|
1302
|
-
promotionCodesEnabled: result.promotionCodesEnabled,
|
|
1303
|
-
// Cookie consent banner (R021)
|
|
1304
|
-
cookieBannerEnabled: result.cookieBannerEnabled,
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* Find all shipping carriers
|
|
1309
|
-
*/
|
|
1310
|
-
export async function findShippingCarriers() {
|
|
1311
|
-
const db = getDb();
|
|
1312
|
-
const carriers = await db
|
|
1313
|
-
.select()
|
|
1314
|
-
.from(schema.shippingCarriers)
|
|
1315
|
-
.orderBy(asc(schema.shippingCarriers.sortOrder), asc(schema.shippingCarriers.name));
|
|
1316
|
-
return carriers.map((carrier) => ({
|
|
1317
|
-
id: carrier.id,
|
|
1318
|
-
name: carrier.name,
|
|
1319
|
-
code: carrier.code,
|
|
1320
|
-
description: carrier.description,
|
|
1321
|
-
isActive: carrier.isActive,
|
|
1322
|
-
sortOrder: carrier.sortOrder,
|
|
1323
|
-
createdAt: carrier.createdAt,
|
|
1324
|
-
updatedAt: carrier.updatedAt,
|
|
1325
|
-
}));
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Find active shipping carriers only
|
|
1329
|
-
*/
|
|
1330
|
-
export async function findActiveShippingCarriers() {
|
|
1331
|
-
const db = getDb();
|
|
1332
|
-
const carriers = await db
|
|
1333
|
-
.select()
|
|
1334
|
-
.from(schema.shippingCarriers)
|
|
1335
|
-
.where(eq(schema.shippingCarriers.isActive, true))
|
|
1336
|
-
.orderBy(asc(schema.shippingCarriers.sortOrder), asc(schema.shippingCarriers.name));
|
|
1337
|
-
return carriers.map((carrier) => ({
|
|
1338
|
-
id: carrier.id,
|
|
1339
|
-
name: carrier.name,
|
|
1340
|
-
code: carrier.code,
|
|
1341
|
-
description: carrier.description,
|
|
1342
|
-
isActive: carrier.isActive,
|
|
1343
|
-
sortOrder: carrier.sortOrder,
|
|
1344
|
-
createdAt: carrier.createdAt,
|
|
1345
|
-
updatedAt: carrier.updatedAt,
|
|
1346
|
-
}));
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Find a shipping carrier by ID
|
|
1350
|
-
*/
|
|
1351
|
-
export async function findShippingCarrierById(id) {
|
|
1352
|
-
const db = getDb();
|
|
1353
|
-
const [carrier] = await db
|
|
1354
|
-
.select()
|
|
1355
|
-
.from(schema.shippingCarriers)
|
|
1356
|
-
.where(eq(schema.shippingCarriers.id, id))
|
|
1357
|
-
.limit(1);
|
|
1358
|
-
if (!carrier)
|
|
1359
|
-
return null;
|
|
1360
|
-
return {
|
|
1361
|
-
id: carrier.id,
|
|
1362
|
-
name: carrier.name,
|
|
1363
|
-
code: carrier.code,
|
|
1364
|
-
description: carrier.description,
|
|
1365
|
-
isActive: carrier.isActive,
|
|
1366
|
-
sortOrder: carrier.sortOrder,
|
|
1367
|
-
createdAt: carrier.createdAt,
|
|
1368
|
-
updatedAt: carrier.updatedAt,
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1371
|
-
/**
|
|
1372
|
-
* Create a new shipping carrier
|
|
1373
|
-
*/
|
|
1374
|
-
export async function createShippingCarrier(data) {
|
|
1375
|
-
const db = getDb();
|
|
1376
|
-
const [carrier] = await db
|
|
1377
|
-
.insert(schema.shippingCarriers)
|
|
1378
|
-
.values({
|
|
1379
|
-
name: data.name,
|
|
1380
|
-
code: data.code,
|
|
1381
|
-
description: data.description || null,
|
|
1382
|
-
isActive: data.isActive ?? true,
|
|
1383
|
-
sortOrder: data.sortOrder ?? 0,
|
|
1384
|
-
})
|
|
1385
|
-
.returning();
|
|
1386
|
-
return carrier;
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Update a shipping carrier
|
|
1390
|
-
*/
|
|
1391
|
-
export async function updateShippingCarrier(id, data) {
|
|
1392
|
-
const db = getDb();
|
|
1393
|
-
const [carrier] = await db
|
|
1394
|
-
.update(schema.shippingCarriers)
|
|
1395
|
-
.set({ ...data, updatedAt: new Date() })
|
|
1396
|
-
.where(eq(schema.shippingCarriers.id, id))
|
|
1397
|
-
.returning();
|
|
1398
|
-
return carrier || null;
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Delete a shipping carrier (cascades to zones and rates)
|
|
1402
|
-
*/
|
|
1403
|
-
export async function deleteShippingCarrier(id) {
|
|
1404
|
-
const db = getDb();
|
|
1405
|
-
const result = await db
|
|
1406
|
-
.delete(schema.shippingCarriers)
|
|
1407
|
-
.where(eq(schema.shippingCarriers.id, id))
|
|
1408
|
-
.returning({ id: schema.shippingCarriers.id });
|
|
1409
|
-
return result.length > 0;
|
|
1410
|
-
}
|
|
1411
|
-
/**
|
|
1412
|
-
* Find all carriers with their zones and rates (full hierarchy for admin UI)
|
|
1413
|
-
*/
|
|
1414
|
-
export async function findCarriersWithZonesAndRates() {
|
|
1415
|
-
const carriers = await findShippingCarriers();
|
|
1416
|
-
const zones = await findShippingZones();
|
|
1417
|
-
// Group zones by carrier
|
|
1418
|
-
const zonesByCarrier = new Map();
|
|
1419
|
-
for (const zone of zones) {
|
|
1420
|
-
if (!zone.carrierId)
|
|
1421
|
-
continue;
|
|
1422
|
-
const existing = zonesByCarrier.get(zone.carrierId) || [];
|
|
1423
|
-
existing.push(zone);
|
|
1424
|
-
zonesByCarrier.set(zone.carrierId, existing);
|
|
1425
|
-
}
|
|
1426
|
-
return carriers.map((carrier) => ({
|
|
1427
|
-
...carrier,
|
|
1428
|
-
zones: zonesByCarrier.get(carrier.id) || [],
|
|
1429
|
-
}));
|
|
1430
|
-
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Find all shipping zones with their rates and carrier info
|
|
1433
|
-
*/
|
|
1434
|
-
export async function findShippingZones() {
|
|
1435
|
-
const db = getDb();
|
|
1436
|
-
// Get all zones with carrier info via join
|
|
1437
|
-
const zonesWithCarriers = await db
|
|
1438
|
-
.select({
|
|
1439
|
-
zone: schema.shippingZones,
|
|
1440
|
-
carrier: {
|
|
1441
|
-
id: schema.shippingCarriers.id,
|
|
1442
|
-
name: schema.shippingCarriers.name,
|
|
1443
|
-
code: schema.shippingCarriers.code,
|
|
1444
|
-
},
|
|
1445
|
-
})
|
|
1446
|
-
.from(schema.shippingZones)
|
|
1447
|
-
.leftJoin(schema.shippingCarriers, eq(schema.shippingZones.carrierId, schema.shippingCarriers.id))
|
|
1448
|
-
.orderBy(asc(schema.shippingZones.sortOrder), asc(schema.shippingZones.name));
|
|
1449
|
-
// Get all rates
|
|
1450
|
-
const rates = await db
|
|
1451
|
-
.select()
|
|
1452
|
-
.from(schema.shippingRates)
|
|
1453
|
-
.orderBy(asc(schema.shippingRates.sortOrder), asc(schema.shippingRates.name));
|
|
1454
|
-
// Group rates by zone
|
|
1455
|
-
const ratesByZone = new Map();
|
|
1456
|
-
for (const rate of rates) {
|
|
1457
|
-
const existing = ratesByZone.get(rate.zoneId) || [];
|
|
1458
|
-
existing.push(rate);
|
|
1459
|
-
ratesByZone.set(rate.zoneId, existing);
|
|
1460
|
-
}
|
|
1461
|
-
// Combine zones with their rates and carrier info
|
|
1462
|
-
return zonesWithCarriers.map(({ zone, carrier }) => ({
|
|
1463
|
-
id: zone.id,
|
|
1464
|
-
carrierId: zone.carrierId,
|
|
1465
|
-
carrier: carrier ? {
|
|
1466
|
-
id: carrier.id,
|
|
1467
|
-
name: carrier.name,
|
|
1468
|
-
code: carrier.code,
|
|
1469
|
-
} : undefined,
|
|
1470
|
-
name: zone.name,
|
|
1471
|
-
countries: zone.countries || [],
|
|
1472
|
-
isActive: zone.isActive,
|
|
1473
|
-
sortOrder: zone.sortOrder,
|
|
1474
|
-
createdAt: zone.createdAt,
|
|
1475
|
-
updatedAt: zone.updatedAt,
|
|
1476
|
-
rates: (ratesByZone.get(zone.id) || []).map((rate) => ({
|
|
1477
|
-
id: rate.id,
|
|
1478
|
-
name: rate.name,
|
|
1479
|
-
description: rate.description,
|
|
1480
|
-
price: Number(rate.price),
|
|
1481
|
-
minOrderAmount: rate.minOrderAmount ? Number(rate.minOrderAmount) : null,
|
|
1482
|
-
maxOrderAmount: rate.maxOrderAmount ? Number(rate.maxOrderAmount) : null,
|
|
1483
|
-
estimatedDays: rate.estimatedDays,
|
|
1484
|
-
isActive: rate.isActive,
|
|
1485
|
-
sortOrder: rate.sortOrder,
|
|
1486
|
-
})),
|
|
1487
|
-
}));
|
|
1488
|
-
}
|
|
1489
|
-
/**
|
|
1490
|
-
* Find active shipping zones only
|
|
1491
|
-
*/
|
|
1492
|
-
export async function findActiveShippingZones() {
|
|
1493
|
-
const allZones = await findShippingZones();
|
|
1494
|
-
return allZones.filter((zone) => zone.isActive);
|
|
1495
|
-
}
|
|
1496
|
-
/**
|
|
1497
|
-
* Find a shipping zone by ID
|
|
1498
|
-
*/
|
|
1499
|
-
export async function findShippingZoneById(id) {
|
|
1500
|
-
const zones = await findShippingZones();
|
|
1501
|
-
return zones.find((z) => z.id === id) || null;
|
|
1502
|
-
}
|
|
1503
|
-
/**
|
|
1504
|
-
* Create a new shipping zone under a carrier
|
|
1505
|
-
*/
|
|
1506
|
-
export async function createShippingZone(data) {
|
|
1507
|
-
const db = getDb();
|
|
1508
|
-
const [zone] = await db
|
|
1509
|
-
.insert(schema.shippingZones)
|
|
1510
|
-
.values({
|
|
1511
|
-
carrierId: data.carrierId,
|
|
1512
|
-
name: data.name,
|
|
1513
|
-
countries: data.countries,
|
|
1514
|
-
isActive: data.isActive ?? true,
|
|
1515
|
-
sortOrder: data.sortOrder ?? 0,
|
|
1516
|
-
})
|
|
1517
|
-
.returning();
|
|
1518
|
-
return zone;
|
|
1519
|
-
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Update a shipping zone
|
|
1522
|
-
*/
|
|
1523
|
-
export async function updateShippingZone(id, data) {
|
|
1524
|
-
const db = getDb();
|
|
1525
|
-
const [zone] = await db
|
|
1526
|
-
.update(schema.shippingZones)
|
|
1527
|
-
.set({ ...data, updatedAt: new Date() })
|
|
1528
|
-
.where(eq(schema.shippingZones.id, id))
|
|
1529
|
-
.returning();
|
|
1530
|
-
return zone || null;
|
|
1531
|
-
}
|
|
1532
|
-
/**
|
|
1533
|
-
* Delete a shipping zone (cascades to rates)
|
|
1534
|
-
*/
|
|
1535
|
-
export async function deleteShippingZone(id) {
|
|
1536
|
-
const db = getDb();
|
|
1537
|
-
const result = await db
|
|
1538
|
-
.delete(schema.shippingZones)
|
|
1539
|
-
.where(eq(schema.shippingZones.id, id))
|
|
1540
|
-
.returning({ id: schema.shippingZones.id });
|
|
1541
|
-
return result.length > 0;
|
|
1542
|
-
}
|
|
1543
|
-
/**
|
|
1544
|
-
* Create a shipping rate for a zone
|
|
1545
|
-
*/
|
|
1546
|
-
export async function createShippingRate(data) {
|
|
1547
|
-
const db = getDb();
|
|
1548
|
-
const [rate] = await db
|
|
1549
|
-
.insert(schema.shippingRates)
|
|
1550
|
-
.values({
|
|
1551
|
-
zoneId: data.zoneId,
|
|
1552
|
-
name: data.name,
|
|
1553
|
-
description: data.description || null,
|
|
1554
|
-
price: String(data.price),
|
|
1555
|
-
minOrderAmount: data.minOrderAmount !== undefined ? String(data.minOrderAmount) : null,
|
|
1556
|
-
maxOrderAmount: data.maxOrderAmount !== undefined ? String(data.maxOrderAmount) : null,
|
|
1557
|
-
estimatedDays: data.estimatedDays || null,
|
|
1558
|
-
isActive: data.isActive ?? true,
|
|
1559
|
-
sortOrder: data.sortOrder ?? 0,
|
|
1560
|
-
})
|
|
1561
|
-
.returning();
|
|
1562
|
-
return rate;
|
|
1563
|
-
}
|
|
1564
|
-
/**
|
|
1565
|
-
* Update a shipping rate
|
|
1566
|
-
*/
|
|
1567
|
-
export async function updateShippingRate(id, data) {
|
|
1568
|
-
const db = getDb();
|
|
1569
|
-
const updateData = {};
|
|
1570
|
-
if (data.name !== undefined)
|
|
1571
|
-
updateData.name = data.name;
|
|
1572
|
-
if (data.description !== undefined)
|
|
1573
|
-
updateData.description = data.description;
|
|
1574
|
-
if (data.price !== undefined)
|
|
1575
|
-
updateData.price = String(data.price);
|
|
1576
|
-
if (data.minOrderAmount !== undefined)
|
|
1577
|
-
updateData.minOrderAmount = data.minOrderAmount !== null ? String(data.minOrderAmount) : null;
|
|
1578
|
-
if (data.maxOrderAmount !== undefined)
|
|
1579
|
-
updateData.maxOrderAmount = data.maxOrderAmount !== null ? String(data.maxOrderAmount) : null;
|
|
1580
|
-
if (data.estimatedDays !== undefined)
|
|
1581
|
-
updateData.estimatedDays = data.estimatedDays;
|
|
1582
|
-
if (data.isActive !== undefined)
|
|
1583
|
-
updateData.isActive = data.isActive;
|
|
1584
|
-
if (data.sortOrder !== undefined)
|
|
1585
|
-
updateData.sortOrder = data.sortOrder;
|
|
1586
|
-
const [rate] = await db
|
|
1587
|
-
.update(schema.shippingRates)
|
|
1588
|
-
.set(updateData)
|
|
1589
|
-
.where(eq(schema.shippingRates.id, id))
|
|
1590
|
-
.returning();
|
|
1591
|
-
return rate || null;
|
|
1592
|
-
}
|
|
1593
|
-
/**
|
|
1594
|
-
* Delete a shipping rate
|
|
1595
|
-
*/
|
|
1596
|
-
export async function deleteShippingRate(id) {
|
|
1597
|
-
const db = getDb();
|
|
1598
|
-
const result = await db
|
|
1599
|
-
.delete(schema.shippingRates)
|
|
1600
|
-
.where(eq(schema.shippingRates.id, id))
|
|
1601
|
-
.returning({ id: schema.shippingRates.id });
|
|
1602
|
-
return result.length > 0;
|
|
1603
|
-
}
|
|
1604
|
-
/**
|
|
1605
|
-
* Find shipping rates applicable to a specific country and order amount.
|
|
1606
|
-
* Used during checkout to determine which shipping options to show.
|
|
1607
|
-
*
|
|
1608
|
-
* @param country - ISO country code (e.g., 'US', 'CA')
|
|
1609
|
-
* @param orderAmount - Order subtotal in dollars
|
|
1610
|
-
* @returns Array of applicable shipping rates with carrier info
|
|
1611
|
-
*/
|
|
1612
|
-
export async function findShippingRatesForCountry(country, orderAmount) {
|
|
1613
|
-
const zones = await findActiveShippingZones();
|
|
1614
|
-
const applicableRates = [];
|
|
1615
|
-
for (const zone of zones) {
|
|
1616
|
-
// Skip zones without a carrier (shouldn't happen, but defensive)
|
|
1617
|
-
if (!zone.carrier)
|
|
1618
|
-
continue;
|
|
1619
|
-
// Check if zone covers this country
|
|
1620
|
-
// '*' means all countries (international catch-all)
|
|
1621
|
-
const coversCountry = zone.countries.includes('*') || zone.countries.includes(country.toUpperCase());
|
|
1622
|
-
if (!coversCountry)
|
|
1623
|
-
continue;
|
|
1624
|
-
// Get active rates that match the order amount
|
|
1625
|
-
for (const rate of zone.rates) {
|
|
1626
|
-
if (!rate.isActive)
|
|
1627
|
-
continue;
|
|
1628
|
-
// Check min/max order amount constraints
|
|
1629
|
-
if (rate.minOrderAmount !== null && orderAmount < rate.minOrderAmount)
|
|
1630
|
-
continue;
|
|
1631
|
-
if (rate.maxOrderAmount !== null && orderAmount > rate.maxOrderAmount)
|
|
1632
|
-
continue;
|
|
1633
|
-
applicableRates.push({
|
|
1634
|
-
id: rate.id,
|
|
1635
|
-
carrierId: zone.carrierId,
|
|
1636
|
-
carrierName: zone.carrier.name,
|
|
1637
|
-
carrierCode: zone.carrier.code,
|
|
1638
|
-
zoneName: zone.name,
|
|
1639
|
-
name: rate.name,
|
|
1640
|
-
description: rate.description,
|
|
1641
|
-
price: rate.price,
|
|
1642
|
-
estimatedDays: rate.estimatedDays,
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
return applicableRates;
|
|
1647
|
-
}
|
|
1648
|
-
/**
|
|
1649
|
-
* Find shipping options for checkout grouped by carrier.
|
|
1650
|
-
* This is the main function used by the pre-checkout flow.
|
|
1651
|
-
*
|
|
1652
|
-
* @param country - ISO country code (e.g., 'US', 'CA')
|
|
1653
|
-
* @param orderAmount - Order subtotal in dollars
|
|
1654
|
-
* @returns Shipping options grouped by carrier
|
|
1655
|
-
*/
|
|
1656
|
-
export async function findShippingOptionsForCountry(country, orderAmount) {
|
|
1657
|
-
const rates = await findShippingRatesForCountry(country, orderAmount);
|
|
1658
|
-
// Group by carrier
|
|
1659
|
-
const carrierMap = new Map();
|
|
1660
|
-
for (const rate of rates) {
|
|
1661
|
-
let group = carrierMap.get(rate.carrierId);
|
|
1662
|
-
if (!group) {
|
|
1663
|
-
group = {
|
|
1664
|
-
carrier: {
|
|
1665
|
-
id: rate.carrierId,
|
|
1666
|
-
name: rate.carrierName,
|
|
1667
|
-
code: rate.carrierCode,
|
|
1668
|
-
},
|
|
1669
|
-
options: [],
|
|
1670
|
-
};
|
|
1671
|
-
carrierMap.set(rate.carrierId, group);
|
|
1672
|
-
}
|
|
1673
|
-
group.options.push({
|
|
1674
|
-
id: rate.id,
|
|
1675
|
-
carrierId: rate.carrierId,
|
|
1676
|
-
carrierName: rate.carrierName,
|
|
1677
|
-
carrierCode: rate.carrierCode,
|
|
1678
|
-
name: rate.name,
|
|
1679
|
-
description: rate.description ?? undefined,
|
|
1680
|
-
price: rate.price,
|
|
1681
|
-
estimatedDays: rate.estimatedDays ?? undefined,
|
|
1682
|
-
source: 'manual',
|
|
1683
|
-
});
|
|
1684
|
-
}
|
|
1685
|
-
// Convert to array and sort by carrier name
|
|
1686
|
-
return Array.from(carrierMap.values()).sort((a, b) => a.carrier.name.localeCompare(b.carrier.name));
|
|
1687
|
-
}
|
|
1688
|
-
/**
|
|
1689
|
-
* Find all tax zones ordered by priority (sortOrder ascending)
|
|
1690
|
-
*/
|
|
1691
|
-
export async function findTaxZones() {
|
|
1692
|
-
const db = getDb();
|
|
1693
|
-
const zones = await db
|
|
1694
|
-
.select()
|
|
1695
|
-
.from(schema.taxZones)
|
|
1696
|
-
.orderBy(asc(schema.taxZones.sortOrder), asc(schema.taxZones.name));
|
|
1697
|
-
return zones.map((zone) => ({
|
|
1698
|
-
id: zone.id,
|
|
1699
|
-
name: zone.name,
|
|
1700
|
-
countries: zone.countries || [],
|
|
1701
|
-
states: zone.states || [],
|
|
1702
|
-
taxRate: Number(zone.taxRate),
|
|
1703
|
-
isActive: zone.isActive,
|
|
1704
|
-
sortOrder: zone.sortOrder,
|
|
1705
|
-
createdAt: zone.createdAt,
|
|
1706
|
-
updatedAt: zone.updatedAt,
|
|
1707
|
-
}));
|
|
1708
|
-
}
|
|
1709
|
-
/**
|
|
1710
|
-
* Find only active tax zones
|
|
1711
|
-
*/
|
|
1712
|
-
export async function findActiveTaxZones() {
|
|
1713
|
-
const allZones = await findTaxZones();
|
|
1714
|
-
return allZones.filter((zone) => zone.isActive);
|
|
1715
|
-
}
|
|
1716
|
-
/**
|
|
1717
|
-
* Find a tax zone by ID
|
|
1718
|
-
*/
|
|
1719
|
-
export async function findTaxZoneById(id) {
|
|
1720
|
-
const zones = await findTaxZones();
|
|
1721
|
-
return zones.find((z) => z.id === id) || null;
|
|
1722
|
-
}
|
|
1723
|
-
/**
|
|
1724
|
-
* Create a new tax zone
|
|
1725
|
-
*/
|
|
1726
|
-
export async function createTaxZone(data) {
|
|
1727
|
-
const db = getDb();
|
|
1728
|
-
const [zone] = await db
|
|
1729
|
-
.insert(schema.taxZones)
|
|
1730
|
-
.values({
|
|
1731
|
-
name: data.name,
|
|
1732
|
-
countries: data.countries.map((c) => c.toUpperCase()),
|
|
1733
|
-
states: (data.states || []).map((s) => s.toUpperCase()),
|
|
1734
|
-
taxRate: String(data.taxRate),
|
|
1735
|
-
isActive: data.isActive ?? true,
|
|
1736
|
-
sortOrder: data.sortOrder ?? 0,
|
|
1737
|
-
})
|
|
1738
|
-
.returning();
|
|
1739
|
-
return zone;
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* Update a tax zone
|
|
1743
|
-
*/
|
|
1744
|
-
export async function updateTaxZone(id, data) {
|
|
1745
|
-
const db = getDb();
|
|
1746
|
-
const updateData = { updatedAt: new Date() };
|
|
1747
|
-
if (data.name !== undefined)
|
|
1748
|
-
updateData.name = data.name;
|
|
1749
|
-
if (data.countries !== undefined)
|
|
1750
|
-
updateData.countries = data.countries.map((c) => c.toUpperCase());
|
|
1751
|
-
if (data.states !== undefined)
|
|
1752
|
-
updateData.states = data.states.map((s) => s.toUpperCase());
|
|
1753
|
-
if (data.taxRate !== undefined)
|
|
1754
|
-
updateData.taxRate = String(data.taxRate);
|
|
1755
|
-
if (data.isActive !== undefined)
|
|
1756
|
-
updateData.isActive = data.isActive;
|
|
1757
|
-
if (data.sortOrder !== undefined)
|
|
1758
|
-
updateData.sortOrder = data.sortOrder;
|
|
1759
|
-
const [zone] = await db
|
|
1760
|
-
.update(schema.taxZones)
|
|
1761
|
-
.set(updateData)
|
|
1762
|
-
.where(eq(schema.taxZones.id, id))
|
|
1763
|
-
.returning();
|
|
1764
|
-
return zone || null;
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* Delete a tax zone
|
|
1768
|
-
*/
|
|
1769
|
-
export async function deleteTaxZone(id) {
|
|
1770
|
-
const db = getDb();
|
|
1771
|
-
const result = await db
|
|
1772
|
-
.delete(schema.taxZones)
|
|
1773
|
-
.where(eq(schema.taxZones.id, id))
|
|
1774
|
-
.returning({ id: schema.taxZones.id });
|
|
1775
|
-
return result.length > 0;
|
|
1776
|
-
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Find the applicable tax rate for a given address.
|
|
1779
|
-
* Returns the first matching zone based on priority (sortOrder).
|
|
1780
|
-
*
|
|
1781
|
-
* Matching logic:
|
|
1782
|
-
* 1. For US addresses: Match country='US' AND state matches (if zone has states)
|
|
1783
|
-
* 2. For non-US addresses: Match country only
|
|
1784
|
-
* 3. Wildcard '*' in countries matches any country
|
|
1785
|
-
*
|
|
1786
|
-
* @param country - ISO-2 country code (e.g., 'US', 'FR')
|
|
1787
|
-
* @param state - State/province code for US (e.g., 'CA', 'NY')
|
|
1788
|
-
* @returns Tax rate info or null if no matching zone
|
|
1789
|
-
*/
|
|
1790
|
-
export async function findTaxRateForAddress(country, state) {
|
|
1791
|
-
const zones = await findActiveTaxZones();
|
|
1792
|
-
const upperCountry = country.toUpperCase();
|
|
1793
|
-
const upperState = state?.toUpperCase() || '';
|
|
1794
|
-
for (const zone of zones) {
|
|
1795
|
-
// Check if zone covers this country
|
|
1796
|
-
const hasWildcard = zone.countries.includes('*');
|
|
1797
|
-
const hasCountry = zone.countries.includes(upperCountry);
|
|
1798
|
-
if (!hasWildcard && !hasCountry)
|
|
1799
|
-
continue;
|
|
1800
|
-
// For US addresses: If zone has specific states, require state match
|
|
1801
|
-
if (upperCountry === 'US' && zone.states.length > 0) {
|
|
1802
|
-
if (!upperState || !zone.states.includes(upperState)) {
|
|
1803
|
-
continue; // Zone requires specific US states that don't match
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
// Found a matching zone
|
|
1807
|
-
return {
|
|
1808
|
-
zoneId: zone.id,
|
|
1809
|
-
zoneName: zone.name,
|
|
1810
|
-
taxRate: zone.taxRate,
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
return null; // No matching tax zone
|
|
1814
|
-
}
|
|
1815
|
-
/**
|
|
1816
|
-
* Runtime self-heal for the `onboarding` column. Stores generated before this
|
|
1817
|
-
* column shipped don't have it; this idempotent migration adds it on first
|
|
1818
|
-
* call and caches success in-process so subsequent calls are no-ops.
|
|
1819
|
-
*
|
|
1820
|
-
* Same pattern as the analytics JSONB self-heal documented in CLAUDE.md §27.
|
|
1821
|
-
* No fleet rollout needed — the column appears on each store's first hit
|
|
1822
|
-
* to the setup-guide endpoint or the writeback hooks.
|
|
1823
|
-
*/
|
|
1824
|
-
let onboardingColumnEnsured = false;
|
|
1825
|
-
export async function ensureOnboardingColumn() {
|
|
1826
|
-
if (onboardingColumnEnsured)
|
|
1827
|
-
return;
|
|
1828
|
-
const db = getDb();
|
|
1829
|
-
try {
|
|
1830
|
-
await db.execute(sql `
|
|
1831
|
-
ALTER TABLE store_settings
|
|
1832
|
-
ADD COLUMN IF NOT EXISTS onboarding jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
1833
|
-
`);
|
|
1834
|
-
onboardingColumnEnsured = true;
|
|
1835
|
-
}
|
|
1836
|
-
catch (err) {
|
|
1837
|
-
// Don't poison the cache on transient failures — try again next call.
|
|
1838
|
-
console.error('[ensureOnboardingColumn] migration failed:', err);
|
|
1839
|
-
throw err;
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
/**
|
|
1843
|
-
* Read the onboarding JSONB. Returns `{}` when the row is missing or the
|
|
1844
|
-
* column hasn't been initialized yet (legacy stores). Always safe to call.
|
|
1845
|
-
*/
|
|
1846
|
-
export async function getOnboardingState() {
|
|
1847
|
-
await ensureOnboardingColumn();
|
|
1848
|
-
const db = getDb();
|
|
1849
|
-
const rows = await db
|
|
1850
|
-
.select({ onboarding: schema.storeSettings.onboarding })
|
|
1851
|
-
.from(schema.storeSettings)
|
|
1852
|
-
.limit(1);
|
|
1853
|
-
return (rows[0]?.onboarding ?? {});
|
|
1854
|
-
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Set or clear a single key on the onboarding JSONB. Uses `jsonb_set` so it
|
|
1857
|
-
* never clobbers other keys written by parallel requests. Safe to call
|
|
1858
|
-
* fire-and-forget from mutating API routes (products POST/PUT, settings PUT).
|
|
1859
|
-
*/
|
|
1860
|
-
export async function setOnboardingFlag(key, value) {
|
|
1861
|
-
await ensureOnboardingColumn();
|
|
1862
|
-
const db = getDb();
|
|
1863
|
-
// jsonb_set with create_missing=true so the column starts at '{}' and
|
|
1864
|
-
// gradually accretes keys. We JSON-stringify the value first so that
|
|
1865
|
-
// booleans, numbers, strings, and null all round-trip with their proper
|
|
1866
|
-
// JSONB types. Earlier versions used `to_jsonb(value::text)::jsonb`
|
|
1867
|
-
// which incorrectly produced JSONB string `"true"` for boolean true —
|
|
1868
|
-
// breaking strict-equality reads in the GET handler so the setup-guide
|
|
1869
|
-
// tasks never marked complete.
|
|
1870
|
-
const jsonValue = JSON.stringify(value);
|
|
1871
|
-
await db.execute(sql `
|
|
1872
|
-
UPDATE store_settings
|
|
1873
|
-
SET onboarding = jsonb_set(
|
|
1874
|
-
COALESCE(onboarding, '{}'::jsonb),
|
|
1875
|
-
${sql.raw(`'{${String(key)}}'`)},
|
|
1876
|
-
${jsonValue}::jsonb,
|
|
1877
|
-
true
|
|
1878
|
-
)
|
|
1879
|
-
`);
|
|
1880
|
-
}
|
|
1881
|
-
/**
|
|
1882
|
-
* Aggregated signal counts used by the setup-guide GET handler. One round-trip
|
|
1883
|
-
* via Promise.all instead of four sequential queries.
|
|
1884
|
-
*/
|
|
1885
|
-
export async function getSetupGuideSignals() {
|
|
1886
|
-
await ensureOnboardingColumn();
|
|
1887
|
-
const db = getDb();
|
|
1888
|
-
const [products, shipping, tax, admins, settings] = await Promise.all([
|
|
1889
|
-
db
|
|
1890
|
-
.select({ count: sql `count(*)` })
|
|
1891
|
-
.from(schema.products),
|
|
1892
|
-
db
|
|
1893
|
-
.select({ count: sql `count(*)` })
|
|
1894
|
-
.from(schema.shippingZones)
|
|
1895
|
-
.where(eq(schema.shippingZones.isActive, true)),
|
|
1896
|
-
db
|
|
1897
|
-
.select({ count: sql `count(*)` })
|
|
1898
|
-
.from(schema.taxZones)
|
|
1899
|
-
.where(eq(schema.taxZones.isActive, true)),
|
|
1900
|
-
db
|
|
1901
|
-
.select({ count: sql `count(*)` })
|
|
1902
|
-
.from(schema.storeAdmins),
|
|
1903
|
-
db
|
|
1904
|
-
.select({
|
|
1905
|
-
logoUrl: schema.storeSettings.logoUrl,
|
|
1906
|
-
// `analytics` is self-healed by the pixel cards (see CLAUDE.md §27);
|
|
1907
|
-
// we read it via raw SQL so the SDK works on stores where the
|
|
1908
|
-
// column doesn't exist yet — the COALESCE keeps that path safe.
|
|
1909
|
-
analytics: sql `COALESCE(
|
|
1910
|
-
(SELECT analytics FROM store_settings LIMIT 1),
|
|
1911
|
-
'{}'::jsonb
|
|
1912
|
-
)`,
|
|
1913
|
-
onboarding: schema.storeSettings.onboarding,
|
|
1914
|
-
})
|
|
1915
|
-
.from(schema.storeSettings)
|
|
1916
|
-
.limit(1),
|
|
1917
|
-
]);
|
|
1918
|
-
return {
|
|
1919
|
-
productCount: Number(products[0]?.count || 0),
|
|
1920
|
-
shippingZonesCount: Number(shipping[0]?.count || 0),
|
|
1921
|
-
taxZonesCount: Number(tax[0]?.count || 0),
|
|
1922
|
-
adminCount: Number(admins[0]?.count || 0),
|
|
1923
|
-
storeSettings: settings[0]
|
|
1924
|
-
? {
|
|
1925
|
-
logoUrl: settings[0].logoUrl ?? null,
|
|
1926
|
-
analytics: settings[0].analytics ?? {},
|
|
1927
|
-
onboarding: settings[0].onboarding ?? {},
|
|
1928
|
-
}
|
|
1929
|
-
: null,
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
//# sourceMappingURL=queries.js.map
|