kofi-stack-template-generator 2.1.27 → 2.1.36

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 (117) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/dist/index.js +1359 -13
  4. package/package.json +8 -8
  5. package/src/generator.ts +57 -0
  6. package/src/templates.generated.ts +108 -15
  7. package/templates/admin/next.config.ts.hbs +7 -0
  8. package/templates/admin/package.json.hbs +38 -0
  9. package/templates/admin/postcss.config.mjs.hbs +7 -0
  10. package/templates/admin/src/app/analytics/page.tsx.hbs +39 -0
  11. package/templates/admin/src/app/globals.css.hbs +2 -0
  12. package/templates/admin/src/app/layout.tsx.hbs +33 -0
  13. package/templates/admin/src/app/page.tsx.hbs +47 -0
  14. package/templates/admin/src/app/settings/page.tsx.hbs +74 -0
  15. package/templates/admin/src/app/users/page.tsx.hbs +16 -0
  16. package/templates/admin/src/components/admin-sidebar.tsx.hbs +51 -0
  17. package/templates/admin/src/components/stats-cards.tsx.hbs +28 -0
  18. package/templates/admin/src/components/user-table.tsx.hbs +65 -0
  19. package/templates/admin/tsconfig.json.hbs +15 -0
  20. package/templates/design-system/next.config.ts.hbs +7 -0
  21. package/templates/design-system/package.json.hbs +35 -0
  22. package/templates/design-system/postcss.config.mjs.hbs +7 -0
  23. package/templates/design-system/src/app/blocks/page.tsx.hbs +140 -0
  24. package/templates/design-system/src/app/colors/page.tsx.hbs +34 -0
  25. package/templates/design-system/src/app/components/page.tsx.hbs +110 -0
  26. package/templates/design-system/src/app/globals.css.hbs +2 -0
  27. package/templates/design-system/src/app/layout.tsx.hbs +46 -0
  28. package/templates/design-system/src/app/page.tsx.hbs +65 -0
  29. package/templates/design-system/src/app/typography/page.tsx.hbs +112 -0
  30. package/templates/design-system/src/components/color-palette.tsx.hbs +117 -0
  31. package/templates/design-system/tsconfig.json.hbs +13 -0
  32. package/templates/marketing/nextjs/package.json.hbs +1 -1
  33. package/templates/marketing/payload/package.json.hbs +1 -1
  34. package/templates/marketing/payload/src/Footer/config.ts.hbs +178 -0
  35. package/templates/marketing/payload/src/Footer/hooks/revalidateFooter.ts.hbs +13 -0
  36. package/templates/marketing/payload/src/Footer/index.ts.hbs +1 -0
  37. package/templates/marketing/payload/src/Header/RowLabel.tsx.hbs +21 -0
  38. package/templates/marketing/payload/src/Header/config.ts.hbs +208 -0
  39. package/templates/marketing/payload/src/Header/hooks/revalidateHeader.ts.hbs +13 -0
  40. package/templates/marketing/payload/src/Header/index.ts.hbs +1 -0
  41. package/templates/marketing/payload/src/access/anyone.ts.hbs +3 -0
  42. package/templates/marketing/payload/src/access/authenticated.ts.hbs +9 -0
  43. package/templates/marketing/payload/src/access/authenticatedOrPublished.ts.hbs +13 -0
  44. package/templates/marketing/payload/src/access/index.ts.hbs +3 -0
  45. package/templates/marketing/payload/src/app/(frontend)/next/seed/route.ts.hbs +31 -0
  46. package/templates/marketing/payload/src/collections/Categories/index.ts.hbs +28 -0
  47. package/templates/marketing/payload/src/collections/FAQs/index.ts.hbs +100 -0
  48. package/templates/marketing/payload/src/collections/Media.ts.hbs +148 -28
  49. package/templates/marketing/payload/src/collections/Pages/hooks/revalidatePage.ts.hbs +43 -0
  50. package/templates/marketing/payload/src/collections/Pages/index.ts.hbs +142 -0
  51. package/templates/marketing/payload/src/collections/Posts/hooks/populateAuthors.ts.hbs +41 -0
  52. package/templates/marketing/payload/src/collections/Posts/hooks/revalidatePost.ts.hbs +44 -0
  53. package/templates/marketing/payload/src/collections/Posts/index.ts.hbs +244 -0
  54. package/templates/marketing/payload/src/collections/Users/index.ts.hbs +26 -0
  55. package/templates/marketing/payload/src/collections/index.ts.hbs +6 -4
  56. package/templates/marketing/payload/src/components/BeforeDashboard/SeedButton/index.scss.hbs +12 -0
  57. package/templates/marketing/payload/src/components/BeforeDashboard/SeedButton/index.tsx.hbs +89 -0
  58. package/templates/marketing/payload/src/components/BeforeDashboard/index.scss.hbs +24 -0
  59. package/templates/marketing/payload/src/components/BeforeDashboard/index.tsx.hbs +69 -0
  60. package/templates/marketing/payload/src/components/BeforeLogin/index.tsx.hbs +14 -0
  61. package/templates/marketing/payload/src/components/Link/index.tsx.hbs +79 -0
  62. package/templates/marketing/payload/src/components/Media/index.tsx.hbs +67 -0
  63. package/templates/marketing/payload/src/components/RichText/index.tsx.hbs +44 -0
  64. package/templates/marketing/payload/src/endpoints/seed/home.ts.hbs +76 -0
  65. package/templates/marketing/payload/src/endpoints/seed/image-1.ts.hbs +5 -0
  66. package/templates/marketing/payload/src/endpoints/seed/image-2.ts.hbs +5 -0
  67. package/templates/marketing/payload/src/endpoints/seed/image-hero.ts.hbs +5 -0
  68. package/templates/marketing/payload/src/endpoints/seed/index.ts.hbs +235 -0
  69. package/templates/marketing/payload/src/endpoints/seed/post-1.ts.hbs +252 -0
  70. package/templates/marketing/payload/src/fields/defaultLexical.ts.hbs +73 -0
  71. package/templates/marketing/payload/src/fields/index.ts.hbs +3 -0
  72. package/templates/marketing/payload/src/fields/link.ts.hbs +139 -0
  73. package/templates/marketing/payload/src/fields/linkGroup.ts.hbs +28 -0
  74. package/templates/marketing/payload/src/globals/index.ts.hbs +2 -2
  75. package/templates/marketing/payload/src/heros/HighImpact/index.tsx.hbs +53 -0
  76. package/templates/marketing/payload/src/heros/LowImpact/index.tsx.hbs +48 -0
  77. package/templates/marketing/payload/src/heros/MediumImpact/index.tsx.hbs +46 -0
  78. package/templates/marketing/payload/src/heros/PostHero/index.tsx.hbs +68 -0
  79. package/templates/marketing/payload/src/heros/ProductShowcase/index.tsx.hbs +88 -0
  80. package/templates/marketing/payload/src/heros/RenderHero.tsx.hbs +27 -0
  81. package/templates/marketing/payload/src/heros/config.ts.hbs +112 -0
  82. package/templates/marketing/payload/src/heros/index.ts.hbs +7 -0
  83. package/templates/marketing/payload/src/hooks/index.ts.hbs +2 -0
  84. package/templates/marketing/payload/src/hooks/populatePublishedAt.ts.hbs +15 -0
  85. package/templates/marketing/payload/src/hooks/revalidateRedirects.ts.hbs +11 -0
  86. package/templates/marketing/payload/src/payload.config.ts.hbs +32 -8
  87. package/templates/marketing/payload/src/providers/HeaderTheme/index.tsx.hbs +34 -0
  88. package/templates/marketing/payload/src/providers/Theme/InitTheme/index.tsx.hbs +44 -0
  89. package/templates/marketing/payload/src/providers/Theme/index.tsx.hbs +60 -0
  90. package/templates/marketing/payload/src/providers/Theme/shared.ts.hbs +17 -0
  91. package/templates/marketing/payload/src/providers/Theme/types.ts.hbs +10 -0
  92. package/templates/marketing/payload/src/providers/index.tsx.hbs +18 -0
  93. package/templates/marketing/payload/src/utilities/canUseDOM.ts.hbs +1 -0
  94. package/templates/marketing/payload/src/utilities/deepMerge.ts.hbs +35 -0
  95. package/templates/marketing/payload/src/utilities/formatAuthors.ts.hbs +24 -0
  96. package/templates/marketing/payload/src/utilities/formatDateTime.ts.hbs +13 -0
  97. package/templates/marketing/payload/src/utilities/generateMeta.ts.hbs +87 -0
  98. package/templates/marketing/payload/src/utilities/generatePreviewPath.ts.hbs +33 -0
  99. package/templates/marketing/payload/src/utilities/getURL.ts.hbs +26 -0
  100. package/templates/marketing/payload/src/utilities/index.ts.hbs +8 -0
  101. package/templates/marketing/payload/src/utilities/mergeOpenGraph.ts.hbs +26 -0
  102. package/templates/mobile/app.json.hbs +39 -0
  103. package/templates/mobile/babel.config.js.hbs +6 -0
  104. package/templates/mobile/package.json.hbs +30 -0
  105. package/templates/mobile/src/app/(tabs)/_layout.tsx.hbs +27 -0
  106. package/templates/mobile/src/app/(tabs)/index.tsx.hbs +28 -0
  107. package/templates/mobile/src/app/(tabs)/profile.tsx.hbs +44 -0
  108. package/templates/mobile/src/app/_layout.tsx.hbs +13 -0
  109. package/templates/mobile/src/app/index.tsx.hbs +5 -0
  110. package/templates/mobile/tsconfig.json.hbs +10 -0
  111. package/templates/monorepo/package.json.hbs +4 -1
  112. package/templates/web/package.json.hbs +1 -1
  113. package/templates/marketing/payload/src/collections/Pages.ts.hbs +0 -66
  114. package/templates/marketing/payload/src/collections/Posts.ts.hbs +0 -65
  115. package/templates/marketing/payload/src/collections/Users.ts.hbs +0 -25
  116. package/templates/marketing/payload/src/globals/Navigation.ts.hbs +0 -51
  117. package/templates/marketing/payload/src/globals/SiteSettings.ts.hbs +0 -49
