@latte-macchiat-io/latte-payload 1.0.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/.claude/settings.local.json +9 -0
- package/.env.example +29 -0
- package/.github/workflows/ci.yml +160 -0
- package/.github/workflows/publish.yml +126 -0
- package/.nvmrc +1 -0
- package/.prettierignore +2 -0
- package/.prettierrc +11 -0
- package/CHANGELOG.md +87 -0
- package/README.md +364 -0
- package/TESTING_AND_DOCUMENTATION_SETUP.md +348 -0
- package/dist/access/adminAccessOnly.d.ts +25 -0
- package/dist/access/adminAccessOnly.d.ts.map +1 -0
- package/dist/access/adminAccessOnly.js +11 -0
- package/dist/access/adminAccessOnly.js.map +1 -0
- package/dist/access/admins.d.ts +72 -0
- package/dist/access/admins.d.ts.map +1 -0
- package/dist/access/admins.js +76 -0
- package/dist/access/admins.js.map +1 -0
- package/dist/access/anyone.d.ts +35 -0
- package/dist/access/anyone.d.ts.map +1 -0
- package/dist/access/anyone.js +34 -0
- package/dist/access/anyone.js.map +1 -0
- package/dist/access/authenticated.d.ts +63 -0
- package/dist/access/authenticated.d.ts.map +1 -0
- package/dist/access/authenticated.js +68 -0
- package/dist/access/authenticated.js.map +1 -0
- package/dist/access/authenticatedAccessOnly.d.ts +13 -0
- package/dist/access/authenticatedAccessOnly.d.ts.map +1 -0
- package/dist/access/authenticatedAccessOnly.js +11 -0
- package/dist/access/authenticatedAccessOnly.js.map +1 -0
- package/dist/access/index.d.ts +7 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +7 -0
- package/dist/access/index.js.map +1 -0
- package/dist/access/publicReadAuthenticatedAccess.d.ts +13 -0
- package/dist/access/publicReadAuthenticatedAccess.d.ts.map +1 -0
- package/dist/access/publicReadAuthenticatedAccess.js +12 -0
- package/dist/access/publicReadAuthenticatedAccess.js.map +1 -0
- package/dist/collections/ContactMessages/hooks/sendEmailNotification.d.ts +31 -0
- package/dist/collections/ContactMessages/hooks/sendEmailNotification.d.ts.map +1 -0
- package/dist/collections/ContactMessages/hooks/sendEmailNotification.js +29 -0
- package/dist/collections/ContactMessages/hooks/sendEmailNotification.js.map +1 -0
- package/dist/collections/ContactMessages/index.d.ts +27 -0
- package/dist/collections/ContactMessages/index.d.ts.map +1 -0
- package/dist/collections/ContactMessages/index.js +81 -0
- package/dist/collections/ContactMessages/index.js.map +1 -0
- package/dist/collections/EmailTemplates/index.d.ts +26 -0
- package/dist/collections/EmailTemplates/index.d.ts.map +1 -0
- package/dist/collections/EmailTemplates/index.js +74 -0
- package/dist/collections/EmailTemplates/index.js.map +1 -0
- package/dist/collections/Media/index.d.ts +3 -0
- package/dist/collections/Media/index.d.ts.map +1 -0
- package/dist/collections/Media/index.js +22 -0
- package/dist/collections/Media/index.js.map +1 -0
- package/dist/collections/QueuedEmails/components/HtmlViewer.d.ts +3 -0
- package/dist/collections/QueuedEmails/components/HtmlViewer.d.ts.map +1 -0
- package/dist/collections/QueuedEmails/components/HtmlViewer.js +11 -0
- package/dist/collections/QueuedEmails/components/HtmlViewer.js.map +1 -0
- package/dist/collections/QueuedEmails/components/RetryEmailButtons.d.ts +2 -0
- package/dist/collections/QueuedEmails/components/RetryEmailButtons.d.ts.map +1 -0
- package/dist/collections/QueuedEmails/components/RetryEmailButtons.js +79 -0
- package/dist/collections/QueuedEmails/components/RetryEmailButtons.js.map +1 -0
- package/dist/collections/QueuedEmails/index.d.ts +3 -0
- package/dist/collections/QueuedEmails/index.d.ts.map +1 -0
- package/dist/collections/QueuedEmails/index.js +245 -0
- package/dist/collections/QueuedEmails/index.js.map +1 -0
- package/dist/collections/Users/auth-emails/forgot-password-email.d.ts +51 -0
- package/dist/collections/Users/auth-emails/forgot-password-email.d.ts.map +1 -0
- package/dist/collections/Users/auth-emails/forgot-password-email.js +90 -0
- package/dist/collections/Users/auth-emails/forgot-password-email.js.map +1 -0
- package/dist/collections/Users/auth-emails/verify-email.d.ts +51 -0
- package/dist/collections/Users/auth-emails/verify-email.d.ts.map +1 -0
- package/dist/collections/Users/auth-emails/verify-email.js +80 -0
- package/dist/collections/Users/auth-emails/verify-email.js.map +1 -0
- package/dist/collections/Users/hooks/ensureFirstUserIsAdmin.d.ts +3 -0
- package/dist/collections/Users/hooks/ensureFirstUserIsAdmin.d.ts.map +1 -0
- package/dist/collections/Users/hooks/ensureFirstUserIsAdmin.js +19 -0
- package/dist/collections/Users/hooks/ensureFirstUserIsAdmin.js.map +1 -0
- package/dist/collections/Users/index.d.ts +76 -0
- package/dist/collections/Users/index.d.ts.map +1 -0
- package/dist/collections/Users/index.js +116 -0
- package/dist/collections/Users/index.js.map +1 -0
- package/dist/collections/index.d.ts +10 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +14 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/forms/states.d.ts +8 -0
- package/dist/forms/states.d.ts.map +1 -0
- package/dist/forms/states.js +16 -0
- package/dist/forms/states.js.map +1 -0
- package/dist/forms/translate-errors.d.ts +8 -0
- package/dist/forms/translate-errors.d.ts.map +1 -0
- package/dist/forms/translate-errors.js +11 -0
- package/dist/forms/translate-errors.js.map +1 -0
- package/dist/forms/validators.d.ts +10 -0
- package/dist/forms/validators.d.ts.map +1 -0
- package/dist/forms/validators.js +23 -0
- package/dist/forms/validators.js.map +1 -0
- package/dist/globals/PrivacyPolicy/hooks/revalidate-cache.d.ts +2 -0
- package/dist/globals/PrivacyPolicy/hooks/revalidate-cache.d.ts.map +1 -0
- package/dist/globals/PrivacyPolicy/hooks/revalidate-cache.js +5 -0
- package/dist/globals/PrivacyPolicy/hooks/revalidate-cache.js.map +1 -0
- package/dist/globals/PrivacyPolicy/index.d.ts +3 -0
- package/dist/globals/PrivacyPolicy/index.d.ts.map +1 -0
- package/dist/globals/PrivacyPolicy/index.js +31 -0
- package/dist/globals/PrivacyPolicy/index.js.map +1 -0
- package/dist/globals/TermsOfUse/hooks/revalidate-cache.d.ts +2 -0
- package/dist/globals/TermsOfUse/hooks/revalidate-cache.d.ts.map +1 -0
- package/dist/globals/TermsOfUse/hooks/revalidate-cache.js +5 -0
- package/dist/globals/TermsOfUse/hooks/revalidate-cache.js.map +1 -0
- package/dist/globals/TermsOfUse/index.d.ts +3 -0
- package/dist/globals/TermsOfUse/index.d.ts.map +1 -0
- package/dist/globals/TermsOfUse/index.js +31 -0
- package/dist/globals/TermsOfUse/index.js.map +1 -0
- package/dist/globals/index.d.ts +3 -0
- package/dist/globals/index.d.ts.map +1 -0
- package/dist/globals/index.js +3 -0
- package/dist/globals/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/tasks/index.d.ts +2 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/process-email-queue.d.ts +46 -0
- package/dist/tasks/process-email-queue.d.ts.map +1 -0
- package/dist/tasks/process-email-queue.js +199 -0
- package/dist/tasks/process-email-queue.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/slug.d.ts +2 -0
- package/dist/types/slug.d.ts.map +1 -0
- package/dist/types/slug.js +11 -0
- package/dist/types/slug.js.map +1 -0
- package/dist/utils/database-dates.d.ts +3 -0
- package/dist/utils/database-dates.d.ts.map +1 -0
- package/dist/utils/database-dates.js +6 -0
- package/dist/utils/database-dates.js.map +1 -0
- package/dist/utils/email/generate-email-html.d.ts +23 -0
- package/dist/utils/email/generate-email-html.d.ts.map +1 -0
- package/dist/utils/email/generate-email-html.js +42 -0
- package/dist/utils/email/generate-email-html.js.map +1 -0
- package/dist/utils/email/get-email-template.d.ts +8 -0
- package/dist/utils/email/get-email-template.d.ts.map +1 -0
- package/dist/utils/email/get-email-template.js +14 -0
- package/dist/utils/email/get-email-template.js.map +1 -0
- package/dist/utils/email/index.d.ts +4 -0
- package/dist/utils/email/index.d.ts.map +1 -0
- package/dist/utils/email/index.js +4 -0
- package/dist/utils/email/index.js.map +1 -0
- package/dist/utils/email/queue-email.d.ts +29 -0
- package/dist/utils/email/queue-email.d.ts.map +1 -0
- package/dist/utils/email/queue-email.js +79 -0
- package/dist/utils/email/queue-email.js.map +1 -0
- package/dist/utils/get-global.d.ts +6 -0
- package/dist/utils/get-global.d.ts.map +1 -0
- package/dist/utils/get-global.js +20 -0
- package/dist/utils/get-global.js.map +1 -0
- package/dist/utils/id-from-payload.d.ts +4 -0
- package/dist/utils/id-from-payload.d.ts.map +1 -0
- package/dist/utils/id-from-payload.js +10 -0
- package/dist/utils/id-from-payload.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/migrations.d.ts +2 -0
- package/dist/utils/migrations.d.ts.map +1 -0
- package/dist/utils/migrations.js +18 -0
- package/dist/utils/migrations.js.map +1 -0
- package/dist/utils/payload-client.d.ts +10 -0
- package/dist/utils/payload-client.d.ts.map +1 -0
- package/dist/utils/payload-client.js +14 -0
- package/dist/utils/payload-client.js.map +1 -0
- package/dist/utils/slugify.d.ts +8 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +15 -0
- package/dist/utils/slugify.js.map +1 -0
- package/eslint.config.mjs +90 -0
- package/package.json +139 -0
- package/pnpm-workspace.yaml +4 -0
- package/src/access/adminAccessOnly.ts +13 -0
- package/src/access/admins.ts +78 -0
- package/src/access/anyone.ts +35 -0
- package/src/access/authenticated.ts +70 -0
- package/src/access/authenticatedAccessOnly.ts +13 -0
- package/src/access/index.ts +6 -0
- package/src/access/publicReadAuthenticatedAccess.ts +14 -0
- package/src/collections/ContactMessages/hooks/sendEmailNotification.ts +58 -0
- package/src/collections/ContactMessages/index.ts +100 -0
- package/src/collections/EmailTemplates/index.ts +89 -0
- package/src/collections/Media/index.ts +24 -0
- package/src/collections/QueuedEmails/components/HtmlViewer.tsx +16 -0
- package/src/collections/QueuedEmails/components/RetryEmailButtons.tsx +115 -0
- package/src/collections/QueuedEmails/index.ts +246 -0
- package/src/collections/Users/auth-emails/forgot-password-email.ts +135 -0
- package/src/collections/Users/auth-emails/verify-email.ts +123 -0
- package/src/collections/Users/hooks/ensureFirstUserIsAdmin.ts +22 -0
- package/src/collections/Users/index.ts +201 -0
- package/src/collections/index.ts +23 -0
- package/src/components/index.ts +3 -0
- package/src/forms/states.ts +23 -0
- package/src/forms/translate-errors.ts +13 -0
- package/src/forms/validators.ts +33 -0
- package/src/globals/PrivacyPolicy/hooks/revalidate-cache.ts +5 -0
- package/src/globals/PrivacyPolicy/index.ts +33 -0
- package/src/globals/TermsOfUse/hooks/revalidate-cache.ts +5 -0
- package/src/globals/TermsOfUse/index.ts +33 -0
- package/src/globals/index.ts +2 -0
- package/src/index.ts +26 -0
- package/src/tasks/index.ts +7 -0
- package/src/tasks/process-email-queue.ts +261 -0
- package/src/types/index.ts +15 -0
- package/src/types/slug.ts +11 -0
- package/src/utils/database-dates.ts +6 -0
- package/src/utils/email/generate-email-html.ts +63 -0
- package/src/utils/email/get-email-template.ts +18 -0
- package/src/utils/email/index.ts +3 -0
- package/src/utils/email/queue-email.ts +109 -0
- package/src/utils/get-global.ts +25 -0
- package/src/utils/id-from-payload.ts +11 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/migrations.ts +18 -0
- package/src/utils/payload-client.ts +16 -0
- package/src/utils/slugify.ts +21 -0
- package/tests/fixtures/email-template.html +58 -0
- package/tests/fixtures/sample-data.ts +56 -0
- package/tests/helpers/create-test-user.ts +37 -0
- package/tests/helpers/init-payload.ts +59 -0
- package/tests/setup.integration.ts +9 -0
- package/tests/setup.ts +4 -0
- package/tests/unit/access/adminAccessOnly.spec.ts +117 -0
- package/tests/unit/access/admins.spec.ts +68 -0
- package/tests/unit/access/anyone.spec.ts +28 -0
- package/tests/unit/access/authenticated.spec.ts +53 -0
- package/tests/unit/access/authenticatedAccessOnly.spec.ts +112 -0
- package/tests/unit/access/publicReadAuthenticatedAccess.spec.ts +112 -0
- package/tests/unit/forms/validators.spec.ts +348 -0
- package/tests/unit/utils/database-dates.spec.ts +97 -0
- package/tests/unit/utils/id-from-payload.spec.ts +142 -0
- package/tests/unit/utils/slugify.spec.ts +185 -0
- package/tsconfig.json +31 -0
- package/typedoc.json +40 -0
- package/vitest.config.ts +31 -0
- package/vitest.integration.config.ts +27 -0
package/package.json
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@latte-macchiat-io/latte-payload",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Reusable Payload CMS collections, utilities, and components for Latte projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./collections": {
|
|
14
|
+
"import": "./dist/collections/index.js",
|
|
15
|
+
"types": "./dist/collections/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./globals": {
|
|
18
|
+
"import": "./dist/globals/index.js",
|
|
19
|
+
"types": "./dist/globals/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./access": {
|
|
22
|
+
"import": "./dist/access/index.js",
|
|
23
|
+
"types": "./dist/access/index.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./utils": {
|
|
26
|
+
"import": "./dist/utils/index.js",
|
|
27
|
+
"types": "./dist/utils/index.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./config": {
|
|
30
|
+
"import": "./dist/config/index.js",
|
|
31
|
+
"types": "./dist/config/index.d.ts"
|
|
32
|
+
},
|
|
33
|
+
"./tasks": {
|
|
34
|
+
"import": "./dist/tasks/index.js",
|
|
35
|
+
"types": "./dist/tasks/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"./components": {
|
|
38
|
+
"import": "./dist/components/index.js",
|
|
39
|
+
"types": "./dist/components/index.d.ts"
|
|
40
|
+
},
|
|
41
|
+
"./types": {
|
|
42
|
+
"import": "./dist/types/index.js",
|
|
43
|
+
"types": "./dist/types/index.d.ts"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc",
|
|
48
|
+
"dev": "tsc --watch",
|
|
49
|
+
"clean": "rm -rf dist",
|
|
50
|
+
"prepublishOnly": "pnpm clean && pnpm build",
|
|
51
|
+
"lint": "eslint",
|
|
52
|
+
"lint:fix": "eslint --fix",
|
|
53
|
+
"prettier": "prettier --check \"**/*.{js,jsx,ts,tsx}\"",
|
|
54
|
+
"prettier:fix": "prettier --write \"**/*.{js,jsx,ts,tsx}\"",
|
|
55
|
+
"test": "vitest",
|
|
56
|
+
"test:unit": "vitest --config vitest.config.ts --run",
|
|
57
|
+
"test:integration": "vitest --config vitest.integration.config.ts --run",
|
|
58
|
+
"test:all": "pnpm test:unit && pnpm test:integration",
|
|
59
|
+
"test:watch": "vitest --config vitest.config.ts",
|
|
60
|
+
"test:ui": "vitest --ui",
|
|
61
|
+
"test:coverage": "vitest --config vitest.config.ts --run --coverage",
|
|
62
|
+
"docs:generate": "typedoc --out docs src/index.ts",
|
|
63
|
+
"docs:serve": "npx http-server docs -p 8080"
|
|
64
|
+
},
|
|
65
|
+
"keywords": [
|
|
66
|
+
"payload",
|
|
67
|
+
"payloadcms",
|
|
68
|
+
"cms",
|
|
69
|
+
"latte",
|
|
70
|
+
"starter",
|
|
71
|
+
"template"
|
|
72
|
+
],
|
|
73
|
+
"author": "Latte Macchiat.io",
|
|
74
|
+
"license": "MIT",
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@date-fns/tz": "^1.4.1",
|
|
77
|
+
"next-intl": "^4.6.1",
|
|
78
|
+
"slugify": "^1.6.6",
|
|
79
|
+
"zod": "^3.25.76"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@payloadcms/db-postgres": "^3.64.0",
|
|
83
|
+
"@payloadcms/email-nodemailer": "^3.64.0",
|
|
84
|
+
"@payloadcms/email-resend": "^3.64.0",
|
|
85
|
+
"@payloadcms/next": "^3.64.0",
|
|
86
|
+
"@payloadcms/richtext-lexical": "^3.64.0",
|
|
87
|
+
"@payloadcms/storage-s3": "^3.64.0",
|
|
88
|
+
"@payloadcms/ui": "^3.64.0",
|
|
89
|
+
"next": "^15.0.0",
|
|
90
|
+
"payload": "^3.64.0",
|
|
91
|
+
"react": "^19.0.0",
|
|
92
|
+
"react-dom": "^19.0.0",
|
|
93
|
+
"sharp": "^0.34.0"
|
|
94
|
+
},
|
|
95
|
+
"peerDependenciesMeta": {
|
|
96
|
+
"@payloadcms/email-nodemailer": {
|
|
97
|
+
"optional": true
|
|
98
|
+
},
|
|
99
|
+
"@payloadcms/email-resend": {
|
|
100
|
+
"optional": true
|
|
101
|
+
},
|
|
102
|
+
"@payloadcms/storage-s3": {
|
|
103
|
+
"optional": true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"devDependencies": {
|
|
107
|
+
"@payloadcms/db-sqlite": "^3.64.0",
|
|
108
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
109
|
+
"@testing-library/react": "^16.1.0",
|
|
110
|
+
"@types/node": "^20.6.0",
|
|
111
|
+
"@types/react": "19.1.10",
|
|
112
|
+
"@types/react-dom": "19.1.7",
|
|
113
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
114
|
+
"@vitest/ui": "^2.1.8",
|
|
115
|
+
"dotenv": "^17.2.3",
|
|
116
|
+
"eslint": "^9.15.0",
|
|
117
|
+
"eslint-config-prettier": "^10.0.1",
|
|
118
|
+
"eslint-import-resolver-typescript": "^4.4.4",
|
|
119
|
+
"eslint-plugin-import": "^2.31.0",
|
|
120
|
+
"eslint-plugin-jsx-a11y": "^6.6.1",
|
|
121
|
+
"eslint-plugin-storybook": "^10.1.6",
|
|
122
|
+
"happy-dom": "^20.0.11",
|
|
123
|
+
"prettier": "^3.7.4",
|
|
124
|
+
"typedoc": "^0.27.5",
|
|
125
|
+
"typedoc-plugin-markdown": "^4.3.3",
|
|
126
|
+
"typescript": "^5.9.2",
|
|
127
|
+
"typescript-eslint": "^8.21.0",
|
|
128
|
+
"vitest": "^2.1.8"
|
|
129
|
+
},
|
|
130
|
+
"packageManager": "pnpm@10.9.0",
|
|
131
|
+
"engines": {
|
|
132
|
+
"node": ">=22.x",
|
|
133
|
+
"pnpm": "^10"
|
|
134
|
+
},
|
|
135
|
+
"repository": {
|
|
136
|
+
"type": "git",
|
|
137
|
+
"url": "https://github.com/latte-macchiat-io/latte-payload"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PayloadRequest } from 'payload';
|
|
2
|
+
|
|
3
|
+
import { admins } from './admins';
|
|
4
|
+
|
|
5
|
+
export const adminAccessOnly = {
|
|
6
|
+
admin: ({ req }: { req: PayloadRequest }) => Boolean(req.user?.roles?.includes('admin')),
|
|
7
|
+
create: admins,
|
|
8
|
+
delete: admins,
|
|
9
|
+
read: admins,
|
|
10
|
+
readVersions: admins,
|
|
11
|
+
unlock: admins,
|
|
12
|
+
update: admins,
|
|
13
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { FieldAccess, PayloadRequest } from 'payload';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Access control function that restricts access to admin users only.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* This function checks if the authenticated user has the 'admin' role in their roles array.
|
|
8
|
+
* Commonly used for collection-level or operation-level access control in PayloadCMS.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} params - Access control parameters
|
|
11
|
+
* @param {PayloadRequest} params.req - The Payload request object containing user information
|
|
12
|
+
*
|
|
13
|
+
* @returns {boolean} True if user has admin role, false otherwise
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Use in a collection config
|
|
18
|
+
* export const AdminOnlyCollection: CollectionConfig = {
|
|
19
|
+
* slug: 'admin-only',
|
|
20
|
+
* access: {
|
|
21
|
+
* create: admins,
|
|
22
|
+
* read: admins,
|
|
23
|
+
* update: admins,
|
|
24
|
+
* delete: admins,
|
|
25
|
+
* },
|
|
26
|
+
* fields: [...],
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Use for specific operations
|
|
33
|
+
* if (admins({ req })) {
|
|
34
|
+
* // User is admin, allow operation
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @see {@link adminsFieldLevel} for field-level admin access control
|
|
39
|
+
* @see {@link adminAccessOnly} for a complete access control object with all operations restricted to admins
|
|
40
|
+
*/
|
|
41
|
+
export const admins = ({ req }: { req: PayloadRequest }) => {
|
|
42
|
+
// Return true or false based on if the user has an admin role
|
|
43
|
+
return Boolean(req.user?.roles?.includes('admin'));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Field-level access control function that restricts field access to admin users only.
|
|
48
|
+
*
|
|
49
|
+
* @description
|
|
50
|
+
* Similar to {@link admins} but designed for use with field-level access control.
|
|
51
|
+
* Checks if the user has the 'admin' role to determine field visibility/editability.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} params - Field access parameters
|
|
54
|
+
* @param {Object} params.req - The request object
|
|
55
|
+
* @param {Object} params.req.user - The authenticated user
|
|
56
|
+
*
|
|
57
|
+
* @returns {boolean} True if user has admin role, false otherwise
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // Restrict field to admins only
|
|
62
|
+
* {
|
|
63
|
+
* name: 'sensitiveField',
|
|
64
|
+
* type: 'text',
|
|
65
|
+
* access: {
|
|
66
|
+
* read: adminsFieldLevel,
|
|
67
|
+
* update: adminsFieldLevel,
|
|
68
|
+
* },
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @see {@link admins} for collection-level admin access control
|
|
73
|
+
*/
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
export const adminsFieldLevel: FieldAccess<any, any> = ({ req: { user } }) => {
|
|
76
|
+
// Return true or false based on if the user has an admin role
|
|
77
|
+
return Boolean(user?.roles?.includes('admin'));
|
|
78
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Access } from 'payload';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Access control function that allows unrestricted public access.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* This function always returns true, allowing access to all users regardless of authentication status.
|
|
8
|
+
* Use this for truly public content like blog posts, landing pages, or public API endpoints.
|
|
9
|
+
*
|
|
10
|
+
* @returns {boolean} Always returns true
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Use for public content
|
|
15
|
+
* export const BlogPostsCollection: CollectionConfig = {
|
|
16
|
+
* slug: 'blog-posts',
|
|
17
|
+
* access: {
|
|
18
|
+
* read: anyone, // Anyone can read
|
|
19
|
+
* create: admins, // Only admins can create
|
|
20
|
+
* update: admins,
|
|
21
|
+
* delete: admins,
|
|
22
|
+
* },
|
|
23
|
+
* fields: [...],
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* // Use with publicReadAuthenticatedAccess for read-only public access
|
|
30
|
+
* import { publicReadAuthenticatedAccess } from '@latte-macchiat-io/latte-payload/access';
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @see {@link publicReadAuthenticatedAccess} for public read with authenticated write pattern
|
|
34
|
+
*/
|
|
35
|
+
export const anyone: Access = () => true;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Access, FieldAccess } from 'payload';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Access control function that allows access to any authenticated user.
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* This function checks if there is an authenticated user in the request, regardless of their role.
|
|
8
|
+
* Useful for content that should be accessible to all logged-in users but not to the public.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} params - Access control parameters
|
|
11
|
+
* @param {Object} params.req - The request object
|
|
12
|
+
* @param {Object} params.req.user - The authenticated user (if any)
|
|
13
|
+
*
|
|
14
|
+
* @returns {boolean} True if user is authenticated, false otherwise
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Use in a collection config
|
|
19
|
+
* export const UserContentCollection: CollectionConfig = {
|
|
20
|
+
* slug: 'user-content',
|
|
21
|
+
* access: {
|
|
22
|
+
* create: authenticated,
|
|
23
|
+
* read: authenticated,
|
|
24
|
+
* update: authenticated,
|
|
25
|
+
* delete: authenticated,
|
|
26
|
+
* },
|
|
27
|
+
* fields: [...],
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @see {@link authenticatedFieldLevel} for field-level authenticated access control
|
|
32
|
+
* @see {@link authenticatedAccessOnly} for a complete access control object
|
|
33
|
+
*/
|
|
34
|
+
export const authenticated: Access = ({ req: { user } }) => {
|
|
35
|
+
// Return true or false based on if the user is authenticated
|
|
36
|
+
return Boolean(!!user);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Field-level access control function that restricts field access to authenticated users.
|
|
41
|
+
*
|
|
42
|
+
* @description
|
|
43
|
+
* Similar to {@link authenticated} but designed for use with field-level access control.
|
|
44
|
+
* Checks if the user is authenticated to determine field visibility/editability.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} params - Field access parameters
|
|
47
|
+
* @param {Object} params.req - The request object
|
|
48
|
+
* @param {Object} params.req.user - The authenticated user (if any)
|
|
49
|
+
*
|
|
50
|
+
* @returns {boolean} True if user is authenticated, false otherwise
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // Hide field from unauthenticated users
|
|
55
|
+
* {
|
|
56
|
+
* name: 'memberOnlyField',
|
|
57
|
+
* type: 'text',
|
|
58
|
+
* access: {
|
|
59
|
+
* read: authenticatedFieldLevel,
|
|
60
|
+
* update: authenticatedFieldLevel,
|
|
61
|
+
* },
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @see {@link authenticated} for collection-level authenticated access control
|
|
66
|
+
*/
|
|
67
|
+
export const authenticatedFieldLevel: FieldAccess = ({ req: { user } }) => {
|
|
68
|
+
// Return true or false based on if the user is authenticated
|
|
69
|
+
return Boolean(!!user);
|
|
70
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PayloadRequest } from 'payload';
|
|
2
|
+
|
|
3
|
+
import { authenticated } from './authenticated';
|
|
4
|
+
|
|
5
|
+
export const authenticatedAccessOnly = {
|
|
6
|
+
admin: ({ req }: { req: PayloadRequest }) => !!req.user,
|
|
7
|
+
create: authenticated,
|
|
8
|
+
delete: authenticated,
|
|
9
|
+
read: authenticated,
|
|
10
|
+
readVersions: authenticated,
|
|
11
|
+
unlock: authenticated,
|
|
12
|
+
update: authenticated,
|
|
13
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { anyone } from './anyone';
|
|
2
|
+
export { authenticated, authenticatedFieldLevel } from './authenticated';
|
|
3
|
+
export { admins, adminsFieldLevel } from './admins';
|
|
4
|
+
export { adminAccessOnly } from './adminAccessOnly';
|
|
5
|
+
export { authenticatedAccessOnly } from './authenticatedAccessOnly';
|
|
6
|
+
export { publicReadAuthenticatedAccess } from './publicReadAuthenticatedAccess';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PayloadRequest } from 'payload';
|
|
2
|
+
|
|
3
|
+
import { anyone } from './anyone';
|
|
4
|
+
import { authenticated } from './authenticated';
|
|
5
|
+
|
|
6
|
+
export const publicReadAuthenticatedAccess = {
|
|
7
|
+
admin: ({ req }: { req: PayloadRequest }) => !!req.user,
|
|
8
|
+
create: authenticated,
|
|
9
|
+
delete: authenticated,
|
|
10
|
+
read: anyone,
|
|
11
|
+
readVersions: authenticated,
|
|
12
|
+
unlock: authenticated,
|
|
13
|
+
update: authenticated,
|
|
14
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CollectionAfterChangeHook } from 'payload';
|
|
2
|
+
|
|
3
|
+
export interface SendEmailNotificationConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Email address to send notifications to
|
|
6
|
+
* If not provided, uses process.env.EMAIL_DEFAULT_CONTACT_ADDRESS
|
|
7
|
+
*/
|
|
8
|
+
contactEmail?: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Subject for the notification email
|
|
12
|
+
* Default: 'Nouveau message de contact'
|
|
13
|
+
*/
|
|
14
|
+
subject?: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom email body generator
|
|
18
|
+
* @param doc - The contact message document
|
|
19
|
+
* @returns Email body text
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
generateEmailBody?: (doc: any) => string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a sendEmailNotification hook with custom configuration
|
|
27
|
+
*
|
|
28
|
+
* @param config - Optional configuration for email notifications
|
|
29
|
+
* @returns CollectionAfterChangeHook
|
|
30
|
+
*/
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
export function createSendEmailNotificationHook(config?: SendEmailNotificationConfig): CollectionAfterChangeHook<any> {
|
|
33
|
+
return async ({ req: { payload }, doc }) => {
|
|
34
|
+
const toEmail = config?.contactEmail || process.env.EMAIL_DEFAULT_CONTACT_ADDRESS;
|
|
35
|
+
|
|
36
|
+
if (!toEmail) {
|
|
37
|
+
payload.logger.error('No contact email address configured for sendEmailNotification hook');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
payload.logger.info('Sending email notification to the admin');
|
|
42
|
+
|
|
43
|
+
const emailBody =
|
|
44
|
+
config?.generateEmailBody?.(doc) ||
|
|
45
|
+
`Un nouveau message de contact a été reçu de la part de ${doc.name} (${doc.email}).\n\nSujet: ${doc.subject}\n\nMessage: ${doc.message}`;
|
|
46
|
+
|
|
47
|
+
await payload.sendEmail({
|
|
48
|
+
to: toEmail,
|
|
49
|
+
subject: config?.subject || 'Nouveau message de contact',
|
|
50
|
+
text: emailBody,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Default sendEmailNotification hook
|
|
57
|
+
*/
|
|
58
|
+
export const sendEmailNotification = createSendEmailNotificationHook();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { CollectionConfig } from 'payload';
|
|
2
|
+
|
|
3
|
+
import { createSendEmailNotificationHook, sendEmailNotification, type SendEmailNotificationConfig } from './hooks/sendEmailNotification';
|
|
4
|
+
import { authenticated } from '../../access/authenticated';
|
|
5
|
+
import type { Locale } from '../../types';
|
|
6
|
+
|
|
7
|
+
export interface ContactMessagesConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Allowed locales for contact messages
|
|
10
|
+
* @default ['en', 'fr', 'nl']
|
|
11
|
+
*/
|
|
12
|
+
locales?: Locale[];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for email notifications
|
|
16
|
+
*/
|
|
17
|
+
emailNotificationConfig?: SendEmailNotificationConfig;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create ContactMessages collection with optional configuration
|
|
22
|
+
*
|
|
23
|
+
* @param config - Optional configuration for the ContactMessages collection
|
|
24
|
+
* @returns CollectionConfig for ContactMessages
|
|
25
|
+
*/
|
|
26
|
+
export function createContactMessagesCollection(config?: ContactMessagesConfig): CollectionConfig {
|
|
27
|
+
const locales = config?.locales || (['en', 'fr', 'nl'] as Locale[]);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
slug: 'contact-messages',
|
|
31
|
+
labels: {
|
|
32
|
+
singular: { fr: 'Message reçu', nl: 'Ontvangen bericht', en: 'Received Message' },
|
|
33
|
+
plural: { fr: 'Messages reçus', nl: 'Ontvangen berichten', en: 'Received Messages' },
|
|
34
|
+
},
|
|
35
|
+
admin: {
|
|
36
|
+
description: {
|
|
37
|
+
fr: 'Messages reçus via le formulaire de contact',
|
|
38
|
+
nl: 'Berichten ontvangen via het contactformulier',
|
|
39
|
+
en: 'Messages received via the contact form',
|
|
40
|
+
},
|
|
41
|
+
group: { fr: '✉️ Communication', nl: '✉️ Communicatie', en: '✉️ Communication' },
|
|
42
|
+
useAsTitle: 'subject',
|
|
43
|
+
defaultColumns: ['name', 'email', 'subject', 'createdAt'],
|
|
44
|
+
hideAPIURL: true,
|
|
45
|
+
hidden: true, // Hide from the admin UI
|
|
46
|
+
},
|
|
47
|
+
access: {
|
|
48
|
+
create: () => false, // Not available
|
|
49
|
+
delete: authenticated,
|
|
50
|
+
read: authenticated,
|
|
51
|
+
update: () => false, // Not available
|
|
52
|
+
},
|
|
53
|
+
hooks: {
|
|
54
|
+
afterChange: [config?.emailNotificationConfig ? createSendEmailNotificationHook(config.emailNotificationConfig) : sendEmailNotification],
|
|
55
|
+
},
|
|
56
|
+
fields: [
|
|
57
|
+
{
|
|
58
|
+
name: 'name',
|
|
59
|
+
label: { fr: 'Nom', nl: 'Naam', en: 'Name' },
|
|
60
|
+
type: 'text',
|
|
61
|
+
required: true,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'email',
|
|
65
|
+
label: { fr: 'Email', nl: 'E-mail', en: 'Email' },
|
|
66
|
+
type: 'email',
|
|
67
|
+
required: true,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'subject',
|
|
71
|
+
label: { fr: 'Sujet', nl: 'Onderwerp', en: 'Subject' },
|
|
72
|
+
type: 'text',
|
|
73
|
+
required: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'message',
|
|
77
|
+
label: { fr: 'Message', nl: 'Bericht', en: 'Message' },
|
|
78
|
+
type: 'textarea',
|
|
79
|
+
required: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'locale',
|
|
83
|
+
label: { fr: 'Langue', nl: 'Taal', en: 'Language' },
|
|
84
|
+
type: 'text',
|
|
85
|
+
required: true,
|
|
86
|
+
validate: (value: string | string[] | null | undefined) => {
|
|
87
|
+
return locales.includes(value as Locale) || 'Invalid locale';
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Default ContactMessages collection
|
|
96
|
+
*/
|
|
97
|
+
export const ContactMessages = createContactMessagesCollection();
|
|
98
|
+
|
|
99
|
+
// Re-export for customization
|
|
100
|
+
export { createSendEmailNotificationHook, type SendEmailNotificationConfig };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { CollectionConfig } from 'payload';
|
|
2
|
+
import { admins } from '../../access/admins';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for EmailTemplates collection
|
|
6
|
+
*/
|
|
7
|
+
export interface EmailTemplatesConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Allowed email template codes
|
|
10
|
+
* Default: ['verify-email', 'password-lost']
|
|
11
|
+
*/
|
|
12
|
+
emailCodes?: Array<{ label: string; value: string }>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create EmailTemplates collection with optional configuration
|
|
17
|
+
*
|
|
18
|
+
* @param config - Optional configuration for email codes
|
|
19
|
+
* @returns CollectionConfig for EmailTemplates
|
|
20
|
+
*/
|
|
21
|
+
export function createEmailTemplatesCollection(config?: EmailTemplatesConfig): CollectionConfig {
|
|
22
|
+
const emailCodes = config?.emailCodes || [
|
|
23
|
+
{ label: 'verify-email', value: 'verify-email' },
|
|
24
|
+
{ label: 'password-lost', value: 'password-lost' },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
slug: 'email-templates',
|
|
29
|
+
labels: {
|
|
30
|
+
singular: { fr: "Modèle d'email", nl: 'E-mail sjabloon', en: 'Email Template' },
|
|
31
|
+
plural: { fr: "Modèles d'emails", nl: 'E-mail sjablonen', en: 'Email Templates' },
|
|
32
|
+
},
|
|
33
|
+
admin: {
|
|
34
|
+
description: { fr: "Gérer les modèles d'emails", nl: 'Beheer e-mail sjablonen', en: 'Manage email templates' },
|
|
35
|
+
group: { fr: '✉️ Communication', nl: '✉️ Communicatie', en: '✉️ Communication' },
|
|
36
|
+
useAsTitle: 'name',
|
|
37
|
+
defaultColumns: ['code', 'name'],
|
|
38
|
+
hideAPIURL: true,
|
|
39
|
+
},
|
|
40
|
+
access: {
|
|
41
|
+
create: admins, // Not allowed to create new emails templates
|
|
42
|
+
delete: () => false, // Not allowed to delete emails templates
|
|
43
|
+
read: admins,
|
|
44
|
+
update: admins,
|
|
45
|
+
},
|
|
46
|
+
fields: [
|
|
47
|
+
{
|
|
48
|
+
type: 'row',
|
|
49
|
+
fields: [
|
|
50
|
+
{
|
|
51
|
+
name: 'name',
|
|
52
|
+
label: { fr: 'Nom (interne)', nl: 'Naam (intern)', en: 'Name (internal)' },
|
|
53
|
+
type: 'text',
|
|
54
|
+
required: true,
|
|
55
|
+
unique: true,
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
name: 'code',
|
|
60
|
+
label: { fr: 'Identifiant', nl: 'Identificatie', en: 'Identifier' },
|
|
61
|
+
type: 'select',
|
|
62
|
+
options: emailCodes,
|
|
63
|
+
required: true,
|
|
64
|
+
unique: true,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'subject',
|
|
70
|
+
label: { fr: "Sujet de l'email", nl: 'Onderwerp van de e-mail', en: 'Email subject' },
|
|
71
|
+
type: 'text',
|
|
72
|
+
required: true,
|
|
73
|
+
localized: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'body',
|
|
77
|
+
label: { fr: "Corps de l'email", nl: 'Inhoud van de e-mail', en: 'Email body' },
|
|
78
|
+
type: 'richText',
|
|
79
|
+
required: true,
|
|
80
|
+
localized: true,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Default EmailTemplates collection with standard email codes
|
|
88
|
+
*/
|
|
89
|
+
export const EmailTemplates = createEmailTemplatesCollection();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CollectionConfig } from 'payload';
|
|
2
|
+
|
|
3
|
+
import { publicReadAuthenticatedAccess } from '../../access/publicReadAuthenticatedAccess';
|
|
4
|
+
|
|
5
|
+
export const Media: CollectionConfig = {
|
|
6
|
+
slug: 'media',
|
|
7
|
+
labels: {
|
|
8
|
+
singular: { fr: 'Upload', en: 'Upload', nl: 'Upload' },
|
|
9
|
+
plural: { fr: 'Uploads', en: 'Uploads', nl: 'Uploads' },
|
|
10
|
+
},
|
|
11
|
+
admin: {
|
|
12
|
+
hideAPIURL: true,
|
|
13
|
+
group: { fr: 'Uploads et médias', en: 'Uploads and Media', nl: 'Uploads en media' },
|
|
14
|
+
},
|
|
15
|
+
access: publicReadAuthenticatedAccess,
|
|
16
|
+
fields: [
|
|
17
|
+
{
|
|
18
|
+
name: 'alt',
|
|
19
|
+
type: 'text',
|
|
20
|
+
required: false,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
upload: true,
|
|
24
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TextareaFieldServerComponent } from 'payload';
|
|
2
|
+
|
|
3
|
+
export const HTMLViewer: TextareaFieldServerComponent = async ({ data }) => {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
style={{
|
|
7
|
+
padding: '1rem',
|
|
8
|
+
border: '1px solid #ccc',
|
|
9
|
+
backgroundColor: '#f9f9f9',
|
|
10
|
+
borderRadius: '6px',
|
|
11
|
+
marginTop: '0.5rem',
|
|
12
|
+
}}
|
|
13
|
+
dangerouslySetInnerHTML={{ __html: data.htmlBody || '' }}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
};
|