bsmnt 0.2.10 → 0.3.0

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 (163) hide show
  1. package/dist/configs/skills.d.ts +27 -0
  2. package/dist/configs/skills.d.ts.map +1 -0
  3. package/dist/configs/skills.js +18 -0
  4. package/dist/configs/skills.js.map +1 -0
  5. package/dist/configs/skills.json +26 -0
  6. package/dist/helpers/add/hooks-config.d.ts.map +1 -1
  7. package/dist/helpers/add/hooks-config.js +0 -6
  8. package/dist/helpers/add/hooks-config.js.map +1 -1
  9. package/dist/helpers/create/copy-template.d.ts +1 -1
  10. package/dist/helpers/create/copy-template.d.ts.map +1 -1
  11. package/dist/helpers/create/index.d.ts.map +1 -1
  12. package/dist/helpers/create/index.js +2 -1
  13. package/dist/helpers/create/index.js.map +1 -1
  14. package/dist/helpers/create/setup-agent.d.ts.map +1 -1
  15. package/dist/helpers/create/setup-agent.js +15 -5
  16. package/dist/helpers/create/setup-agent.js.map +1 -1
  17. package/dist/helpers/integrate/merge-config.d.ts.map +1 -1
  18. package/dist/helpers/integrate/merge-config.js +1 -2
  19. package/dist/helpers/integrate/merge-config.js.map +1 -1
  20. package/dist/helpers/integrate/sanity/config.d.ts.map +1 -1
  21. package/dist/helpers/integrate/sanity/config.js +5 -10
  22. package/dist/helpers/integrate/sanity/config.js.map +1 -1
  23. package/dist/helpers/integrate/sanity/mergers/layout-merger.d.ts.map +1 -1
  24. package/dist/helpers/integrate/sanity/mergers/layout-merger.js +13 -12
  25. package/dist/helpers/integrate/sanity/mergers/layout-merger.js.map +1 -1
  26. package/dist/helpers/skills/index.d.ts +10 -0
  27. package/dist/helpers/skills/index.d.ts.map +1 -0
  28. package/dist/helpers/skills/index.js +136 -0
  29. package/dist/helpers/skills/index.js.map +1 -0
  30. package/dist/index.js +102 -35
  31. package/dist/index.js.map +1 -1
  32. package/package.json +3 -2
  33. package/src/helpers/integrate/sanity/files/app/api/blog/[slug]/route.ts +2 -1
  34. package/src/helpers/integrate/sanity/files/lib/integrations/sanity/confirm-publish-action.ts +31 -0
  35. package/src/helpers/integrate/sanity/files/lib/integrations/sanity/sanity.config.ts +17 -0
  36. package/src/helpers/integrate/sanity/files/lib/utils/json-ld.tsx +249 -0
  37. package/src/template-hooks/config.js +0 -6
  38. package/src/templates/next-default/app/layout.tsx +18 -0
  39. package/src/templates/next-default/lib/hooks/use-device-detection.ts +1 -1
  40. package/src/templates/next-default/lib/hooks/use-media-breakpoint.ts +1 -1
  41. package/src/templates/next-default/lib/hooks/use-media.ts +29 -0
  42. package/src/templates/next-default/lib/utils/json-ld.tsx +199 -0
  43. package/src/templates/next-default/package.json +1 -1
  44. package/src/templates/next-default/tsconfig.json +1 -0
  45. package/src/templates/next-experiments/app/layout.tsx +18 -0
  46. package/src/templates/next-experiments/lib/hooks/use-device-detection.ts +1 -1
  47. package/src/templates/next-experiments/lib/hooks/use-media-breakpoint.ts +1 -1
  48. package/src/templates/next-experiments/lib/hooks/use-media.ts +29 -0
  49. package/src/templates/next-experiments/lib/utils/json-ld.tsx +199 -0
  50. package/src/templates/next-experiments/package.json +1 -1
  51. package/src/templates/next-experiments/tsconfig.json +1 -0
  52. package/src/templates/next-pagebuilder/.env.example +11 -0
  53. package/src/templates/next-pagebuilder/README.md +23 -0
  54. package/src/templates/next-pagebuilder/_gitignore +67 -0
  55. package/src/templates/next-pagebuilder/app/(content)/[[...slug]]/page.tsx +68 -0
  56. package/src/templates/next-pagebuilder/app/(content)/layout.tsx +13 -0
  57. package/src/templates/next-pagebuilder/app/api/[[...slug]]/route.ts +100 -0
  58. package/src/templates/next-pagebuilder/app/api/draft-mode/disable/route.ts +7 -0
  59. package/src/templates/next-pagebuilder/app/api/draft-mode/enable/route.ts +20 -0
  60. package/src/templates/next-pagebuilder/app/api/revalidate/route.ts +121 -0
  61. package/src/templates/next-pagebuilder/app/favicon.ico +0 -0
  62. package/src/templates/next-pagebuilder/app/layout.tsx +80 -0
  63. package/src/templates/next-pagebuilder/app/robots.ts +15 -0
  64. package/src/templates/next-pagebuilder/app/sitemap.md/route.ts +124 -0
  65. package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +80 -0
  66. package/src/templates/next-pagebuilder/app/studio/[[...tool]]/page.tsx +8 -0
  67. package/src/templates/next-pagebuilder/biome.json +239 -0
  68. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +95 -0
  69. package/src/templates/next-pagebuilder/components/layout/header/components/cta-button.tsx +28 -0
  70. package/src/templates/next-pagebuilder/components/layout/header/components/mega-menu-panel.tsx +90 -0
  71. package/src/templates/next-pagebuilder/components/layout/header/components/nav-item-renderer.tsx +98 -0
  72. package/src/templates/next-pagebuilder/components/layout/header/components/nav-leaf-item.tsx +33 -0
  73. package/src/templates/next-pagebuilder/components/layout/header/components/types.ts +7 -0
  74. package/src/templates/next-pagebuilder/components/layout/header/header-client.tsx +110 -0
  75. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +8 -0
  76. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +45 -0
  77. package/src/templates/next-pagebuilder/components/layout/wrapper/index.tsx +30 -0
  78. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/index.tsx +83 -0
  79. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/related-post-item.tsx +27 -0
  80. package/src/templates/next-pagebuilder/components/page-builder/components/description.tsx +17 -0
  81. package/src/templates/next-pagebuilder/components/page-builder/components/hero.tsx +17 -0
  82. package/src/templates/next-pagebuilder/components/page-builder/components/post-collection/content-card.tsx +66 -0
  83. package/src/templates/next-pagebuilder/components/page-builder/components/post-collection/content-grid.tsx +42 -0
  84. package/src/templates/next-pagebuilder/components/page-builder/components/post-collection/index.tsx +28 -0
  85. package/src/templates/next-pagebuilder/components/page-builder/components/post-collection/types.ts +16 -0
  86. package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +36 -0
  87. package/src/templates/next-pagebuilder/components/page-builder/types.ts +23 -0
  88. package/src/templates/next-pagebuilder/components/page-document/index.tsx +91 -0
  89. package/src/templates/next-pagebuilder/components/sanity/draft-mode-toggle.tsx +27 -0
  90. package/src/templates/next-pagebuilder/components/sanity/rich-text.tsx +87 -0
  91. package/src/templates/next-pagebuilder/components/sanity/visual-editing.tsx +27 -0
  92. package/src/templates/next-pagebuilder/components/ui/image/index.tsx +216 -0
  93. package/src/templates/next-pagebuilder/components/ui/link/index.tsx +152 -0
  94. package/src/templates/next-pagebuilder/components/ui/sanity-image/index.tsx +41 -0
  95. package/src/templates/next-pagebuilder/lib/integrations/check-integration.ts +5 -0
  96. package/src/templates/next-pagebuilder/lib/integrations/sanity/client.ts +27 -0
  97. package/src/templates/next-pagebuilder/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  98. package/src/templates/next-pagebuilder/lib/integrations/sanity/components/page-builder-input.tsx +36 -0
  99. package/src/templates/next-pagebuilder/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  100. package/src/templates/next-pagebuilder/lib/integrations/sanity/components/rich-text.tsx +84 -0
  101. package/src/templates/next-pagebuilder/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  102. package/src/templates/next-pagebuilder/lib/integrations/sanity/env.ts +34 -0
  103. package/src/templates/next-pagebuilder/lib/integrations/sanity/fetchers/layout.ts +35 -0
  104. package/src/templates/next-pagebuilder/lib/integrations/sanity/icons.ts +58 -0
  105. package/src/templates/next-pagebuilder/lib/integrations/sanity/live/index.tsx +61 -0
  106. package/src/templates/next-pagebuilder/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  107. package/src/templates/next-pagebuilder/lib/integrations/sanity/page-builder-config.ts +132 -0
  108. package/src/templates/next-pagebuilder/lib/integrations/sanity/page-category.ts +28 -0
  109. package/src/templates/next-pagebuilder/lib/integrations/sanity/queries.ts +281 -0
  110. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.cli.ts +29 -0
  111. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.config.ts +211 -0
  112. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/components/index.ts +4 -0
  113. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  114. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  115. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  116. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  117. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/content/author.ts +70 -0
  118. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  119. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/index.ts +96 -0
  120. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/layout/company-data.ts +62 -0
  121. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  122. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/layout/navbar.ts +74 -0
  123. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  124. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  125. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/metadata.ts +68 -0
  126. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  127. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/page-builder.ts +39 -0
  128. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  129. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/page.ts +232 -0
  130. package/src/templates/next-pagebuilder/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  131. package/src/templates/next-pagebuilder/lib/integrations/sanity/singletons.ts +44 -0
  132. package/src/templates/next-pagebuilder/lib/integrations/sanity/structure.ts +453 -0
  133. package/src/templates/next-pagebuilder/lib/integrations/sanity/utils/image.ts +8 -0
  134. package/src/templates/next-pagebuilder/lib/integrations/sanity/utils/link.ts +137 -0
  135. package/src/templates/next-pagebuilder/lib/integrations/sanity/utils/page-builder-markdown.ts +81 -0
  136. package/src/templates/next-pagebuilder/lib/scripts/sanity-typegen.ts +45 -0
  137. package/src/templates/next-pagebuilder/lib/styles/cn.ts +5 -0
  138. package/src/templates/next-pagebuilder/lib/styles/global.css +70 -0
  139. package/src/templates/next-pagebuilder/lib/utils/base-url.ts +17 -0
  140. package/src/templates/next-pagebuilder/lib/utils/format-date.ts +8 -0
  141. package/src/templates/next-pagebuilder/lib/utils/json-ld.tsx +213 -0
  142. package/src/templates/next-pagebuilder/lib/utils/metadata.ts +167 -0
  143. package/src/templates/next-pagebuilder/lib/utils/sitemap.ts +37 -0
  144. package/src/templates/next-pagebuilder/lib/utils/slug-tag.ts +6 -0
  145. package/src/templates/next-pagebuilder/next.config.ts +134 -0
  146. package/src/templates/next-pagebuilder/package.json +71 -0
  147. package/src/templates/next-pagebuilder/postcss.config.mjs +39 -0
  148. package/src/templates/next-pagebuilder/proxy.ts +81 -0
  149. package/src/templates/next-pagebuilder/svg.d.ts +5 -0
  150. package/src/templates/next-pagebuilder/tsconfig.json +38 -0
  151. package/src/templates/next-webgl/app/layout.tsx +18 -0
  152. package/src/templates/next-webgl/lib/hooks/use-device-detection.ts +1 -1
  153. package/src/templates/next-webgl/lib/hooks/use-media-breakpoint.ts +1 -1
  154. package/src/templates/next-webgl/lib/hooks/use-media.ts +29 -0
  155. package/src/templates/next-webgl/lib/utils/json-ld.tsx +199 -0
  156. package/src/templates/next-webgl/package.json +1 -1
  157. package/src/templates/next-webgl/tsconfig.json +1 -0
  158. package/plugins/no-anchor-element.grit +0 -11
  159. package/plugins/no-relative-parent-imports.grit +0 -6
  160. package/plugins/no-unnecessary-forwardref.grit +0 -5
  161. package/src/helpers/integrate/sanity/files/lib/scripts/copy-sanity-mcp.ts +0 -23
  162. package/src/helpers/integrate/sanity/files/lib/scripts/generate-page.ts +0 -297
  163. package/src/template-hooks/use-media.ts +0 -33
