create-m5kdev 0.25.3 → 0.26.1

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.
Files changed (143) hide show
  1. package/dist/src/create.cjs +18 -1
  2. package/dist/src/create.cjs.map +1 -1
  3. package/dist/src/fs.cjs +65 -5
  4. package/dist/src/fs.cjs.map +1 -1
  5. package/dist/src/fs.d.cts +13 -2
  6. package/dist/src/index.cjs +0 -0
  7. package/dist/src/paths.cjs +2 -2
  8. package/dist/src/prompts.cjs +22 -0
  9. package/dist/src/prompts.cjs.map +1 -1
  10. package/dist/src/runCli.cjs +13 -2
  11. package/dist/src/runCli.cjs.map +1 -1
  12. package/dist/src/types.d.cts +9 -1
  13. package/package.json +8 -6
  14. package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +46 -3
  15. package/templates/minimal-app/.cursor/rules/module-db-guide.mdc +15 -1
  16. package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -0
  17. package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +98 -193
  18. package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -173
  19. package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +133 -240
  20. package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -113
  21. package/templates/minimal-app/README.md.tpl +3 -3
  22. package/templates/minimal-app/apps/.gitignore.tpl +13 -0
  23. package/templates/minimal-app/apps/e2e/package.json.tpl +23 -0
  24. package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +39 -0
  25. package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -0
  26. package/templates/minimal-app/apps/e2e/servers.ts.tpl +100 -0
  27. package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -0
  28. package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -0
  29. package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -0
  30. package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -0
  31. package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -0
  32. package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -0
  33. package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -0
  34. package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -0
  35. package/templates/minimal-app/apps/email/package.json.tpl +34 -34
  36. package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +35 -34
  37. package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +34 -33
  38. package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +35 -34
  39. package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +37 -36
  40. package/templates/minimal-app/apps/email/src/emails/waitlistEmails.tsx.tpl +87 -0
  41. package/templates/minimal-app/apps/email/src/index.ts.tpl +72 -32
  42. package/templates/minimal-app/apps/email/src/resources.ts.tpl +65 -36
  43. package/templates/minimal-app/apps/email/tsconfig.json.tpl +16 -11
  44. package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -0
  45. package/templates/minimal-app/apps/expo/.prettierignore +13 -0
  46. package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -0
  47. package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -0
  48. package/templates/minimal-app/apps/expo/README.md.tpl +44 -0
  49. package/templates/minimal-app/apps/expo/app.json.tpl +36 -0
  50. package/templates/minimal-app/apps/expo/assets/images/adaptive-icon.png +0 -0
  51. package/templates/minimal-app/apps/expo/assets/images/favicon.png +0 -0
  52. package/templates/minimal-app/apps/expo/assets/images/icon.png +0 -0
  53. package/templates/minimal-app/apps/expo/assets/images/splash-icon.png +0 -0
  54. package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -0
  55. package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
  56. package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -0
  57. package/templates/minimal-app/apps/expo/package.json.tpl +59 -0
  58. package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -0
  59. package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -0
  60. package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -0
  61. package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -0
  62. package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -0
  63. package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -0
  64. package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -0
  65. package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -0
  66. package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -0
  67. package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -0
  68. package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -0
  69. package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -0
  70. package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -0
  71. package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -0
  72. package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -0
  73. package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -0
  74. package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -0
  75. package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -0
  76. package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -0
  77. package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
  78. package/templates/minimal-app/apps/server/AGENTS.md.tpl +39 -39
  79. package/templates/minimal-app/apps/server/drizzle/0000_wealthy_absorbing_man.sql +268 -0
  80. package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +26 -0
  81. package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +1890 -0
  82. package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +13 -0
  83. package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +33 -0
  84. package/templates/minimal-app/apps/server/drizzle/seed.e2e.ts.tpl +244 -0
  85. package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +181 -180
  86. package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +14 -21
  87. package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +33 -35
  88. package/templates/minimal-app/apps/server/package.json.tpl +56 -52
  89. package/templates/minimal-app/apps/server/src/app.ts.tpl +142 -126
  90. package/templates/minimal-app/apps/server/src/index.ts.tpl +58 -58
  91. package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +12 -12
  92. package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +25 -25
  93. package/templates/minimal-app/apps/server/src/modules/posts/posts.dto.ts.tpl +23 -0
  94. package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +30 -40
  95. package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
  96. package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -112
  97. package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +104 -170
  98. package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -48
  99. package/templates/minimal-app/apps/server/src/modules/posts/posts.utils.ts.tpl +19 -0
  100. package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -0
  101. package/templates/minimal-app/apps/server/src/schema.ts.tpl +20 -0
  102. package/templates/minimal-app/apps/server/src/types.ts.tpl +1 -1
  103. package/templates/minimal-app/apps/server/tsconfig.json.tpl +20 -14
  104. package/templates/minimal-app/apps/server/tsup.config.ts.tpl +9 -9
  105. package/templates/minimal-app/apps/shared/package.json.tpl +24 -24
  106. package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -2
  107. package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
  108. package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
  109. package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -76
  110. package/templates/minimal-app/apps/shared/tsconfig.json.tpl +16 -12
  111. package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
  112. package/templates/minimal-app/apps/webapp/package.json.tpl +53 -52
  113. package/templates/minimal-app/apps/webapp/public/{push-sw.js → push-sw.js.tpl} +34 -34
  114. package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +44 -13
  115. package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +18 -12
  116. package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +33 -40
  117. package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -75
  118. package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
  119. package/templates/minimal-app/apps/webapp/src/modules/posts/PostsRoute.tsx.tpl +278 -307
  120. package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostCard.tsx.tpl +96 -138
  121. package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostEditorModal.tsx.tpl +118 -124
  122. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostActions.ts.tpl +108 -116
  123. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsList.ts.tpl +15 -9
  124. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsRoute.ts.tpl +13 -13
  125. package/templates/minimal-app/apps/webapp/src/modules/posts/posts.utils.ts.tpl +16 -16
  126. package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
  127. package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -6
  128. package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
  129. package/templates/minimal-app/apps/webapp/translations/{app.json.tpl → en/app.json.tpl} +7 -7
  130. package/templates/minimal-app/apps/webapp/translations/en/{blog-app.json.tpl → {{APP_SLUG}}.json.tpl} +135 -135
  131. package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
  132. package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
  133. package/templates/minimal-app/template.manifest.json +21 -0
  134. package/templates/minimal-app/apps/server/drizzle/generate-schema.ts.tpl +0 -31
  135. package/templates/minimal-app/apps/server/src/db.ts.tpl +0 -7
  136. package/templates/minimal-app/apps/server/src/generated/schema.ts.tpl +0 -13
  137. package/templates/minimal-app/apps/server/src/utils/trpc.ts.tpl +0 -8
  138. package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +0 -7
  139. package/templates/minimal-app/apps/shared/src/modules/app/roles.constants.ts.tpl +0 -17
  140. package/templates/minimal-app/apps/webapp/src/Providers.tsx.tpl +0 -36
  141. package/templates/minimal-app/apps/webapp/src/modules/notification/PushNotificationsPanel.tsx.tpl +0 -89
  142. package/templates/minimal-app/apps/webapp/src/navigation/navigation.config.tsx.tpl +0 -85
  143. /package/templates/minimal-app/apps/webapp/public/{logo.svg → logo.svg.tpl} +0 -0
