Skip to content
This repository was archived by the owner on Nov 19, 2025. It is now read-only.

Commit cfebbd2

Browse files
committed
feat: add page builder
1 parent adb2b95 commit cfebbd2

File tree

23 files changed

+491
-21
lines changed

23 files changed

+491
-21
lines changed

apps/cms/sanity.types.ts

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,16 @@ export type SanityAssetSourceData = {
125125
url?: string;
126126
};
127127

128-
export type LinkRequired = {
129-
_type: "linkRequired";
130-
children: string;
131-
type: "href" | "page" | "post";
128+
export type PageBuilder = Array<{
129+
_key: string;
130+
} & Hero | {
131+
_key: string;
132+
} & Heading>;
133+
134+
export type Link = {
135+
_type: "link";
136+
children?: string;
137+
type?: "href" | "page" | "post";
132138
href?: string;
133139
page?: {
134140
_ref: string;
@@ -144,10 +150,17 @@ export type LinkRequired = {
144150
};
145151
};
146152

147-
export type Link = {
148-
_type: "link";
149-
children?: string;
150-
type?: "href" | "page" | "post";
153+
export type Hero = {
154+
_type: "hero";
155+
title: string;
156+
description: string;
157+
link: LinkRequired;
158+
};
159+
160+
export type LinkRequired = {
161+
_type: "linkRequired";
162+
children: string;
163+
type: "href" | "page" | "post";
151164
href?: string;
152165
page?: {
153166
_ref: string;
@@ -163,6 +176,11 @@ export type Link = {
163176
};
164177
};
165178

179+
export type Heading = {
180+
_type: "heading";
181+
heading: string;
182+
};
183+
166184
export type Content = Array<{
167185
children?: Array<{
168186
marks?: Array<string>;
@@ -231,6 +249,7 @@ export type Page = {
231249
_rev: string;
232250
title: string;
233251
slug: Slug;
252+
pageBuilder: PageBuilder;
234253
seo: Seo;
235254
};
236255

@@ -376,13 +395,29 @@ export type SanityAssistSchemaTypeField = {
376395
} & SanityAssistInstruction>;
377396
};
378397

379-
export type AllSanitySchemaTypes = SanityImagePaletteSwatch | SanityImagePalette | SanityImageDimensions | SanityImageHotspot | SanityImageCrop | SanityFileAsset | SanityImageAsset | SanityImageMetadata | Geopoint | SanityAssetSourceData | LinkRequired | Link | Content | Post | Person | Page | Seo | Slug | Settings | SanityAssistInstructionTask | SanityAssistTaskStatus | SanityAssistSchemaTypeAnnotations | SanityAssistOutputType | SanityAssistOutputField | SanityAssistInstructionContext | AssistInstructionContext | SanityAssistInstructionUserInput | SanityAssistInstructionPrompt | SanityAssistInstructionFieldRef | SanityAssistInstruction | SanityAssistSchemaTypeField;
398+
export type AllSanitySchemaTypes = SanityImagePaletteSwatch | SanityImagePalette | SanityImageDimensions | SanityImageHotspot | SanityImageCrop | SanityFileAsset | SanityImageAsset | SanityImageMetadata | Geopoint | SanityAssetSourceData | PageBuilder | Link | Hero | LinkRequired | Heading | Content | Post | Person | Page | Seo | Slug | Settings | SanityAssistInstructionTask | SanityAssistTaskStatus | SanityAssistSchemaTypeAnnotations | SanityAssistOutputType | SanityAssistOutputField | SanityAssistInstructionContext | AssistInstructionContext | SanityAssistInstructionUserInput | SanityAssistInstructionPrompt | SanityAssistInstructionFieldRef | SanityAssistInstruction | SanityAssistSchemaTypeField;
380399
export declare const internalGroqTypeReferenceTo: unique symbol;
381400
// Source: ./src/queries/get-page-query.ts
382401
// Variable: getPageQuery
383-
// Query: *[_type == 'page' && slug.current == $slug][0] { title, }
402+
// Query: *[_type == 'page' && slug.current == $slug][0] { _id, _type, title, pageBuilder[] { _type, _key, _type == "hero" => { title, description, link { children, "href": coalesce( select( type == "page" => "/" + page->slug.current, type == "post" => "/posts/" + post->slug.current, href ), "" )},}, _type == "heading" => { heading,},}, }
384403
export type GetPageQueryResult = {
404+
_id: string;
405+
_type: "page";
385406
title: string;
407+
pageBuilder: Array<{
408+
_type: "heading";
409+
_key: string;
410+
heading: string;
411+
} | {
412+
_type: "hero";
413+
_key: string;
414+
title: string;
415+
description: string;
416+
link: {
417+
children: string;
418+
href: string | "";
419+
};
420+
}>;
386421
} | null;
387422

388423
// Source: ./src/queries/get-post-query.ts
@@ -428,7 +463,7 @@ export type GetPostQueryResult = {
428463
import "@sanity/client";
429464
declare module "@sanity/client" {
430465
interface SanityQueries {
431-
"\n *[_type == 'page' && slug.current == $slug][0] {\n title,\n }\n": GetPageQueryResult;
466+
"\n *[_type == 'page' && slug.current == $slug][0] {\n _id,\n _type,\n title,\n pageBuilder[] {\n _type,\n _key,\n _type == \"hero\" => {\n title,\n description,\n link {\n children,\n \"href\": coalesce(\n select(\n type == \"page\" => \"/\" + page->slug.current,\n type == \"post\" => \"/posts/\" + post->slug.current,\n href\n ),\n \"\"\n )\n},\n},\n _type == \"heading\" => {\n heading,\n},\n},\n }\n": GetPageQueryResult;
432467
"\n *[_type == 'post' && slug.current == $slug][0] {\n title,\n content[] {\n ...,\n markDefs[] {\n ...,\n _type == \"link\" => {\n children,\n \"href\": coalesce(\n select(\n type == \"page\" => \"/\" + page->slug.current,\n type == \"post\" => \"/posts/\" + post->slug.current,\n href\n ),\n \"\"\n )\n},\n }\n},\n }\n": GetPostQueryResult;
433468
}
434469
}

apps/cms/schema.json

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,49 @@
663663
}
664664
},
665665
{
666-
"name": "linkRequired",
666+
"name": "pageBuilder",
667+
"type": "type",
668+
"value": {
669+
"type": "array",
670+
"of": {
671+
"type": "union",
672+
"of": [
673+
{
674+
"type": "object",
675+
"attributes": {
676+
"_key": {
677+
"type": "objectAttribute",
678+
"value": {
679+
"type": "string"
680+
}
681+
}
682+
},
683+
"rest": {
684+
"type": "inline",
685+
"name": "hero"
686+
}
687+
},
688+
{
689+
"type": "object",
690+
"attributes": {
691+
"_key": {
692+
"type": "objectAttribute",
693+
"value": {
694+
"type": "string"
695+
}
696+
}
697+
},
698+
"rest": {
699+
"type": "inline",
700+
"name": "heading"
701+
}
702+
}
703+
]
704+
}
705+
}
706+
},
707+
{
708+
"name": "link",
667709
"type": "type",
668710
"value": {
669711
"type": "object",
@@ -672,15 +714,15 @@
672714
"type": "objectAttribute",
673715
"value": {
674716
"type": "string",
675-
"value": "linkRequired"
717+
"value": "link"
676718
}
677719
},
678720
"children": {
679721
"type": "objectAttribute",
680722
"value": {
681723
"type": "string"
682724
},
683-
"optional": false
725+
"optional": true
684726
},
685727
"type": {
686728
"type": "objectAttribute",
@@ -701,7 +743,7 @@
701743
}
702744
]
703745
},
704-
"optional": false
746+
"optional": true
705747
},
706748
"href": {
707749
"type": "objectAttribute",
@@ -774,7 +816,7 @@
774816
}
775817
},
776818
{
777-
"name": "link",
819+
"name": "hero",
778820
"type": "type",
779821
"value": {
780822
"type": "object",
@@ -783,15 +825,53 @@
783825
"type": "objectAttribute",
784826
"value": {
785827
"type": "string",
786-
"value": "link"
828+
"value": "hero"
829+
}
830+
},
831+
"title": {
832+
"type": "objectAttribute",
833+
"value": {
834+
"type": "string"
835+
},
836+
"optional": false
837+
},
838+
"description": {
839+
"type": "objectAttribute",
840+
"value": {
841+
"type": "string"
842+
},
843+
"optional": false
844+
},
845+
"link": {
846+
"type": "objectAttribute",
847+
"value": {
848+
"type": "inline",
849+
"name": "linkRequired"
850+
},
851+
"optional": false
852+
}
853+
}
854+
}
855+
},
856+
{
857+
"name": "linkRequired",
858+
"type": "type",
859+
"value": {
860+
"type": "object",
861+
"attributes": {
862+
"_type": {
863+
"type": "objectAttribute",
864+
"value": {
865+
"type": "string",
866+
"value": "linkRequired"
787867
}
788868
},
789869
"children": {
790870
"type": "objectAttribute",
791871
"value": {
792872
"type": "string"
793873
},
794-
"optional": true
874+
"optional": false
795875
},
796876
"type": {
797877
"type": "objectAttribute",
@@ -812,7 +892,7 @@
812892
}
813893
]
814894
},
815-
"optional": true
895+
"optional": false
816896
},
817897
"href": {
818898
"type": "objectAttribute",
@@ -884,6 +964,29 @@
884964
}
885965
}
886966
},
967+
{
968+
"name": "heading",
969+
"type": "type",
970+
"value": {
971+
"type": "object",
972+
"attributes": {
973+
"_type": {
974+
"type": "objectAttribute",
975+
"value": {
976+
"type": "string",
977+
"value": "heading"
978+
}
979+
},
980+
"heading": {
981+
"type": "objectAttribute",
982+
"value": {
983+
"type": "string"
984+
},
985+
"optional": false
986+
}
987+
}
988+
}
989+
},
887990
{
888991
"name": "content",
889992
"type": "type",
@@ -1343,6 +1446,14 @@
13431446
},
13441447
"optional": false
13451448
},
1449+
"pageBuilder": {
1450+
"type": "objectAttribute",
1451+
"value": {
1452+
"type": "inline",
1453+
"name": "pageBuilder"
1454+
},
1455+
"optional": false
1456+
},
13461457
"seo": {
13471458
"type": "objectAttribute",
13481459
"value": {

apps/cms/src/documents/page.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,11 @@ export const page = compose(
2929
},
3030
validation: (Rule) => Rule.required(),
3131
}),
32+
33+
defineField({
34+
name: "pageBuilder",
35+
title: "Page Builder",
36+
type: "pageBuilder",
37+
}),
3238
],
3339
});

apps/cms/src/objects/heading.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { defineField, defineType } from "sanity";
2+
3+
export const heading = defineType({
4+
name: "heading",
5+
title: "Heading",
6+
type: "object",
7+
fields: [
8+
defineField({
9+
name: "heading",
10+
title: "Heading",
11+
type: "string",
12+
validation: (Rule) => Rule.required(),
13+
}),
14+
],
15+
validation: (Rule) => Rule.required(),
16+
});

apps/cms/src/objects/hero.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { defineField, defineType } from "sanity";
2+
3+
export const hero = defineType({
4+
name: "hero",
5+
title: "Hero",
6+
type: "object",
7+
fields: [
8+
defineField({
9+
name: "title",
10+
title: "Title",
11+
type: "string",
12+
validation: (Rule) => Rule.required(),
13+
}),
14+
15+
defineField({
16+
name: "description",
17+
title: "Description",
18+
type: "text",
19+
validation: (Rule) => Rule.required(),
20+
}),
21+
22+
defineField({
23+
name: "link",
24+
title: "Link",
25+
type: "linkRequired",
26+
validation: (Rule) => Rule.required(),
27+
}),
28+
],
29+
validation: (Rule) => Rule.required(),
30+
});

0 commit comments

Comments
 (0)