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.
- package/dist/src/create.cjs +18 -1
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/fs.cjs +65 -5
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/fs.d.cts +13 -2
- package/dist/src/index.cjs +0 -0
- package/dist/src/paths.cjs +2 -2
- package/dist/src/prompts.cjs +22 -0
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/runCli.cjs +13 -2
- package/dist/src/runCli.cjs.map +1 -1
- package/dist/src/types.d.cts +9 -1
- package/package.json +8 -6
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +46 -3
- package/templates/minimal-app/.cursor/rules/module-db-guide.mdc +15 -1
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -0
- package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +98 -193
- package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -173
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +133 -240
- package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -113
- package/templates/minimal-app/README.md.tpl +3 -3
- package/templates/minimal-app/apps/.gitignore.tpl +13 -0
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -0
- package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +39 -0
- package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -0
- package/templates/minimal-app/apps/e2e/servers.ts.tpl +100 -0
- package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -0
- package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -0
- package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -0
- package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -0
- package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -0
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -0
- package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -0
- package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -0
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +35 -34
- package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +34 -33
- package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +35 -34
- package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +37 -36
- package/templates/minimal-app/apps/email/src/emails/waitlistEmails.tsx.tpl +87 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +72 -32
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +65 -36
- package/templates/minimal-app/apps/email/tsconfig.json.tpl +16 -11
- package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -0
- package/templates/minimal-app/apps/expo/.prettierignore +13 -0
- package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -0
- package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -0
- package/templates/minimal-app/apps/expo/README.md.tpl +44 -0
- package/templates/minimal-app/apps/expo/app.json.tpl +36 -0
- package/templates/minimal-app/apps/expo/assets/images/adaptive-icon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/favicon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/icon.png +0 -0
- package/templates/minimal-app/apps/expo/assets/images/splash-icon.png +0 -0
- package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -0
- package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
- package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -0
- package/templates/minimal-app/apps/expo/package.json.tpl +59 -0
- package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -0
- package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -0
- package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -0
- package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -0
- package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -0
- package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -0
- package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -0
- package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -0
- package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -0
- package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -0
- package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -0
- package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -0
- package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -0
- package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -0
- package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -0
- package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -0
- package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -0
- package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -0
- package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -0
- package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +39 -39
- package/templates/minimal-app/apps/server/drizzle/0000_wealthy_absorbing_man.sql +268 -0
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +26 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +1890 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +13 -0
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +33 -0
- package/templates/minimal-app/apps/server/drizzle/seed.e2e.ts.tpl +244 -0
- package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +181 -180
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +14 -21
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +33 -35
- package/templates/minimal-app/apps/server/package.json.tpl +56 -52
- package/templates/minimal-app/apps/server/src/app.ts.tpl +142 -126
- package/templates/minimal-app/apps/server/src/index.ts.tpl +58 -58
- package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +12 -12
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +25 -25
- package/templates/minimal-app/apps/server/src/modules/posts/posts.dto.ts.tpl +23 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +30 -40
- package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -112
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +104 -170
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -48
- package/templates/minimal-app/apps/server/src/modules/posts/posts.utils.ts.tpl +19 -0
- package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -0
- package/templates/minimal-app/apps/server/src/schema.ts.tpl +20 -0
- package/templates/minimal-app/apps/server/src/types.ts.tpl +1 -1
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +20 -14
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +9 -9
- package/templates/minimal-app/apps/shared/package.json.tpl +24 -24
- package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -2
- package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -76
- package/templates/minimal-app/apps/shared/tsconfig.json.tpl +16 -12
- package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -52
- package/templates/minimal-app/apps/webapp/public/{push-sw.js → push-sw.js.tpl} +34 -34
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +44 -13
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +18 -12
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +33 -40
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -75
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
- package/templates/minimal-app/apps/webapp/src/modules/posts/PostsRoute.tsx.tpl +278 -307
- package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostCard.tsx.tpl +96 -138
- package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostEditorModal.tsx.tpl +118 -124
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostActions.ts.tpl +108 -116
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsList.ts.tpl +15 -9
- package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsRoute.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/modules/posts/posts.utils.ts.tpl +16 -16
- package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -6
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/webapp/translations/{app.json.tpl → en/app.json.tpl} +7 -7
- package/templates/minimal-app/apps/webapp/translations/en/{blog-app.json.tpl → {{APP_SLUG}}.json.tpl} +135 -135
- package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
- package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
- package/templates/minimal-app/template.manifest.json +21 -0
- package/templates/minimal-app/apps/server/drizzle/generate-schema.ts.tpl +0 -31
- package/templates/minimal-app/apps/server/src/db.ts.tpl +0 -7
- package/templates/minimal-app/apps/server/src/generated/schema.ts.tpl +0 -13
- package/templates/minimal-app/apps/server/src/utils/trpc.ts.tpl +0 -8
- package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +0 -7
- package/templates/minimal-app/apps/shared/src/modules/app/roles.constants.ts.tpl +0 -17
- package/templates/minimal-app/apps/webapp/src/Providers.tsx.tpl +0 -36
- package/templates/minimal-app/apps/webapp/src/modules/notification/PushNotificationsPanel.tsx.tpl +0 -89
- package/templates/minimal-app/apps/webapp/src/navigation/navigation.config.tsx.tpl +0 -85
- /package/templates/minimal-app/apps/webapp/public/{logo.svg → logo.svg.tpl} +0 -0
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PACKAGE_SCOPE}}/email",
|
|
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
|
-
"email:dev": "react-email dev --dir src/emails"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@m5kdev/commons": "catalog:",
|
|
14
|
-
"@m5kdev/email": "catalog:",
|
|
15
|
-
"@react-email/components": "catalog:",
|
|
16
|
-
"react": "catalog:",
|
|
17
|
-
"react-dom": "catalog:"
|
|
18
|
-
},
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"@m5kdev/backend": "catalog:",
|
|
21
|
-
"@m5kdev/config": "catalog:",
|
|
22
|
-
"@types/node": "catalog:",
|
|
23
|
-
"@types/react": "catalog:",
|
|
24
|
-
"@types/react-dom": "catalog:",
|
|
25
|
-
"react-email": "catalog:",
|
|
26
|
-
"tslib": "catalog:",
|
|
27
|
-
"typescript": "catalog:"
|
|
28
|
-
},
|
|
29
|
-
"main": "./src/index.ts",
|
|
30
|
-
"exports": {
|
|
31
|
-
".": "./src/index.ts",
|
|
32
|
-
"./resources": "./src/resources.ts"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PACKAGE_SCOPE}}/email",
|
|
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
|
+
"email:dev": "react-email dev --dir src/emails"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@m5kdev/commons": "catalog:",
|
|
14
|
+
"@m5kdev/email": "catalog:",
|
|
15
|
+
"@react-email/components": "catalog:",
|
|
16
|
+
"react": "catalog:",
|
|
17
|
+
"react-dom": "catalog:"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@m5kdev/backend": "catalog:",
|
|
21
|
+
"@m5kdev/config": "catalog:",
|
|
22
|
+
"@types/node": "catalog:",
|
|
23
|
+
"@types/react": "catalog:",
|
|
24
|
+
"@types/react-dom": "catalog:",
|
|
25
|
+
"react-email": "catalog:",
|
|
26
|
+
"tslib": "catalog:",
|
|
27
|
+
"typescript": "catalog:"
|
|
28
|
+
},
|
|
29
|
+
"main": "./src/index.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./src/index.ts",
|
|
32
|
+
"./resources": "./src/resources.ts"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
import { Heading, Text } from "@react-email/components";
|
|
3
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
4
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
5
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
6
|
+
|
|
7
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
8
|
+
return t ?? ((key: string) => key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function AccountDeletionEmail({
|
|
12
|
+
previewText,
|
|
13
|
+
url,
|
|
14
|
+
brand,
|
|
15
|
+
t,
|
|
16
|
+
htmlLang,
|
|
17
|
+
}: {
|
|
18
|
+
previewText: string;
|
|
19
|
+
url: string;
|
|
20
|
+
brand: Brand;
|
|
21
|
+
t?: EmailTranslateFn;
|
|
22
|
+
htmlLang?: string;
|
|
23
|
+
}) {
|
|
24
|
+
const translate = resolveT(t);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
28
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
29
|
+
{translate("accountDeletion.title")}
|
|
30
|
+
</Heading>
|
|
31
|
+
<Text className="mb-6 text-base text-gray-700">{translate("accountDeletion.body")}</Text>
|
|
32
|
+
<CtaButton href={url}>{translate("accountDeletion.action")}</CtaButton>
|
|
33
|
+
</EmailLayout>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
import { Heading, Text } from "@react-email/components";
|
|
3
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
4
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
5
|
+
import type { EmailTranslateFn, OrganizationInviteTemplateProps } from "@m5kdev/email/types";
|
|
6
|
+
|
|
7
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
8
|
+
return t ?? ((key: string) => key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function OrganizationInviteEmail({
|
|
12
|
+
previewText,
|
|
13
|
+
inviterName,
|
|
14
|
+
organizationName,
|
|
15
|
+
role,
|
|
16
|
+
url,
|
|
17
|
+
brand,
|
|
18
|
+
t,
|
|
19
|
+
htmlLang,
|
|
20
|
+
}: OrganizationInviteTemplateProps) {
|
|
21
|
+
const translate = resolveT(t);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
25
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
26
|
+
{translate("organizationInvite.title", { organizationName })}
|
|
27
|
+
</Heading>
|
|
28
|
+
<Text className="mb-6 text-base text-gray-700">
|
|
29
|
+
{translate("organizationInvite.body", { inviterName, organizationName, role })}
|
|
30
|
+
</Text>
|
|
31
|
+
<CtaButton href={url}>{translate("organizationInvite.action")}</CtaButton>
|
|
32
|
+
</EmailLayout>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -1,34 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
import { Heading, Text } from "@react-email/components";
|
|
3
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
4
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
5
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
6
|
+
|
|
7
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
8
|
+
return t ?? ((key: string) => key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function PasswordResetEmail({
|
|
12
|
+
previewText,
|
|
13
|
+
url,
|
|
14
|
+
brand,
|
|
15
|
+
t,
|
|
16
|
+
htmlLang,
|
|
17
|
+
}: {
|
|
18
|
+
previewText: string;
|
|
19
|
+
url: string;
|
|
20
|
+
brand: Brand;
|
|
21
|
+
t?: EmailTranslateFn;
|
|
22
|
+
htmlLang?: string;
|
|
23
|
+
}) {
|
|
24
|
+
const translate = resolveT(t);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
28
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
29
|
+
{translate("passwordReset.title")}
|
|
30
|
+
</Heading>
|
|
31
|
+
<Text className="mb-6 text-base text-gray-700">{translate("passwordReset.body")}</Text>
|
|
32
|
+
<CtaButton href={url}>{translate("passwordReset.action")}</CtaButton>
|
|
33
|
+
</EmailLayout>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
import { Heading, Text } from "@react-email/components";
|
|
3
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
4
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
5
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
6
|
+
|
|
7
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
8
|
+
return t ?? ((key: string) => key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function VerificationEmail({
|
|
12
|
+
previewText,
|
|
13
|
+
url,
|
|
14
|
+
brand,
|
|
15
|
+
t,
|
|
16
|
+
htmlLang,
|
|
17
|
+
}: {
|
|
18
|
+
previewText: string;
|
|
19
|
+
url: string;
|
|
20
|
+
brand: Brand;
|
|
21
|
+
t?: EmailTranslateFn;
|
|
22
|
+
htmlLang?: string;
|
|
23
|
+
}) {
|
|
24
|
+
const translate = resolveT(t);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
28
|
+
<Heading className="mb-4 text-2xl font-bold text-black">
|
|
29
|
+
{translate("verification.title")}
|
|
30
|
+
</Heading>
|
|
31
|
+
<Text className="mb-6 text-base text-gray-700">
|
|
32
|
+
{translate("verification.body", { appName: "{{APP_NAME}}" })}
|
|
33
|
+
</Text>
|
|
34
|
+
<CtaButton href={url}>{translate("verification.action")}</CtaButton>
|
|
35
|
+
</EmailLayout>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** @jsxRuntime automatic */
|
|
2
|
+
import { Heading, Text } from "@react-email/components";
|
|
3
|
+
import { CtaButton } from "@m5kdev/email/components/CtaButton";
|
|
4
|
+
import { EmailLayout } from "@m5kdev/email/components/EmailLayout";
|
|
5
|
+
import type { Brand, EmailTranslateFn } from "@m5kdev/email/types";
|
|
6
|
+
|
|
7
|
+
function resolveT(t?: EmailTranslateFn): EmailTranslateFn {
|
|
8
|
+
return t ?? ((key: string) => key);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface WaitlistEmailProps {
|
|
12
|
+
previewText: string;
|
|
13
|
+
url?: string;
|
|
14
|
+
brand: Brand;
|
|
15
|
+
t?: EmailTranslateFn;
|
|
16
|
+
htmlLang?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function WaitlistActionEmail({
|
|
20
|
+
previewText,
|
|
21
|
+
url,
|
|
22
|
+
brand,
|
|
23
|
+
htmlLang,
|
|
24
|
+
title,
|
|
25
|
+
body,
|
|
26
|
+
action,
|
|
27
|
+
}: WaitlistEmailProps & { title: string; body: string; action: string }) {
|
|
28
|
+
return (
|
|
29
|
+
<EmailLayout previewText={previewText} brand={brand} htmlLang={htmlLang}>
|
|
30
|
+
<Heading className="mb-4 text-2xl font-bold text-black">{title}</Heading>
|
|
31
|
+
<Text className="mb-6 text-base text-gray-700">{body}</Text>
|
|
32
|
+
{url ? <CtaButton href={url}>{action}</CtaButton> : null}
|
|
33
|
+
</EmailLayout>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function WaitlistConfirmationEmail(props: WaitlistEmailProps) {
|
|
38
|
+
const t = resolveT(props.t);
|
|
39
|
+
return (
|
|
40
|
+
<WaitlistActionEmail
|
|
41
|
+
{...props}
|
|
42
|
+
title={t("waitlistConfirmation.title")}
|
|
43
|
+
body={t("waitlistConfirmation.body")}
|
|
44
|
+
action={t("waitlistConfirmation.action")}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function WaitlistInviteEmail(props: WaitlistEmailProps) {
|
|
50
|
+
const t = resolveT(props.t);
|
|
51
|
+
return (
|
|
52
|
+
<WaitlistActionEmail
|
|
53
|
+
{...props}
|
|
54
|
+
title={t("waitlistInvite.title")}
|
|
55
|
+
body={t("waitlistInvite.body")}
|
|
56
|
+
action={t("waitlistInvite.action")}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function WaitlistUserInviteEmail(
|
|
62
|
+
props: WaitlistEmailProps & { inviter?: string; name?: string }
|
|
63
|
+
) {
|
|
64
|
+
const t = resolveT(props.t);
|
|
65
|
+
const inviter = props.inviter ?? "A teammate";
|
|
66
|
+
const invitee = props.name ? t("waitlistUserInvite.inviteeSuffix", { name: props.name }) : "";
|
|
67
|
+
return (
|
|
68
|
+
<WaitlistActionEmail
|
|
69
|
+
{...props}
|
|
70
|
+
title={t("waitlistUserInvite.title", { inviter, invitee })}
|
|
71
|
+
body={t("waitlistUserInvite.body")}
|
|
72
|
+
action={t("waitlistUserInvite.action")}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function SystemWaitlistNotificationEmail(props: WaitlistEmailProps) {
|
|
78
|
+
const t = resolveT(props.t);
|
|
79
|
+
return (
|
|
80
|
+
<WaitlistActionEmail
|
|
81
|
+
{...props}
|
|
82
|
+
title={t("systemWaitlistNotification.title")}
|
|
83
|
+
body={t("systemWaitlistNotification.body")}
|
|
84
|
+
action={t("systemWaitlistNotification.action")}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -1,32 +1,72 @@
|
|
|
1
|
-
import type { FunctionComponent } from "react";
|
|
2
|
-
import AccountDeletionEmail from "./emails/accountDeletionEmail";
|
|
3
|
-
import OrganizationInviteEmail from "./emails/organizationInviteEmail";
|
|
4
|
-
import PasswordResetEmail from "./emails/passwordResetEmail";
|
|
5
|
-
import VerificationEmail from "./emails/verificationEmail";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
import type { FunctionComponent } from "react";
|
|
2
|
+
import AccountDeletionEmail from "./emails/accountDeletionEmail";
|
|
3
|
+
import OrganizationInviteEmail from "./emails/organizationInviteEmail";
|
|
4
|
+
import PasswordResetEmail from "./emails/passwordResetEmail";
|
|
5
|
+
import VerificationEmail from "./emails/verificationEmail";
|
|
6
|
+
import {
|
|
7
|
+
SystemWaitlistNotificationEmail,
|
|
8
|
+
WaitlistConfirmationEmail,
|
|
9
|
+
WaitlistInviteEmail,
|
|
10
|
+
WaitlistUserInviteEmail,
|
|
11
|
+
} from "./emails/waitlistEmails";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* EmailService renders templates with untyped props (they come from send-time
|
|
15
|
+
* data), so the registry erases each template's prop type. The props each
|
|
16
|
+
* template receives are guaranteed by the corresponding EmailService.send*
|
|
17
|
+
* method.
|
|
18
|
+
*/
|
|
19
|
+
function asTemplate(component: FunctionComponent<never>): FunctionComponent<Record<string, unknown>> {
|
|
20
|
+
return component as unknown as FunctionComponent<Record<string, unknown>>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const templates = {
|
|
24
|
+
accountDeletion: {
|
|
25
|
+
id: "account-deletion",
|
|
26
|
+
subject: "accountDeletion.subject",
|
|
27
|
+
previewText: "accountDeletion.previewText",
|
|
28
|
+
react: asTemplate(AccountDeletionEmail),
|
|
29
|
+
},
|
|
30
|
+
passwordReset: {
|
|
31
|
+
id: "password-reset",
|
|
32
|
+
subject: "passwordReset.subject",
|
|
33
|
+
previewText: "passwordReset.previewText",
|
|
34
|
+
react: asTemplate(PasswordResetEmail),
|
|
35
|
+
},
|
|
36
|
+
verification: {
|
|
37
|
+
id: "verification",
|
|
38
|
+
subject: "verification.subject",
|
|
39
|
+
previewText: "verification.previewText",
|
|
40
|
+
react: asTemplate(VerificationEmail),
|
|
41
|
+
},
|
|
42
|
+
organizationInvite: {
|
|
43
|
+
id: "organization-invite",
|
|
44
|
+
subject: "organizationInvite.subject",
|
|
45
|
+
previewText: "organizationInvite.previewText",
|
|
46
|
+
react: asTemplate(OrganizationInviteEmail),
|
|
47
|
+
},
|
|
48
|
+
waitlistConfirmation: {
|
|
49
|
+
id: "waitlist-confirmation",
|
|
50
|
+
subject: "waitlistConfirmation.subject",
|
|
51
|
+
previewText: "waitlistConfirmation.previewText",
|
|
52
|
+
react: asTemplate(WaitlistConfirmationEmail),
|
|
53
|
+
},
|
|
54
|
+
waitlistInvite: {
|
|
55
|
+
id: "waitlist-invite",
|
|
56
|
+
subject: "waitlistInvite.subject",
|
|
57
|
+
previewText: "waitlistInvite.previewText",
|
|
58
|
+
react: asTemplate(WaitlistInviteEmail),
|
|
59
|
+
},
|
|
60
|
+
waitlistUserInvite: {
|
|
61
|
+
id: "waitlist-user-invite",
|
|
62
|
+
subject: "waitlistUserInvite.subject",
|
|
63
|
+
previewText: "waitlistUserInvite.previewText",
|
|
64
|
+
react: asTemplate(WaitlistUserInviteEmail),
|
|
65
|
+
},
|
|
66
|
+
systemWaitlistNotification: {
|
|
67
|
+
id: "system-waitlist-notification",
|
|
68
|
+
subject: "systemWaitlistNotification.subject",
|
|
69
|
+
previewText: "systemWaitlistNotification.previewText",
|
|
70
|
+
react: asTemplate(SystemWaitlistNotificationEmail),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -1,36 +1,65 @@
|
|
|
1
|
-
import type { BackendAppI18nResources } from "@m5kdev/backend/i18n/app-i18n";
|
|
2
|
-
|
|
3
|
-
export const emailResources = {
|
|
4
|
-
en: {
|
|
5
|
-
translation: {
|
|
6
|
-
verification: {
|
|
7
|
-
subject: "Verify your email",
|
|
8
|
-
previewText: "Verify your email address",
|
|
9
|
-
title: "Verify your email",
|
|
10
|
-
body: "Confirm your email address to finish setting up {{APP_NAME}} and access your editorial workspace.",
|
|
11
|
-
action: "Verify account",
|
|
12
|
-
},
|
|
13
|
-
passwordReset: {
|
|
14
|
-
subject: "Reset your password",
|
|
15
|
-
previewText: "Reset your password request",
|
|
16
|
-
title: "Reset your password",
|
|
17
|
-
body: "Use this link to choose a new password for your account.",
|
|
18
|
-
action: "Reset password",
|
|
19
|
-
},
|
|
20
|
-
accountDeletion: {
|
|
21
|
-
subject: "Delete your account",
|
|
22
|
-
previewText: "Confirm your account deletion",
|
|
23
|
-
title: "Confirm account deletion",
|
|
24
|
-
body: "Use this link to confirm deleting your account.",
|
|
25
|
-
action: "Confirm deletion",
|
|
26
|
-
},
|
|
27
|
-
organizationInvite: {
|
|
28
|
-
subject: "Join the organization",
|
|
29
|
-
previewText: "You have been invited to join an organization",
|
|
30
|
-
title: "Join {{organizationName}}",
|
|
31
|
-
body: "{{inviterName}} invited you to join {{organizationName}} as {{role}}.",
|
|
32
|
-
action: "Accept invitation",
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import type { BackendAppI18nResources } from "@m5kdev/backend/i18n/app-i18n";
|
|
2
|
+
|
|
3
|
+
export const emailResources = {
|
|
4
|
+
en: {
|
|
5
|
+
translation: {
|
|
6
|
+
verification: {
|
|
7
|
+
subject: "Verify your email",
|
|
8
|
+
previewText: "Verify your email address",
|
|
9
|
+
title: "Verify your email",
|
|
10
|
+
body: "Confirm your email address to finish setting up {{APP_NAME}} and access your editorial workspace.",
|
|
11
|
+
action: "Verify account",
|
|
12
|
+
},
|
|
13
|
+
passwordReset: {
|
|
14
|
+
subject: "Reset your password",
|
|
15
|
+
previewText: "Reset your password request",
|
|
16
|
+
title: "Reset your password",
|
|
17
|
+
body: "Use this link to choose a new password for your account.",
|
|
18
|
+
action: "Reset password",
|
|
19
|
+
},
|
|
20
|
+
accountDeletion: {
|
|
21
|
+
subject: "Delete your account",
|
|
22
|
+
previewText: "Confirm your account deletion",
|
|
23
|
+
title: "Confirm account deletion",
|
|
24
|
+
body: "Use this link to confirm deleting your account.",
|
|
25
|
+
action: "Confirm deletion",
|
|
26
|
+
},
|
|
27
|
+
organizationInvite: {
|
|
28
|
+
subject: "Join the organization",
|
|
29
|
+
previewText: "You have been invited to join an organization",
|
|
30
|
+
title: "Join {{organizationName}}",
|
|
31
|
+
body: "{{inviterName}} invited you to join {{organizationName}} as {{role}}.",
|
|
32
|
+
action: "Accept invitation",
|
|
33
|
+
},
|
|
34
|
+
waitlistConfirmation: {
|
|
35
|
+
subject: "You joined the waitlist",
|
|
36
|
+
previewText: "Your waitlist request was recorded",
|
|
37
|
+
title: "You joined the waitlist",
|
|
38
|
+
body: "Your waitlist request was recorded. We will let you know as soon as a spot opens up.",
|
|
39
|
+
action: "Open app",
|
|
40
|
+
},
|
|
41
|
+
waitlistInvite: {
|
|
42
|
+
subject: "Your invitation is ready",
|
|
43
|
+
previewText: "Create your account",
|
|
44
|
+
title: "Your invitation is ready",
|
|
45
|
+
body: "Use this invitation link to create your account.",
|
|
46
|
+
action: "Create account",
|
|
47
|
+
},
|
|
48
|
+
waitlistUserInvite: {
|
|
49
|
+
subject: "{{inviter}} invited you to join {{brandName}}",
|
|
50
|
+
previewText: "Create your {{brandName}} account today",
|
|
51
|
+
title: "{{inviter}} invited you{{invitee}}",
|
|
52
|
+
body: "This invite lets you skip the waitlist and create your account now.",
|
|
53
|
+
action: "Accept invite",
|
|
54
|
+
inviteeSuffix: " for {{name}}",
|
|
55
|
+
},
|
|
56
|
+
systemWaitlistNotification: {
|
|
57
|
+
subject: "New waitlist signup",
|
|
58
|
+
previewText: "A user joined the waitlist",
|
|
59
|
+
title: "New waitlist signup",
|
|
60
|
+
body: "A user joined the {{APP_NAME}} waitlist.",
|
|
61
|
+
action: "Open admin",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
} satisfies BackendAppI18nResources;
|