@@ -0,0 +1,281 @@
1
+ import { defineQuery } from "next-sanity"
2
+
3
+ const pageResolvedSlugExpression = `
4
+ select(
5
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
6
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
7
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
8
+ defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
9
+ defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current,
10
+ defined(pageFolder->slug.current) && (!defined(slug.current) || slug.current == "" || slug.current == "/") => pageFolder->slug.current,
11
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
12
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
13
+ defined(pageFolder->parentFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
14
+ defined(pageFolder->parentFolder->parentFolder->slug.current) && defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->parentFolder->slug.current + "/" + pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
15
+ defined(pageFolder->parentFolder->slug.current) && defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->parentFolder->slug.current + "/" + pageFolder->slug.current + "/" + slug.current,
16
+ defined(pageFolder->slug.current) && defined(slug.current) && slug.current != "" && slug.current != "/" => pageFolder->slug.current + "/" + slug.current,
17
+ slug.current
18
+ )
19
+ `
20
+
21
+ const linkItemProjection = `
22
+ ...,
23
+ _type == "pageReference" => {
24
+ page->{
25
+ _id,
26
+ _type,
27
+ slug,
28
+ title,
29
+ "resolvedSlug": select(
30
+ _type == "page" => ${pageResolvedSlugExpression},
31
+ slug.current
32
+ )
33
+ }
34
+ }
35
+ `
36
+
37
+ // Helper for rich text link projections (expandable inside any array field)
38
+ const richTextLinkProjection = `
39
+ ...,
40
+ markDefs[]{
41
+ ...,
42
+ _type == "pageReference" => {
43
+ page->{
44
+ _id,
45
+ _type,
46
+ slug,
47
+ title,
48
+ "resolvedSlug": select(
49
+ _type == "page" => ${pageResolvedSlugExpression},
50
+ slug.current
51
+ )
52
+ }
53
+ }
54
+ }
55
+ `
56
+
57
+ const pageBuilderProjection = `
58
+ pageBuilder[]{
59
+ _key,
60
+ ...@->{
61
+ ...,
62
+ _type == "blogContent" => {
63
+ ...,
64
+ categories[]->{
65
+ _id,
66
+ title,
67
+ slug
68
+ },
69
+ author->{
70
+ _id,
71
+ name,
72
+ avatar,
73
+ role
74
+ },
75
+ body[]{
76
+ ${richTextLinkProjection}
77
+ },
78
+ relatedPosts[]->{
79
+ _id,
80
+ title,
81
+ "resolvedSlug": ${pageResolvedSlugExpression},
82
+ metadata
83
+ }
84
+ }
85
+ }
86
+ }
87
+ `
88
+
89
+ const pageProjection = `
90
+ _id,
91
+ _type,
92
+ title,
93
+ slug,
94
+ "resolvedSlug": ${pageResolvedSlugExpression},
95
+ ${pageBuilderProjection},
96
+ metadata,
97
+ _updatedAt
98
+ `
99
+
100
+ export const PAGE_QUERY = defineQuery(`
101
+ *[
102
+ _type == "page" &&
103
+ (
104
+ ($slug == null && !defined(pageFolder._ref) && (!defined(slug.current) || slug.current == "" || slug.current == "/")) ||
105
+ ${pageResolvedSlugExpression} == $slug
106
+ )
107
+ ][0] {
108
+ ${pageProjection}
109
+ }
110
+ `)
111
+
112
+ export const ALL_PAGE_SLUGS_QUERY = defineQuery(`
113
+ *[_type == "page" && (defined(pageFolder->slug.current) || defined(slug.current))] | order(title asc) {
114
+ title,
115
+ "slug": ${pageResolvedSlugExpression},
116
+ _updatedAt
117
+ }
118
+ `)
119
+
120
+ export const P_REV_SLUGS_QUERY = defineQuery(`
121
+ *[
122
+ _type == "page" &&
123
+ _id in $documentIds
124
+ ] | order(_updatedAt desc) {
125
+ "slug": ${pageResolvedSlugExpression}
126
+ }
127
+ `)
128
+
129
+ export const P_REF_PB_COMP_QUERY = defineQuery(`
130
+ *[
131
+ _type == "page" &&
132
+ count(pageBuilder[_ref == $documentId]) > 0
133
+ ] | order(title asc) {
134
+ _id,
135
+ title,
136
+ "slug": ${pageResolvedSlugExpression}
137
+ }
138
+ `)
139
+
140
+ export const PAGE_SITEMAP_QUERY = defineQuery(`
141
+ *[
142
+ _type == "page" &&
143
+ (
144
+ _id == "page-homepage" ||
145
+ defined(pageFolder->slug.current) ||
146
+ defined(slug.current)
147
+ )
148
+ ] | order(title asc) {
149
+ title,
150
+ "slug": ${pageResolvedSlugExpression},
151
+ _updatedAt,
152
+ "noIndex": select(
153
+ defined(metadata.index) => !metadata.index,
154
+ defined(metadata.noIndex) => metadata.noIndex,
155
+ false
156
+ )
157
+ }
158
+ `)
159
+
160
+ // Blog collection -- fetches pages under a "blog" folder
161
+ // Reaches into the blogContent page-builder block for article-level fields
162
+ export const ALL_BLOG_ARTICLES_QUERY = defineQuery(`
163
+ *[
164
+ _type == "page" &&
165
+ pageFolder->slug.current == "blog" &&
166
+ defined(slug.current) &&
167
+ slug.current != "" &&
168
+ slug.current != "/"
169
+ ] | order(_updatedAt desc) {
170
+ _id,
171
+ title,
172
+ "slug": slug,
173
+ "resolvedSlug": ${pageResolvedSlugExpression},
174
+ "blogContent": pageBuilder[_ref in *[_type == "blogContent"]._id][0]->{
175
+ thumbnail{
176
+ ...,
177
+ alt
178
+ },
179
+ categories[]->{
180
+ _id,
181
+ title,
182
+ slug
183
+ },
184
+ date,
185
+ author->{
186
+ _id,
187
+ name,
188
+ avatar
189
+ }
190
+ },
191
+ metadata,
192
+ _updatedAt
193
+ }
194
+ `)
195
+
196
+ // Company Data
197
+ export const COMPANY_DATA_QUERY = defineQuery(`
198
+ *[_type == "companyData"][0]{
199
+ socialLinks[]{
200
+ _key,
201
+ name,
202
+ icon,
203
+ url,
204
+ label
205
+ }
206
+ }
207
+ `)
208
+
209
+ // Navbar
210
+ // Shared link projection for navbar -- flattens array-of-one to single object
211
+ const navLinkProjection = `
212
+ "link": link[0]{
213
+ ${linkItemProjection}
214
+ }
215
+ `
216
+
217
+ const navLeafItemProjection = `
218
+ _key,
219
+ title,
220
+ ${navLinkProjection}
221
+ `
222
+
223
+ const navColumnProjection = `
224
+ _key,
225
+ title,
226
+ ${navLinkProjection},
227
+ items[]{
228
+ ${navLeafItemProjection}
229
+ }
230
+ `
231
+
232
+ const navMegaMenuProjection = `
233
+ columns[]{
234
+ ${navColumnProjection}
235
+ }
236
+ `
237
+
238
+ export const NAVBAR_QUERY = defineQuery(`
239
+ *[_type == "navbar"][0]{
240
+ logo{
241
+ ...,
242
+ asset->{
243
+ _id,
244
+ url,
245
+ metadata { dimensions }
246
+ }
247
+ },
248
+ navigationItems[]{
249
+ _key,
250
+ title,
251
+ itemType,
252
+ ${navLinkProjection},
253
+ megaMenu{
254
+ ${navMegaMenuProjection}
255
+ }
256
+ },
257
+ ctaButtons[]{
258
+ _key,
259
+ label,
260
+ "link": link[0]{
261
+ ${linkItemProjection}
262
+ },
263
+ variant
264
+ }
265
+ }
266
+ `)
267
+
268
+ // Footer
269
+ export const FOOTER_QUERY = defineQuery(`
270
+ *[_type == "footer"][0]{
271
+ links[]{
272
+ _key,
273
+ title,
274
+ items[]{
275
+ _key,
276
+ label,
277
+ href
278
+ }
279
+ }
280
+ }
281
+ `)
@@ -0,0 +1,29 @@
1
+ /**
2
+ * This configuration file lets you run `$ sanity [command]` in this folder
3
+ * Go to https://www.sanity.io/docs/cli to learn more.
4
+ **/
5
+ import path from "node:path"
6
+ import { fileURLToPath } from "node:url"
7
+ import { defineCliConfig } from "sanity/cli"
8
+ import { dataset, projectId } from "./env"
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
11
+
12
+ export default defineCliConfig({
13
+ api: { projectId, dataset },
14
+ project: {
15
+ basePath: "/studio",
16
+ },
17
+ typegen: {
18
+ path: "./queries.ts",
19
+ schema: "./schema.json",
20
+ generates: "./sanity.types.ts",
21
+ },
22
+ vite: {
23
+ resolve: {
24
+ alias: {
25
+ "~": __dirname,
26
+ },
27
+ },
28
+ },
29
+ })
@@ -0,0 +1,211 @@
1
+ import { table } from "@sanity/table"
2
+ import { defineConfig } from "sanity"
3
+ import {
4
+ defineDocuments,
5
+ defineLocations,
6
+ presentationTool,
7
+ } from "sanity/presentation"
8
+ import { structureTool } from "sanity/structure"
9
+ import { media } from "sanity-plugin-media"
10
+ import { createConfirmPublishAction } from "./confirm-publish-action"
11
+ import { dataset, previewURL, projectId } from "./env"
12
+ import { schema } from "./schemas"
13
+ import { singletonComponentTypes } from "./singletons"
14
+ import { structure } from "./structure"
15
+
16
+ const isProd = process.env.NODE_ENV === "production"
17
+
18
+ const singletonDocumentActions = new Set([
19
+ "publish",
20
+ "discardChanges",
21
+ "restore",
22
+ ])
23
+
24
+ const isSingletonDocument = (schemaType: string, documentId?: string) =>
25
+ singletonComponentTypes.has(schemaType) ||
26
+ (schemaType === "page" && documentId === "page-homepage")
27
+
28
+ const MAX_FOLDER_DEPTH = 6
29
+
30
+ const buildNestedSlugExpression = (
31
+ parentReferenceField: string,
32
+ leafSlugField: string
33
+ ) => {
34
+ const cases = Array.from({ length: MAX_FOLDER_DEPTH }, (_, index) => {
35
+ const depth = MAX_FOLDER_DEPTH - index
36
+ const parentSlugRefs = Array.from({ length: depth }, (_, parentIndex) => {
37
+ const remainingDepth = depth - parentIndex - 1
38
+ return `${parentReferenceField}${"->parentFolder".repeat(remainingDepth)}->slug.current`
39
+ })
40
+
41
+ const pathDefinedChecks = parentSlugRefs
42
+ .map((field) => `defined(${field})`)
43
+ .join(" && ")
44
+
45
+ const childPath = [...parentSlugRefs, leafSlugField].join(' + "/" + ')
46
+ const folderRootPath = parentSlugRefs.join(' + "/" + ')
47
+
48
+ return [
49
+ `${pathDefinedChecks} && ${leafSlugField} == "/" => ${folderRootPath}`,
50
+ `${pathDefinedChecks} && defined(${leafSlugField}) => ${childPath}`,
51
+ ]
52
+ }).flat()
53
+
54
+ return `
55
+ select(
56
+ ${cases.join(",\n ")},
57
+ ${leafSlugField}
58
+ )
59
+ `
60
+ }
61
+
62
+ const pageResolvedSlugExpression = buildNestedSlugExpression(
63
+ "pageFolder",
64
+ "slug.current"
65
+ )
66
+
67
+ export default defineConfig({
68
+ basePath: "/studio",
69
+ projectId,
70
+ dataset,
71
+ schema: {
72
+ ...schema,
73
+ templates: (previousTemplates) => [
74
+ ...previousTemplates.filter(
75
+ (template) =>
76
+ ![
77
+ "page-homepage",
78
+ "page-with-folder",
79
+ "page-folder-with-parent",
80
+ ].includes(template.id)
81
+ ),
82
+ {
83
+ id: "page-homepage",
84
+ title: "Homepage",
85
+ schemaType: "page",
86
+ value: {
87
+ title: "Homepage",
88
+ },
89
+ },
90
+ {
91
+ id: "page-with-folder",
92
+ title: "Page in Folder",
93
+ schemaType: "page",
94
+ parameters: [
95
+ { name: "pageFolderId", title: "Page Folder ID", type: "string" },
96
+ ],
97
+ value: ({ pageFolderId }: { pageFolderId?: string }) => ({
98
+ pageFolder: pageFolderId
99
+ ? {
100
+ _type: "reference",
101
+ _ref: pageFolderId,
102
+ }
103
+ : undefined,
104
+ }),
105
+ },
106
+ {
107
+ id: "page-folder-with-parent",
108
+ title: "Page Folder in Folder",
109
+ schemaType: "pageFolder",
110
+ parameters: [
111
+ { name: "parentFolderId", title: "Parent Folder ID", type: "string" },
112
+ ],
113
+ value: ({ parentFolderId }: { parentFolderId?: string }) => ({
114
+ parentFolder: parentFolderId
115
+ ? {
116
+ _type: "reference",
117
+ _ref: parentFolderId,
118
+ }
119
+ : undefined,
120
+ }),
121
+ },
122
+ ],
123
+ },
124
+ document: {
125
+ actions: (previousActions, context) => {
126
+ const filtered = isSingletonDocument(
127
+ context.schemaType,
128
+ context.documentId
129
+ )
130
+ ? previousActions.filter(
131
+ (action) =>
132
+ action.action !== undefined &&
133
+ singletonDocumentActions.has(action.action)
134
+ )
135
+ : previousActions
136
+
137
+ if (!isProd) return filtered
138
+
139
+ return filtered.map((action) =>
140
+ action.action === "publish"
141
+ ? createConfirmPublishAction(action)
142
+ : action
143
+ )
144
+ },
145
+ newDocumentOptions: (previousOptions) =>
146
+ previousOptions.filter(
147
+ (option) =>
148
+ !singletonComponentTypes.has(option.templateId) &&
149
+ option.templateId !== "page-homepage"
150
+ ),
151
+ },
152
+ plugins: [
153
+ structureTool({ structure }),
154
+
155
+ presentationTool({
156
+ name: "preview",
157
+ title: "Preview",
158
+ resolve: {
159
+ // Map routes to documents and GROQ filters
160
+ mainDocuments: defineDocuments([
161
+ {
162
+ route: "/",
163
+ filter: '_type == "page" && _id == "page-homepage"',
164
+ },
165
+ // Page builder pages - all other slugs (supports nested paths)
166
+ {
167
+ route: "/:slug+",
168
+ filter: `_type == "page" && ${pageResolvedSlugExpression} == $slug`,
169
+ },
170
+ ]),
171
+ locations: {
172
+ page: defineLocations({
173
+ select: {
174
+ _id: "_id",
175
+ title: "title",
176
+ slug: "slug.current",
177
+ resolvedSlug: pageResolvedSlugExpression,
178
+ },
179
+ resolve: (doc) => {
180
+ if (doc?._id === "page-homepage") {
181
+ return {
182
+ locations: [{ title: doc?.title || "Homepage", href: "/" }],
183
+ }
184
+ }
185
+ if (doc?.resolvedSlug) {
186
+ return {
187
+ locations: [
188
+ {
189
+ title: doc?.title || "Page",
190
+ href: `/${doc.resolvedSlug}`,
191
+ },
192
+ ],
193
+ }
194
+ }
195
+ return { locations: [] }
196
+ },
197
+ }),
198
+ },
199
+ },
200
+ previewUrl: {
201
+ origin: previewURL,
202
+ draftMode: {
203
+ enable: "/api/draft-mode/enable",
204
+ disable: "/api/draft-mode/disable",
205
+ },
206
+ },
207
+ }),
208
+ media(),
209
+ table(),
210
+ ],
211
+ })
@@ -0,0 +1,4 @@
1
+ export { blogContent } from "./reusable/blog-content"
2
+ export { description } from "./reusable/description"
3
+ export { hero } from "./reusable/hero"
4
+ export { blogCollection } from "./singleton/content-collection"
@@ -0,0 +1,89 @@
1
+ import { defineField, defineType } from "sanity"
2
+ import { contentIcon } from "@/lib/integrations/sanity/icons"
3
+
4
+ export const blogContent = defineType({
5
+ name: "blogContent",
6
+ title: "Blog Content",
7
+ type: "document",
8
+ icon: contentIcon,
9
+ fields: [
10
+ defineField({
11
+ name: "title",
12
+ title: "Title",
13
+ type: "string",
14
+ validation: (Rule) => Rule.required(),
15
+ }),
16
+ defineField({
17
+ name: "thumbnail",
18
+ title: "Thumbnail",
19
+ type: "image",
20
+ description: "Featured image for the article",
21
+ options: { hotspot: true },
22
+ fields: [
23
+ {
24
+ name: "alt",
25
+ title: "Alt Text",
26
+ type: "string",
27
+ description: "Alternative text for screen readers",
28
+ validation: (Rule) => Rule.required(),
29
+ },
30
+ ],
31
+ }),
32
+ defineField({
33
+ name: "categories",
34
+ title: "Categories",
35
+ type: "array",
36
+ of: [{ type: "reference", to: [{ type: "blogCategory" }] }],
37
+ description: "Category tags for filtering",
38
+ }),
39
+ defineField({
40
+ name: "date",
41
+ title: "Publication Date",
42
+ type: "date",
43
+ options: { dateFormat: "YYYY-MM-DD" },
44
+ }),
45
+ defineField({
46
+ name: "author",
47
+ title: "Author",
48
+ type: "reference",
49
+ to: [{ type: "author" }],
50
+ }),
51
+ defineField({
52
+ name: "body",
53
+ title: "Body",
54
+ type: "richText",
55
+ description: "Article content",
56
+ }),
57
+ defineField({
58
+ name: "relatedPosts",
59
+ title: "Related Posts",
60
+ type: "array",
61
+ of: [
62
+ {
63
+ type: "reference",
64
+ to: [{ type: "page" }],
65
+ options: {
66
+ filter:
67
+ 'pageFolder->slug.current == "blog" && defined(slug.current) && slug.current != ""',
68
+ },
69
+ },
70
+ ],
71
+ description: "Manually curated related blog pages",
72
+ validation: (Rule) => Rule.max(3),
73
+ }),
74
+ ],
75
+ preview: {
76
+ select: {
77
+ title: "title",
78
+ date: "date",
79
+ media: "thumbnail",
80
+ },
81
+ prepare({ title, date, media }) {
82
+ return {
83
+ title: title || "Blog Content",
84
+ subtitle: date || "No date set",
85
+ media,
86
+ }
87
+ },
88
+ },
89
+ })
@@ -0,0 +1,29 @@
1
+ import { defineField, defineType } from "sanity"
2
+ import { descriptionIcon } from "@/lib/integrations/sanity/icons"
3
+
4
+ export const description = defineType({
5
+ name: "description",
6
+ title: "Description",
7
+ type: "document",
8
+ icon: descriptionIcon,
9
+ fields: [
10
+ defineField({
11
+ name: "description",
12
+ title: "Description",
13
+ type: "text",
14
+ rows: 4,
15
+ validation: (Rule) => Rule.required(),
16
+ }),
17
+ ],
18
+ preview: {
19
+ select: {
20
+ description: "description",
21
+ },
22
+ prepare({ description }) {
23
+ return {
24
+ title: "Description",
25
+ subtitle: description || "Untitled",
26
+ }
27
+ },
28
+ },
29
+ })
@@ -0,0 +1,28 @@
1
+ import { defineField, defineType } from "sanity"
2
+ import { heroIcon } from "@/lib/integrations/sanity/icons"
3
+
4
+ export const hero = defineType({
5
+ name: "hero",
6
+ title: "Heading",
7
+ type: "document",
8
+ icon: heroIcon,
9
+ fields: [
10
+ defineField({
11
+ name: "headline",
12
+ title: "Heading",
13
+ type: "string",
14
+ validation: (Rule) => Rule.required(),
15
+ }),
16
+ ],
17
+ preview: {
18
+ select: {
19
+ title: "headline",
20
+ },
21
+ prepare({ title }) {
22
+ return {
23
+ title: title || "Untitled",
24
+ subtitle: "Heading",
25
+ }
26
+ },
27
+ },
28
+ })