Skip to content
Merged
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 bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/apps-nuvemshop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
},
"dependencies": {
"@decocms/blocks": "workspace:*",
"@decocms/apps-commerce": "workspace:*"
"@decocms/apps-commerce": "workspace:*",
"@decocms/apps-website": "workspace:*"
},
"peerDependencies": { "react": "^19.0.0" },
"devDependencies": {
Expand Down
21 changes: 20 additions & 1 deletion packages/apps-nuvemshop/src/__tests__/mod.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { getNuvemshopConfig } from "../client";
import { configure } from "../mod";
import NuvemshopApp, { configure, type Props } from "../mod";
import { nuvemshopOperationRouter } from "../utils/operationRouter";

const noSecret = async () => null;
Expand Down Expand Up @@ -34,6 +34,25 @@ describe("configure", () => {
});
});

describe("CMS Props (admin form for the deco-nuvemshop block)", () => {
it("is the shape configure() reads, and is the default export's input", async () => {
const props: Props = {
storeId: "8336778",
currency: "ARS",
defaultSort: "price-ascending",
apiVersion: "v2026-11",
};
expect(NuvemshopApp(props)).toEqual({ state: props });
await configure({ ...props }, noSecret);
expect(getNuvemshopConfig()).toMatchObject({
storeId: "8336778",
currency: "ARS",
defaultSort: "price-ascending",
apiVersion: "v2026-11",
});
});
});

describe("nuvemshopOperationRouter", () => {
const base = "https://storefront-api.tiendanube.com/v2026-11/stores/1";
it.each([
Expand Down
42 changes: 42 additions & 0 deletions packages/apps-nuvemshop/src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,45 @@
* production — see `client.ts` for the rate-limit trade-off.
*/
import type { AppDefinition, ResolveSecretFn } from "@decocms/apps-commerce/app-types";
import type { Secret } from "@decocms/apps-website/mod";
import { configureNuvemshop, type NuvemshopConfig } from "./client";
import manifest from "./manifest.gen";
import type { SortValue } from "./utils/listing";

// -------------------------------------------------------------------------
// CMS Props — the admin form for the `deco-nuvemshop` block. Sites expose it
// through a bridge (`src/apps/nuvemshop.ts` re-exporting this module's default
// and `Props`); generate-schema builds the form from this interface.
// -------------------------------------------------------------------------

/** @title Nuvemshop */
export interface Props {
/**
* @title Store ID
* @description Numeric Nuvemshop/Tiendanube store id (e.g. 8336778).
*/
storeId: string;
/**
* @title Storefront token
* @description Optional. Without it all buyers share 120 req/min per IP; with it the store gets 1,200 req/min.
*/
token?: Secret;
/**
* @title Currency
* @description The API returns no currency. @default BRL
*/
currency?: string;
/**
* @title Default category sort
* @description The API doesn't expose the category's configured sort. @default created-descending
*/
defaultSort?: SortValue;
/**
* @title API version
* @default v2026-11
*/
apiVersion?: string;
}

export interface NuvemshopState {
config: NuvemshopConfig;
Expand Down Expand Up @@ -40,3 +77,8 @@ export async function configure(
}

export const preview = undefined;

/** Default export for schema generation and app bridges. */
export default function Nuvemshop(props: Props) {
return { state: props };
}
12 changes: 11 additions & 1 deletion packages/apps-nuvemshop/src/utils/listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ export const LISTING_WINDOW = 200;
* The theme's "user" (merchant's manual order) isn't in the API, so it's not
* offered; `?sort_by=user` falls back to the default sort.
*/
export const SORT_OPTIONS: SortOption[] = [
export type SortValue =
| "best-selling"
| "price-ascending"
| "price-descending"
| "alpha-ascending"
| "alpha-descending"
| "created-descending"
| "created-ascending"
| "discount-descending";

export const SORT_OPTIONS: (SortOption & { value: SortValue })[] = [
{ value: "best-selling", label: "Mais vendidos" },
{ value: "price-ascending", label: "Menor preço" },
{ value: "price-descending", label: "Maior preço" },
Expand Down
Loading