create-questpie 2.0.1 → 2.0.3
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 +10 -6
- package/dist/index.mjs +139 -24
- package/package.json +5 -3
- package/skills/questpie/AGENTS.md +2670 -0
- package/skills/questpie/SKILL.md +260 -0
- package/skills/questpie/references/auth.md +121 -0
- package/skills/questpie/references/business-logic.md +550 -0
- package/skills/questpie/references/codegen-plugin-api.md +382 -0
- package/skills/questpie/references/crud-api.md +378 -0
- package/skills/questpie/references/data-modeling.md +493 -0
- package/skills/questpie/references/extend.md +557 -0
- package/skills/questpie/references/field-types.md +386 -0
- package/skills/questpie/references/infrastructure-adapters.md +545 -0
- package/skills/questpie/references/multi-tenancy.md +364 -0
- package/skills/questpie/references/production.md +475 -0
- package/skills/questpie/references/query-operators.md +125 -0
- package/skills/questpie/references/quickstart.md +564 -0
- package/skills/questpie/references/rules.md +389 -0
- package/skills/questpie/references/tanstack-query.md +520 -0
- package/skills/questpie-admin/AGENTS.md +1508 -0
- package/skills/questpie-admin/SKILL.md +436 -0
- package/skills/questpie-admin/references/blocks.md +331 -0
- package/skills/questpie-admin/references/custom-ui.md +305 -0
- package/skills/questpie-admin/references/views.md +449 -0
- package/templates/tanstack-start/AGENTS.md +17 -13
- package/templates/tanstack-start/CLAUDE.md +15 -12
- package/templates/tanstack-start/README.md +19 -13
- package/templates/tanstack-start/env.example +1 -1
- package/templates/tanstack-start/package.json +20 -6
- package/templates/tanstack-start/src/lib/env.ts +1 -1
- package/templates/tanstack-start/src/lib/query-client.ts +10 -1
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
- package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
- package/templates/tanstack-start/src/routes/__root.tsx +0 -2
- package/templates/tanstack-start/src/routes/admin/$.tsx +12 -1
- package/templates/tanstack-start/src/routes/admin/index.tsx +12 -5
- package/templates/tanstack-start/src/routes/admin.tsx +8 -1
- package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
- package/templates/tanstack-start/src/vite-env.d.ts +1 -0
- package/templates/tanstack-start/vite.config.ts +1 -3
|
@@ -11,9 +11,20 @@
|
|
|
11
11
|
"dev": "bun --bun vite dev --port 3000",
|
|
12
12
|
"build": "vite build",
|
|
13
13
|
"start": "bun run .output/server/index.mjs",
|
|
14
|
-
"check-types": "tsc --noEmit"
|
|
14
|
+
"check-types": "tsc --noEmit",
|
|
15
|
+
"routes:generate": "tsr generate",
|
|
16
|
+
"questpie:generate": "questpie generate -c src/questpie/server/questpie.config.ts",
|
|
17
|
+
"scaffold:generate": "bun run routes:generate && bun run questpie:generate",
|
|
18
|
+
"migrate": "questpie migrate -c questpie.config.ts",
|
|
19
|
+
"migrate:create": "questpie migrate:create -c questpie.config.ts",
|
|
20
|
+
"migrate:status": "questpie migrate:status -c questpie.config.ts",
|
|
21
|
+
"migrate:down": "questpie migrate:down -c questpie.config.ts",
|
|
22
|
+
"migrate:reset": "questpie migrate:reset -c questpie.config.ts",
|
|
23
|
+
"migrate:fresh": "questpie migrate:fresh -c questpie.config.ts",
|
|
24
|
+
"scaffold:verify": "bun run scaffold:generate && bun run check-types"
|
|
15
25
|
},
|
|
16
26
|
"dependencies": {
|
|
27
|
+
"@electric-sql/pglite": "^0.3.14",
|
|
17
28
|
"@questpie/admin": "latest",
|
|
18
29
|
"@questpie/openapi": "latest",
|
|
19
30
|
"@questpie/tanstack-query": "latest",
|
|
@@ -22,28 +33,31 @@
|
|
|
22
33
|
"@tanstack/react-router": "^1.132.0",
|
|
23
34
|
"@tanstack/react-start": "^1.132.0",
|
|
24
35
|
"drizzle-orm": "1.0.0-beta.6-4414a19",
|
|
36
|
+
"nodemailer": "^7.0.12",
|
|
37
|
+
"pg": "^8.13.1",
|
|
38
|
+
"pg-boss": "^12.5.4",
|
|
25
39
|
"questpie": "latest",
|
|
26
40
|
"react": "^19.0.0",
|
|
27
41
|
"react-dom": "^19.0.0",
|
|
28
42
|
"zod": "^4.2.1"
|
|
29
43
|
},
|
|
30
44
|
"devDependencies": {
|
|
31
|
-
"@iconify/json": ">=2",
|
|
32
|
-
"@questpie/vite-plugin-iconify": "latest",
|
|
33
45
|
"@tailwindcss/vite": "^4.0.0",
|
|
34
46
|
"@tanstack/devtools-vite": "latest",
|
|
35
47
|
"@tanstack/react-devtools": "latest",
|
|
36
48
|
"@tanstack/react-router-devtools": "latest",
|
|
49
|
+
"@tanstack/router-cli": "^1.132.0",
|
|
37
50
|
"@types/react": "^19.0.0",
|
|
38
51
|
"@types/react-dom": "^19.0.0",
|
|
39
|
-
"@vitejs/plugin-react": "^
|
|
52
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
40
53
|
"bun-types": "latest",
|
|
41
54
|
"drizzle-kit": "1.0.0-beta.6-4414a19",
|
|
42
55
|
"nitro": "latest",
|
|
56
|
+
"shadcn": "^3.6.1",
|
|
43
57
|
"tailwindcss": "^4.0.0",
|
|
44
58
|
"tw-animate-css": "^1.0.0",
|
|
45
59
|
"typescript": "^5.9.2",
|
|
46
|
-
"vite": "^
|
|
47
|
-
"vite-tsconfig-paths": "^
|
|
60
|
+
"vite": "^8.0.0",
|
|
61
|
+
"vite-tsconfig-paths": "^6.1.1"
|
|
48
62
|
}
|
|
49
63
|
}
|
|
@@ -9,7 +9,7 @@ export const env = createEnv({
|
|
|
9
9
|
.string()
|
|
10
10
|
.transform(Number)
|
|
11
11
|
.pipe(z.number().int().positive())
|
|
12
|
-
.default(
|
|
12
|
+
.default(3000),
|
|
13
13
|
BETTER_AUTH_SECRET: z.string().min(1).default("change-me-in-production"),
|
|
14
14
|
MAIL_ADAPTER: z.enum(["console", "smtp"]).default("console"),
|
|
15
15
|
SMTP_HOST: z.string().optional(),
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
2
|
|
|
3
|
+
const ONE_MINUTE = 60 * 1000;
|
|
4
|
+
const FIVE_MINUTES = 5 * ONE_MINUTE;
|
|
5
|
+
|
|
3
6
|
export const queryClient = new QueryClient({
|
|
4
7
|
defaultOptions: {
|
|
5
8
|
queries: {
|
|
6
|
-
staleTime:
|
|
9
|
+
staleTime: ONE_MINUTE,
|
|
10
|
+
gcTime: FIVE_MINUTES,
|
|
11
|
+
refetchOnWindowFocus: false,
|
|
12
|
+
retry: 1,
|
|
13
|
+
},
|
|
14
|
+
mutations: {
|
|
15
|
+
retry: 0,
|
|
7
16
|
},
|
|
8
17
|
},
|
|
9
18
|
});
|
|
@@ -9,23 +9,40 @@ export default adminConfig({
|
|
|
9
9
|
{
|
|
10
10
|
id: "main",
|
|
11
11
|
title: "Content",
|
|
12
|
-
items: [
|
|
13
|
-
{
|
|
14
|
-
type: "link",
|
|
15
|
-
label: "Dashboard",
|
|
16
|
-
href: "/admin",
|
|
17
|
-
icon: { type: "icon", props: { name: "ph:house" } },
|
|
18
|
-
},
|
|
19
|
-
{ type: "collection", collection: "posts" },
|
|
20
|
-
{ type: "global", global: "siteSettings" },
|
|
21
|
-
],
|
|
22
12
|
},
|
|
23
13
|
],
|
|
14
|
+
items: [
|
|
15
|
+
{
|
|
16
|
+
sectionId: "main",
|
|
17
|
+
type: "link",
|
|
18
|
+
label: "Dashboard",
|
|
19
|
+
href: "/admin",
|
|
20
|
+
icon: { type: "icon", props: { name: "ph:house" } },
|
|
21
|
+
},
|
|
22
|
+
{ sectionId: "main", type: "collection", collection: "posts" },
|
|
23
|
+
{ sectionId: "main", type: "global", global: "siteSettings" },
|
|
24
|
+
],
|
|
24
25
|
},
|
|
25
26
|
dashboard: {
|
|
26
27
|
title: "Dashboard",
|
|
27
28
|
description: "Overview of your content",
|
|
28
29
|
columns: 4,
|
|
30
|
+
actions: [
|
|
31
|
+
{
|
|
32
|
+
id: "new-post",
|
|
33
|
+
label: "New Post",
|
|
34
|
+
href: "/admin/collections/posts/create",
|
|
35
|
+
icon: { type: "icon", props: { name: "ph:article" } },
|
|
36
|
+
variant: "primary",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "site-settings",
|
|
40
|
+
label: "Site Settings",
|
|
41
|
+
href: "/admin/globals/siteSettings",
|
|
42
|
+
icon: { type: "icon", props: { name: "ph:gear" } },
|
|
43
|
+
variant: "outline",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
29
46
|
sections: [
|
|
30
47
|
{ id: "content", label: "Content", layout: "grid", columns: 2 },
|
|
31
48
|
{ id: "recent", label: "Recent", layout: "grid", columns: 4 },
|
|
@@ -58,26 +75,6 @@ export default adminConfig({
|
|
|
58
75
|
limit: 5,
|
|
59
76
|
span: 2,
|
|
60
77
|
},
|
|
61
|
-
{
|
|
62
|
-
sectionId: "recent",
|
|
63
|
-
id: "quick-actions",
|
|
64
|
-
type: "quickActions",
|
|
65
|
-
label: "Quick Actions",
|
|
66
|
-
actions: [
|
|
67
|
-
{
|
|
68
|
-
label: "New Post",
|
|
69
|
-
action: { type: "create", collection: "posts" },
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
label: "Site Settings",
|
|
73
|
-
action: {
|
|
74
|
-
type: "link",
|
|
75
|
-
href: "/admin/globals/siteSettings",
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
span: 2,
|
|
80
|
-
},
|
|
81
78
|
],
|
|
82
79
|
},
|
|
83
80
|
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as AdminRouteImport } from './routes/admin'
|
|
13
|
+
import { Route as AdminIndexRouteImport } from './routes/admin/index'
|
|
14
|
+
import { Route as ApiSplatRouteImport } from './routes/api/$'
|
|
15
|
+
import { Route as AdminLoginRouteImport } from './routes/admin/login'
|
|
16
|
+
import { Route as AdminSplatRouteImport } from './routes/admin/$'
|
|
17
|
+
|
|
18
|
+
const AdminRoute = AdminRouteImport.update({
|
|
19
|
+
id: '/admin',
|
|
20
|
+
path: '/admin',
|
|
21
|
+
getParentRoute: () => rootRouteImport,
|
|
22
|
+
} as any)
|
|
23
|
+
const AdminIndexRoute = AdminIndexRouteImport.update({
|
|
24
|
+
id: '/',
|
|
25
|
+
path: '/',
|
|
26
|
+
getParentRoute: () => AdminRoute,
|
|
27
|
+
} as any)
|
|
28
|
+
const ApiSplatRoute = ApiSplatRouteImport.update({
|
|
29
|
+
id: '/api/$',
|
|
30
|
+
path: '/api/$',
|
|
31
|
+
getParentRoute: () => rootRouteImport,
|
|
32
|
+
} as any)
|
|
33
|
+
const AdminLoginRoute = AdminLoginRouteImport.update({
|
|
34
|
+
id: '/login',
|
|
35
|
+
path: '/login',
|
|
36
|
+
getParentRoute: () => AdminRoute,
|
|
37
|
+
} as any)
|
|
38
|
+
const AdminSplatRoute = AdminSplatRouteImport.update({
|
|
39
|
+
id: '/$',
|
|
40
|
+
path: '/$',
|
|
41
|
+
getParentRoute: () => AdminRoute,
|
|
42
|
+
} as any)
|
|
43
|
+
|
|
44
|
+
export interface FileRoutesByFullPath {
|
|
45
|
+
'/admin': typeof AdminRouteWithChildren
|
|
46
|
+
'/admin/$': typeof AdminSplatRoute
|
|
47
|
+
'/admin/login': typeof AdminLoginRoute
|
|
48
|
+
'/api/$': typeof ApiSplatRoute
|
|
49
|
+
'/admin/': typeof AdminIndexRoute
|
|
50
|
+
}
|
|
51
|
+
export interface FileRoutesByTo {
|
|
52
|
+
'/admin/$': typeof AdminSplatRoute
|
|
53
|
+
'/admin/login': typeof AdminLoginRoute
|
|
54
|
+
'/api/$': typeof ApiSplatRoute
|
|
55
|
+
'/admin': typeof AdminIndexRoute
|
|
56
|
+
}
|
|
57
|
+
export interface FileRoutesById {
|
|
58
|
+
__root__: typeof rootRouteImport
|
|
59
|
+
'/admin': typeof AdminRouteWithChildren
|
|
60
|
+
'/admin/$': typeof AdminSplatRoute
|
|
61
|
+
'/admin/login': typeof AdminLoginRoute
|
|
62
|
+
'/api/$': typeof ApiSplatRoute
|
|
63
|
+
'/admin/': typeof AdminIndexRoute
|
|
64
|
+
}
|
|
65
|
+
export interface FileRouteTypes {
|
|
66
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
67
|
+
fullPaths: '/admin' | '/admin/$' | '/admin/login' | '/api/$' | '/admin/'
|
|
68
|
+
fileRoutesByTo: FileRoutesByTo
|
|
69
|
+
to: '/admin/$' | '/admin/login' | '/api/$' | '/admin'
|
|
70
|
+
id: '__root__' | '/admin' | '/admin/$' | '/admin/login' | '/api/$' | '/admin/'
|
|
71
|
+
fileRoutesById: FileRoutesById
|
|
72
|
+
}
|
|
73
|
+
export interface RootRouteChildren {
|
|
74
|
+
AdminRoute: typeof AdminRouteWithChildren
|
|
75
|
+
ApiSplatRoute: typeof ApiSplatRoute
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare module '@tanstack/react-router' {
|
|
79
|
+
interface FileRoutesByPath {
|
|
80
|
+
'/admin': {
|
|
81
|
+
id: '/admin'
|
|
82
|
+
path: '/admin'
|
|
83
|
+
fullPath: '/admin'
|
|
84
|
+
preLoaderRoute: typeof AdminRouteImport
|
|
85
|
+
parentRoute: typeof rootRouteImport
|
|
86
|
+
}
|
|
87
|
+
'/admin/': {
|
|
88
|
+
id: '/admin/'
|
|
89
|
+
path: '/'
|
|
90
|
+
fullPath: '/admin/'
|
|
91
|
+
preLoaderRoute: typeof AdminIndexRouteImport
|
|
92
|
+
parentRoute: typeof AdminRoute
|
|
93
|
+
}
|
|
94
|
+
'/api/$': {
|
|
95
|
+
id: '/api/$'
|
|
96
|
+
path: '/api/$'
|
|
97
|
+
fullPath: '/api/$'
|
|
98
|
+
preLoaderRoute: typeof ApiSplatRouteImport
|
|
99
|
+
parentRoute: typeof rootRouteImport
|
|
100
|
+
}
|
|
101
|
+
'/admin/login': {
|
|
102
|
+
id: '/admin/login'
|
|
103
|
+
path: '/login'
|
|
104
|
+
fullPath: '/admin/login'
|
|
105
|
+
preLoaderRoute: typeof AdminLoginRouteImport
|
|
106
|
+
parentRoute: typeof AdminRoute
|
|
107
|
+
}
|
|
108
|
+
'/admin/$': {
|
|
109
|
+
id: '/admin/$'
|
|
110
|
+
path: '/$'
|
|
111
|
+
fullPath: '/admin/$'
|
|
112
|
+
preLoaderRoute: typeof AdminSplatRouteImport
|
|
113
|
+
parentRoute: typeof AdminRoute
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface AdminRouteChildren {
|
|
119
|
+
AdminSplatRoute: typeof AdminSplatRoute
|
|
120
|
+
AdminLoginRoute: typeof AdminLoginRoute
|
|
121
|
+
AdminIndexRoute: typeof AdminIndexRoute
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const AdminRouteChildren: AdminRouteChildren = {
|
|
125
|
+
AdminSplatRoute: AdminSplatRoute,
|
|
126
|
+
AdminLoginRoute: AdminLoginRoute,
|
|
127
|
+
AdminIndexRoute: AdminIndexRoute,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const AdminRouteWithChildren = AdminRoute._addFileChildren(AdminRouteChildren)
|
|
131
|
+
|
|
132
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
133
|
+
AdminRoute: AdminRouteWithChildren,
|
|
134
|
+
ApiSplatRoute: ApiSplatRoute,
|
|
135
|
+
}
|
|
136
|
+
export const routeTree = rootRouteImport
|
|
137
|
+
._addFileChildren(rootRouteChildren)
|
|
138
|
+
._addFileTypes<FileRouteTypes>()
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
import { useMemo } from "react";
|
|
2
3
|
|
|
3
4
|
import { AdminRouter } from "@questpie/admin/client";
|
|
4
5
|
|
|
6
|
+
function createAdminNavigate(navigate: ReturnType<typeof useNavigate>) {
|
|
7
|
+
return (path: string) => {
|
|
8
|
+
void navigate({ to: path });
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
function AdminCatchAll() {
|
|
6
13
|
const navigate = useNavigate();
|
|
7
14
|
const params = Route.useParams();
|
|
8
15
|
const splat = params._splat as string;
|
|
16
|
+
const handleNavigate = useMemo(
|
|
17
|
+
() => createAdminNavigate(navigate),
|
|
18
|
+
[navigate],
|
|
19
|
+
);
|
|
9
20
|
const segments = splat ? splat.split("/").filter(Boolean) : [];
|
|
10
21
|
|
|
11
22
|
return (
|
|
12
23
|
<AdminRouter
|
|
13
24
|
segments={segments}
|
|
14
|
-
navigate={
|
|
25
|
+
navigate={handleNavigate}
|
|
15
26
|
basePath="/admin"
|
|
16
27
|
/>
|
|
17
28
|
);
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
|
2
|
+
import { useMemo } from "react";
|
|
2
3
|
|
|
3
4
|
import { AdminRouter } from "@questpie/admin/client";
|
|
4
5
|
|
|
6
|
+
function createAdminNavigate(navigate: ReturnType<typeof useNavigate>) {
|
|
7
|
+
return (path: string) => {
|
|
8
|
+
void navigate({ to: path });
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
function AdminDashboard() {
|
|
6
13
|
const navigate = useNavigate();
|
|
14
|
+
const handleNavigate = useMemo(
|
|
15
|
+
() => createAdminNavigate(navigate),
|
|
16
|
+
[navigate],
|
|
17
|
+
);
|
|
7
18
|
|
|
8
19
|
return (
|
|
9
|
-
<AdminRouter
|
|
10
|
-
segments={[]}
|
|
11
|
-
navigate={(path) => navigate({ to: path })}
|
|
12
|
-
basePath="/admin"
|
|
13
|
-
/>
|
|
20
|
+
<AdminRouter segments={[]} navigate={handleNavigate} basePath="/admin" />
|
|
14
21
|
);
|
|
15
22
|
}
|
|
16
23
|
|
|
@@ -20,14 +20,21 @@ function AdminLink({
|
|
|
20
20
|
className,
|
|
21
21
|
children,
|
|
22
22
|
activeProps,
|
|
23
|
+
activeOptions,
|
|
23
24
|
}: {
|
|
24
25
|
to: string;
|
|
25
26
|
className?: string;
|
|
26
27
|
children: React.ReactNode;
|
|
27
28
|
activeProps?: { className?: string };
|
|
29
|
+
activeOptions?: { exact?: boolean };
|
|
28
30
|
}) {
|
|
29
31
|
return (
|
|
30
|
-
<Link
|
|
32
|
+
<Link
|
|
33
|
+
to={to}
|
|
34
|
+
className={className}
|
|
35
|
+
activeProps={activeProps}
|
|
36
|
+
activeOptions={activeOptions}
|
|
37
|
+
>
|
|
31
38
|
{children}
|
|
32
39
|
</Link>
|
|
33
40
|
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "@tanstack/react-start";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { iconifyPreload } from "@questpie/vite-plugin-iconify";
|
|
2
1
|
import tailwindcss from "@tailwindcss/vite";
|
|
3
2
|
import { devtools } from "@tanstack/devtools-vite";
|
|
4
3
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
@@ -9,7 +8,6 @@ import viteTsConfigPaths from "vite-tsconfig-paths";
|
|
|
9
8
|
|
|
10
9
|
export default defineConfig({
|
|
11
10
|
plugins: [
|
|
12
|
-
iconifyPreload(),
|
|
13
11
|
devtools(),
|
|
14
12
|
nitro({ preset: "bun" }) as any,
|
|
15
13
|
viteTsConfigPaths({ projects: ["./tsconfig.json"] }),
|
|
@@ -22,7 +20,7 @@ export default defineConfig({
|
|
|
22
20
|
},
|
|
23
21
|
build: {
|
|
24
22
|
rollupOptions: {
|
|
25
|
-
external: ["bun", /^drizzle-kit
|
|
23
|
+
external: ["bun", /^drizzle-kit/, /^@aws-sdk\//],
|
|
26
24
|
},
|
|
27
25
|
},
|
|
28
26
|
});
|