Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Logicommerce [WIP] #970

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deco.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const config = {
app("decohub"),
app("htmx"),
app("sap"),
app("logicommerce"),
...compatibilityApps,
],
};
Expand Down
8 changes: 7 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,14 @@
"exclude": [
"static",
"README.md",
"**/README.md"
"**/README.md",
"**/openapi/api.openapi.gen.ts"
],
"lint": {
"exclude": [
"**/openapi/api.openapi.gen.ts"
]
},
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
Expand Down
39 changes: 39 additions & 0 deletions logicommerce/loaders/productDetailsPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { ProductDetailsPage } from "../../commerce/types.ts";
import type { AppContext } from "../mod.ts";
import { toProduct } from "../utils/transform.ts";
import type { RequestURLParam } from "../../website/functions/requestToParam.ts";

interface Props {
slug: RequestURLParam;
}

/**
* @title PDP - Logicommerce Integration
* @description Product Details Page loader
*/
const loader = async (
{ slug }: Props,
req: Request,
ctx: AppContext,
): Promise<ProductDetailsPage | null> => {
const products = await ctx.api["GET /products/:id"]({ id: slug }, {
headers: req.headers,
}).then((res) => res.json());

return {
"@type": "ProductDetailsPage",
breadcrumbList: {
"@type": "BreadcrumbList",
itemListElement: [],
numberOfItems: 0,
},
product: toProduct(products),
seo: {
title: products.language?.name ?? "",
description: products.language?.longDescription ?? "",
canonical: products.language?.urlSeo ?? "",
},
};
};

export default loader;
58 changes: 58 additions & 0 deletions logicommerce/loaders/productList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Product } from "../../commerce/types.ts";
import type { AppContext } from "../mod.ts";
import type { LogicommerceProductSorts } from "../types.ts";
import { toProduct } from "../utils/transform.ts";

/** @title {{{name}}} - {{{value}}} */
interface Filter {
name: string;
value: string;
}

interface Props {
/**
* @title query
* @description query to use on search
*/
q?: string;
/** @description total number of items to display */
count?: number;
/** @description sort variable */
sort?: LogicommerceProductSorts;
/** @description Possible values: https://devcenter.logicommerce.com/apiCore/359#operation/getProducts (Query Parameters) */
filters?: Filter[];
}

/**
* @title Shelf - Logicommerce Integration
* @description Product List loader
*/
const loader = async (
props: Props,
req: Request,
ctx: AppContext,
): Promise<Product[] | null> => {
const { filters = [], ...params } = props;

Object.assign(
params,
filters.reduce(
(acc, filter) => {
acc[filter.name] = filter.value;
return acc;
},
{} as Record<string, string>,
),
);

console.log(params);
console.log(params);

const products = await ctx.api["GET /products"](params, {
headers: req.headers,
}).then((res) => res.json());

return products.items?.map(toProduct) ?? [];
};

export default loader;
56 changes: 56 additions & 0 deletions logicommerce/loaders/productListingPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { Product } from "../../commerce/types.ts";
import type { AppContext } from "../mod.ts";
import type { LogicommerceProductSorts } from "../types.ts";

/** @title {{{name}}} - {{{value}}} */
interface Filter {
name: string;
value: string;
}

interface Props {
/**
* @title Query
* @description overides the query term
*/
q?: string;
/**
* @title Items per page
* @description number of products per page to display
*/
count?: number;
/** @description sort variable */
sort?: LogicommerceProductSorts;
/** @description Possible values: https://devcenter.logicommerce.com/apiCore/359#operation/getProducts (Query Parameters) */
filters?: Filter[];
}

/**
* @title (Not implemented) PLP - Logicommerce Integration
* @description Product Listing Page loader
*/
const loader = (
_props: Props,
_req: Request,
_ctx: AppContext,
): Promise<Product[] | null> => {
throw new Error("Not implemented");
// const { filters = [], ...params } = props

// Object.assign(
// params,
// filters.reduce(
// (acc, filter) => {
// acc[filter.name] = filter.value
// return acc
// },
// {} as Record<string, string>,
// ),
// )

// const products = await ctx.api['GET /products'](params, { headers: req.headers }).then(res => res.json())

// return products.items?.map(toProduct) ?? []
};

export default loader;
Binary file added logicommerce/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions logicommerce/manifest.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// DO NOT EDIT. This file is generated by deco.
// This file SHOULD be checked into source version control.
// This file is automatically updated during development when running `dev.ts`.

import * as $$$0 from "./loaders/productDetailsPage.ts";
import * as $$$1 from "./loaders/productList.ts";
import * as $$$2 from "./loaders/productListingPage.ts";

const manifest = {
"loaders": {
"logicommerce/loaders/productDetailsPage.ts": $$$0,
"logicommerce/loaders/productList.ts": $$$1,
"logicommerce/loaders/productListingPage.ts": $$$2,
},
"name": "logicommerce",
"baseUrl": import.meta.url,
};

export type Manifest = typeof manifest;

