create-questpie 1.0.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.
- package/README.md +81 -0
- package/dist/index.mjs +284 -0
- package/package.json +44 -0
- package/templates/tanstack-start/AGENTS.md +563 -0
- package/templates/tanstack-start/CLAUDE.md +105 -0
- package/templates/tanstack-start/Dockerfile +23 -0
- package/templates/tanstack-start/README.md +94 -0
- package/templates/tanstack-start/components.json +22 -0
- package/templates/tanstack-start/docker-compose.yml +20 -0
- package/templates/tanstack-start/env.example +16 -0
- package/templates/tanstack-start/gitignore +12 -0
- package/templates/tanstack-start/package.json +43 -0
- package/templates/tanstack-start/questpie.config.ts +12 -0
- package/templates/tanstack-start/src/admin.css +4 -0
- package/templates/tanstack-start/src/lib/auth-client.ts +12 -0
- package/templates/tanstack-start/src/lib/cms-client.ts +12 -0
- package/templates/tanstack-start/src/lib/env.ts +27 -0
- package/templates/tanstack-start/src/lib/query-client.ts +9 -0
- package/templates/tanstack-start/src/migrations/index.ts +8 -0
- package/templates/tanstack-start/src/questpie/admin/admin.ts +5 -0
- package/templates/tanstack-start/src/questpie/admin/builder.ts +4 -0
- package/templates/tanstack-start/src/questpie/server/app.ts +52 -0
- package/templates/tanstack-start/src/questpie/server/builder.ts +4 -0
- package/templates/tanstack-start/src/questpie/server/collections/index.ts +1 -0
- package/templates/tanstack-start/src/questpie/server/collections/posts.collection.ts +72 -0
- package/templates/tanstack-start/src/questpie/server/dashboard.ts +68 -0
- package/templates/tanstack-start/src/questpie/server/globals/index.ts +1 -0
- package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +24 -0
- package/templates/tanstack-start/src/questpie/server/rpc.ts +4 -0
- package/templates/tanstack-start/src/questpie/server/sidebar.ts +26 -0
- package/templates/tanstack-start/src/router.tsx +10 -0
- package/templates/tanstack-start/src/routes/__root.tsx +16 -0
- package/templates/tanstack-start/src/routes/admin/$.tsx +21 -0
- package/templates/tanstack-start/src/routes/admin/index.tsx +18 -0
- package/templates/tanstack-start/src/routes/admin/login.tsx +17 -0
- package/templates/tanstack-start/src/routes/admin.tsx +68 -0
- package/templates/tanstack-start/src/routes/api/cms/$.ts +45 -0
- package/templates/tanstack-start/src/styles.css +125 -0
- package/templates/tanstack-start/tsconfig.json +27 -0
- package/templates/tanstack-start/vite.config.ts +26 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A [QUESTPIE CMS](https://questpie.com) project built with TanStack Start.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- [Bun](https://bun.sh) (v1.3+)
|
|
10
|
+
- [Docker](https://docker.com) (for PostgreSQL)
|
|
11
|
+
|
|
12
|
+
### Setup
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Start PostgreSQL
|
|
16
|
+
docker compose up -d
|
|
17
|
+
|
|
18
|
+
# Run database migrations
|
|
19
|
+
bun questpie migrate
|
|
20
|
+
|
|
21
|
+
# Start the dev server
|
|
22
|
+
bun dev
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The admin panel will be available at [http://localhost:3000/admin](http://localhost:3000/admin).
|
|
26
|
+
|
|
27
|
+
The API docs (Scalar UI) are at [http://localhost:3000/api/cms/docs](http://localhost:3000/api/cms/docs).
|
|
28
|
+
|
|
29
|
+
## Project Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
src/
|
|
33
|
+
questpie/
|
|
34
|
+
server/
|
|
35
|
+
app.ts # Main CMS configuration
|
|
36
|
+
builder.ts # Server-side builder
|
|
37
|
+
rpc.ts # RPC router
|
|
38
|
+
collections/
|
|
39
|
+
posts.collection.ts # Posts collection
|
|
40
|
+
globals/
|
|
41
|
+
site-settings.global.ts # Site settings
|
|
42
|
+
admin/
|
|
43
|
+
admin.ts # Admin UI configuration
|
|
44
|
+
builder.ts # Client-side builder
|
|
45
|
+
routes/
|
|
46
|
+
admin.tsx # Admin layout
|
|
47
|
+
admin/ # Admin routes
|
|
48
|
+
api/cms/$.ts # CMS API handler
|
|
49
|
+
lib/
|
|
50
|
+
env.ts # Type-safe environment variables
|
|
51
|
+
cms-client.ts # CMS API client
|
|
52
|
+
auth-client.ts # Auth client
|
|
53
|
+
query-client.ts # React Query client
|
|
54
|
+
migrations/ # Database migrations
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Scripts
|
|
58
|
+
|
|
59
|
+
| Command | Description |
|
|
60
|
+
| ----------------------------- | ------------------------ |
|
|
61
|
+
| `bun dev` | Start development server |
|
|
62
|
+
| `bun build` | Build for production |
|
|
63
|
+
| `bun start` | Start production server |
|
|
64
|
+
| `bun questpie migrate` | Run database migrations |
|
|
65
|
+
| `bun questpie migrate:create` | Create a new migration |
|
|
66
|
+
|
|
67
|
+
## Adding Collections
|
|
68
|
+
|
|
69
|
+
Create a new file following the naming convention:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
src/questpie/server/collections/my-collection.collection.ts
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then register it in `app.ts`:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { myCollection } from "./collections/my-collection.collection.js";
|
|
79
|
+
|
|
80
|
+
// Add to .collections()
|
|
81
|
+
.collections({ posts, myCollection })
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Adding Globals
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
src/questpie/server/globals/my-global.global.ts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then register in `app.ts`:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
.globals({ siteSettings, myGlobal })
|
|
94
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "base-lyra",
|
|
4
|
+
"rsc": false,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/styles.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "hugeicons",
|
|
14
|
+
"aliases": {
|
|
15
|
+
"components": "@/components",
|
|
16
|
+
"utils": "@/lib/utils",
|
|
17
|
+
"ui": "@/components/ui",
|
|
18
|
+
"lib": "@/lib",
|
|
19
|
+
"hooks": "@/hooks"
|
|
20
|
+
},
|
|
21
|
+
"registries": {}
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:17
|
|
4
|
+
container_name: {{projectName}}-postgres
|
|
5
|
+
environment:
|
|
6
|
+
POSTGRES_DB: {{databaseName}}
|
|
7
|
+
POSTGRES_USER: {{databaseUser}}
|
|
8
|
+
POSTGRES_PASSWORD: {{databasePassword}}
|
|
9
|
+
ports:
|
|
10
|
+
- "5432:5432"
|
|
11
|
+
volumes:
|
|
12
|
+
- postgres_data:/var/lib/postgresql/data
|
|
13
|
+
healthcheck:
|
|
14
|
+
test: ["CMD-SHELL", "pg_isready -U {{databaseUser}}"]
|
|
15
|
+
interval: 5s
|
|
16
|
+
timeout: 5s
|
|
17
|
+
retries: 5
|
|
18
|
+
|
|
19
|
+
volumes:
|
|
20
|
+
postgres_data:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Database
|
|
2
|
+
DATABASE_URL=postgresql://{{databaseUser}}:{{databasePassword}}@localhost:5432/{{databaseName}}
|
|
3
|
+
|
|
4
|
+
# Application
|
|
5
|
+
APP_URL=http://localhost:3000
|
|
6
|
+
PORT=3000
|
|
7
|
+
|
|
8
|
+
# Auth (Better Auth)
|
|
9
|
+
BETTER_AUTH_SECRET=your-secret-key-change-in-production
|
|
10
|
+
|
|
11
|
+
# Email Adapter (console | smtp)
|
|
12
|
+
MAIL_ADAPTER=console
|
|
13
|
+
|
|
14
|
+
# SMTP (if using smtp adapter)
|
|
15
|
+
# SMTP_HOST=localhost
|
|
16
|
+
# SMTP_PORT=1025
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "bun --bun vite dev --port 3000",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"start": "bun run .output/server/index.mjs",
|
|
10
|
+
"check-types": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@questpie/admin": "latest",
|
|
14
|
+
"@questpie/openapi": "latest",
|
|
15
|
+
"@questpie/tanstack-query": "latest",
|
|
16
|
+
"@t3-oss/env-core": "^0.12.0",
|
|
17
|
+
"@tanstack/react-query": "^5.62.11",
|
|
18
|
+
"@tanstack/react-router": "^1.132.0",
|
|
19
|
+
"@tanstack/react-start": "^1.132.0",
|
|
20
|
+
"drizzle-orm": "1.0.0-beta.6-4414a19",
|
|
21
|
+
"questpie": "latest",
|
|
22
|
+
"react": "^19.0.0",
|
|
23
|
+
"react-dom": "^19.0.0",
|
|
24
|
+
"zod": "^4.2.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
28
|
+
"@tanstack/devtools-vite": "latest",
|
|
29
|
+
"@tanstack/react-devtools": "latest",
|
|
30
|
+
"@tanstack/react-router-devtools": "latest",
|
|
31
|
+
"@types/react": "^19.0.0",
|
|
32
|
+
"@types/react-dom": "^19.0.0",
|
|
33
|
+
"@vitejs/plugin-react": "^4.4.1",
|
|
34
|
+
"bun-types": "latest",
|
|
35
|
+
"drizzle-kit": "1.0.0-beta.6-4414a19",
|
|
36
|
+
"nitro": "latest",
|
|
37
|
+
"tailwindcss": "^4.0.0",
|
|
38
|
+
"tw-animate-css": "^1.0.0",
|
|
39
|
+
"typescript": "^5.9.2",
|
|
40
|
+
"vite": "^6.3.0",
|
|
41
|
+
"vite-tsconfig-paths": "^4.3.2"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createAdminAuthClient } from "@questpie/admin/client";
|
|
2
|
+
import type { AppCMS } from "@/questpie/server/app.js";
|
|
3
|
+
|
|
4
|
+
export const authClient = createAdminAuthClient<AppCMS>({
|
|
5
|
+
baseURL:
|
|
6
|
+
typeof window !== "undefined"
|
|
7
|
+
? window.location.origin
|
|
8
|
+
: process.env.APP_URL || "http://localhost:3000",
|
|
9
|
+
basePath: "/api/cms/auth",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type AuthClient = typeof authClient;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createClient } from "questpie/client";
|
|
2
|
+
import type { AppCMS, AppRpc } from "@/questpie/server/app.js";
|
|
3
|
+
|
|
4
|
+
export const client = createClient<AppCMS, AppRpc>({
|
|
5
|
+
baseURL:
|
|
6
|
+
typeof window !== "undefined"
|
|
7
|
+
? window.location.origin
|
|
8
|
+
: process.env.APP_URL || "http://localhost:3000",
|
|
9
|
+
basePath: "/api/cms",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type CMSClient = typeof client;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createEnv } from "@t3-oss/env-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export const env = createEnv({
|
|
5
|
+
server: {
|
|
6
|
+
DATABASE_URL: z.string().url(),
|
|
7
|
+
APP_URL: z.string().url().default("http://localhost:3000"),
|
|
8
|
+
PORT: z
|
|
9
|
+
.string()
|
|
10
|
+
.transform(Number)
|
|
11
|
+
.pipe(z.number().int().positive())
|
|
12
|
+
.default("3000"),
|
|
13
|
+
BETTER_AUTH_SECRET: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1)
|
|
16
|
+
.default("change-me-in-production"),
|
|
17
|
+
MAIL_ADAPTER: z.enum(["console", "smtp"]).default("console"),
|
|
18
|
+
SMTP_HOST: z.string().optional(),
|
|
19
|
+
SMTP_PORT: z
|
|
20
|
+
.string()
|
|
21
|
+
.transform(Number)
|
|
22
|
+
.pipe(z.number().int().positive())
|
|
23
|
+
.optional(),
|
|
24
|
+
},
|
|
25
|
+
runtimeEnv: process.env,
|
|
26
|
+
emptyStringAsUndefined: true,
|
|
27
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { adminModule, adminRpc } from "@questpie/admin/server";
|
|
2
|
+
import { ConsoleAdapter, q } from "questpie";
|
|
3
|
+
import { env } from "@/lib/env.js";
|
|
4
|
+
import { migrations } from "../../migrations/index.js";
|
|
5
|
+
import { posts } from "./collections/index.js";
|
|
6
|
+
import { configureDashboard } from "./dashboard.js";
|
|
7
|
+
import { siteSettings } from "./globals/index.js";
|
|
8
|
+
import { r } from "./rpc.js";
|
|
9
|
+
import { configureSidebar } from "./sidebar.js";
|
|
10
|
+
|
|
11
|
+
// ─── CMS Instance ───────────────────────────────────────────────────────────
|
|
12
|
+
// The built CMS application. Standalone — does NOT depend on appRpc.
|
|
13
|
+
|
|
14
|
+
export const cms = q({ name: "{{projectName}}" })
|
|
15
|
+
.use(adminModule)
|
|
16
|
+
.collections({ posts })
|
|
17
|
+
.globals({ siteSettings })
|
|
18
|
+
.sidebar(configureSidebar)
|
|
19
|
+
.branding({ name: "{{projectName}}" })
|
|
20
|
+
.dashboard(configureDashboard)
|
|
21
|
+
.auth({
|
|
22
|
+
emailAndPassword: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
requireEmailVerification: false,
|
|
25
|
+
},
|
|
26
|
+
baseURL: env.APP_URL,
|
|
27
|
+
basePath: "/api/cms/auth",
|
|
28
|
+
secret: env.BETTER_AUTH_SECRET,
|
|
29
|
+
})
|
|
30
|
+
.build({
|
|
31
|
+
app: { url: env.APP_URL },
|
|
32
|
+
db: { url: env.DATABASE_URL },
|
|
33
|
+
storage: { basePath: "/api/cms" },
|
|
34
|
+
migrations,
|
|
35
|
+
email: {
|
|
36
|
+
adapter: new ConsoleAdapter({ logHtml: false }),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ─── RPC Router ─────────────────────────────────────────────────────────────
|
|
41
|
+
// Standalone router. Both cms and appRpc are passed to createFetchHandler()
|
|
42
|
+
// in routes/api/cms/$.ts. Add your custom RPC functions here.
|
|
43
|
+
|
|
44
|
+
export const appRpc = r.router({
|
|
45
|
+
...adminRpc,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// ─── Type Exports ───────────────────────────────────────────────────────────
|
|
49
|
+
// Used by: rpc.ts (AppCMS for typed handlers), client (AppCMS + AppRpc)
|
|
50
|
+
|
|
51
|
+
export type AppCMS = typeof cms;
|
|
52
|
+
export type AppRpc = typeof appRpc;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { posts } from "./posts.collection.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { qb } from "@/questpie/server/builder.js";
|
|
2
|
+
|
|
3
|
+
export const posts = qb
|
|
4
|
+
.collection("posts")
|
|
5
|
+
.fields((f) => ({
|
|
6
|
+
title: f.text({
|
|
7
|
+
label: "Title",
|
|
8
|
+
required: true,
|
|
9
|
+
maxLength: 255,
|
|
10
|
+
}),
|
|
11
|
+
slug: f.text({
|
|
12
|
+
label: "Slug",
|
|
13
|
+
required: true,
|
|
14
|
+
maxLength: 255,
|
|
15
|
+
input: "optional",
|
|
16
|
+
meta: {
|
|
17
|
+
admin: {
|
|
18
|
+
compute: {
|
|
19
|
+
handler: ({
|
|
20
|
+
data,
|
|
21
|
+
prev,
|
|
22
|
+
}: {
|
|
23
|
+
data: Record<string, unknown>;
|
|
24
|
+
prev: { data: Record<string, unknown> };
|
|
25
|
+
}) => {
|
|
26
|
+
const title = data.title;
|
|
27
|
+
const currentSlug = data.slug;
|
|
28
|
+
const prevTitle = prev.data.title;
|
|
29
|
+
|
|
30
|
+
if (currentSlug && prevTitle === title) return undefined;
|
|
31
|
+
|
|
32
|
+
if (title && typeof title === "string") {
|
|
33
|
+
return title
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
36
|
+
.replace(/^-|-$/g, "");
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
},
|
|
40
|
+
deps: ({ data }: { data: Record<string, unknown> }) => [
|
|
41
|
+
data.title,
|
|
42
|
+
data.slug,
|
|
43
|
+
],
|
|
44
|
+
debounce: 300,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
content: f.richText({
|
|
50
|
+
label: "Content",
|
|
51
|
+
}),
|
|
52
|
+
published: f.boolean({
|
|
53
|
+
label: "Published",
|
|
54
|
+
default: false,
|
|
55
|
+
required: true,
|
|
56
|
+
}),
|
|
57
|
+
}))
|
|
58
|
+
.title(({ f }) => f.title)
|
|
59
|
+
.admin(({ c }) => ({
|
|
60
|
+
label: "Posts",
|
|
61
|
+
icon: c.icon("ph:article"),
|
|
62
|
+
}))
|
|
63
|
+
.list(({ v }) => v.table({}))
|
|
64
|
+
.form(({ v, f }) =>
|
|
65
|
+
v.form({
|
|
66
|
+
sidebar: {
|
|
67
|
+
position: "right",
|
|
68
|
+
fields: [f.slug, f.published],
|
|
69
|
+
},
|
|
70
|
+
fields: [f.title, f.content],
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function configureDashboard({ d }: any) {
|
|
2
|
+
return d.dashboard({
|
|
3
|
+
title: "Dashboard",
|
|
4
|
+
description: "Overview of your content",
|
|
5
|
+
columns: 4,
|
|
6
|
+
items: [
|
|
7
|
+
{
|
|
8
|
+
type: "section",
|
|
9
|
+
label: "Content",
|
|
10
|
+
layout: "grid",
|
|
11
|
+
columns: 2,
|
|
12
|
+
items: [
|
|
13
|
+
{
|
|
14
|
+
id: "total-posts",
|
|
15
|
+
type: "stats",
|
|
16
|
+
collection: "posts",
|
|
17
|
+
label: "Total Posts",
|
|
18
|
+
span: 1,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "published-posts",
|
|
22
|
+
type: "stats",
|
|
23
|
+
collection: "posts",
|
|
24
|
+
label: "Published",
|
|
25
|
+
filter: { published: true },
|
|
26
|
+
variant: "primary",
|
|
27
|
+
span: 1,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: "section",
|
|
33
|
+
label: "Recent",
|
|
34
|
+
layout: "grid",
|
|
35
|
+
columns: 4,
|
|
36
|
+
items: [
|
|
37
|
+
{
|
|
38
|
+
id: "recent-posts",
|
|
39
|
+
type: "recentItems",
|
|
40
|
+
collection: "posts",
|
|
41
|
+
label: "Recent Posts",
|
|
42
|
+
limit: 5,
|
|
43
|
+
span: 2,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "quick-actions",
|
|
47
|
+
type: "quickActions",
|
|
48
|
+
label: "Quick Actions",
|
|
49
|
+
actions: [
|
|
50
|
+
{
|
|
51
|
+
label: "New Post",
|
|
52
|
+
action: { type: "create", collection: "posts" },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "Site Settings",
|
|
56
|
+
action: {
|
|
57
|
+
type: "link",
|
|
58
|
+
href: "/admin/globals/siteSettings",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
span: 2,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { siteSettings } from "./site-settings.global.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { qb } from "@/questpie/server/builder.js";
|
|
2
|
+
|
|
3
|
+
export const siteSettings = qb
|
|
4
|
+
.global("site_settings")
|
|
5
|
+
.fields((f) => ({
|
|
6
|
+
siteName: f.text({
|
|
7
|
+
label: "Site Name",
|
|
8
|
+
required: true,
|
|
9
|
+
default: "{{projectName}}",
|
|
10
|
+
}),
|
|
11
|
+
description: f.textarea({
|
|
12
|
+
label: "Site Description",
|
|
13
|
+
default: "A QUESTPIE CMS powered site",
|
|
14
|
+
}),
|
|
15
|
+
}))
|
|
16
|
+
.admin(({ c }) => ({
|
|
17
|
+
label: "Site Settings",
|
|
18
|
+
icon: c.icon("ph:gear"),
|
|
19
|
+
}))
|
|
20
|
+
.form(({ v, f }) =>
|
|
21
|
+
v.form({
|
|
22
|
+
fields: [f.siteName, f.description],
|
|
23
|
+
}),
|
|
24
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { qb } from "./builder.js";
|
|
2
|
+
|
|
3
|
+
type SidebarBuilder = Parameters<
|
|
4
|
+
Parameters<ReturnType<typeof qb.collection>["sidebar"]>[0]
|
|
5
|
+
>[0];
|
|
6
|
+
|
|
7
|
+
export function configureSidebar({ s, c }: any) {
|
|
8
|
+
return s.sidebar({
|
|
9
|
+
sections: [
|
|
10
|
+
s.section({
|
|
11
|
+
id: "main",
|
|
12
|
+
title: "Content",
|
|
13
|
+
items: [
|
|
14
|
+
{
|
|
15
|
+
type: "link",
|
|
16
|
+
label: "Dashboard",
|
|
17
|
+
href: "/admin",
|
|
18
|
+
icon: c.icon("ph:house"),
|
|
19
|
+
},
|
|
20
|
+
{ type: "collection", collection: "posts" },
|
|
21
|
+
{ type: "global", global: "siteSettings" },
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRootRoute } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
export const Route = createRootRoute({
|
|
4
|
+
head: () => ({
|
|
5
|
+
meta: [
|
|
6
|
+
{ charSet: "utf-8" },
|
|
7
|
+
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
|
8
|
+
{ title: "{{projectName}}" },
|
|
9
|
+
],
|
|
10
|
+
}),
|
|
11
|
+
shellComponent: RootDocument,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function RootDocument({ children }: { children: React.ReactNode }) {
|
|
15
|
+
return <>{children}</>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AdminRouter } from "@questpie/admin/client";
|
|
2
|
+
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
|
3
|
+
|
|
4
|
+
function AdminCatchAll() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
const params = Route.useParams();
|
|
7
|
+
const splat = params._splat as string;
|
|
8
|
+
const segments = splat ? splat.split("/").filter(Boolean) : [];
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<AdminRouter
|
|
12
|
+
segments={segments}
|
|
13
|
+
navigate={(path) => navigate({ to: path })}
|
|
14
|
+
basePath="/admin"
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const Route = createFileRoute("/admin/$")({
|
|
20
|
+
component: AdminCatchAll,
|
|
21
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AdminRouter } from "@questpie/admin/client";
|
|
2
|
+
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
|
3
|
+
|
|
4
|
+
function AdminDashboard() {
|
|
5
|
+
const navigate = useNavigate();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<AdminRouter
|
|
9
|
+
segments={[]}
|
|
10
|
+
navigate={(path) => navigate({ to: path })}
|
|
11
|
+
basePath="/admin"
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const Route = createFileRoute("/admin/")({
|
|
17
|
+
component: AdminDashboard,
|
|
18
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { LoginPage } from "@questpie/admin/client";
|
|
2
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute("/admin/login")({
|
|
5
|
+
component: AdminLoginPage,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
function AdminLoginPage() {
|
|
9
|
+
return (
|
|
10
|
+
<LoginPage
|
|
11
|
+
title="Welcome back"
|
|
12
|
+
description="Sign in to access admin panel"
|
|
13
|
+
showForgotPassword={false}
|
|
14
|
+
showSignUp={false}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|