@@ -0,0 +1,20 @@
1
+ export {
2
+ accountClaimMagicLinks,
3
+ accounts,
4
+ apikeys,
5
+ invitations,
6
+ members,
7
+ organizations,
8
+ sessions,
9
+ teamMembers,
10
+ teams,
11
+ users,
12
+ verifications,
13
+ waitlist,
14
+ } from "@m5kdev/backend/modules/auth/auth.db";
15
+ export {
16
+ notificationDevices,
17
+ notificationSendLogs,
18
+ } from "@m5kdev/backend/modules/notification/notification.db";
19
+ export { workflows } from "@m5kdev/backend/modules/workflow/workflow.db";
20
+ export { posts } from "./modules/posts/posts.db";
@@ -1 +1 @@
1
- export type { AppRouter } from "./app";
1
+ export type { AppRouter } from "./app";
@@ -1,14 +1,20 @@
1
- {
2
- "extends": "@m5kdev/config/tsconfig.node.json",
3
- "rootDir": ".",
4
- "compilerOptions": {
5
- "esModuleInterop": true,
6
- "ignoreDeprecations": "6.0",
7
- "importHelpers": true,
8
- "jsx": "react-jsx",
9
- "noEmit": true,
10
- "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
11
- },
12
- "exclude": ["node_modules"],
13
- "include": ["src/**/*.ts", "drizzle/**/*.ts", "drizzle.config.ts", "tsup.config.ts"]
14
- }
1
+ {
2
+ "extends": "@m5kdev/config/tsconfig.node.json",
3
+ "compilerOptions": {
4
+ "esModuleInterop": true,
5
+ "importHelpers": true,
6
+ "jsx": "react-jsx",
7
+ "noEmit": true,
8
+ "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
9
+ "rootDir": "."
10
+ },
11
+ "exclude": [
12
+ "node_modules"
13
+ ],
14
+ "include": [
15
+ "src/**/*.ts",
16
+ "drizzle/**/*.ts",
17
+ "drizzle.config.ts",
18
+ "tsup.config.ts"
19
+ ]
20
+ }
@@ -1,9 +1,9 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts"],
5
- format: ["cjs"],
6
- outDir: "dist",
7
- sourcemap: true,
8
- clean: true,
9
- });
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs"],
6
+ outDir: "dist",
7
+ sourcemap: true,
8
+ clean: true,
9
+ });
@@ -1,24 +1,24 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/shared",
3
- "version": "0.0.0",
4
- "private": true,
5
- "scripts": {
6
- "build": "tsc --noEmit",
7
- "lint": "biome check .",
8
- "lint:fix": "biome check . --write",
9
- "check-types": "tsc --noEmit"
10
- },
11
- "dependencies": {
12
- "@m5kdev/commons": "catalog:",
13
- "zod": "catalog:"
14
- },
15
- "devDependencies": {
16
- "@m5kdev/config": "catalog:",
17
- "tslib": "catalog:",
18
- "typescript": "catalog:"
19
- },
20
- "exports": {
21
- "./modules/app/*": "./src/modules/app/*.ts",
22
- "./modules/posts/*": "./src/modules/posts/*.ts"
23
- }
24
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/shared",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "tsc --noEmit",
7
+ "lint": "biome check .",
8
+ "lint:fix": "biome check . --write",
9
+ "check-types": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@m5kdev/commons": "catalog:",
13
+ "zod": "catalog:"
14
+ },
15
+ "devDependencies": {
16
+ "@m5kdev/config": "catalog:",
17
+ "tslib": "catalog:",
18
+ "typescript": "catalog:"
19
+ },
20
+ "exports": {
21
+ "./modules/app/*": "./src/modules/app/*.ts",
22
+ "./modules/posts/*": "./src/modules/posts/*.ts"
23
+ }
24
+ }
@@ -1,2 +1,28 @@
1
- export const APP_NAME = "{{APP_NAME}}";
2
- export const APP_SLUG = "{{APP_SLUG}}";
1
+ import { defineAuthRoles } from "@m5kdev/commons/modules/auth/auth.roles";
2
+
3
+ export const APP_NAME = "{{APP_NAME}}";
4
+ export const APP_SLUG = "{{APP_SLUG}}";
5
+
6
+ export const APP_LOCALE_CONFIG = {
7
+ defaultLocale: "en",
8
+ locales: [
9
+ { code: "en", displayName: "English" },
10
+ { code: "en_GB", displayName: "English (UK)" },
11
+ ],
12
+ } as const;
13
+
14
+ export const APP_ROLES_CONFIG = defineAuthRoles({
15
+ user: {
16
+ roles: ["user", "admin"],
17
+ },
18
+ organization: {
19
+ roles: ["member", "admin", "owner"],
20
+ managerRoles: ["admin", "owner"],
21
+ assignableRoles: ["member", "admin", "owner"],
22
+ defaultRole: "member",
23
+ },
24
+ team: {
25
+ roles: ["owner"],
26
+ defaultRole: "owner",
27
+ },
28
+ });
@@ -1,3 +1,3 @@
1
- /** Re-export shared push-notification contracts from commons. */
2
- export * from "@m5kdev/commons/modules/notification/notification.constants";
3
- export * from "@m5kdev/commons/modules/notification/notification.schema";
1
+ /** Re-export shared push-notification contracts from commons. */
2
+ export * from "@m5kdev/commons/modules/notification/notification.constants";
3
+ export * from "@m5kdev/commons/modules/notification/notification.schema";
@@ -1,5 +1,5 @@
1
- export const POST_STATUS_VALUES = ["draft", "published"] as const;
2
-
3
- export const POST_FILTER_VALUES = ["all", ...POST_STATUS_VALUES] as const;
4
-
5
- export const POSTS_PAGE_SIZE = 6;
1
+ export const POST_STATUS_VALUES = ["draft", "published"] as const;
2
+
3
+ export const POST_FILTER_VALUES = ["all", ...POST_STATUS_VALUES] as const;
4
+
5
+ export const POSTS_PAGE_SIZE = 6;
@@ -1,76 +1,25 @@
1
- import { querySchema } from "@m5kdev/commons/modules/schemas/query.schema";
2
- import { z } from "zod";
3
- import { POST_STATUS_VALUES } from "./posts.constants";
4
-
5
- export const postStatusSchema = z.enum(POST_STATUS_VALUES);
6
- export type PostStatusSchema = z.infer<typeof postStatusSchema>;
7
-
8
- export const postSchema = z.object({
9
- id: z.string(),
10
- authorUserId: z.string().nullish(),
11
- organizationId: z.string().nullish(),
12
- teamId: z.string().nullish(),
13
- title: z.string(),
14
- slug: z.string(),
15
- excerpt: z.string().nullish(),
16
- content: z.string(),
17
- status: postStatusSchema,
18
- publishedAt: z.date().nullish(),
19
- createdAt: z.date(),
20
- updatedAt: z.date().nullish(),
21
- deletedAt: z.date().nullish(),
22
- });
23
- export type PostSchema = z.infer<typeof postSchema>;
24
-
25
- export const postsListInputSchema = querySchema.extend({
26
- search: z.string().optional(),
27
- status: postStatusSchema.optional(),
28
- });
29
- export type PostsListInputSchema = z.infer<typeof postsListInputSchema>;
30
-
31
- export const postsListOutputSchema = z.object({
32
- rows: z.array(postSchema),
33
- total: z.number(),
34
- });
35
- export type PostsListOutputSchema = z.infer<typeof postsListOutputSchema>;
36
-
37
- export const postCreateInputSchema = z.object({
38
- title: z.string().min(1),
39
- slug: z.string().optional(),
40
- excerpt: z.string().optional(),
41
- content: z.string().min(1),
42
- });
43
- export type PostCreateInputSchema = z.infer<typeof postCreateInputSchema>;
44
-
45
- export const postCreateOutputSchema = postSchema;
46
- export type PostCreateOutputSchema = z.infer<typeof postCreateOutputSchema>;
47
-
48
- export const postUpdateInputSchema = z.object({
49
- id: z.string(),
50
- title: z.string().min(1),
51
- slug: z.string().optional(),
52
- excerpt: z.string().optional(),
53
- content: z.string().min(1),
54
- });
55
- export type PostUpdateInputSchema = z.infer<typeof postUpdateInputSchema>;
56
-
57
- export const postUpdateOutputSchema = postSchema;
58
- export type PostUpdateOutputSchema = z.infer<typeof postUpdateOutputSchema>;
59
-
60
- export const postPublishInputSchema = z.object({
61
- id: z.string(),
62
- });
63
- export type PostPublishInputSchema = z.infer<typeof postPublishInputSchema>;
64
-
65
- export const postPublishOutputSchema = postSchema;
66
- export type PostPublishOutputSchema = z.infer<typeof postPublishOutputSchema>;
67
-
68
- export const postSoftDeleteInputSchema = z.object({
69
- id: z.string(),
70
- });
71
- export type PostSoftDeleteInputSchema = z.infer<typeof postSoftDeleteInputSchema>;
72
-
73
- export const postSoftDeleteOutputSchema = z.object({
74
- id: z.string(),
75
- });
76
- export type PostSoftDeleteOutputSchema = z.infer<typeof postSoftDeleteOutputSchema>;
1
+ import { z } from "zod";
2
+ import { POST_STATUS_VALUES } from "./posts.constants";
3
+
4
+ /**
5
+ * Shared schemas have a deliberately narrow role: server row/list types are
6
+ * inferred from the tRPC router (AppRouter), and the API surface is defined by
7
+ * the server module's DTO. A schema lives here only when the frontend needs it
8
+ * at runtime (form validation) or when both sides genuinely share it — the
9
+ * DTO imports these create/update schemas so the contract cannot drift.
10
+ */
11
+ export const postStatusSchema = z.enum(POST_STATUS_VALUES);
12
+ export type PostStatusSchema = z.infer<typeof postStatusSchema>;
13
+
14
+ export const postCreateInputSchema = z.object({
15
+ title: z.string().min(1),
16
+ slug: z.string().optional(),
17
+ excerpt: z.string().optional(),
18
+ content: z.string().min(1),
19
+ });
20
+ export type PostCreateInputSchema = z.infer<typeof postCreateInputSchema>;
21
+
22
+ export const postUpdateInputSchema = postCreateInputSchema.extend({
23
+ id: z.string(),
24
+ });
25
+ export type PostUpdateInputSchema = z.infer<typeof postUpdateInputSchema>;
@@ -1,12 +1,16 @@
1
- {
2
- "extends": "@m5kdev/config/tsconfig.base.json",
3
- "rootDir": ".",
4
- "compilerOptions": {
5
- "esModuleInterop": true,
6
- "importHelpers": true,
7
- "noEmit": true,
8
- "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo"
9
- },
10
- "exclude": ["node_modules"],
11
- "include": ["src/**/*.ts"]
12
- }
1
+ {
2
+ "extends": "@m5kdev/config/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "esModuleInterop": true,
5
+ "importHelpers": true,
6
+ "noEmit": true,
7
+ "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
8
+ "rootDir": "."
9
+ },
10
+ "exclude": [
11
+ "node_modules"
12
+ ],
13
+ "include": [
14
+ "src/**/*.ts"
15
+ ]
16
+ }
@@ -1,22 +1,22 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta
6
- name="viewport"
7
- content="width=device-width, initial-scale=1.0"
8
- />
9
- <meta
10
- name="description"
11
- content="{{APP_DESCRIPTION}}"
12
- />
13
- <title>{{APP_NAME}}</title>
14
- </head>
15
- <body>
16
- <div id="root"></div>
17
- <script
18
- type="module"
19
- src="/src/main.tsx"
20
- ></script>
21
- </body>
22
- </html>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1.0"
8
+ />
9
+ <meta
10
+ name="description"
11
+ content="The m5kdev starter application."
12
+ />
13
+ <title>{{APP_NAME}}</title>
14
+ </head>
15
+ <body>
16
+ <div id="root"></div>
17
+ <script
18
+ type="module"
19
+ src="/src/main.tsx"
20
+ ></script>
21
+ </body>
22
+ </html>
@@ -1,52 +1,53 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/webapp",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "lint": "biome check .",
10
- "lint:fix": "biome check . --write",
11
- "check-types": "tsc --noEmit",
12
- "preview": "vite preview"
13
- },
14
- "dependencies": {
15
- "{{PACKAGE_SCOPE}}/shared": "workspace:*",
16
- "@heroui/react": "catalog:",
17
- "@heroui/styles": "catalog:",
18
- "@m5kdev/commons": "catalog:",
19
- "@m5kdev/frontend": "catalog:",
20
- "@m5kdev/web-ui": "catalog:",
21
- "@tanstack/react-query": "catalog:",
22
- "@trpc/client": "catalog:",
23
- "@trpc/tanstack-react-query": "catalog:",
24
- "better-auth": "catalog:",
25
- "i18next": "catalog:",
26
- "lucide-react": "catalog:",
27
- "nuqs": "catalog:",
28
- "posthog-js": "catalog:",
29
- "react": "catalog:",
30
- "react-dom": "catalog:",
31
- "react-hook-form": "catalog:",
32
- "react-i18next": "catalog:",
33
- "react-router": "catalog:",
34
- "sonner": "catalog:",
35
- "tailwindcss": "catalog:",
36
- "tw-animate-css": "catalog:",
37
- "zod": "catalog:"
38
- },
39
- "devDependencies": {
40
- "{{PACKAGE_SCOPE}}/server": "workspace:*",
41
- "@m5kdev/config": "catalog:",
42
- "@tailwindcss/vite": "catalog:",
43
- "@types/react": "catalog:",
44
- "@types/react-dom": "catalog:",
45
- "@vitejs/plugin-react": "catalog:",
46
- "babel-plugin-react-compiler": "catalog:",
47
- "typescript": "catalog:",
48
- "vite": "catalog:",
49
- "vite-plugin-i18next-loader": "catalog:",
50
- "vite-tsconfig-paths": "catalog:"
51
- }
52
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/webapp",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "biome check .",
10
+ "lint:fix": "biome check . --write",
11
+ "check-types": "tsc --noEmit",
12
+ "preview": "vite preview"
13
+ },
14
+ "dependencies": {
15
+ "{{PACKAGE_SCOPE}}/shared": "workspace:*",
16
+ "@heroui/react": "catalog:",
17
+ "@heroui/styles": "catalog:",
18
+ "@m5kdev/commons": "catalog:",
19
+ "@m5kdev/frontend": "catalog:",
20
+ "@m5kdev/web-ui": "catalog:",
21
+ "@tanstack/react-query": "catalog:",
22
+ "@trpc/client": "catalog:",
23
+ "@trpc/tanstack-react-query": "catalog:",
24
+ "better-auth": "catalog:",
25
+ "i18next": "catalog:",
26
+ "lucide-react": "catalog:",
27
+ "nuqs": "catalog:",
28
+ "posthog-js": "catalog:",
29
+ "react": "catalog:",
30
+ "react-dom": "catalog:",
31
+ "react-hook-form": "catalog:",
32
+ "react-i18next": "catalog:",
33
+ "react-router": "catalog:",
34
+ "sonner": "catalog:",
35
+ "tailwindcss": "catalog:",
36
+ "tw-animate-css": "catalog:",
37
+ "zod": "catalog:"
38
+ },
39
+ "devDependencies": {
40
+ "{{PACKAGE_SCOPE}}/server": "workspace:*",
41
+ "@m5kdev/config": "catalog:",
42
+ "@tailwindcss/vite": "catalog:",
43
+ "@types/react": "catalog:",
44
+ "@types/react-dom": "catalog:",
45
+ "@vitejs/plugin-react": "catalog:",
46
+ "babel-plugin-react-compiler": "catalog:",
47
+ "typescript": "catalog:",
48
+ "vite": "catalog:",
49
+ "vite-plugin-i18next-loader": "catalog:",
50
+ "vite-tsconfig-paths": "catalog:",
51
+ "@trpc/server": "catalog:"
52
+ }
53
+ }
@@ -1,34 +1,34 @@
1
- /* global self */
2
- self.addEventListener("push", (event) => {
3
- let payload = { title: "Notification", body: "" };
4
- try {
5
- if (event.data) {
6
- payload = { ...payload, ...event.data.json() };
7
- }
8
- } catch {
9
- const text = event.data?.text();
10
- if (text) payload = { title: "Notification", body: text };
11
- }
12
- event.waitUntil(
13
- self.registration.showNotification(payload.title, {
14
- body: payload.body,
15
- data: payload.data ?? {},
16
- })
17
- );
18
- });
19
-
20
- self.addEventListener("notificationclick", (event) => {
21
- event.notification.close();
22
- event.waitUntil(
23
- self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
24
- if (clientList.length > 0) {
25
- const client = clientList[0];
26
- if ("focus" in client) return client.focus();
27
- }
28
- if (self.clients.openWindow) {
29
- return self.clients.openWindow("/");
30
- }
31
- return undefined;
32
- })
33
- );
34
- });
1
+ /* global self */
2
+ self.addEventListener("push", (event) => {
3
+ let payload = { title: "Notification", body: "" };
4
+ try {
5
+ if (event.data) {
6
+ payload = { ...payload, ...event.data.json() };
7
+ }
8
+ } catch {
9
+ const text = event.data?.text();
10
+ if (text) payload = { title: "Notification", body: text };
11
+ }
12
+ event.waitUntil(
13
+ self.registration.showNotification(payload.title, {
14
+ body: payload.body,
15
+ data: payload.data ?? {},
16
+ })
17
+ );
18
+ });
19
+
20
+ self.addEventListener("notificationclick", (event) => {
21
+ event.notification.close();
22
+ event.waitUntil(
23
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
24
+ if (clientList.length > 0) {
25
+ const client = clientList[0];
26
+ if ("focus" in client) return client.focus();
27
+ }
28
+ if (self.clients.openWindow) {
29
+ return self.clients.openWindow("/");
30
+ }
31
+ return undefined;
32
+ })
33
+ );
34
+ });
@@ -1,13 +1,44 @@
1
- import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
2
- import { BrowserRouter } from "react-router";
3
- import { Providers } from "./Providers";
4
-
5
- export function App() {
6
- return (
7
- <NuqsAdapter>
8
- <BrowserRouter>
9
- <Providers />
10
- </BrowserRouter>
11
- </NuqsAdapter>
12
- );
13
- }
1
+ import { AppConfigProvider } from "@m5kdev/frontend/modules/app/components/AppConfigProvider";
2
+ import { AppTrpcQueryProvider } from "@m5kdev/frontend/modules/app/components/AppTrpcQueryProvider";
3
+ import { AuthProvider } from "@m5kdev/frontend/modules/auth/components/AuthProvider";
4
+ import { DialogProvider } from "@m5kdev/web-ui/components/DialogProvider";
5
+ import { ThemeProvider } from "@m5kdev/web-ui/components/theme-provider";
6
+ import { AppLoader } from "@m5kdev/web-ui/modules/app/components/AppLoader";
7
+ import {
8
+ APP_LOCALE_CONFIG,
9
+ APP_NAME,
10
+ APP_ROLES_CONFIG,
11
+ } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
12
+ import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
13
+ import { BrowserRouter } from "react-router";
14
+ import { Toaster } from "sonner";
15
+ import { Router } from "./Router";
16
+
17
+ export function App() {
18
+ return (
19
+ <NuqsAdapter>
20
+ <BrowserRouter>
21
+ <AppConfigProvider
22
+ config={{
23
+ appName: APP_NAME,
24
+ appUrl: import.meta.env.VITE_APP_URL,
25
+ serverUrl: import.meta.env.VITE_SERVER_URL,
26
+ locales: APP_LOCALE_CONFIG,
27
+ roles: APP_ROLES_CONFIG,
28
+ }}
29
+ >
30
+ <ThemeProvider defaultTheme="light" storageKey="m5kdev-theme">
31
+ <AuthProvider loader={<AppLoader />}>
32
+ <AppTrpcQueryProvider>
33
+ <DialogProvider>
34
+ <Router />
35
+ </DialogProvider>
36
+ <Toaster richColors closeButton />
37
+ </AppTrpcQueryProvider>
38
+ </AuthProvider>
39
+ </ThemeProvider>
40
+ </AppConfigProvider>
41
+ </BrowserRouter>
42
+ </NuqsAdapter>
43
+ );
44
+ }
@@ -7,15 +7,14 @@ import { AppSidebarUser } from "@m5kdev/web-ui/modules/app/components/AppSidebar
7
7
  import { AuthOrganizationSelect } from "@m5kdev/web-ui/modules/auth/components/AuthOrganizationSelect";
