@decocms/apps-commerce 7.2.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.
@@ -0,0 +1,8 @@
1
+ import type { BreadcrumbList } from "../types/commerce";
2
+
3
+ export const canonicalFromBreadcrumblist = (b?: BreadcrumbList) => {
4
+ const items = b?.itemListElement ?? [];
5
+ if (!Array.isArray(items) || items.length === 0) return undefined;
6
+
7
+ return items.reduce((acc, curr) => (acc.position < curr.position ? curr : acc)).item;
8
+ };
@@ -0,0 +1,8 @@
1
+ import type { ImageObject } from "../types/commerce";
2
+
3
+ export const DEFAULT_IMAGE: ImageObject = {
4
+ "@type": "ImageObject",
5
+ encodingFormat: "image",
6
+ alternateName: "Default Image Placeholder",
7
+ url: "https://ozksgdmyrqcxcwhnbepg.supabase.co/storage/v1/object/public/assets/1818/ff6bb37e-0eab-40e1-a454-86856efc278e",
8
+ };
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatRange, parseRange } from "./filters";
3
+
4
+ describe("parseRange", () => {
5
+ it("parses valid range", () => {
6
+ expect(parseRange("10:50")).toEqual({ from: 10, to: 50 });
7
+ });
8
+
9
+ it("parses decimal range", () => {
10
+ expect(parseRange("34.90:56.90")).toEqual({ from: 34.9, to: 56.9 });
11
+ });
12
+
13
+ it("returns null for invalid range", () => {
14
+ expect(parseRange("abc:def")).toBeNull();
15
+ });
16
+
17
+ it("returns null for empty string", () => {
18
+ expect(parseRange("")).toBeNull();
19
+ });
20
+
21
+ it("returns null for single value", () => {
22
+ expect(parseRange("10")).toBeNull();
23
+ });
24
+ });
25
+
26
+ describe("formatRange", () => {
27
+ it("formats range correctly", () => {
28
+ expect(formatRange(10, 50)).toBe("10:50");
29
+ });
30
+
31
+ it("formats decimal range", () => {
32
+ expect(formatRange(34.9, 56.9)).toBe("34.9:56.9");
33
+ });
34
+ });
@@ -0,0 +1,10 @@
1
+ export const parseRange = (price: string) => {
2
+ const splitted = price.split(":");
3
+
4
+ const from = Number(splitted?.[0]);
5
+ const to = Number(splitted?.[1]);
6
+
7
+ return Number.isNaN(from) || Number.isNaN(to) ? null : { from, to };
8
+ };
9
+
10
+ export const formatRange = (from: number, to: number) => `${from}:${to}`;
@@ -0,0 +1,63 @@
1
+ import type { AnalyticsItem, BreadcrumbList, Product } from "../types/commerce";
2
+
3
+ export const mapCategoriesToAnalyticsCategories = (
4
+ categories: string[],
5
+ ): Record<`item_category${number | ""}`, string> => {
6
+ return categories.slice(0, 5).reduce(
7
+ (result, category, index) => {
8
+ result[`item_category${index === 0 ? "" : index + 1}`] = category;
9
+ return result;
10
+ },
11
+ {} as Record<`item_category${number | ""}`, string>,
12
+ );
13
+ };
14
+
15
+ export const mapProductCategoryToAnalyticsCategories = (category: string) => {
16
+ return category.split(">").reduce(
17
+ (result, category, index) => {
18
+ result[`item_category${index === 0 ? "" : index + 1}`] = category.trim();
19
+ return result;
20
+ },
21
+ {} as Record<`item_category${number | ""}`, string>,
22
+ );
23
+ };
24
+
25
+ export const mapProductToAnalyticsItem = ({
26
+ product,
27
+ breadcrumbList,
28
+ price,
29
+ listPrice,
30
+ index = 0,
31
+ quantity = 1,
32
+ coupon = "",
33
+ }: {
34
+ product: Product;
35
+ breadcrumbList?: BreadcrumbList;
36
+ price?: number;
37
+ listPrice?: number;
38
+ index?: number;
39
+ quantity?: number;
40
+ coupon?: string;
41
+ }): AnalyticsItem => {
42
+ const { name, productID, inProductGroupWithID, isVariantOf, url } = product;
43
+ const categories = breadcrumbList?.itemListElement
44
+ ? mapCategoriesToAnalyticsCategories(
45
+ breadcrumbList?.itemListElement.map(({ name: _name }) => _name ?? "").filter(Boolean) ?? [],
46
+ )
47
+ : mapProductCategoryToAnalyticsCategories(product.category ?? "");
48
+
49
+ return {
50
+ item_id: productID,
51
+ item_group_id: inProductGroupWithID,
52
+ quantity,
53
+ coupon,
54
+ price,
55
+ index,
56
+ discount: Number((price && listPrice ? listPrice - price : 0).toFixed(2)),
57
+ item_name: isVariantOf?.name ?? name ?? "",
58
+ item_variant: name,
59
+ item_brand: product.brand?.name ?? "",
60
+ item_url: url,
61
+ ...categories,
62
+ };
63
+ };
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import getStateFromZip from "./stateByZip";
3
+
4
+ describe("getStateFromZip", () => {
5
+ it("returns SP for São Paulo ZIP", () => {
6
+ expect(getStateFromZip("01001000")).toBe("SP");
7
+ });
8
+
9
+ it("returns RJ for Rio de Janeiro ZIP", () => {
10
+ expect(getStateFromZip("20000000")).toBe("RJ");
11
+ });
12
+
13
+ it("returns MG for Minas Gerais ZIP", () => {
14
+ expect(getStateFromZip("30000000")).toBe("MG");
15
+ });
16
+
17
+ it("returns RS for Rio Grande do Sul ZIP", () => {
18
+ expect(getStateFromZip("90000000")).toBe("RS");
19
+ });
20
+
21
+ it("strips non-numeric characters", () => {
22
+ expect(getStateFromZip("01001-000")).toBe("SP");
23
+ expect(getStateFromZip("01.001-000")).toBe("SP");
24
+ });
25
+
26
+ it("returns empty string for invalid ZIP", () => {
27
+ expect(getStateFromZip("00000000")).toBe("");
28
+ expect(getStateFromZip("")).toBe("");
29
+ });
30
+
31
+ it("handles boundary: AM upper range", () => {
32
+ expect(getStateFromZip("69299999")).toBe("AM");
33
+ });
34
+
35
+ it("handles boundary: RR range", () => {
36
+ expect(getStateFromZip("69300000")).toBe("RR");
37
+ expect(getStateFromZip("69399999")).toBe("RR");
38
+ });
39
+
40
+ it("handles boundary: AM second range", () => {
41
+ expect(getStateFromZip("69400000")).toBe("AM");
42
+ });
43
+
44
+ it("handles boundary: AC range", () => {
45
+ expect(getStateFromZip("69900000")).toBe("AC");
46
+ expect(getStateFromZip("69999999")).toBe("AC");
47
+ });
48
+
49
+ it("returns BA for Bahia ZIP", () => {
50
+ expect(getStateFromZip("40000000")).toBe("BA");
51
+ });
52
+
53
+ it("returns DF for Brasília ZIP", () => {
54
+ expect(getStateFromZip("70000000")).toBe("DF");
55
+ });
56
+ });
@@ -0,0 +1,50 @@
1
+ const getStateFromZip = (cep: string) => {
2
+ // Remove non-numeric characters
3
+ cep = cep.replace(/\D/g, "");
4
+
5
+ // zip range by: https://buscacepinter.correios.com.br/app/faixa_cep_uf_localidade/index.php
6
+ const zipRange = [
7
+ { state: "AC", startRange: 69900000, endRange: 69999999 },
8
+ { state: "AL", startRange: 57000000, endRange: 57999999 },
9
+ { state: "AM", startRange: 69000000, endRange: 69299999 },
10
+ { state: "AM", startRange: 69400000, endRange: 69899999 },
11
+ { state: "AP", startRange: 68900000, endRange: 68999999 },
12
+ { state: "BA", startRange: 40000000, endRange: 48999999 },
13
+ { state: "CE", startRange: 60000000, endRange: 63999999 },
14
+ { state: "DF", startRange: 70000000, endRange: 72799999 },
15
+ { state: "DF", startRange: 73000000, endRange: 73699999 },
16
+ { state: "ES", startRange: 29000000, endRange: 29999999 },
17
+ { state: "GO", startRange: 72800000, endRange: 72999999 },
18
+ { state: "GO", startRange: 73700000, endRange: 76799999 },
19
+ { state: "MA", startRange: 65000000, endRange: 65999999 },
20
+ { state: "MG", startRange: 30000000, endRange: 39999999 },
21
+ { state: "MS", startRange: 79000000, endRange: 79999999 },
22
+ { state: "MT", startRange: 78000000, endRange: 78899999 },
23
+ { state: "PA", startRange: 66000000, endRange: 68899999 },
24
+ { state: "PB", startRange: 58000000, endRange: 58999999 },
25
+ { state: "PE", startRange: 50000000, endRange: 56999999 },
26
+ { state: "PI", startRange: 64000000, endRange: 64999999 },
27
+ { state: "PR", startRange: 80000000, endRange: 87999999 },
28
+ { state: "RJ", startRange: 20000000, endRange: 28999999 },
29
+ { state: "RN", startRange: 59000000, endRange: 59999999 },
30
+ { state: "RO", startRange: 76800000, endRange: 76999999 },
31
+ { state: "RR", startRange: 69300000, endRange: 69399999 },
32
+ { state: "RS", startRange: 90000000, endRange: 99999999 },
33
+ { state: "SC", startRange: 88000000, endRange: 89999999 },
34
+ { state: "SE", startRange: 49000000, endRange: 49999999 },
35
+ { state: "SP", startRange: 1000000, endRange: 19999999 },
36
+ { state: "TO", startRange: 77000000, endRange: 77999999 },
37
+ ];
38
+
39
+ const zipCode = parseInt(cep, 10);
40
+
41
+ for (const range of zipRange) {
42
+ if (zipCode >= range.startRange && zipCode <= range.endRange) {
43
+ return range.state;
44
+ }
45
+ }
46
+
47
+ return "";
48
+ };
49
+
50
+ export default getStateFromZip;
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist"
5
+ },
6
+ "include": ["src/**/*"]
7
+ }