Skip to content
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
8 changes: 8 additions & 0 deletions .changeset/onboarding-app-initial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"saleor-app-onboarding": patch
---

Introduce the Onboarding app — a standalone Saleor App that mounts on the Dashboard home page
(`HOME_WIDGETS` extension) and walks new users through first-run tasks: creating a product,
exploring orders, opening the GraphQL playground, browsing extensions, and inviting staff.
Completion state persists in user metadata so progress is preserved across sessions.
20 changes: 20 additions & 0 deletions apps/onboarding/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# See `src/lib/env.ts` for the exhaustive list of supported env variables.

# App identifier used when building the manifest. Override per-environment if needed.
# MANIFEST_APP_ID=saleor.app.onboarding

# App name displayed in Dashboard. Override to recognize app instance during development.
# APP_NAME=Onboarding

# APP_LOG_LEVEL=info # one of "fatal" | "error" | "warn" | "info" | "debug" | "trace"

# Local development variables. When developed locally with Saleor inside docker, these can be set to:
# APP_IFRAME_BASE_URL=http://localhost:3000 # so Dashboard on host can access iframe
# APP_API_BASE_URL=http://host.docker.internal:3000 # so Saleor can reach the App from container
# https://docs.saleor.io/developer/extending/apps/local-app-development

# Sentry — optional, enable error tracking
# NEXT_PUBLIC_SENTRY_DSN=
# SENTRY_AUTH_TOKEN=
# SENTRY_ORG=
# SENTRY_PROJECT=
29 changes: 29 additions & 0 deletions apps/onboarding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Saleor Onboarding App

Saleor App that provides a "Welcome / Onboarding" widget on the Dashboard home page. It guides
users through first-run tasks (creating a product, exploring orders, opening the GraphQL
playground, browsing extensions, inviting staff).

The app is a port of the Dashboard's built-in `WelcomePageOnboarding` widget into a standalone
Saleor App so onboarding can ship independently of Dashboard releases.

## Tech overview

- **Next.js App Router** for both API endpoints (`/api/manifest`, `/api/register`) and UI (`/`).
- **Manifest extension**: `HOME_WIDGETS` mount, `WIDGET` target, `GET` method (the staff JWT
comes from AppBridge automatically).
- **Client-only**: all GraphQL calls run in the browser using the user's JWT issued by AppBridge.
No webhooks, no APL persistence (a no-op APL satisfies the SDK's register handler).
- **State persistence**: completion state is stored under user metadata key `onboarding`
(drop-in compatible with the Dashboard widget's existing key) via `updateMetadata`.

## Local development

```bash
pnpm install
cp .env.example .env
pnpm --filter saleor-app-onboarding dev
```

See [the Saleor docs](https://docs.saleor.io/developer/extending/apps/local-app-development) for
running an app against a local Saleor instance.
59 changes: 59 additions & 0 deletions apps/onboarding/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { type CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
generates: {
"generated/graphql.ts": {
schema: "graphql/schema.graphql",
documents: ["graphql/**/*.graphql"],
plugins: [
{
add: {
content:
"type JSONValue = string | number | boolean | null | { [key: string]: JSONValue } | JSONValue[];",
},
},
{
typescript: {
enumsAsTypes: true,
},
},
"typescript-operations",
{
"typescript-urql": {
documentVariablePrefix: "Untyped",
fragmentVariablePrefix: "Untyped",
withHooks: false,
},
},
"typed-document-node",
],
config: {
dedupeFragments: true,
defaultScalarType: "unknown",
immutableTypes: true,
strictScalars: true,
skipTypename: true,
scalars: {
_Any: "unknown",
Date: "string",
DateTime: "string",
Decimal: "number",
Minute: "number",
GenericScalar: "JSONValue",
JSON: "JSONValue",
JSONString: "string",
Metadata: "Record<string, string>",
PositiveDecimal: "number",
Upload: "unknown",
UUID: "string",
WeightScalar: "number",
Day: "string",
Hour: "number",
PositiveInt: "number",
},
},
},
},
};

export default config;
31 changes: 31 additions & 0 deletions apps/onboarding/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { config } from "@saleor/eslint-config-apps/index.js";
import nodePlugin from "eslint-plugin-n";

/** @type {import("eslint").Linter.Config} */
export default [
...config,
{
name: "saleor-app-onboarding/custom-config",
files: ["**/*.ts", "**/*.tsx"],
plugins: {
n: nodePlugin,
},
rules: {
"n/no-process-env": "error",
},
},
{
name: "saleor-app-onboarding/override-no-process-env",
files: ["next.config.ts", "src/lib/env.ts", "src/instrumentation.ts"],
rules: {
"n/no-process-env": "off",
},
},
{
name: "saleor-app-onboarding/router-default-exports",
files: ["src/app/**/*", "src/pages/**/*"],
rules: {
"import/no-default-export": "off",
},
},
];
Loading
Loading