minutework 0.1.31 → 0.1.33
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/EXTERNAL_ALPHA.md +33 -33
- package/README.md +34 -34
- package/assets/claude-local/CLAUDE.md.template +12 -12
- package/assets/claude-local/skills/README.md +3 -1
- package/assets/claude-local/skills/app-pack-authoring/SKILL.md +17 -4
- package/assets/claude-local/skills/capability-gap-reporting/SKILL.md +3 -3
- package/assets/claude-local/skills/contract-first-public-intake/SKILL.md +11 -3
- package/assets/claude-local/skills/generated-workspace-architecture/SKILL.md +12 -5
- package/assets/claude-local/skills/layering-and-import-modes/SKILL.md +2 -2
- package/assets/claude-local/skills/openclaw-skill-importer/SKILL.md +2 -2
- package/assets/claude-local/skills/project-overview-and-strategy/SKILL.md +8 -8
- package/assets/claude-local/skills/published-web-and-mw-core-site/SKILL.md +11 -8
- package/assets/claude-local/skills/standalone-mobile-client/SKILL.md +6 -5
- package/assets/claude-local/skills/vuilder-discovery-output-contract/SKILL.md +6 -6
- package/assets/claude-local/skills/workspace-guidance-refresh/SKILL.md +4 -4
- package/assets/templates/fastapi-sidecar/pyproject.toml +1 -1
- package/assets/templates/fastapi-sidecar/src/fastapi_sidecar/main.py +1 -1
- package/assets/templates/mobile-app/.env.example +4 -4
- package/assets/templates/mobile-app/AGENTS.md +3 -3
- package/assets/templates/mobile-app/README.md +10 -10
- package/assets/templates/mobile-app/app/(app)/_layout.tsx +2 -2
- package/assets/templates/mobile-app/app/(app)/index.tsx +2 -2
- package/assets/templates/mobile-app/app/(auth)/login.tsx +3 -3
- package/assets/templates/mobile-app/app/_layout.tsx +1 -1
- package/assets/templates/mobile-app/babel.config.js +1 -1
- package/assets/templates/mobile-app/eas.json +1 -1
- package/assets/templates/mobile-app/expo-env.d.ts +1 -1
- package/assets/templates/mobile-app/metro.config.js +2 -2
- package/assets/templates/mobile-app/package.json +1 -1
- package/assets/templates/mobile-app/src/mw/client.ts +3 -3
- package/assets/templates/mobile-app/src/mw/contracts.ts +2 -2
- package/assets/templates/mobile-app/src/mw/endpoints.ts +2 -2
- package/assets/templates/mobile-app/src/mw/env.ts +4 -4
- package/assets/templates/mobile-app/src/mw/session.ts +1 -1
- package/assets/templates/mobile-app/template.json +1 -1
- package/assets/templates/mobile-app/tools/template/validate-template.mjs +2 -2
- package/assets/templates/mobile-app/tsconfig.json +1 -1
- package/assets/templates/next-tenant-app/.env.example +1 -1
- package/assets/templates/next-tenant-app/README.md +26 -138
- package/assets/templates/next-tenant-app/package.json +1 -0
- package/assets/templates/next-tenant-app/src/app/app/demo/page.tsx +15 -0
- package/assets/templates/next-tenant-app/src/app/app/layout.tsx +1 -4
- package/assets/templates/next-tenant-app/src/app/app/page.tsx +2 -17
- package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.tsx +9 -67
- package/assets/templates/next-tenant-app/src/app/blog/page.tsx +10 -46
- package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.tsx +9 -65
- package/assets/templates/next-tenant-app/src/app/docs/page.tsx +10 -46
- package/assets/templates/next-tenant-app/src/app/layout.tsx +8 -10
- package/assets/templates/next-tenant-app/src/app/login/page.tsx +3 -23
- package/assets/templates/next-tenant-app/src/app/page.tsx +11 -44
- package/assets/templates/next-tenant-app/src/app/pricing/page.tsx +10 -44
- package/assets/templates/next-tenant-app/src/app/providers.tsx +2 -1
- package/assets/templates/next-tenant-app/src/app/robots.ts +7 -18
- package/assets/templates/next-tenant-app/src/app/sitemap.ts +4 -39
- package/assets/templates/next-tenant-app/src/features/auth/components/login-screen.tsx +97 -98
- package/assets/templates/next-tenant-app/src/features/dashboard/components/tenant-dashboard.tsx +43 -78
- package/assets/templates/next-tenant-app/src/features/demo/components/manifest-demo.tsx +89 -0
- package/assets/templates/next-tenant-app/src/features/public-shell/components/static-public-page.tsx +58 -0
- package/assets/templates/next-tenant-app/src/features/shell/components/private-app-shell.tsx +48 -552
- package/assets/templates/next-tenant-app/src/lib/app-routes.ts +2 -2
- package/assets/templates/next-tenant-app/src/lib/public-site.test.ts +1 -1
- package/assets/templates/next-tenant-app/src/lib/public-site.ts +5 -30
- package/assets/templates/next-tenant-app/src/mw/client.ts +18 -0
- package/assets/templates/next-tenant-app/src/mw/mock.test.ts +21 -0
- package/assets/templates/next-tenant-app/src/mw/mock.ts +35 -0
- package/assets/templates/next-tenant-app/src/mw/provider.tsx +17 -0
- package/assets/templates/next-tenant-app/template.json +3 -3
- package/assets/templates/next-tenant-app/template.schema.json +1 -0
- package/assets/templates/next-tenant-app/tools/template/validate-route-contract.mjs +4 -5
- package/assets/templates/next-tenant-app/tools/template/with-public-site-fixture.mjs +2 -2
- package/bin/minutework.js +1 -1
- package/dist/agent.js +7 -7
- package/dist/agent.js.map +1 -1
- package/dist/auth.js +7 -7
- package/dist/auth.js.map +1 -1
- package/dist/compile.js +5 -5
- package/dist/config.js +6 -6
- package/dist/config.js.map +1 -1
- package/dist/deploy.js +7 -7
- package/dist/deploy.js.map +1 -1
- package/dist/developer-client.js +2 -2
- package/dist/developer-client.js.map +1 -1
- package/dist/index.js +30 -30
- package/dist/index.js.map +1 -1
- package/dist/init.js +10 -10
- package/dist/init.js.map +1 -1
- package/dist/launcher.js +1 -1
- package/dist/launcher.js.map +1 -1
- package/dist/managed-engine.js +6 -6
- package/dist/managed-engine.js.map +1 -1
- package/dist/orchestrator-context.js +1 -1
- package/dist/orchestrator-context.js.map +1 -1
- package/dist/orchestrator.js +15 -15
- package/dist/orchestrator.js.map +1 -1
- package/dist/paths.js +1 -1
- package/dist/paths.js.map +1 -1
- package/dist/publish.js +3 -3
- package/dist/publish.js.map +1 -1
- package/dist/reporting.js +8 -8
- package/dist/reporting.js.map +1 -1
- package/dist/sandbox.js +5 -5
- package/dist/sandbox.js.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/tokens.js +9 -9
- package/dist/tokens.js.map +1 -1
- package/dist/workspace-assets.js +6 -6
- package/dist/workspace-assets.js.map +1 -1
- package/dist/workspace.js +3 -3
- package/dist/workspace.js.map +1 -1
- package/package.json +3 -3
- package/vendor/workspace-mcp/context.d.ts +6 -6
- package/vendor/workspace-mcp/context.js +56 -56
- package/vendor/workspace-mcp/context.js.map +1 -1
- package/vendor/workspace-mcp/types.d.ts +4 -0
- package/assets/templates/next-tenant-app/src/app/(cms)/[...path]/page.tsx +0 -89
- package/assets/templates/next-tenant-app/src/app/api/auth/context/route.test.ts +0 -90
- package/assets/templates/next-tenant-app/src/app/api/auth/context/route.ts +0 -78
- package/assets/templates/next-tenant-app/src/app/api/auth/login/route.ts +0 -31
- package/assets/templates/next-tenant-app/src/app/api/auth/logout/route.ts +0 -16
- package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.test.ts +0 -79
- package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.ts +0 -40
- package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.test.ts +0 -42
- package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.ts +0 -29
- package/assets/templates/next-tenant-app/src/app/api/auth/session/route.ts +0 -26
- package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.test.ts +0 -40
- package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.ts +0 -47
- package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.test.ts +0 -43
- package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.ts +0 -45
- package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.test.ts +0 -83
- package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.tsx +0 -30
- package/assets/templates/next-tenant-app/src/app/app/page.test.ts +0 -62
- package/assets/templates/next-tenant-app/src/app/app/private-content-source.test.ts +0 -88
- package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.test.ts +0 -70
- package/assets/templates/next-tenant-app/src/app/blog/page.test.ts +0 -46
- package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.test.ts +0 -70
- package/assets/templates/next-tenant-app/src/app/docs/page.test.ts +0 -46
- package/assets/templates/next-tenant-app/src/app/login/page.test.ts +0 -55
- package/assets/templates/next-tenant-app/src/app/page.test.ts +0 -90
- package/assets/templates/next-tenant-app/src/app/pricing/page.test.ts +0 -59
- package/assets/templates/next-tenant-app/src/app/robots.test.ts +0 -40
- package/assets/templates/next-tenant-app/src/app/sitemap.test.ts +0 -63
- package/assets/templates/next-tenant-app/src/features/examples/runtime-command-demo/components/runtime-command-demo.tsx +0 -342
- package/assets/templates/next-tenant-app/src/features/public-shell/components/content-article.tsx +0 -66
- package/assets/templates/next-tenant-app/src/features/public-shell/components/content-collection.tsx +0 -108
- package/assets/templates/next-tenant-app/src/features/public-shell/components/marketing-page-canvas.tsx +0 -111
- package/assets/templates/next-tenant-app/src/features/public-shell/components/public-site-shell.tsx +0 -111
- package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.test.ts +0 -38
- package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.tsx +0 -145
- package/assets/templates/next-tenant-app/src/lib/content/__fixtures__/public-site-snapshot.ts +0 -189
- package/assets/templates/next-tenant-app/src/lib/content/adapter.server.test.ts +0 -444
- package/assets/templates/next-tenant-app/src/lib/content/adapter.server.ts +0 -383
- package/assets/templates/next-tenant-app/src/lib/content/contracts.test.ts +0 -138
- package/assets/templates/next-tenant-app/src/lib/content/contracts.ts +0 -399
- package/assets/templates/next-tenant-app/src/lib/content/custom-adapter.ts +0 -5
- package/assets/templates/next-tenant-app/src/lib/content/empty-state.ts +0 -96
- package/assets/templates/next-tenant-app/src/lib/content/release-manifest.test.ts +0 -93
- package/assets/templates/next-tenant-app/src/lib/content/release-manifest.ts +0 -123
- package/assets/templates/next-tenant-app/src/lib/platform/auth.server.test.ts +0 -75
- package/assets/templates/next-tenant-app/src/lib/platform/auth.server.ts +0 -25
- package/assets/templates/next-tenant-app/src/lib/platform/client.server.test.ts +0 -170
- package/assets/templates/next-tenant-app/src/lib/platform/client.server.ts +0 -661
- package/assets/templates/next-tenant-app/src/lib/platform/contracts.ts +0 -131
- package/assets/templates/next-tenant-app/src/lib/platform/endpoints.server.ts +0 -34
- package/assets/templates/next-tenant-app/src/lib/platform/env.server.test.ts +0 -211
- package/assets/templates/next-tenant-app/src/lib/platform/env.server.ts +0 -151
- package/assets/templates/next-tenant-app/src/lib/platform/route-response.ts +0 -33
- package/assets/templates/next-tenant-app/src/lib/platform/session.server.ts +0 -108
|
@@ -3,157 +3,45 @@
|
|
|
3
3
|
This directory is the canonical combined Next.js web starter for Builder.
|
|
4
4
|
|
|
5
5
|
In the runtime Builder flow, this bundle materializes into
|
|
6
|
-
`BuilderWorkspace.sandbox_root/app/`.
|
|
6
|
+
`BuilderWorkspace.sandbox_root/app/`. In the external CLI scaffold flow, the
|
|
7
|
+
same starter is copied into `tenant-app/`.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
On managed tenant VMs, `BuilderWorkspace.sandbox_root` normally lives under
|
|
12
|
-
`/srv/minutework/workspaces/<workspace_id>/`, so the Builder-edited app copy is
|
|
13
|
-
typically `/srv/minutework/workspaces/<workspace_id>/app/`.
|
|
14
|
-
|
|
15
|
-
`apps/mw-next-client` is still the repo-side seed used to harden and evolve this scaffold. It is not the runtime template location.
|
|
9
|
+
`apps/mw-next-client` may still seed visual conventions, but this template is
|
|
10
|
+
no longer a platform-session BFF app.
|
|
16
11
|
|
|
17
12
|
## Auth profile
|
|
18
13
|
|
|
19
|
-
This template
|
|
20
|
-
|
|
21
|
-
- browsers use this BFF only (cookie session, server-owned CSRF); native clients
|
|
22
|
-
(e.g. a mobile app) instead use a platform-issued native session token
|
|
23
|
-
directly against `/api/v1/native/...` -- see the `mobile` starter /
|
|
24
|
-
`standalone-mobile-client` skill
|
|
25
|
-
- upstream platform `sessionid` and `csrftoken` stay server-owned
|
|
26
|
-
- unsafe upstream requests bootstrap and forward CSRF through the BFF
|
|
27
|
-
- there is no browser-to-runtime direct access
|
|
28
|
-
- there is no local JWT system, local user table, or parallel auth stack
|
|
29
|
-
|
|
30
|
-
## Runtime requirements
|
|
31
|
-
|
|
32
|
-
- Node.js `20.9.0` or newer
|
|
33
|
-
- `pnpm@9.0.0`
|
|
14
|
+
This template uses the `tenant_web_auth_sdk` profile:
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
- browser auth and manifest API calls go through `@minutework/web-auth`
|
|
17
|
+
- the browser stores no bearer token, runtime key, or platform credential
|
|
18
|
+
- SDK calls use same-origin `/_mw` routes with `credentials: "include"`
|
|
19
|
+
- CSRF is handled by the SDK using the non-secret CSRF cookie
|
|
20
|
+
- runtime query/action calls are authorized as `principal_kind=tenant_customer`
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
- Root `pnpm install` stays Node-only and does not auto-install sidecar Python deps.
|
|
41
|
-
- If you need the sidecar locally, run `pnpm run install:sidecar` from the workspace root or `cd sidecar && poetry install`.
|
|
42
|
-
- For Vercel, set the project Root Directory to `tenant-app`.
|
|
43
|
-
|
|
44
|
-
Those host-specific instructions apply to the CLI scaffold layout, not the
|
|
45
|
-
runtime Builder sandbox layout under `app/`.
|
|
22
|
+
Only `src/mw/` is MinuteWork substrate. Keep product UI and product logic in
|
|
23
|
+
`src/app`, `src/features`, and developer-owned modules.
|
|
46
24
|
|
|
47
25
|
## Default route shape
|
|
48
26
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
- optional runtime example under `/app/examples/runtime-commands`
|
|
54
|
-
|
|
55
|
-
Public marketing, docs, and blog routes render through a swappable public-content adapter seam. The authenticated shell keeps tenant context controls, password flows, and the platform-session BFF boundary.
|
|
56
|
-
|
|
57
|
-
## Public content adapter
|
|
58
|
-
|
|
59
|
-
The public content seam lives under `src/lib/content`.
|
|
60
|
-
|
|
61
|
-
The default built-in adapter is `MinuteWorkPublicSiteContentAdapter`. It calls the
|
|
62
|
-
PandaWork gateway with a server-only content token and reads the narrow
|
|
63
|
-
`mw.core.site` public-site snapshot contract.
|
|
64
|
-
|
|
65
|
-
`mw.core.site` is treated as a runtime baseline capability. This starter
|
|
66
|
-
composes against that baseline rather than trying to install it locally.
|
|
67
|
-
|
|
68
|
-
The gateway response carries an explicit boundary:
|
|
69
|
-
|
|
70
|
-
- `environment=preview` must declare `source_boundary=runtime_preview`
|
|
71
|
-
- `environment=live` may declare `source_boundary=published_live` for hosted releases or `source_boundary=runtime_live` for runtime-served sidecar releases
|
|
72
|
-
|
|
73
|
-
That keeps preview free to use runtime-backed draft reads while keeping live
|
|
74
|
-
publication-safe for hosted delivery while still allowing an explicit
|
|
75
|
-
runtime-served public lane when the release is activated through
|
|
76
|
-
`runtime_local_sidecar`.
|
|
77
|
-
|
|
78
|
-
To replace the built-ins for a tenant-specific CMS or content API, implement the hook in `src/lib/content/custom-adapter.ts`.
|
|
79
|
-
|
|
80
|
-
## Example gating
|
|
27
|
+
- public routes at `/`, `/pricing`, `/docs`, and `/blog`
|
|
28
|
+
- customer auth at `/login`
|
|
29
|
+
- authenticated workspace at `/app`
|
|
30
|
+
- SDK manifest demo at `/app/demo`
|
|
81
31
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- `/app/examples/runtime-commands` returns `404`
|
|
87
|
-
- the example gateway API routes return `404`
|
|
88
|
-
- the example navigation entry is omitted
|
|
32
|
+
The template intentionally does not include `src/lib/platform/*`,
|
|
33
|
+
`src/app/api/auth/*`, `src/app/api/gateway/*`, an operator-console link, a
|
|
34
|
+
runtime-command demo, or a server-only public-content token adapter.
|
|
89
35
|
|
|
90
36
|
## Environment
|
|
91
37
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
`
|
|
96
|
-
|
|
97
|
-
- `MW_PLATFORM_BASE_URL` is required
|
|
98
|
-
- `MW_PUBLIC_CONTENT_SOURCE` defaults to `none` when omitted; supported values are `minutework_cms`, `custom`, `static_json`, and `none`
|
|
99
|
-
- `MW_CONTENT_API_TOKEN` is required only for `minutework_cms` and must stay server-only
|
|
100
|
-
- `MW_TEMPLATE_APP_NAME` defaults to `PandaWork Combined Starter`
|
|
101
|
-
- `MW_PUBLIC_BASE_URL` is required for public content sources and should match the deployed public origin; it may be omitted when `MW_PUBLIC_CONTENT_SOURCE=none`
|
|
102
|
-
- `MW_PUBLIC_SITE_PROPERTY_KEY` is required only for `minutework_cms` and selects the `PublishedWebProperty` backing the site
|
|
103
|
-
- `MW_PUBLIC_SITE_ENV` defaults to `preview`
|
|
104
|
-
- `MW_STATIC_PUBLIC_CONTENT_PATH` defaults to `content/public-site.json` and is used when `MW_PUBLIC_CONTENT_SOURCE=static_json`
|
|
105
|
-
- `MW_ENABLE_RUNTIME_COMMAND_EXAMPLE` defaults to `false`
|
|
106
|
-
|
|
107
|
-
## SEO baseline
|
|
108
|
-
|
|
109
|
-
The public route surface ships with:
|
|
110
|
-
|
|
111
|
-
- canonical URLs derived from `MW_PUBLIC_BASE_URL`
|
|
112
|
-
- `robots.ts` and `sitemap.ts`
|
|
113
|
-
- Open Graph metadata for marketing and article/detail pages
|
|
114
|
-
- server-rendered public pages by default, with no tenant/session reads during render
|
|
115
|
-
- docs/blog static params generated from the same public-site snapshot used during render
|
|
116
|
-
- graceful empty states for fresh properties with no authored or no published content
|
|
117
|
-
|
|
118
|
-
Private routes under `/app` are marked `noindex`.
|
|
119
|
-
|
|
120
|
-
## Generated artifact policy
|
|
121
|
-
|
|
122
|
-
Checked-in generated artifacts are limited to:
|
|
123
|
-
|
|
124
|
-
- `next-env.d.ts`
|
|
125
|
-
- `src/design-system/tokens/manifest.ts`
|
|
126
|
-
- `src/design-system/tokens/manifest.json`
|
|
127
|
-
- `pnpm-lock.yaml`
|
|
128
|
-
|
|
129
|
-
Generated build outputs such as `.next/`, `storybook-static/`, `test-results/`, and `tools/design-system/__screenshots__/` are not canonical template contents.
|
|
38
|
+
- `NEXT_PUBLIC_MW_APP_ID` selects the manifest app id used by the SDK and
|
|
39
|
+
defaults to `tenant.app`
|
|
40
|
+
- `MW_TEMPLATE_APP_NAME` controls the display name and defaults to `Tenant App`
|
|
41
|
+
- `MW_PUBLIC_BASE_URL` is optional and only affects metadata routes
|
|
130
42
|
|
|
131
43
|
## Validation
|
|
132
44
|
|
|
133
|
-
Use `pnpm validate` from this directory. It runs
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
2. `template:route-contract`
|
|
137
|
-
3. `next typegen`
|
|
138
|
-
4. `design-system:tokens`
|
|
139
|
-
5. `design-system:check`
|
|
140
|
-
6. `typecheck`
|
|
141
|
-
7. `lint`
|
|
142
|
-
8. `test`
|
|
143
|
-
9. `build:validate`
|
|
144
|
-
10. `build-storybook:validate`
|
|
145
|
-
|
|
146
|
-
`template:validate` validates `template.json` against the bundled `template.schema.json`, so the template remains self-validating after Builder materializes it into a workspace sandbox.
|
|
147
|
-
|
|
148
|
-
When the CLI scaffolds this starter into `tenant-app/`, the same validation
|
|
149
|
-
contract still applies to that copied workspace surface. When Runtime Builder
|
|
150
|
-
materializes it into `app/`, the same validation contract applies there too.
|
|
151
|
-
|
|
152
|
-
The validation-only build steps inject fixture values for the required
|
|
153
|
-
production env vars and run against a local public-site fixture server, so the
|
|
154
|
-
template can prove its build contract in-repo without weakening the real
|
|
155
|
-
`pnpm build` requirement.
|
|
156
|
-
|
|
157
|
-
`template:route-contract` verifies that the combined public/private route tree, metadata routes, and content-adapter entrypoints remain present in the template.
|
|
158
|
-
|
|
159
|
-
Inside the repo, the shared schema at `runtime/builder/templates/template.schema.json` is authoritative. The bundled `template.schema.json` inside this template must stay byte-equivalent, and `template:validate` fails if the two drift.
|
|
45
|
+
Use `pnpm validate` from this directory. It runs template validation, route
|
|
46
|
+
contract validation, typegen, design-system checks, typecheck, tests, and build
|
|
47
|
+
validation.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ManifestDemo } from "@/features/demo/components/manifest-demo";
|
|
2
|
+
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Demo",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default function DemoPage() {
|
|
8
|
+
return (
|
|
9
|
+
<main className="min-h-screen bg-background text-foreground">
|
|
10
|
+
<div className="mx-auto flex min-h-screen max-w-5xl flex-col gap-6 px-6 py-8">
|
|
11
|
+
<ManifestDemo />
|
|
12
|
+
</div>
|
|
13
|
+
</main>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
|
|
4
|
-
import { resolveAuthenticatedSession } from "@/lib/platform/auth.server";
|
|
5
|
-
|
|
6
4
|
export const metadata: Metadata = {
|
|
7
5
|
robots: {
|
|
8
6
|
index: false,
|
|
@@ -10,11 +8,10 @@ export const metadata: Metadata = {
|
|
|
10
8
|
},
|
|
11
9
|
};
|
|
12
10
|
|
|
13
|
-
export default
|
|
11
|
+
export default function AuthenticatedAppLayout({
|
|
14
12
|
children,
|
|
15
13
|
}: {
|
|
16
14
|
children: ReactNode;
|
|
17
15
|
}) {
|
|
18
|
-
await resolveAuthenticatedSession();
|
|
19
16
|
return children;
|
|
20
17
|
}
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
import { PrivateAppShell } from "@/features/shell/components/private-app-shell";
|
|
2
|
-
import { resolveAuthenticatedSession } from "@/lib/platform/auth.server";
|
|
3
|
-
import { platformAuthEndpoints } from "@/lib/platform/endpoints.server";
|
|
4
|
-
import { env } from "@/lib/platform/env.server";
|
|
5
2
|
|
|
6
3
|
export const metadata = {
|
|
7
4
|
title: "Workspace",
|
|
8
|
-
description:
|
|
9
|
-
"Authenticated workspace surface for the combined PandaWork starter.",
|
|
10
5
|
};
|
|
11
6
|
|
|
12
|
-
export default
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<PrivateAppShell
|
|
17
|
-
initialSession={session}
|
|
18
|
-
appName={env.MW_TEMPLATE_APP_NAME}
|
|
19
|
-
operatorConsoleHref={platformAuthEndpoints.operatorConsole}
|
|
20
|
-
runtimeCommandExampleEnabled={env.MW_ENABLE_RUNTIME_COMMAND_EXAMPLE}
|
|
21
|
-
view="dashboard"
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
7
|
+
export default function AppHomePage() {
|
|
8
|
+
return <PrivateAppShell appName={process.env.MW_TEMPLATE_APP_NAME || "Tenant App"} />;
|
|
24
9
|
}
|
|
@@ -1,73 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticPublicPage } from "@/features/public-shell/components/static-public-page";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { getEntry, getSiteConfig, listEntries } from "@/lib/content/adapter.server";
|
|
6
|
-
import { appRoutes } from "@/lib/app-routes";
|
|
7
|
-
import {
|
|
8
|
-
buildDisabledPublicMetadata,
|
|
9
|
-
buildPublicMetadata,
|
|
10
|
-
isPublicContentDisabled,
|
|
11
|
-
} from "@/lib/public-site";
|
|
12
|
-
|
|
13
|
-
type BlogArticlePageProps = {
|
|
14
|
-
params: Promise<{ slug: string }>;
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Blog",
|
|
15
5
|
};
|
|
16
6
|
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
export async function generateStaticParams() {
|
|
20
|
-
if (isPublicContentDisabled()) {
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (await listEntries("blog")).map((entry) => ({
|
|
25
|
-
slug: entry.slug[0],
|
|
26
|
-
}));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function generateMetadata({ params }: BlogArticlePageProps) {
|
|
30
|
-
if (isPublicContentDisabled()) {
|
|
31
|
-
return buildDisabledPublicMetadata();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const [{ slug }, siteConfig] = await Promise.all([params, getSiteConfig()]);
|
|
35
|
-
const entry = await getEntry("blog", [slug]);
|
|
36
|
-
|
|
37
|
-
if (!entry) {
|
|
38
|
-
return buildPublicMetadata({
|
|
39
|
-
title: siteConfig.collections.blog.title,
|
|
40
|
-
description: siteConfig.collections.blog.description,
|
|
41
|
-
path: appRoutes.blogIndex,
|
|
42
|
-
siteName: siteConfig.siteName,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return buildPublicMetadata({
|
|
47
|
-
title: entry.seo.title,
|
|
48
|
-
description: entry.seo.description,
|
|
49
|
-
path: appRoutes.blogPost(entry.slug),
|
|
50
|
-
siteName: siteConfig.siteName,
|
|
51
|
-
type: "article",
|
|
52
|
-
publishedTime: entry.publishedAt,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export default async function BlogArticlePage({ params }: BlogArticlePageProps) {
|
|
57
|
-
if (isPublicContentDisabled()) {
|
|
58
|
-
notFound();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const [{ slug }, siteConfig] = await Promise.all([params, getSiteConfig()]);
|
|
62
|
-
const entry = await getEntry("blog", [slug]);
|
|
63
|
-
|
|
64
|
-
if (!entry) {
|
|
65
|
-
notFound();
|
|
66
|
-
}
|
|
67
|
-
|
|
7
|
+
export default function BlogPostPage() {
|
|
68
8
|
return (
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
9
|
+
<StaticPublicPage
|
|
10
|
+
eyebrow="Blog"
|
|
11
|
+
title="Update"
|
|
12
|
+
body="A customer-facing update."
|
|
13
|
+
/>
|
|
72
14
|
);
|
|
73
15
|
}
|
|
@@ -1,51 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticPublicPage } from "@/features/public-shell/components/static-public-page";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { appRoutes } from "@/lib/app-routes";
|
|
7
|
-
import {
|
|
8
|
-
buildDisabledPublicMetadata,
|
|
9
|
-
buildPublicMetadata,
|
|
10
|
-
isPublicContentDisabled,
|
|
11
|
-
} from "@/lib/public-site";
|
|
12
|
-
|
|
13
|
-
export async function generateMetadata() {
|
|
14
|
-
if (isPublicContentDisabled()) {
|
|
15
|
-
return buildDisabledPublicMetadata();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const siteConfig = await getSiteConfig();
|
|
19
|
-
const collection = siteConfig.collections.blog;
|
|
20
|
-
|
|
21
|
-
return buildPublicMetadata({
|
|
22
|
-
title: collection.title,
|
|
23
|
-
description: collection.description,
|
|
24
|
-
path: appRoutes.blogIndex,
|
|
25
|
-
siteName: siteConfig.siteName,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default async function BlogIndexPage() {
|
|
30
|
-
if (isPublicContentDisabled()) {
|
|
31
|
-
notFound();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const [siteConfig, entries] = await Promise.all([
|
|
35
|
-
getSiteConfig(),
|
|
36
|
-
listEntries("blog"),
|
|
37
|
-
]);
|
|
38
|
-
const collection = siteConfig.collections.blog;
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Blog",
|
|
5
|
+
};
|
|
39
6
|
|
|
7
|
+
export default function BlogIndexPage() {
|
|
40
8
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
kind="blog"
|
|
47
|
-
title={collection.title}
|
|
48
|
-
/>
|
|
49
|
-
</PublicSiteShell>
|
|
9
|
+
<StaticPublicPage
|
|
10
|
+
eyebrow="Blog"
|
|
11
|
+
title="Updates"
|
|
12
|
+
body="News, releases, and customer notes."
|
|
13
|
+
/>
|
|
50
14
|
);
|
|
51
15
|
}
|
|
@@ -1,71 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticPublicPage } from "@/features/public-shell/components/static-public-page";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { getEntry, getSiteConfig, listEntries } from "@/lib/content/adapter.server";
|
|
6
|
-
import { appRoutes } from "@/lib/app-routes";
|
|
7
|
-
import {
|
|
8
|
-
buildDisabledPublicMetadata,
|
|
9
|
-
buildPublicMetadata,
|
|
10
|
-
isPublicContentDisabled,
|
|
11
|
-
} from "@/lib/public-site";
|
|
12
|
-
|
|
13
|
-
type DocsArticlePageProps = {
|
|
14
|
-
params: Promise<{ slug: string[] }>;
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Docs",
|
|
15
5
|
};
|
|
16
6
|
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
export async function generateStaticParams() {
|
|
20
|
-
if (isPublicContentDisabled()) {
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (await listEntries("docs")).map((entry) => ({
|
|
25
|
-
slug: entry.slug,
|
|
26
|
-
}));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function generateMetadata({ params }: DocsArticlePageProps) {
|
|
30
|
-
if (isPublicContentDisabled()) {
|
|
31
|
-
return buildDisabledPublicMetadata();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const [{ slug }, siteConfig] = await Promise.all([params, getSiteConfig()]);
|
|
35
|
-
const entry = await getEntry("docs", slug);
|
|
36
|
-
|
|
37
|
-
if (!entry) {
|
|
38
|
-
return buildPublicMetadata({
|
|
39
|
-
title: siteConfig.collections.docs.title,
|
|
40
|
-
description: siteConfig.collections.docs.description,
|
|
41
|
-
path: appRoutes.docsIndex,
|
|
42
|
-
siteName: siteConfig.siteName,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return buildPublicMetadata({
|
|
47
|
-
title: entry.seo.title,
|
|
48
|
-
description: entry.seo.description,
|
|
49
|
-
path: appRoutes.docsPage(entry.slug),
|
|
50
|
-
siteName: siteConfig.siteName,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default async function DocsArticlePage({ params }: DocsArticlePageProps) {
|
|
55
|
-
if (isPublicContentDisabled()) {
|
|
56
|
-
notFound();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const [{ slug }, siteConfig] = await Promise.all([params, getSiteConfig()]);
|
|
60
|
-
const entry = await getEntry("docs", slug);
|
|
61
|
-
|
|
62
|
-
if (!entry) {
|
|
63
|
-
notFound();
|
|
64
|
-
}
|
|
65
|
-
|
|
7
|
+
export default function DocsPage() {
|
|
66
8
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
9
|
+
<StaticPublicPage
|
|
10
|
+
eyebrow="Docs"
|
|
11
|
+
title="Documentation page"
|
|
12
|
+
body="Helpful context for customer workflows."
|
|
13
|
+
/>
|
|
70
14
|
);
|
|
71
15
|
}
|
|
@@ -1,51 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticPublicPage } from "@/features/public-shell/components/static-public-page";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { appRoutes } from "@/lib/app-routes";
|
|
7
|
-
import {
|
|
8
|
-
buildDisabledPublicMetadata,
|
|
9
|
-
buildPublicMetadata,
|
|
10
|
-
isPublicContentDisabled,
|
|
11
|
-
} from "@/lib/public-site";
|
|
12
|
-
|
|
13
|
-
export async function generateMetadata() {
|
|
14
|
-
if (isPublicContentDisabled()) {
|
|
15
|
-
return buildDisabledPublicMetadata();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const siteConfig = await getSiteConfig();
|
|
19
|
-
const collection = siteConfig.collections.docs;
|
|
20
|
-
|
|
21
|
-
return buildPublicMetadata({
|
|
22
|
-
title: collection.title,
|
|
23
|
-
description: collection.description,
|
|
24
|
-
path: appRoutes.docsIndex,
|
|
25
|
-
siteName: siteConfig.siteName,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default async function DocsIndexPage() {
|
|
30
|
-
if (isPublicContentDisabled()) {
|
|
31
|
-
notFound();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const [siteConfig, entries] = await Promise.all([
|
|
35
|
-
getSiteConfig(),
|
|
36
|
-
listEntries("docs"),
|
|
37
|
-
]);
|
|
38
|
-
const collection = siteConfig.collections.docs;
|
|
3
|
+
export const metadata = {
|
|
4
|
+
title: "Docs",
|
|
5
|
+
};
|
|
39
6
|
|
|
7
|
+
export default function DocsIndexPage() {
|
|
40
8
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
kind="docs"
|
|
47
|
-
title={collection.title}
|
|
48
|
-
/>
|
|
49
|
-
</PublicSiteShell>
|
|
9
|
+
<StaticPublicPage
|
|
10
|
+
eyebrow="Docs"
|
|
11
|
+
title="Customer documentation"
|
|
12
|
+
body="Guides, onboarding notes, and product documentation."
|
|
13
|
+
/>
|
|
50
14
|
);
|
|
51
15
|
}
|
|
@@ -5,7 +5,6 @@ import { cookies } from "next/headers";
|
|
|
5
5
|
import type { ReactNode } from "react";
|
|
6
6
|
|
|
7
7
|
import { AppProviders } from "./providers";
|
|
8
|
-
import { env } from "@/lib/platform/env.server";
|
|
9
8
|
import { resolvePublicMetadataBase } from "@/lib/public-site";
|
|
10
9
|
import {
|
|
11
10
|
isThemeMode,
|
|
@@ -25,22 +24,21 @@ const geistMono = Geist_Mono({
|
|
|
25
24
|
|
|
26
25
|
export function generateMetadata(): Metadata {
|
|
27
26
|
const metadataBase = resolvePublicMetadataBase();
|
|
27
|
+
const appName = process.env.MW_TEMPLATE_APP_NAME || "Tenant App";
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
...(metadataBase ? { metadataBase } : {}),
|
|
31
31
|
title: {
|
|
32
|
-
default:
|
|
33
|
-
template: `%s | ${
|
|
32
|
+
default: appName,
|
|
33
|
+
template: `%s | ${appName}`,
|
|
34
34
|
},
|
|
35
|
-
description:
|
|
36
|
-
"SEO-first public and authenticated Next.js starter with a server-owned platform session BFF.",
|
|
35
|
+
description: "Customer-facing tenant app.",
|
|
37
36
|
openGraph: {
|
|
38
|
-
title:
|
|
39
|
-
description:
|
|
40
|
-
|
|
41
|
-
siteName: env.MW_TEMPLATE_APP_NAME,
|
|
37
|
+
title: appName,
|
|
38
|
+
description: "Customer-facing tenant app.",
|
|
39
|
+
siteName: appName,
|
|
42
40
|
type: "website",
|
|
43
|
-
...(env.MW_PUBLIC_BASE_URL ? { url: env.MW_PUBLIC_BASE_URL } : {}),
|
|
41
|
+
...(process.env.MW_PUBLIC_BASE_URL ? { url: process.env.MW_PUBLIC_BASE_URL } : {}),
|
|
44
42
|
},
|
|
45
43
|
};
|
|
46
44
|
}
|
|
@@ -1,33 +1,13 @@
|
|
|
1
|
-
import { redirect } from "next/navigation";
|
|
2
|
-
|
|
3
1
|
import { LoginScreen } from "@/features/auth/components/login-screen";
|
|
4
|
-
import { appRoutes } from "@/lib/app-routes";
|
|
5
|
-
import { resolveCurrentSession } from "@/lib/platform/client.server";
|
|
6
|
-
import { platformAuthEndpoints } from "@/lib/platform/endpoints.server";
|
|
7
|
-
import { env } from "@/lib/platform/env.server";
|
|
8
|
-
import { readPlatformAuthState } from "@/lib/platform/session.server";
|
|
9
2
|
|
|
10
3
|
export const metadata = {
|
|
11
|
-
title: "
|
|
12
|
-
description:
|
|
13
|
-
"Authenticate into the combined PandaWork starter to continue into the private workspace surface under /app.",
|
|
4
|
+
title: "Log In",
|
|
14
5
|
robots: {
|
|
15
6
|
index: false,
|
|
16
7
|
follow: false,
|
|
17
8
|
},
|
|
18
9
|
};
|
|
19
10
|
|
|
20
|
-
export default
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (session.authenticated) {
|
|
24
|
-
redirect(appRoutes.appHome);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<LoginScreen
|
|
29
|
-
appName={env.MW_TEMPLATE_APP_NAME}
|
|
30
|
-
operatorConsoleHref={platformAuthEndpoints.operatorConsole}
|
|
31
|
-
/>
|
|
32
|
-
);
|
|
11
|
+
export default function LoginPage() {
|
|
12
|
+
return <LoginScreen appName={process.env.MW_TEMPLATE_APP_NAME || "Tenant App"} />;
|
|
33
13
|
}
|