@fayz-ai/shop 0.8.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/dist/index.cjs +149 -3
- 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 +149 -3
- 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 +36 -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 +1 -0
- package/dist/tables.d.ts.map +1 -1
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +4 -0
- package/src/mock-provider.ts +5 -0
- package/src/provider.ts +39 -0
- package/src/supabase-provider.ts +196 -5
- package/src/tables.ts +4 -0
- package/src/types.ts +110 -0
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()
|
|
@@ -659,7 +664,11 @@ var T = {
|
|
|
659
664
|
orders: "plg_shop_orders",
|
|
660
665
|
orderItems: "plg_shop_order_items",
|
|
661
666
|
customers: "plg_shop_customers",
|
|
662
|
-
discounts: "plg_shop_discounts"
|
|
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"
|
|
663
672
|
};
|
|
664
673
|
|
|
665
674
|
// src/supabase-provider.ts
|
|
@@ -676,6 +685,40 @@ function asUuid(value) {
|
|
|
676
685
|
if (!value) return null;
|
|
677
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;
|
|
678
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
|
+
}
|
|
679
722
|
function rowToProduct(row) {
|
|
680
723
|
return {
|
|
681
724
|
id: row.id,
|
|
@@ -724,6 +767,19 @@ function rowToCategory(row) {
|
|
|
724
767
|
createdAt: row.created_at
|
|
725
768
|
};
|
|
726
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
|
+
}
|
|
727
783
|
function rowToOrder(row) {
|
|
728
784
|
return {
|
|
729
785
|
id: row.id,
|
|
@@ -743,7 +799,10 @@ function rowToOrder(row) {
|
|
|
743
799
|
customerEmail: row.customer_email,
|
|
744
800
|
discountCode: row.discount_code,
|
|
745
801
|
notes: row.notes,
|
|
746
|
-
|
|
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) : [],
|
|
747
806
|
createdAt: row.created_at,
|
|
748
807
|
updatedAt: row.updated_at
|
|
749
808
|
};
|
|
@@ -865,7 +924,32 @@ var SupabaseShopProvider = class {
|
|
|
865
924
|
const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
|
|
866
925
|
if (uploadErr) throw uploadErr;
|
|
867
926
|
const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
|
|
868
|
-
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();
|
|
869
953
|
if (error) throw error;
|
|
870
954
|
return rowToImage(data);
|
|
871
955
|
}
|
|
@@ -955,6 +1039,52 @@ var SupabaseShopProvider = class {
|
|
|
955
1039
|
if (error) throw error;
|
|
956
1040
|
return this.getOrder(id);
|
|
957
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
|
+
});
|
|
1085
|
+
if (error) throw error;
|
|
1086
|
+
return this.getOrder(id);
|
|
1087
|
+
}
|
|
958
1088
|
// Trusted placement — delegates all price/discount/inventory authority to the
|
|
959
1089
|
// shop_place_order RPC (SECURITY DEFINER). The browser never sets a price.
|
|
960
1090
|
async placeOrder(input) {
|
|
@@ -970,6 +1100,22 @@ var SupabaseShopProvider = class {
|
|
|
970
1100
|
p_currency: input.currency ?? "BRL",
|
|
971
1101
|
p_discount_code: input.discountCode ?? null,
|
|
972
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,
|
|
973
1119
|
p_notes: input.notes ?? null
|
|
974
1120
|
});
|
|
975
1121
|
if (error) throw error;
|