export default manifest;
76 changes: 76 additions & 0 deletions logicommerce/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { getCookies } from "@std/http/cookie";
import { decode } from "@zaubrik/djwt";
import { setCookie } from "std/http/cookie.ts";
import type { AppMiddlewareContext } from "./mod.ts";

const DAY = 60 * 60 * 24;

const setToken = (headers: Headers, token: string) => {
setCookie(headers, {
name: "authToken",
value: token,
path: "/",
httpOnly: true,
secure: true,
maxAge: DAY * 30,
});
};
export const middleware = async (
_props: unknown,
req: Request,
ctx: AppMiddlewareContext,
) => {
// Set IP
// const ips = headers.get('CF-Connecting-IP') || headers.get('x-forwarded-for') || headers.get('x-real-ip')
// const ip = ips?.split(',')[0]

// if (!ip) throw new Error('IP not found')
// req.headers.set('ip', ip)

let authToken = getCookies(req.headers).authToken;

// You just need these headers for /auth actually
const headers = new Headers({
"X-App-id": ctx.appId,
"X-App-key": ctx.appKey,
});

const auth = () =>
ctx.api["GET /auth"]({}, { headers }).then((res) => res.json());
const authRefresh = () =>
ctx.api["GET /auth/refreshToken"]({}, { headers }).then((res) =>
res.json()
);

// Set Token
if (authToken) {
const [_, { refreshTokenExpirationDate, exp: expSeconds }] = decode<{
refreshTokenExpirationDate: number;
exp: number;
}>(authToken);

// https://stackoverflow.com/a/49624860
const exp = new Date(0);
exp.setUTCSeconds(expSeconds);

// If token can't be refreshed, get a new auth token
if (refreshTokenExpirationDate < Date.now()) {
authToken = (await auth()).token as string;
setToken(ctx.response.headers, authToken);
} // If the token just expired, refresh it
else if (exp.getTime() < Date.now()) {
headers.set("Authorization", `Bearer ${authToken}`);

authToken = (await authRefresh()).token as string;
setToken(ctx.response.headers, authToken);
}
} // If no token, get a new auth token
else {
authToken = (await auth()).token as string;
setToken(ctx.response.headers, authToken);
}

req.headers.set("Authorization", `Bearer ${authToken}`);

return await ctx.next?.();
};
72 changes: 72 additions & 0 deletions logicommerce/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { createHttpClient } from "../utils/http.ts";
import type { OpenAPI } from "./utils/openapi/api.openapi.gen.ts";
import { fetchSafe } from "../utils/fetch.ts";
import manifest, { type Manifest } from "./manifest.gen.ts";
import { PreviewContainer } from "../utils/preview.tsx";
import type {
App,
AppContext as AC,
AppMiddlewareContext as AMC,
} from "@deco/deco";
import { middleware } from "./middleware.ts";

interface Props {
appId: string;
appKey: string;
/**
* @ignore
*/
platform: "logicommerce";
/**
* @ignore
*/
api: ReturnType<typeof createHttpClient<OpenAPI>>;
}

export type AppContext = AC<ReturnType<typeof Logicommerce>>;
export type AppMiddlewareContext = AMC<ReturnType<typeof Logicommerce>>;

export const color = 0x4091a5;

/**
* @title Logicommerce
* @description Loaders, actions and workflows for adding Logicommerce to your website.
* @logo https://raw.githubusercontent.com/deco-cx/apps/refs/heads/feat--logicommerce/logicommerce/logo.png
* @category Ecommerce
*/
export default function Logicommerce(
{ appId, appKey }: Props,
): App<Manifest, Props> {
const headers = new Headers();
headers.set("ip", "127.0.0.1");

const api = createHttpClient<OpenAPI>({
base: "https://api-studio.logicommerce.cloud",
headers,
fetcher: fetchSafe,
});

return {
manifest,
state: { api, appId, appKey, platform: "logicommerce" },
middleware,
};
}

export const preview = () => {
return {
Component: PreviewContainer,
props: {
name: "Logicommerce",
owner: "deco.cx",
description:
"Loaders, actions and workflows for adding Wap Commerce Platform to your website.",
logo:
"https://raw.githubusercontent.com/deco-cx/apps/refs/heads/feat--logicommerce/logicommerce/logo.png",
images: [
"https://deco-sites-assets.s3.sa-east-1.amazonaws.com/starting/235b17e1-6f7a-4077-98cf-dad53ef075e5/2.Home-Galeria-de-topicos-principais-575x455px.jpg",
],
tabs: [],
},
};
};
19 changes: 19 additions & 0 deletions logicommerce/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type LogicommerceProductSorts =
| "id.asc"
| "id.desc"
| "pId.asc"
| "pId.desc"
| "sku.asc"
| "sku.desc"
| "name.asc"
| "name.desc"
| "priority.asc"
| "priority.desc"
| "price.asc"
| "price.desc"
| "offer.asc"
| "offer.desc"
| "featured.asc"
| "featured.desc"
| "publicationDate.asc"
| "publicationDate.desc";
Loading
Loading