@fayz-ai/shop 0.6.0 → 0.8.0
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 +84 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -27
- package/dist/index.js.map +1 -1
- package/dist/mock-provider.d.ts +6 -1
- package/dist/mock-provider.d.ts.map +1 -1
- package/dist/mock.cjs +46 -0
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.js +46 -0
- package/dist/mock.js.map +1 -1
- package/dist/provider.d.ts +9 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/runtime.cjs +46 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +46 -0
- package/dist/runtime.js.map +1 -1
- package/dist/supabase-provider.d.ts.map +1 -1
- package/dist/tables.d.ts +10 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -4
- package/src/index.ts +2 -1
- package/src/mock-provider.ts +46 -1
- package/src/provider.ts +12 -1
- package/src/supabase-provider.ts +30 -29
- package/src/tables.ts +12 -0
- package/src/types.ts +45 -0
package/dist/index.js
CHANGED
|
@@ -176,6 +176,7 @@ function now() {
|
|
|
176
176
|
}
|
|
177
177
|
var ORDERS_KEY = "fayz.shop.mock.orders.v1";
|
|
178
178
|
var CUSTOMERS_KEY = "fayz.shop.mock.customers.v1";
|
|
179
|
+
var ENQUIRIES_KEY = "fayz.shop.mock.enquiries.v1";
|
|
179
180
|
function loadStored(key) {
|
|
180
181
|
try {
|
|
181
182
|
if (typeof localStorage === "undefined") return [];
|
|
@@ -196,6 +197,7 @@ var MockShopProvider = class {
|
|
|
196
197
|
constructor(seed) {
|
|
197
198
|
this.orders = loadStored(ORDERS_KEY);
|
|
198
199
|
this.customers = loadStored(CUSTOMERS_KEY);
|
|
200
|
+
this.enquiries = loadStored(ENQUIRIES_KEY);
|
|
199
201
|
this.products = (seed?.products ?? MOCK_PRODUCTS).map((p) => ({ ...p }));
|
|
200
202
|
this.categories = (seed?.categories ?? MOCK_CATEGORIES).map((c) => ({ ...c }));
|
|
201
203
|
this.discounts = (seed?.discounts ?? MOCK_DISCOUNTS).map((d) => ({ ...d }));
|
|
@@ -208,12 +210,18 @@ var MockShopProvider = class {
|
|
|
208
210
|
syncCustomers() {
|
|
209
211
|
this.customers = loadStored(CUSTOMERS_KEY);
|
|
210
212
|
}
|
|
213
|
+
syncEnquiries() {
|
|
214
|
+
this.enquiries = loadStored(ENQUIRIES_KEY);
|
|
215
|
+
}
|
|
211
216
|
persistOrders() {
|
|
212
217
|
store(ORDERS_KEY, this.orders);
|
|
213
218
|
}
|
|
214
219
|
persistCustomers() {
|
|
215
220
|
store(CUSTOMERS_KEY, this.customers);
|
|
216
221
|
}
|
|
222
|
+
persistEnquiries() {
|
|
223
|
+
store(ENQUIRIES_KEY, this.enquiries);
|
|
224
|
+
}
|
|
217
225
|
// ---------------------------------------------------------------------------
|
|
218
226
|
// Products
|
|
219
227
|
// ---------------------------------------------------------------------------
|
|
@@ -268,6 +276,44 @@ var MockShopProvider = class {
|
|
|
268
276
|
}
|
|
269
277
|
async deleteProductImage(_imageId) {
|
|
270
278
|
}
|
|
279
|
+
async createProductEnquiry(input) {
|
|
280
|
+
this.syncEnquiries();
|
|
281
|
+
const ts = now();
|
|
282
|
+
const enquiry = {
|
|
283
|
+
id: uid(),
|
|
284
|
+
tenantId: "mock",
|
|
285
|
+
productId: input.productId ?? null,
|
|
286
|
+
productName: input.productName,
|
|
287
|
+
productSlug: input.productSlug ?? null,
|
|
288
|
+
customerName: input.customerName,
|
|
289
|
+
customerEmail: input.customerEmail,
|
|
290
|
+
customerPhone: input.customerPhone ?? null,
|
|
291
|
+
subject: input.subject ?? null,
|
|
292
|
+
message: input.message,
|
|
293
|
+
sourceUrl: input.sourceUrl ?? null,
|
|
294
|
+
status: "new",
|
|
295
|
+
metadata: input.metadata ?? {},
|
|
296
|
+
createdAt: ts,
|
|
297
|
+
updatedAt: ts
|
|
298
|
+
};
|
|
299
|
+
this.enquiries.unshift(enquiry);
|
|
300
|
+
this.persistEnquiries();
|
|
301
|
+
return enquiry;
|
|
302
|
+
}
|
|
303
|
+
async listProductEnquiries(opts) {
|
|
304
|
+
this.syncEnquiries();
|
|
305
|
+
let list = [...this.enquiries];
|
|
306
|
+
if (opts?.productId) list = list.filter((e) => e.productId === opts.productId);
|
|
307
|
+
if (opts?.customerEmail) {
|
|
308
|
+
const email = opts.customerEmail.toLowerCase();
|
|
309
|
+
list = list.filter((e) => e.customerEmail.toLowerCase() === email);
|
|
310
|
+
}
|
|
311
|
+
if (opts?.status) list = list.filter((e) => e.status === opts.status);
|
|
312
|
+
list.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
313
|
+
const offset = opts?.offset ?? 0;
|
|
314
|
+
if (offset || opts?.limit) list = list.slice(offset, opts?.limit ? offset + opts.limit : void 0);
|
|
315
|
+
return list;
|
|
316
|
+
}
|
|
271
317
|
// ---------------------------------------------------------------------------
|
|
272
318
|
// Categories
|
|
273
319
|
// ---------------------------------------------------------------------------
|
|
@@ -603,6 +649,17 @@ function getShopTenantId() {
|
|
|
603
649
|
}
|
|
604
650
|
}
|
|
605
651
|
|
|
652
|
+
// src/tables.ts
|
|
653
|
+
var T = {
|
|
654
|
+
products: "plg_shop_products",
|
|
655
|
+
productImages: "plg_shop_product_images",
|
|
656
|
+
categories: "plg_shop_categories",
|
|
657
|
+
orders: "plg_shop_orders",
|
|
658
|
+
orderItems: "plg_shop_order_items",
|
|
659
|
+
customers: "plg_shop_customers",
|
|
660
|
+
discounts: "plg_shop_discounts"
|
|
661
|
+
};
|
|
662
|
+
|
|
606
663
|
// src/supabase-provider.ts
|
|
607
664
|
function getDb() {
|
|
608
665
|
const supabase = getSupabaseClientOptional();
|
|
@@ -730,7 +787,7 @@ var SupabaseShopProvider = class {
|
|
|
730
787
|
async listProducts(options) {
|
|
731
788
|
const db = getDb();
|
|
732
789
|
const tenantId = getTenantId();
|
|
733
|
-
let q = db.from(
|
|
790
|
+
let q = db.from(T.products).select(`*, images:${T.productImages}(*)`);
|
|
734
791
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
735
792
|
if (options?.status) q = q.eq("status", options.status);
|
|
736
793
|
if (options?.categoryId) q = q.eq("category_id", options.categoryId);
|
|
@@ -745,14 +802,14 @@ var SupabaseShopProvider = class {
|
|
|
745
802
|
}
|
|
746
803
|
async getProduct(id) {
|
|
747
804
|
const db = getDb();
|
|
748
|
-
const { data, error } = await db.from(
|
|
805
|
+
const { data, error } = await db.from(T.products).select(`*, images:${T.productImages}(*)`).eq("id", id).maybeSingle();
|
|
749
806
|
if (error) throw error;
|
|
750
807
|
return data ? rowToProduct(data) : null;
|
|
751
808
|
}
|
|
752
809
|
async createProduct(input) {
|
|
753
810
|
const db = getDb();
|
|
754
811
|
const tenantId = getTenantId();
|
|
755
|
-
const { data, error } = await db.from(
|
|
812
|
+
const { data, error } = await db.from(T.products).insert({
|
|
756
813
|
tenant_id: tenantId,
|
|
757
814
|
name: input.name,
|
|
758
815
|
slug: input.slug ?? slugify(input.name),
|
|
@@ -769,7 +826,7 @@ var SupabaseShopProvider = class {
|
|
|
769
826
|
is_physical: input.isPhysical ?? true,
|
|
770
827
|
weight: input.weight ?? null,
|
|
771
828
|
weight_unit: input.weightUnit ?? "kg"
|
|
772
|
-
}).select(
|
|
829
|
+
}).select(`*, images:${T.productImages}(*)`).single();
|
|
773
830
|
if (error) throw error;
|
|
774
831
|
return rowToProduct(data);
|
|
775
832
|
}
|
|
@@ -791,12 +848,12 @@ var SupabaseShopProvider = class {
|
|
|
791
848
|
if (input.categoryId !== void 0) updates.category_id = input.categoryId;
|
|
792
849
|
if (input.isPhysical !== void 0) updates.is_physical = input.isPhysical;
|
|
793
850
|
if (input.weight !== void 0) updates.weight = input.weight;
|
|
794
|
-
const { data, error } = await db.from(
|
|
851
|
+
const { data, error } = await db.from(T.products).update(updates).eq("id", id).select(`*, images:${T.productImages}(*)`).single();
|
|
795
852
|
if (error) throw error;
|
|
796
853
|
return rowToProduct(data);
|
|
797
854
|
}
|
|
798
855
|
async deleteProduct(id) {
|
|
799
|
-
const { error } = await getDb().from(
|
|
856
|
+
const { error } = await getDb().from(T.products).delete().eq("id", id);
|
|
800
857
|
if (error) throw error;
|
|
801
858
|
}
|
|
802
859
|
async uploadProductImage(productId, file) {
|
|
@@ -806,12 +863,12 @@ var SupabaseShopProvider = class {
|
|
|
806
863
|
const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
|
|
807
864
|
if (uploadErr) throw uploadErr;
|
|
808
865
|
const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
|
|
809
|
-
const { data, error } = await db.from(
|
|
866
|
+
const { data, error } = await db.from(T.productImages).insert({ product_id: productId, tenant_id: tenantId, url: publicUrl, sort_order: 0, is_primary: false }).select().single();
|
|
810
867
|
if (error) throw error;
|
|
811
868
|
return rowToImage(data);
|
|
812
869
|
}
|
|
813
870
|
async deleteProductImage(imageId) {
|
|
814
|
-
const { error } = await getDb().from(
|
|
871
|
+
const { error } = await getDb().from(T.productImages).delete().eq("id", imageId);
|
|
815
872
|
if (error) throw error;
|
|
816
873
|
}
|
|
817
874
|
// ---------------------------------------------------------------------------
|
|
@@ -820,7 +877,7 @@ var SupabaseShopProvider = class {
|
|
|
820
877
|
async listCategories() {
|
|
821
878
|
const db = getDb();
|
|
822
879
|
const tenantId = getTenantId();
|
|
823
|
-
let q = db.from(
|
|
880
|
+
let q = db.from(T.categories).select("*").order("sort_order", { ascending: true });
|
|
824
881
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
825
882
|
const { data, error } = await q;
|
|
826
883
|
if (error) throw error;
|
|
@@ -828,7 +885,7 @@ var SupabaseShopProvider = class {
|
|
|
828
885
|
}
|
|
829
886
|
async createCategory(input) {
|
|
830
887
|
const db = getDb();
|
|
831
|
-
const { data, error } = await db.from(
|
|
888
|
+
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();
|
|
832
889
|
if (error) throw error;
|
|
833
890
|
return rowToCategory(data);
|
|
834
891
|
}
|
|
@@ -840,12 +897,12 @@ var SupabaseShopProvider = class {
|
|
|
840
897
|
if (input.description !== void 0) updates.description = input.description;
|
|
841
898
|
if (input.parentId !== void 0) updates.parent_id = input.parentId;
|
|
842
899
|
if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
|
|
843
|
-
const { data, error } = await db.from(
|
|
900
|
+
const { data, error } = await db.from(T.categories).update(updates).eq("id", id).select().single();
|
|
844
901
|
if (error) throw error;
|
|
845
902
|
return rowToCategory(data);
|
|
846
903
|
}
|
|
847
904
|
async deleteCategory(id) {
|
|
848
|
-
const { error } = await getDb().from(
|
|
905
|
+
const { error } = await getDb().from(T.categories).delete().eq("id", id);
|
|
849
906
|
if (error) throw error;
|
|
850
907
|
}
|
|
851
908
|
// ---------------------------------------------------------------------------
|
|
@@ -854,7 +911,7 @@ var SupabaseShopProvider = class {
|
|
|
854
911
|
async listOrders(options) {
|
|
855
912
|
const db = getDb();
|
|
856
913
|
const tenantId = getTenantId();
|
|
857
|
-
let q = db.from(
|
|
914
|
+
let q = db.from(T.orders).select(`*, items:${T.orderItems}(*)`);
|
|
858
915
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
859
916
|
if (options?.status) q = q.eq("status", options.status);
|
|
860
917
|
if (options?.financialStatus) q = q.eq("financial_status", options.financialStatus);
|
|
@@ -869,7 +926,7 @@ var SupabaseShopProvider = class {
|
|
|
869
926
|
return (data ?? []).map(rowToOrder);
|
|
870
927
|
}
|
|
871
928
|
async getOrder(id) {
|
|
872
|
-
const { data, error } = await getDb().from(
|
|
929
|
+
const { data, error } = await getDb().from(T.orders).select(`*, items:${T.orderItems}(*)`).eq("id", id).maybeSingle();
|
|
873
930
|
if (error) throw error;
|
|
874
931
|
return data ? rowToOrder(data) : null;
|
|
875
932
|
}
|
|
@@ -880,10 +937,10 @@ var SupabaseShopProvider = class {
|
|
|
880
937
|
const discountTotal = input.discountTotal ?? 0;
|
|
881
938
|
const shippingTotal = input.shippingTotal ?? 0;
|
|
882
939
|
const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100;
|
|
883
|
-
const { data: order, error: orderErr } = await db.from(
|
|
940
|
+
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();
|
|
884
941
|
if (orderErr) throw orderErr;
|
|
885
942
|
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 }));
|
|
886
|
-
await db.from(
|
|
943
|
+
await db.from(T.orderItems).insert(items);
|
|
887
944
|
return this.getOrder(order.id);
|
|
888
945
|
}
|
|
889
946
|
async updateOrder(id, input) {
|
|
@@ -892,7 +949,7 @@ var SupabaseShopProvider = class {
|
|
|
892
949
|
if (input.financialStatus !== void 0) updates.financial_status = input.financialStatus;
|
|
893
950
|
if (input.fulfillmentStatus !== void 0) updates.fulfillment_status = input.fulfillmentStatus;
|
|
894
951
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
895
|
-
const { error } = await getDb().from(
|
|
952
|
+
const { error } = await getDb().from(T.orders).update(updates).eq("id", id);
|
|
896
953
|
if (error) throw error;
|
|
897
954
|
return this.getOrder(id);
|
|
898
955
|
}
|
|
@@ -925,7 +982,7 @@ var SupabaseShopProvider = class {
|
|
|
925
982
|
async listCustomers(options) {
|
|
926
983
|
const db = getDb();
|
|
927
984
|
const tenantId = getTenantId();
|
|
928
|
-
let q = db.from(
|
|
985
|
+
let q = db.from(T.customers).select("*").order("first_name", { ascending: true });
|
|
929
986
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
930
987
|
if (options?.search) q = q.or(`first_name.ilike.%${options.search}%,last_name.ilike.%${options.search}%,email.ilike.%${options.search}%`);
|
|
931
988
|
if (options?.limit) q = q.limit(options.limit);
|
|
@@ -934,12 +991,12 @@ var SupabaseShopProvider = class {
|
|
|
934
991
|
return (data ?? []).map(rowToCustomer);
|
|
935
992
|
}
|
|
936
993
|
async getCustomer(id) {
|
|
937
|
-
const { data, error } = await getDb().from(
|
|
994
|
+
const { data, error } = await getDb().from(T.customers).select("*").eq("id", id).maybeSingle();
|
|
938
995
|
if (error) throw error;
|
|
939
996
|
return data ? rowToCustomer(data) : null;
|
|
940
997
|
}
|
|
941
998
|
async createCustomer(input) {
|
|
942
|
-
const { data, error } = await getDb().from(
|
|
999
|
+
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();
|
|
943
1000
|
if (error) throw error;
|
|
944
1001
|
return rowToCustomer(data);
|
|
945
1002
|
}
|
|
@@ -950,12 +1007,12 @@ var SupabaseShopProvider = class {
|
|
|
950
1007
|
if (input.email !== void 0) updates.email = input.email;
|
|
951
1008
|
if (input.phone !== void 0) updates.phone = input.phone;
|
|
952
1009
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
953
|
-
const { data, error } = await getDb().from(
|
|
1010
|
+
const { data, error } = await getDb().from(T.customers).update(updates).eq("id", id).select().single();
|
|
954
1011
|
if (error) throw error;
|
|
955
1012
|
return rowToCustomer(data);
|
|
956
1013
|
}
|
|
957
1014
|
async deleteCustomer(id) {
|
|
958
|
-
const { error } = await getDb().from(
|
|
1015
|
+
const { error } = await getDb().from(T.customers).delete().eq("id", id);
|
|
959
1016
|
if (error) throw error;
|
|
960
1017
|
}
|
|
961
1018
|
// Find-or-create + link to auth.uid server-side (SECURITY DEFINER RPC), so the
|
|
@@ -980,7 +1037,7 @@ var SupabaseShopProvider = class {
|
|
|
980
1037
|
async listDiscounts(options) {
|
|
981
1038
|
const db = getDb();
|
|
982
1039
|
const tenantId = getTenantId();
|
|
983
|
-
let q = db.from(
|
|
1040
|
+
let q = db.from(T.discounts).select("*").order("created_at", { ascending: false });
|
|
984
1041
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
985
1042
|
if (options?.status) q = q.eq("status", options.status);
|
|
986
1043
|
if (options?.type) q = q.eq("type", options.type);
|
|
@@ -991,12 +1048,12 @@ var SupabaseShopProvider = class {
|
|
|
991
1048
|
return (data ?? []).map(rowToDiscount);
|
|
992
1049
|
}
|
|
993
1050
|
async getDiscount(id) {
|
|
994
|
-
const { data, error } = await getDb().from(
|
|
1051
|
+
const { data, error } = await getDb().from(T.discounts).select("*").eq("id", id).maybeSingle();
|
|
995
1052
|
if (error) throw error;
|
|
996
1053
|
return data ? rowToDiscount(data) : null;
|
|
997
1054
|
}
|
|
998
1055
|
async createDiscount(input) {
|
|
999
|
-
const { data, error } = await getDb().from(
|
|
1056
|
+
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();
|
|
1000
1057
|
if (error) throw error;
|
|
1001
1058
|
return rowToDiscount(data);
|
|
1002
1059
|
}
|
|
@@ -1009,12 +1066,12 @@ var SupabaseShopProvider = class {
|
|
|
1009
1066
|
if (input.usageLimit !== void 0) updates.usage_limit = input.usageLimit;
|
|
1010
1067
|
if (input.status !== void 0) updates.status = input.status;
|
|
1011
1068
|
if (input.endsAt !== void 0) updates.ends_at = input.endsAt;
|
|
1012
|
-
const { data, error } = await getDb().from(
|
|
1069
|
+
const { data, error } = await getDb().from(T.discounts).update(updates).eq("id", id).select().single();
|
|
1013
1070
|
if (error) throw error;
|
|
1014
1071
|
return rowToDiscount(data);
|
|
1015
1072
|
}
|
|
1016
1073
|
async deleteDiscount(id) {
|
|
1017
|
-
const { error } = await getDb().from(
|
|
1074
|
+
const { error } = await getDb().from(T.discounts).delete().eq("id", id);
|
|
1018
1075
|
if (error) throw error;
|
|
1019
1076
|
}
|
|
1020
1077
|
};
|