@fayz-ai/shop 0.7.0 → 0.8.2
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/README.md +2 -0
- package/dist/index.cjs +185 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -28
- package/dist/index.js.map +1 -1
- package/dist/mock-provider.d.ts.map +1 -1
- package/dist/mock.cjs +5 -0
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.js +5 -0
- package/dist/mock.js.map +1 -1
- package/dist/provider.d.ts +42 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/runtime.cjs +5 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -1
- package/dist/supabase-provider.d.ts +20 -1
- package/dist/supabase-provider.d.ts.map +1 -1
- package/dist/tables.d.ts +11 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -4
- package/src/index.ts +4 -0
- package/src/mock-provider.ts +5 -0
- package/src/provider.ts +45 -0
- package/src/supabase-provider.ts +226 -34
- package/src/tables.ts +16 -0
- package/src/types.ts +110 -0
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@fayz-ai/shop)
|
|
6
6
|
[](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
|
|
7
7
|
|
|
8
|
+
**Status:** beta — published to npm and used across Fayz dogfood apps. Pre-1.0: minor APIs may change before 1.0.
|
|
9
|
+
|
|
8
10
|
`@fayz-ai/shop` is the headless ecommerce engine: a `ShopProvider` contract plus the domain types for products, carts, orders, customers, discounts, and shipping. It owns *what commerce means* so the storefront (`@fayz-ai/storefront`) and admin surfaces only have to render it.
|
|
9
11
|
|
|
10
12
|
It's provider-first by design. Ship a demo on `createMockShopProvider`, then drop in `createSupabaseShopProvider` (or your own `ShopProvider`) for real data — the UI doesn't change. Tenancy is resolved through a pluggable resolver, so the same engine serves one store or a marketplace of thousands.
|
package/dist/index.cjs
CHANGED
|
@@ -390,6 +390,11 @@ var MockShopProvider = class {
|
|
|
390
390
|
customerEmail: input.customerEmail ?? null,
|
|
391
391
|
discountCode: input.discountCode ?? null,
|
|
392
392
|
notes: input.notes ?? null,
|
|
393
|
+
shippingAddress: null,
|
|
394
|
+
// CreateOrderInput has no payment method — that is a PlaceOrderInput
|
|
395
|
+
// concern; the mock records none rather than inventing one.
|
|
396
|
+
paymentMethodKind: null,
|
|
397
|
+
paidAt: null,
|
|
393
398
|
items: input.items.map((i) => ({ id: uid(), orderId, productId: i.productId ?? null, name: i.name, sku: i.sku ?? null, quantity: i.quantity, unitPrice: i.unitPrice, total: Math.round(i.quantity * i.unitPrice * 100) / 100, imageUrl: i.imageUrl ?? null })),
|
|
394
399
|
createdAt: now(),
|
|
395
400
|
updatedAt: now()
|
|
@@ -651,6 +656,21 @@ function getShopTenantId() {
|
|
|
651
656
|
}
|
|
652
657
|
}
|
|
653
658
|
|
|
659
|
+
// src/tables.ts
|
|
660
|
+
var T = {
|
|
661
|
+
products: "plg_shop_products",
|
|
662
|
+
productImages: "plg_shop_product_images",
|
|
663
|
+
categories: "plg_shop_categories",
|
|
664
|
+
orders: "plg_shop_orders",
|
|
665
|
+
orderItems: "plg_shop_order_items",
|
|
666
|
+
customers: "plg_shop_customers",
|
|
667
|
+
discounts: "plg_shop_discounts",
|
|
668
|
+
// Core, not plg_shop_*: a delivery zone is a fact about the business, and
|
|
669
|
+
// fulfilment and financial read it too. Same reasoning as addresses and
|
|
670
|
+
// orders — see migration 0021.
|
|
671
|
+
shippingZones: "shipping_zones"
|
|
672
|
+
};
|
|
673
|
+
|
|
654
674
|
// src/supabase-provider.ts
|
|
655
675
|
function getDb() {
|
|
656
676
|
const supabase = core.getSupabaseClientOptional();
|
|
@@ -665,6 +685,40 @@ function asUuid(value) {
|
|
|
665
685
|
if (!value) return null;
|
|
666
686
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value) ? value : null;
|
|
667
687
|
}
|
|
688
|
+
function toPostalDigits(value) {
|
|
689
|
+
const digits = String(value ?? "").replace(/\D/g, "");
|
|
690
|
+
return digits ? digits.padStart(8, "0").slice(0, 8) : void 0;
|
|
691
|
+
}
|
|
692
|
+
function rowToShippingZone(row) {
|
|
693
|
+
return {
|
|
694
|
+
id: row.id,
|
|
695
|
+
tenantId: row.tenant_id,
|
|
696
|
+
name: row.name,
|
|
697
|
+
carrier: row.carrier ?? null,
|
|
698
|
+
postalFrom: String(row.postal_from ?? "").trim(),
|
|
699
|
+
postalTo: String(row.postal_to ?? "").trim(),
|
|
700
|
+
rate: Number(row.rate ?? 0),
|
|
701
|
+
freeAbove: row.free_above == null ? null : Number(row.free_above),
|
|
702
|
+
etaMinDays: row.eta_min_days ?? null,
|
|
703
|
+
etaMaxDays: row.eta_max_days ?? null,
|
|
704
|
+
active: row.active ?? true,
|
|
705
|
+
sortOrder: row.sort_order ?? 0
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function shippingZonePayload(input) {
|
|
709
|
+
const payload = {};
|
|
710
|
+
if (input.name !== void 0) payload.name = input.name;
|
|
711
|
+
if (input.carrier !== void 0) payload.carrier = input.carrier || null;
|
|
712
|
+
if (input.postalFrom !== void 0) payload.postal_from = toPostalDigits(input.postalFrom);
|
|
713
|
+
if (input.postalTo !== void 0) payload.postal_to = toPostalDigits(input.postalTo);
|
|
714
|
+
if (input.rate !== void 0) payload.rate = input.rate;
|
|
715
|
+
if (input.freeAbove !== void 0) payload.free_above = input.freeAbove ?? null;
|
|
716
|
+
if (input.etaMinDays !== void 0) payload.eta_min_days = input.etaMinDays ?? null;
|
|
717
|
+
if (input.etaMaxDays !== void 0) payload.eta_max_days = input.etaMaxDays ?? null;
|
|
718
|
+
if (input.active !== void 0) payload.active = input.active;
|
|
719
|
+
if (input.sortOrder !== void 0) payload.sort_order = input.sortOrder;
|
|
720
|
+
return payload;
|
|
721
|
+
}
|
|
668
722
|
function rowToProduct(row) {
|
|
669
723
|
return {
|
|
670
724
|
id: row.id,
|
|
@@ -713,6 +767,19 @@ function rowToCategory(row) {
|
|
|
713
767
|
createdAt: row.created_at
|
|
714
768
|
};
|
|
715
769
|
}
|
|
770
|
+
function rowToOrderItem(row) {
|
|
771
|
+
return {
|
|
772
|
+
id: row.id,
|
|
773
|
+
orderId: row.order_id,
|
|
774
|
+
productId: row.product_id ?? null,
|
|
775
|
+
name: row.name,
|
|
776
|
+
sku: row.sku ?? null,
|
|
777
|
+
quantity: row.quantity ?? 0,
|
|
778
|
+
unitPrice: row.unit_price ?? 0,
|
|
779
|
+
total: row.total ?? 0,
|
|
780
|
+
imageUrl: row.image_url ?? null
|
|
781
|
+
};
|
|
782
|
+
}
|
|
716
783
|
function rowToOrder(row) {
|
|
717
784
|
return {
|
|
718
785
|
id: row.id,
|
|
@@ -732,7 +799,10 @@ function rowToOrder(row) {
|
|
|
732
799
|
customerEmail: row.customer_email,
|
|
733
800
|
discountCode: row.discount_code,
|
|
734
801
|
notes: row.notes,
|
|
735
|
-
|
|
802
|
+
shippingAddress: row.shipping_address ?? null,
|
|
803
|
+
paymentMethodKind: row.payment_method_kind ?? null,
|
|
804
|
+
paidAt: row.paid_at ?? null,
|
|
805
|
+
items: Array.isArray(row.items) ? row.items.map(rowToOrderItem) : [],
|
|
736
806
|
createdAt: row.created_at,
|
|
737
807
|
updatedAt: row.updated_at
|
|
738
808
|
};
|
|
@@ -778,7 +848,7 @@ var SupabaseShopProvider = class {
|
|
|
778
848
|
async listProducts(options) {
|
|
779
849
|
const db = getDb();
|
|
780
850
|
const tenantId = getTenantId();
|
|
781
|
-
let q = db.from(
|
|
851
|
+
let q = db.from(T.products).select(`*, images:${T.productImages}(*)`);
|
|
782
852
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
783
853
|
if (options?.status) q = q.eq("status", options.status);
|
|
784
854
|
if (options?.categoryId) q = q.eq("category_id", options.categoryId);
|
|
@@ -793,14 +863,14 @@ var SupabaseShopProvider = class {
|
|
|
793
863
|
}
|
|
794
864
|
async getProduct(id) {
|
|
795
865
|
const db = getDb();
|
|
796
|
-
const { data, error } = await db.from(
|
|
866
|
+
const { data, error } = await db.from(T.products).select(`*, images:${T.productImages}(*)`).eq("id", id).maybeSingle();
|
|
797
867
|
if (error) throw error;
|
|
798
868
|
return data ? rowToProduct(data) : null;
|
|
799
869
|
}
|
|
800
870
|
async createProduct(input) {
|
|
801
871
|
const db = getDb();
|
|
802
872
|
const tenantId = getTenantId();
|
|
803
|
-
const { data, error } = await db.from(
|
|
873
|
+
const { data, error } = await db.from(T.products).insert({
|
|
804
874
|
tenant_id: tenantId,
|
|
805
875
|
name: input.name,
|
|
806
876
|
slug: input.slug ?? slugify(input.name),
|
|
@@ -817,7 +887,7 @@ var SupabaseShopProvider = class {
|
|
|
817
887
|
is_physical: input.isPhysical ?? true,
|
|
818
888
|
weight: input.weight ?? null,
|
|
819
889
|
weight_unit: input.weightUnit ?? "kg"
|
|
820
|
-
}).select(
|
|
890
|
+
}).select(`*, images:${T.productImages}(*)`).single();
|
|
821
891
|
if (error) throw error;
|
|
822
892
|
return rowToProduct(data);
|
|
823
893
|
}
|
|
@@ -839,12 +909,12 @@ var SupabaseShopProvider = class {
|
|
|
839
909
|
if (input.categoryId !== void 0) updates.category_id = input.categoryId;
|
|
840
910
|
if (input.isPhysical !== void 0) updates.is_physical = input.isPhysical;
|
|
841
911
|
if (input.weight !== void 0) updates.weight = input.weight;
|
|
842
|
-
const { data, error } = await db.from(
|
|
912
|
+
const { data, error } = await db.from(T.products).update(updates).eq("id", id).select(`*, images:${T.productImages}(*)`).single();
|
|
843
913
|
if (error) throw error;
|
|
844
914
|
return rowToProduct(data);
|
|
845
915
|
}
|
|
846
916
|
async deleteProduct(id) {
|
|
847
|
-
const { error } = await getDb().from(
|
|
917
|
+
const { error } = await getDb().from(T.products).delete().eq("id", id);
|
|
848
918
|
if (error) throw error;
|
|
849
919
|
}
|
|
850
920
|
async uploadProductImage(productId, file) {
|
|
@@ -854,12 +924,37 @@ var SupabaseShopProvider = class {
|
|
|
854
924
|
const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
|
|
855
925
|
if (uploadErr) throw uploadErr;
|
|
856
926
|
const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
|
|
857
|
-
const { data
|
|
927
|
+
const { data: existing } = await db.from(T.productImages).select("id,sort_order").eq("product_id", productId).order("sort_order", { ascending: false }).limit(1);
|
|
928
|
+
const first = !existing || existing.length === 0;
|
|
929
|
+
const nextOrder = first ? 0 : (existing[0].sort_order ?? 0) + 1;
|
|
930
|
+
const { data, error } = await db.from(T.productImages).insert({
|
|
931
|
+
product_id: productId,
|
|
932
|
+
tenant_id: tenantId,
|
|
933
|
+
url: publicUrl,
|
|
934
|
+
sort_order: nextOrder,
|
|
935
|
+
is_primary: first
|
|
936
|
+
}).select().single();
|
|
937
|
+
if (error) throw error;
|
|
938
|
+
return rowToImage(data);
|
|
939
|
+
}
|
|
940
|
+
async updateProductImage(imageId, input) {
|
|
941
|
+
const db = getDb();
|
|
942
|
+
const updates = {};
|
|
943
|
+
if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
|
|
944
|
+
if (input.altText !== void 0) updates.alt_text = input.altText;
|
|
945
|
+
if (input.isPrimary !== void 0) updates.is_primary = input.isPrimary;
|
|
946
|
+
if (input.isPrimary) {
|
|
947
|
+
const { data: target } = await db.from(T.productImages).select("product_id").eq("id", imageId).single();
|
|
948
|
+
if (target?.product_id) {
|
|
949
|
+
await db.from(T.productImages).update({ is_primary: false }).eq("product_id", target.product_id).neq("id", imageId);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
const { data, error } = await db.from(T.productImages).update(updates).eq("id", imageId).select().single();
|
|
858
953
|
if (error) throw error;
|
|
859
954
|
return rowToImage(data);
|
|
860
955
|
}
|
|
861
956
|
async deleteProductImage(imageId) {
|
|
862
|
-
const { error } = await getDb().from(
|
|
957
|
+
const { error } = await getDb().from(T.productImages).delete().eq("id", imageId);
|
|
863
958
|
if (error) throw error;
|
|
864
959
|
}
|
|
865
960
|
// ---------------------------------------------------------------------------
|
|
@@ -868,7 +963,7 @@ var SupabaseShopProvider = class {
|
|
|
868
963
|
async listCategories() {
|
|
869
964
|
const db = getDb();
|
|
870
965
|
const tenantId = getTenantId();
|
|
871
|
-
let q = db.from(
|
|
966
|
+
let q = db.from(T.categories).select("*").order("sort_order", { ascending: true });
|
|
872
967
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
873
968
|
const { data, error } = await q;
|
|
874
969
|
if (error) throw error;
|
|
@@ -876,7 +971,7 @@ var SupabaseShopProvider = class {
|
|
|
876
971
|
}
|
|
877
972
|
async createCategory(input) {
|
|
878
973
|
const db = getDb();
|
|
879
|
-
const { data, error } = await db.from(
|
|
974
|
+
const { data, error } = await db.from(T.categories).insert({ tenant_id: getTenantId(), name: input.name, slug: input.slug ?? slugify(input.name), description: input.description ?? null, parent_id: input.parentId ?? null, sort_order: input.sortOrder ?? 0 }).select().single();
|
|
880
975
|
if (error) throw error;
|
|
881
976
|
return rowToCategory(data);
|
|
882
977
|
}
|
|
@@ -888,12 +983,12 @@ var SupabaseShopProvider = class {
|
|
|
888
983
|
if (input.description !== void 0) updates.description = input.description;
|
|
889
984
|
if (input.parentId !== void 0) updates.parent_id = input.parentId;
|
|
890
985
|
if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
|
|
891
|
-
const { data, error } = await db.from(
|
|
986
|
+
const { data, error } = await db.from(T.categories).update(updates).eq("id", id).select().single();
|
|
892
987
|
if (error) throw error;
|
|
893
988
|
return rowToCategory(data);
|
|
894
989
|
}
|
|
895
990
|
async deleteCategory(id) {
|
|
896
|
-
const { error } = await getDb().from(
|
|
991
|
+
const { error } = await getDb().from(T.categories).delete().eq("id", id);
|
|
897
992
|
if (error) throw error;
|
|
898
993
|
}
|
|
899
994
|
// ---------------------------------------------------------------------------
|
|
@@ -902,7 +997,7 @@ var SupabaseShopProvider = class {
|
|
|
902
997
|
async listOrders(options) {
|
|
903
998
|
const db = getDb();
|
|
904
999
|
const tenantId = getTenantId();
|
|
905
|
-
let q = db.from(
|
|
1000
|
+
let q = db.from(T.orders).select(`*, items:${T.orderItems}(*)`);
|
|
906
1001
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
907
1002
|
if (options?.status) q = q.eq("status", options.status);
|
|
908
1003
|
if (options?.financialStatus) q = q.eq("financial_status", options.financialStatus);
|
|
@@ -917,7 +1012,7 @@ var SupabaseShopProvider = class {
|
|
|
917
1012
|
return (data ?? []).map(rowToOrder);
|
|
918
1013
|
}
|
|
919
1014
|
async getOrder(id) {
|
|
920
|
-
const { data, error } = await getDb().from(
|
|
1015
|
+
const { data, error } = await getDb().from(T.orders).select(`*, items:${T.orderItems}(*)`).eq("id", id).maybeSingle();
|
|
921
1016
|
if (error) throw error;
|
|
922
1017
|
return data ? rowToOrder(data) : null;
|
|
923
1018
|
}
|
|
@@ -928,10 +1023,10 @@ var SupabaseShopProvider = class {
|
|
|
928
1023
|
const discountTotal = input.discountTotal ?? 0;
|
|
929
1024
|
const shippingTotal = input.shippingTotal ?? 0;
|
|
930
1025
|
const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100;
|
|
931
|
-
const { data: order, error: orderErr } = await db.from(
|
|
1026
|
+
const { data: order, error: orderErr } = await db.from(T.orders).insert({ tenant_id: tenantId, customer_id: input.customerId ?? null, customer_name: input.customerName ?? null, customer_email: input.customerEmail ?? null, currency: input.currency ?? "BRL", notes: input.notes ?? null, subtotal, discount_total: discountTotal, shipping_total: shippingTotal, discount_code: input.discountCode ?? null, total, status: "open", financial_status: "pending", fulfillment_status: "unfulfilled" }).select().single();
|
|
932
1027
|
if (orderErr) throw orderErr;
|
|
933
1028
|
const items = input.items.map((i) => ({ order_id: order.id, product_id: asUuid(i.productId), name: i.name, sku: i.sku ?? null, quantity: i.quantity, unit_price: i.unitPrice, total: i.quantity * i.unitPrice, image_url: i.imageUrl ?? null }));
|
|
934
|
-
await db.from(
|
|
1029
|
+
await db.from(T.orderItems).insert(items);
|
|
935
1030
|
return this.getOrder(order.id);
|
|
936
1031
|
}
|
|
937
1032
|
async updateOrder(id, input) {
|
|
@@ -940,7 +1035,53 @@ var SupabaseShopProvider = class {
|
|
|
940
1035
|
if (input.financialStatus !== void 0) updates.financial_status = input.financialStatus;
|
|
941
1036
|
if (input.fulfillmentStatus !== void 0) updates.fulfillment_status = input.fulfillmentStatus;
|
|
942
1037
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
943
|
-
const { error } = await getDb().from(
|
|
1038
|
+
const { error } = await getDb().from(T.orders).update(updates).eq("id", id);
|
|
1039
|
+
if (error) throw error;
|
|
1040
|
+
return this.getOrder(id);
|
|
1041
|
+
}
|
|
1042
|
+
// -------------------------------------------------------------------------
|
|
1043
|
+
// Delivery zones
|
|
1044
|
+
//
|
|
1045
|
+
// Postal codes are stored as 8 bare digits so the range test in
|
|
1046
|
+
// shop_quote_shipping is a plain BETWEEN. Accepting '22.041-001' from the
|
|
1047
|
+
// form and normalising here means the merchant never has to think about it.
|
|
1048
|
+
// -------------------------------------------------------------------------
|
|
1049
|
+
async listShippingZones() {
|
|
1050
|
+
const tenantId = getTenantId();
|
|
1051
|
+
const { data, error } = await getDb().from(T.shippingZones).select("*").eq("tenant_id", tenantId).order("sort_order", { ascending: true }).order("postal_from", { ascending: true });
|
|
1052
|
+
if (error) throw error;
|
|
1053
|
+
return (data ?? []).map(rowToShippingZone);
|
|
1054
|
+
}
|
|
1055
|
+
async createShippingZone(input) {
|
|
1056
|
+
const tenantId = getTenantId();
|
|
1057
|
+
if (!tenantId) throw new Error("@fayz-ai/shop: createShippingZone requires a tenant id.");
|
|
1058
|
+
const { data, error } = await getDb().from(T.shippingZones).insert({ tenant_id: tenantId, ...shippingZonePayload(input) }).select().single();
|
|
1059
|
+
if (error) throw error;
|
|
1060
|
+
return rowToShippingZone(data);
|
|
1061
|
+
}
|
|
1062
|
+
async updateShippingZone(id, input) {
|
|
1063
|
+
const { data, error } = await getDb().from(T.shippingZones).update(shippingZonePayload(input)).eq("id", id).select().single();
|
|
1064
|
+
if (error) throw error;
|
|
1065
|
+
return rowToShippingZone(data);
|
|
1066
|
+
}
|
|
1067
|
+
async deleteShippingZone(id) {
|
|
1068
|
+
const { error } = await getDb().from(T.shippingZones).delete().eq("id", id);
|
|
1069
|
+
if (error) throw error;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Settle a pending order: the merchant confirming that the money arrived.
|
|
1073
|
+
*
|
|
1074
|
+
* Goes through the RPC rather than an UPDATE so the payment ledger row in
|
|
1075
|
+
* public.transactions closes in the same transaction as the order — set
|
|
1076
|
+
* financial_status by hand and the order says paid while the ledger still
|
|
1077
|
+
* says pending. Since 0019 the RPC also enforces who may call it: a member of
|
|
1078
|
+
* the owning tenant, or a PSP webhook with the service role.
|
|
1079
|
+
*/
|
|
1080
|
+
async confirmPayment(id, reference) {
|
|
1081
|
+
const { error } = await getDb().rpc("shop_confirm_payment", {
|
|
1082
|
+
p_order_id: id,
|
|
1083
|
+
p_reference: reference ?? null
|
|
1084
|
+
});
|
|
944
1085
|
if (error) throw error;
|
|
945
1086
|
return this.getOrder(id);
|
|
946
1087
|
}
|
|
@@ -959,6 +1100,22 @@ var SupabaseShopProvider = class {
|
|
|
959
1100
|
p_currency: input.currency ?? "BRL",
|
|
960
1101
|
p_discount_code: input.discountCode ?? null,
|
|
961
1102
|
p_shipping_total: input.shippingTotal ?? 0,
|
|
1103
|
+
// snake_case keys on purpose: the RPC freezes this jsonb onto the
|
|
1104
|
+
// order and public.addresses uses these exact column names.
|
|
1105
|
+
p_shipping_address: input.shippingAddress ? {
|
|
1106
|
+
postal_code: input.shippingAddress.postalCode,
|
|
1107
|
+
street: input.shippingAddress.street,
|
|
1108
|
+
number: input.shippingAddress.number ?? null,
|
|
1109
|
+
complement: input.shippingAddress.complement ?? null,
|
|
1110
|
+
district: input.shippingAddress.district ?? null,
|
|
1111
|
+
city: input.shippingAddress.city,
|
|
1112
|
+
state: input.shippingAddress.state,
|
|
1113
|
+
country: input.shippingAddress.country ?? "BR",
|
|
1114
|
+
recipient: input.shippingAddress.recipient ?? null,
|
|
1115
|
+
phone: input.shippingAddress.phone ?? null,
|
|
1116
|
+
label: input.shippingAddress.label ?? null
|
|
1117
|
+
} : null,
|
|
1118
|
+
p_payment_method: input.paymentMethod ?? null,
|
|
962
1119
|
p_notes: input.notes ?? null
|
|
963
1120
|
});
|
|
964
1121
|
if (error) throw error;
|
|
@@ -973,7 +1130,7 @@ var SupabaseShopProvider = class {
|
|
|
973
1130
|
async listCustomers(options) {
|
|
974
1131
|
const db = getDb();
|
|
975
1132
|
const tenantId = getTenantId();
|
|
976
|
-
let q = db.from(
|
|
1133
|
+
let q = db.from(T.customers).select("*").order("first_name", { ascending: true });
|
|
977
1134
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
978
1135
|
if (options?.search) q = q.or(`first_name.ilike.%${options.search}%,last_name.ilike.%${options.search}%,email.ilike.%${options.search}%`);
|
|
979
1136
|
if (options?.limit) q = q.limit(options.limit);
|
|
@@ -982,12 +1139,12 @@ var SupabaseShopProvider = class {
|
|
|
982
1139
|
return (data ?? []).map(rowToCustomer);
|
|
983
1140
|
}
|
|
984
1141
|
async getCustomer(id) {
|
|
985
|
-
const { data, error } = await getDb().from(
|
|
1142
|
+
const { data, error } = await getDb().from(T.customers).select("*").eq("id", id).maybeSingle();
|
|
986
1143
|
if (error) throw error;
|
|
987
1144
|
return data ? rowToCustomer(data) : null;
|
|
988
1145
|
}
|
|
989
1146
|
async createCustomer(input) {
|
|
990
|
-
const { data, error } = await getDb().from(
|
|
1147
|
+
const { data, error } = await getDb().from(T.customers).insert({ tenant_id: getTenantId(), first_name: input.firstName, last_name: input.lastName ?? "", email: input.email ?? null, phone: input.phone ?? null, notes: input.notes ?? null }).select().single();
|
|
991
1148
|
if (error) throw error;
|
|
992
1149
|
return rowToCustomer(data);
|
|
993
1150
|
}
|
|
@@ -998,12 +1155,12 @@ var SupabaseShopProvider = class {
|
|
|
998
1155
|
if (input.email !== void 0) updates.email = input.email;
|
|
999
1156
|
if (input.phone !== void 0) updates.phone = input.phone;
|
|
1000
1157
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
1001
|
-
const { data, error } = await getDb().from(
|
|
1158
|
+
const { data, error } = await getDb().from(T.customers).update(updates).eq("id", id).select().single();
|
|
1002
1159
|
if (error) throw error;
|
|
1003
1160
|
return rowToCustomer(data);
|
|
1004
1161
|
}
|
|
1005
1162
|
async deleteCustomer(id) {
|
|
1006
|
-
const { error } = await getDb().from(
|
|
1163
|
+
const { error } = await getDb().from(T.customers).delete().eq("id", id);
|
|
1007
1164
|
if (error) throw error;
|
|
1008
1165
|
}
|
|
1009
1166
|
// Find-or-create + link to auth.uid server-side (SECURITY DEFINER RPC), so the
|
|
@@ -1028,7 +1185,7 @@ var SupabaseShopProvider = class {
|
|
|
1028
1185
|
async listDiscounts(options) {
|
|
1029
1186
|
const db = getDb();
|
|
1030
1187
|
const tenantId = getTenantId();
|
|
1031
|
-
let q = db.from(
|
|
1188
|
+
let q = db.from(T.discounts).select("*").order("created_at", { ascending: false });
|
|
1032
1189
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
1033
1190
|
if (options?.status) q = q.eq("status", options.status);
|
|
1034
1191
|
if (options?.type) q = q.eq("type", options.type);
|
|
@@ -1039,12 +1196,12 @@ var SupabaseShopProvider = class {
|
|
|
1039
1196
|
return (data ?? []).map(rowToDiscount);
|
|
1040
1197
|
}
|
|
1041
1198
|
async getDiscount(id) {
|
|
1042
|
-
const { data, error } = await getDb().from(
|
|
1199
|
+
const { data, error } = await getDb().from(T.discounts).select("*").eq("id", id).maybeSingle();
|
|
1043
1200
|
if (error) throw error;
|
|
1044
1201
|
return data ? rowToDiscount(data) : null;
|
|
1045
1202
|
}
|
|
1046
1203
|
async createDiscount(input) {
|
|
1047
|
-
const { data, error } = await getDb().from(
|
|
1204
|
+
const { data, error } = await getDb().from(T.discounts).insert({ tenant_id: getTenantId(), title: input.title, code: input.code ?? null, type: input.type, method: input.method ?? "code", value: input.value, usage_limit: input.usageLimit ?? null, once_per_customer: input.oncePerCustomer ?? false, starts_at: input.startsAt ?? (/* @__PURE__ */ new Date()).toISOString(), ends_at: input.endsAt ?? null, status: input.status ?? "active", times_used: 0 }).select().single();
|
|
1048
1205
|
if (error) throw error;
|
|
1049
1206
|
return rowToDiscount(data);
|
|
1050
1207
|
}
|
|
@@ -1057,12 +1214,12 @@ var SupabaseShopProvider = class {
|
|
|
1057
1214
|
if (input.usageLimit !== void 0) updates.usage_limit = input.usageLimit;
|
|
1058
1215
|
if (input.status !== void 0) updates.status = input.status;
|
|
1059
1216
|
if (input.endsAt !== void 0) updates.ends_at = input.endsAt;
|
|
1060
|
-
const { data, error } = await getDb().from(
|
|
1217
|
+
const { data, error } = await getDb().from(T.discounts).update(updates).eq("id", id).select().single();
|
|
1061
1218
|
if (error) throw error;
|
|
1062
1219
|
return rowToDiscount(data);
|
|
1063
1220
|
}
|
|
1064
1221
|
async deleteDiscount(id) {
|
|
1065
|
-
const { error } = await getDb().from(
|
|
1222
|
+
const { error } = await getDb().from(T.discounts).delete().eq("id", id);
|
|
1066
1223
|
if (error) throw error;
|
|
1067
1224
|
}
|
|
1068
1225
|
};
|