@@ -0,0 +1,43 @@
1
+ import type { CollectionAfterChangeHook, CollectionAfterDeleteHook } from "payload"
2
+
3
+ import { revalidatePath, revalidateTag } from "next/cache"
4
+
5
+ import type { Page } from "../../../payload-types"
6
+
7
+ export const revalidatePage: CollectionAfterChangeHook<Page> = ({
8
+ doc,
9
+ previousDoc,
10
+ req: { payload, context },
11
+ }) => {
12
+ if (!context.disableRevalidate) {
13
+ if (doc._status === "published") {
14
+ const path = doc.slug === "home" ? "/" : `/${doc.slug}`
15
+
16
+ payload.logger.info(`Revalidating page at path: ${path}`)
17
+
18
+ revalidatePath(path)
19
+ revalidateTag("pages-sitemap")
20
+ }
21
+
22
+ // If the page was previously published, we need to revalidate the old path
23
+ if (previousDoc?._status === "published" && doc._status !== "published") {
24
+ const oldPath = previousDoc.slug === "home" ? "/" : `/${previousDoc.slug}`
25
+
26
+ payload.logger.info(`Revalidating old page at path: ${oldPath}`)
27
+
28
+ revalidatePath(oldPath)
29
+ revalidateTag("pages-sitemap")
30
+ }
31
+ }
32
+ return doc
33
+ }
34
+
35
+ export const revalidateDelete: CollectionAfterDeleteHook<Page> = ({ doc, req: { context } }) => {
36
+ if (!context.disableRevalidate) {
37
+ const path = doc?.slug === "home" ? "/" : `/${doc?.slug}`
38
+ revalidatePath(path)
39
+ revalidateTag("pages-sitemap")
40
+ }
41
+
42
+ return doc
43
+ }
@@ -0,0 +1,142 @@
1
+ import type { CollectionConfig } from "payload"
2
+
3
+ import { slugField } from "payload"
4
+ import { authenticated } from "../../access/authenticated"
5
+ import { authenticatedOrPublished } from "../../access/authenticatedOrPublished"
6
+ import { populatePublishedAt } from "../../hooks/populatePublishedAt"
7
+ import { generatePreviewPath } from "../../utilities/generatePreviewPath"
8
+ import { revalidateDelete, revalidatePage } from "./hooks/revalidatePage"
9
+
10
+ // Import blocks
11
+ import {
12
+ HeroBlock,
13
+ LogoBannerBlock,
14
+ FeaturesBlock,
15
+ BenefitsBlock,
16
+ PricingBlock,
17
+ TestimonialsBlock,
18
+ FAQBlock,
19
+ ContentBlock,
20
+ CTABlock,
21
+ } from "../../blocks"
22
+
23
+ import {
24
+ MetaDescriptionField,
25
+ MetaImageField,
26
+ MetaTitleField,
27
+ OverviewField,
28
+ PreviewField,
29
+ } from "@payloadcms/plugin-seo/fields"
30
+
31
+ export const Pages: CollectionConfig<"pages"> = {
32
+ slug: "pages",
33
+ access: {
34
+ create: authenticated,
35
+ delete: authenticated,
36
+ read: authenticatedOrPublished,
37
+ update: authenticated,
38
+ },
39
+ defaultPopulate: {
40
+ title: true,
41
+ slug: true,
42
+ },
43
+ admin: {
44
+ defaultColumns: ["title", "slug", "updatedAt"],
45
+ livePreview: {
46
+ url: ({ data, req }) =>
47
+ generatePreviewPath({
48
+ slug: data?.slug,
49
+ collection: "pages",
50
+ req,
51
+ }),
52
+ },
53
+ preview: (data, { req }) =>
54
+ generatePreviewPath({
55
+ slug: data?.slug as string,
56
+ collection: "pages",
57
+ req,
58
+ }),
59
+ useAsTitle: "title",
60
+ },
61
+ fields: [
62
+ {
63
+ name: "title",
64
+ type: "text",
65
+ required: true,
66
+ },
67
+ {
68
+ type: "tabs",
69
+ tabs: [
70
+ {
71
+ fields: [
72
+ {
73
+ name: "layout",
74
+ type: "blocks",
75
+ blocks: [
76
+ HeroBlock,
77
+ LogoBannerBlock,
78
+ FeaturesBlock,
79
+ BenefitsBlock,
80
+ PricingBlock,
81
+ TestimonialsBlock,
82
+ FAQBlock,
83
+ ContentBlock,
84
+ CTABlock,
85
+ ],
86
+ required: true,
87
+ admin: {
88
+ initCollapsed: true,
89
+ },
90
+ },
91
+ ],
92
+ label: "Content",
93
+ },
94
+ {
95
+ name: "meta",
96
+ label: "SEO",
97
+ fields: [
98
+ OverviewField({
99
+ titlePath: "meta.title",
100
+ descriptionPath: "meta.description",
101
+ imagePath: "meta.image",
102
+ }),
103
+ MetaTitleField({
104
+ hasGenerateFn: true,
105
+ }),
106
+ MetaImageField({
107
+ relationTo: "media",
108
+ }),
109
+ MetaDescriptionField({}),
110
+ PreviewField({
111
+ hasGenerateFn: true,
112
+ titlePath: "meta.title",
113
+ descriptionPath: "meta.description",
114
+ }),
115
+ ],
116
+ },
117
+ ],
118
+ },
119
+ {
120
+ name: "publishedAt",
121
+ type: "date",
122
+ admin: {
123
+ position: "sidebar",
124
+ },
125
+ },
126
+ slugField(),
127
+ ],
128
+ hooks: {
129
+ afterChange: [revalidatePage],
130
+ beforeChange: [populatePublishedAt],
131
+ afterDelete: [revalidateDelete],
132
+ },
133
+ versions: {
134
+ drafts: {
135
+ autosave: {
136
+ interval: 100,
137
+ },
138
+ schedulePublish: true,
139
+ },
140
+ maxPerDoc: 50,
141
+ },
142
+ }
@@ -0,0 +1,41 @@
1
+ import type { CollectionAfterReadHook } from "payload"
2
+ import type { User } from "src/payload-types"
3
+
4
+ // The `user` collection has access control locked so that users are not publicly accessible
5
+ // This means that we need to populate the authors manually here to protect user privacy
6
+ // GraphQL will not return mutated user data that differs from the underlying schema
7
+ // So we use an alternative `populatedAuthors` field to populate the user data, hidden from the admin UI
8
+ export const populateAuthors: CollectionAfterReadHook = async ({
9
+ doc,
10
+ req: _req,
11
+ req: { payload },
12
+ }) => {
13
+ if (doc?.authors && doc?.authors?.length > 0) {
14
+ const authorDocs: User[] = []
15
+
16
+ for (const author of doc.authors) {
17
+ try {
18
+ const authorDoc = await payload.findByID({
19
+ id: typeof author === "object" ? author?.id : author,
20
+ collection: "users",
21
+ depth: 0,
22
+ })
23
+
24
+ if (authorDoc) {
25
+ authorDocs.push(authorDoc)
26
+ }
27
+
28
+ if (authorDocs.length > 0) {
29
+ doc.populatedAuthors = authorDocs.map((authorDoc) => ({
30
+ id: authorDoc.id,
31
+ name: authorDoc.name,
32
+ }))
33
+ }
34
+ } catch {
35
+ // swallow error
36
+ }
37
+ }
38
+ }
39
+
40
+ return doc
41
+ }
@@ -0,0 +1,44 @@
1
+ import type { CollectionAfterChangeHook, CollectionAfterDeleteHook } from "payload"
2
+
3
+ import { revalidatePath, revalidateTag } from "next/cache"
4
+
5
+ import type { Post } from "../../../payload-types"
6
+
7
+ export const revalidatePost: CollectionAfterChangeHook<Post> = ({
8
+ doc,
9
+ previousDoc,
10
+ req: { payload, context },
11
+ }) => {
12
+ if (!context.disableRevalidate) {
13
+ if (doc._status === "published") {
14
+ const path = `/posts/${doc.slug}`
15
+
16
+ payload.logger.info(`Revalidating post at path: ${path}`)
17
+
18
+ revalidatePath(path)
19
+ revalidateTag("posts-sitemap")
20
+ }
21
+
22
+ // If the post was previously published, we need to revalidate the old path
23
+ if (previousDoc._status === "published" && doc._status !== "published") {
24
+ const oldPath = `/posts/${previousDoc.slug}`
25
+
26
+ payload.logger.info(`Revalidating old post at path: ${oldPath}`)
27
+
28
+ revalidatePath(oldPath)
29
+ revalidateTag("posts-sitemap")
30
+ }
31
+ }
32
+ return doc
33
+ }
34
+
35
+ export const revalidateDelete: CollectionAfterDeleteHook<Post> = ({ doc, req: { context } }) => {
36
+ if (!context.disableRevalidate) {
37
+ const path = `/posts/${doc?.slug}`
38
+
39
+ revalidatePath(path)
40
+ revalidateTag("posts-sitemap")
41
+ }
42
+
43
+ return doc
44
+ }
@@ -0,0 +1,244 @@
1
+ import type { CollectionConfig } from "payload"
2
+
3
+ import {
4
+ AlignFeature,
5
+ BlockquoteFeature,
6
+ ChecklistFeature,
7
+ EXPERIMENTAL_TableFeature,
8
+ FixedToolbarFeature,
9
+ HeadingFeature,
10
+ HorizontalRuleFeature,
11
+ IndentFeature,
12
+ InlineCodeFeature,
13
+ InlineToolbarFeature,
14
+ OrderedListFeature,
15
+ RelationshipFeature,
16
+ StrikethroughFeature,
17
+ SubscriptFeature,
18
+ SuperscriptFeature,
19
+ UnorderedListFeature,
20
+ UploadFeature,
21
+ lexicalEditor,
22
+ } from "@payloadcms/richtext-lexical"
23
+
24
+ import { authenticated } from "../../access/authenticated"
25
+ import { authenticatedOrPublished } from "../../access/authenticatedOrPublished"
26
+ import { generatePreviewPath } from "../../utilities/generatePreviewPath"
27
+ import { populateAuthors } from "./hooks/populateAuthors"
28
+ import { revalidateDelete, revalidatePost } from "./hooks/revalidatePost"
29
+
30
+ import {
31
+ MetaDescriptionField,
32
+ MetaImageField,
33
+ MetaTitleField,
34
+ OverviewField,
35
+ PreviewField,
36
+ } from "@payloadcms/plugin-seo/fields"
37
+ import { slugField } from "payload"
38
+
39
+ export const Posts: CollectionConfig<"posts"> = {
40
+ slug: "posts",
41
+ access: {
42
+ create: authenticated,
43
+ delete: authenticated,
44
+ read: authenticatedOrPublished,
45
+ update: authenticated,
46
+ },
47
+ defaultPopulate: {
48
+ title: true,
49
+ slug: true,
50
+ categories: true,
51
+ meta: {
52
+ image: true,
53
+ description: true,
54
+ },
55
+ },
56
+ admin: {
57
+ defaultColumns: ["title", "slug", "updatedAt"],
58
+ livePreview: {
59
+ url: ({ data, req }) =>
60
+ generatePreviewPath({
61
+ slug: data?.slug,
62
+ collection: "posts",
63
+ req,
64
+ }),
65
+ },
66
+ preview: (data, { req }) =>
67
+ generatePreviewPath({
68
+ slug: data?.slug as string,
69
+ collection: "posts",
70
+ req,
71
+ }),
72
+ useAsTitle: "title",
73
+ },
74
+ fields: [
75
+ {
76
+ name: "title",
77
+ type: "text",
78
+ required: true,
79
+ },
80
+ {
81
+ type: "tabs",
82
+ tabs: [
83
+ {
84
+ fields: [
85
+ {
86
+ name: "heroImage",
87
+ type: "upload",
88
+ relationTo: "media",
89
+ },
90
+ {
91
+ name: "content",
92
+ type: "richText",
93
+ editor: lexicalEditor({
94
+ features: ({ rootFeatures }) => {
95
+ return [
96
+ ...rootFeatures,
97
+ HeadingFeature({ enabledHeadingSizes: ["h1", "h2", "h3", "h4"] }),
98
+ FixedToolbarFeature(),
99
+ InlineToolbarFeature(),
100
+ HorizontalRuleFeature(),
101
+ StrikethroughFeature(),
102
+ SubscriptFeature(),
103
+ SuperscriptFeature(),
104
+ InlineCodeFeature(),
105
+ BlockquoteFeature(),
106
+ UnorderedListFeature(),
107
+ OrderedListFeature(),
108
+ ChecklistFeature(),
109
+ AlignFeature(),
110
+ IndentFeature(),
111
+ RelationshipFeature(),
112
+ UploadFeature(),
113
+ EXPERIMENTAL_TableFeature(),
114
+ ]
115
+ },
116
+ }),
117
+ label: false,
118
+ required: true,
119
+ },
120
+ ],
121
+ label: "Content",
122
+ },
123
+ {
124
+ fields: [
125
+ {
126
+ name: "relatedPosts",
127
+ type: "relationship",
128
+ admin: {
129
+ position: "sidebar",
130
+ },
131
+ filterOptions: ({ id }) => {
132
+ return {
133
+ id: {
134
+ not_in: [id],
135
+ },
136
+ }
137
+ },
138
+ hasMany: true,
139
+ relationTo: "posts",
140
+ },
141
+ {
142
+ name: "categories",
143
+ type: "relationship",
144
+ admin: {
145
+ position: "sidebar",
146
+ },
147
+ hasMany: true,
148
+ relationTo: "categories",
149
+ },
150
+ ],
151
+ label: "Meta",
152
+ },
153
+ {
154
+ name: "meta",
155
+ label: "SEO",
156
+ fields: [
157
+ OverviewField({
158
+ titlePath: "meta.title",
159
+ descriptionPath: "meta.description",
160
+ imagePath: "meta.image",
161
+ }),
162
+ MetaTitleField({
163
+ hasGenerateFn: true,
164
+ }),
165
+ MetaImageField({
166
+ relationTo: "media",
167
+ }),
168
+ MetaDescriptionField({}),
169
+ PreviewField({
170
+ hasGenerateFn: true,
171
+ titlePath: "meta.title",
172
+ descriptionPath: "meta.description",
173
+ }),
174
+ ],
175
+ },
176
+ ],
177
+ },
178
+ {
179
+ name: "publishedAt",
180
+ type: "date",
181
+ admin: {
182
+ date: {
183
+ pickerAppearance: "dayAndTime",
184
+ },
185
+ position: "sidebar",
186
+ },
187
+ hooks: {
188
+ beforeChange: [
189
+ ({ siblingData, value }) => {
190
+ if (siblingData._status === "published" && !value) {
191
+ return new Date()
192
+ }
193
+ return value
194
+ },
195
+ ],
196
+ },
197
+ },
198
+ {
199
+ name: "authors",
200
+ type: "relationship",
201
+ admin: {
202
+ position: "sidebar",
203
+ },
204
+ hasMany: true,
205
+ relationTo: "users",
206
+ },
207
+ {
208
+ name: "populatedAuthors",
209
+ type: "array",
210
+ access: {
211
+ update: () => false,
212
+ },
213
+ admin: {
214
+ disabled: true,
215
+ readOnly: true,
216
+ },
217
+ fields: [
218
+ {
219
+ name: "id",
220
+ type: "text",
221
+ },
222
+ {
223
+ name: "name",
224
+ type: "text",
225
+ },
226
+ ],
227
+ },
228
+ slugField(),
229
+ ],
230
+ hooks: {
231
+ afterChange: [revalidatePost],
232
+ afterRead: [populateAuthors],
233
+ afterDelete: [revalidateDelete],
234
+ },
235
+ versions: {
236
+ drafts: {
237
+ autosave: {
238
+ interval: 100,
239
+ },
240
+ schedulePublish: true,
241
+ },
242
+ maxPerDoc: 50,
243
+ },
244
+ }
@@ -0,0 +1,26 @@
1
+ import type { CollectionConfig } from "payload"
2
+
3
+ import { authenticated } from "../../access/authenticated"
4
+
5
+ export const Users: CollectionConfig = {
6
+ slug: "users",
7
+ access: {
8
+ admin: authenticated,
9
+ create: authenticated,
10
+ delete: authenticated,
11
+ read: authenticated,
12
+ update: authenticated,
13
+ },
14
+ admin: {
15
+ defaultColumns: ["name", "email"],
16
+ useAsTitle: "name",
17
+ },
18
+ auth: true,
19
+ fields: [
20
+ {
21
+ name: "name",
22
+ type: "text",
23
+ },
24
+ ],
25
+ timestamps: true,
26
+ }
@@ -1,4 +1,6 @@
1
- export { Users } from './Users'
2
- export { Media } from './Media'
3
- export { Pages } from './Pages'
4
- export { Posts } from './Posts'
1
+ export { Users } from "./Users"
2
+ export { Media } from "./Media"
3
+ export { Pages } from "./Pages"
4
+ export { Posts } from "./Posts"
5
+ export { Categories } from "./Categories"
6
+ export { FAQs } from "./FAQs"
@@ -0,0 +1,12 @@
1
+ .seedButton {
2
+ appearance: none;
3
+ background: none;
4
+ border: none;
5
+ padding: 0;
6
+ text-decoration: underline;
7
+
8
+ &:hover {
9
+ cursor: pointer;
10
+ opacity: 0.85;
11
+ }
12
+ }
@@ -0,0 +1,89 @@
1
+ "use client"
2
+
3
+ import { toast } from "@payloadcms/ui"
4
+ import type React from "react"
5
+ import { Fragment, useCallback, useState } from "react"
6
+
7
+ import "./index.scss"
8
+
9
+ const SuccessMessage: React.FC = () => (
10
+ <div>
11
+ Database seeded! You can now{" "}
12
+ <a target="_blank" href="/" rel="noreferrer">
13
+ visit your website
14
+ </a>
15
+ </div>
16
+ )
17
+
18
+ export const SeedButton: React.FC = () => {
19
+ const [loading, setLoading] = useState(false)
20
+ const [seeded, setSeeded] = useState(false)
21
+ const [error, setError] = useState<null | string>(null)
22
+
23
+ const handleClick = useCallback(
24
+ async (e: React.MouseEvent<HTMLButtonElement>) => {
25
+ e.preventDefault()
26
+
27
+ if (seeded) {
28
+ toast.info("Database already seeded.")
29
+ return
30
+ }
31
+ if (loading) {
32
+ toast.info("Seeding already in progress.")
33
+ return
34
+ }
35
+ if (error) {
36
+ toast.error(`An error occurred, please refresh and try again.`)
37
+ return
38
+ }
39
+
40
+ setLoading(true)
41
+
42
+ try {
43
+ toast.promise(
44
+ new Promise((resolve, reject) => {
45
+ try {
46
+ fetch("/next/seed", { method: "POST", credentials: "include" })
47
+ .then((res) => {
48
+ if (res.ok) {
49
+ resolve(true)
50
+ setSeeded(true)
51
+ } else {
52
+ reject("An error occurred while seeding.")
53
+ }
54
+ })
55
+ .catch((error) => {
56
+ reject(error)
57
+ })
58
+ } catch (error) {
59
+ reject(error)
60
+ }
61
+ }),
62
+ {
63
+ loading: "Seeding with data....",
64
+ success: <SuccessMessage />,
65
+ error: "An error occurred while seeding.",
66
+ },
67
+ )
68
+ } catch (err) {
69
+ const error = err instanceof Error ? err.message : String(err)
70
+ setError(error)
71
+ }
72
+ },
73
+ [loading, seeded, error],
74
+ )
75
+
76
+ let message = ""
77
+ if (loading) message = " (seeding...)"
78
+ if (seeded) message = " (done!)"
79
+ if (error) message = ` (error: ${error})`
80
+
81
+ return (
82
+ <Fragment>
83
+ <button className="seedButton" onClick={handleClick}>
84
+ Seed your database
85
+ </button>
86
+ {message}
87
+ </Fragment>
88
+ )
89
+ }
@@ -0,0 +1,24 @@
1
+ @import '~@payloadcms/ui/scss';
2
+
3
+ .dashboard .before-dashboard {
4
+ margin-bottom: base(1.5);
5
+
6
+ &__banner {
7
+ & h4 {
8
+ margin: 0;
9
+ }
10
+ }
11
+
12
+ &__instructions {
13
+ list-style: decimal;
14
+ margin-bottom: base(0.5);
15
+
16
+ & li {
17
+ width: 100%;
18
+ }
19
+ }
20
+
21
+ & a:hover {
22
+ opacity: 0.85;
23
+ }
24
+ }