8
8
  import { AuthUtilityImpersonationBanner } from "@m5kdev/web-ui/modules/auth/components/AuthUtilityImpersonationBanner";
9
9
  import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
10
+ import { FileIcon, UsersIcon } from "lucide-react";
10
11
  import { useState } from "react";
11
12
  import { useTranslation } from "react-i18next";
12
13
  import { useNavigate } from "react-router";
13
- import { PushNotificationsPanel } from "./modules/notification/PushNotificationsPanel";
14
- import { APP_ROUTES, buildNavigationItems } from "./navigation/navigation.config";
15
14
 
16
15
  export function Layout() {
17
16
  const { data: session } = useSession();
18
- const { t } = useTranslation("blog{{PACKAGE_SCOPE}}");
17
+ const { t } = useTranslation("{{APP_SLUG}}");
19
18
  const navigate = useNavigate();
20
19
 
21
20
  // ephemeral UI state: which collapsible sidebar groups are open
@@ -37,21 +36,28 @@ export function Layout() {
37
36
  <AuthOrganizationSelect />
38
37
  </div>
39
38
  <AppSidebarContent
40
- navigationItems={buildNavigationItems(t)}
39
+ navigationItems={[
40
+ {
41
+ label: t("layout.navigation.posts"),
42
+ icon: <FileIcon />,
43
+ link: "/",
44
+ },
45
+ {
46
+ label: t("layout.navigation.members"),
47
+ icon: <UsersIcon />,
48
+ link: "/organization/members",
49
+ },
50
+ ]}
41
51
  navigationState={navigationState}
42
52
  onNavigationStateChange={setNavigationState}
43
53
  />
44
- <div className="mt-auto px-2 group-data-[collapsible=icon]:hidden">
45
- <PushNotificationsPanel />
46
- </div>
47
54
  </>
48
55
  ),
49
56
  footer: (
50
- <AppSidebarUser
51
- user={session?.user}
52
- onSignOut={onSignOut}
53
- organizationSettingsPath={APP_ROUTES.organization.preferences}
54
- />
57
+ // data-testid so e2e (and app tests) can assert on the signed-in user
58
+ <div data-testid="session-email" className="contents">
59
+ <AppSidebarUser user={session?.user} onSignOut={onSignOut} />
60
+ </div>
55
61
  ),
56
62
  }}
57
63
  />