Skip to content

Commit f8d3bf4

Browse files
authored
feat: schema redesign — socialPreview, podcastSeries, category, short + field updates (#680)
New partial:\n- socialPreview (ogTitle, ogDescription, ogImage with hotspot, twitterCardType, noIndex) — applied to post, podcast, page, sponsor\n\nNew document types:\n- podcastSeries (title, slug, description, coverImage, youtubePlaylistId, isActive)\n- category (title, slug, description, color)\n- short (title, slug, youtube, thumbnail, parentEpisode→podcast, publishedAt, duration, categories)\n\nUpdated documents:\n- podcast: +11 fields (thumbnail, duration, chapters with title/timestamp/seconds, series→podcastSeries, seriesOrder, listenLinks object, transcript, contentType enum, relatedShorts, relatedBlogPost)\n- guest: +company, +role\n- post: +categories (refs to category)\n- page, sponsor: +socialPreview partial\n\nAll new types registered in sanity.config.ts.\nDeferred: course, lesson, page builder blocks."
1 parent 1a79e1d commit f8d3bf4

File tree

10 files changed

+456
-2
lines changed

10 files changed

+456
-2
lines changed

apps/sanity/sanity.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ import videoAnalytics from "./schemas/documents/videoAnalytics";
4242
import sponsorLead from "./schemas/documents/sponsorLead";
4343
import sponsorPool from "./schemas/documents/sponsorPool";
4444
import tableSchema, { rowType } from "./schemas/custom/table";
45+
import podcastSeries from "./schemas/documents/podcastSeries";
46+
import category from "./schemas/documents/category";
47+
import short from "./schemas/documents/short";
4548

4649
// Sanity Studio env vars (SANITY_STUDIO_ prefix is auto-exposed by Sanity CLI)
4750
const projectId = process.env.SANITY_STUDIO_PROJECT_ID || "hfh83o0w";
@@ -163,6 +166,10 @@ export default defineConfig({
163166
videoAnalytics,
164167
sponsorLead,
165168
sponsorPool,
169+
// New document types
170+
podcastSeries,
171+
category,
172+
short,
166173
],
167174
},
168175
document: {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { defineField, defineType } from "sanity";
2+
import { TagIcon } from "@sanity/icons";
3+
4+
export default defineType({
5+
name: "category",
6+
title: "Category",
7+
type: "document",
8+
icon: TagIcon,
9+
fields: [
10+
defineField({
11+
name: "title",
12+
title: "Title",
13+
type: "string",
14+
validation: (rule) => rule.required(),
15+
}),
16+
defineField({
17+
name: "slug",
18+
title: "Slug",
19+
type: "slug",
20+
options: {
21+
source: "title",
22+
maxLength: 96,
23+
isUnique: (value, context) =>
24+
context.defaultIsUnique(value, context),
25+
},
26+
validation: (rule) => rule.required(),
27+
}),
28+
defineField({
29+
name: "description",
30+
title: "Description",
31+
type: "text",
32+
}),
33+
defineField({
34+
name: "color",
35+
title: "Color",
36+
type: "string",
37+
description: "Hex color for category badge (e.g. #7c3aed)",
38+
}),
39+
],
40+
preview: {
41+
select: {
42+
title: "title",
43+
},
44+
},
45+
});

apps/sanity/schemas/documents/guest.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { UserIcon } from "@sanity/icons";
2-
import { defineType } from "sanity";
2+
import { defineField, defineType } from "sanity";
33

44
import userType from "../partials/user";
55

@@ -9,4 +9,19 @@ export default defineType({
99
title: "Guest",
1010
icon: UserIcon,
1111
type: "document",
12+
fields: [
13+
...userType.fields,
14+
defineField({
15+
name: "company",
16+
title: "Company",
17+
type: "string",
18+
description: "Company or organization",
19+
}),
20+
defineField({
21+
name: "role",
22+
title: "Role",
23+
type: "string",
24+
description: "Job title or role",
25+
}),
26+
],
1227
});

apps/sanity/schemas/documents/page.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ import { DocumentTextIcon } from "@sanity/icons";
22
import { defineType } from "sanity";
33

44
import contentType from "../partials/content";
5+
import {
6+
socialPreviewFields,
7+
socialPreviewGroup,
8+
} from "../partials/socialPreview";
59

610
export default defineType({
711
...contentType,
812
name: "page",
913
title: "Page",
1014
icon: DocumentTextIcon,
1115
type: "document",
16+
groups: [...(contentType.groups || []), socialPreviewGroup],
17+
fields: [...contentType.fields, ...socialPreviewFields],
1218
});

apps/sanity/schemas/documents/podcast.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@ import { format, parseISO } from "date-fns";
33
import { defineField, defineType } from "sanity";
44

55
import contentType from "../partials/content";
6+
import {
7+
socialPreviewFields,
8+
socialPreviewGroup,
9+
} from "../partials/socialPreview";
610
import podcastType from "./podcastType";
711
import guestType from "./guest";
812
import authorType from "./author";
13+
import podcastSeriesType from "./podcastSeries";
14+
import shortType from "./short";
15+
import postType from "./post";
916

1017
export default defineType({
1118
...contentType,
@@ -15,13 +22,16 @@ export default defineType({
1522
type: "document",
1623
groups: [
1724
...(contentType.groups || []),
25+
socialPreviewGroup,
1826
{
1927
name: "podcast",
2028
title: "Podcast Details",
2129
},
2230
],
2331
fields: [
2432
...contentType.fields,
33+
...socialPreviewFields,
34+
// --- Existing podcast-specific fields ---
2535
defineField({
2636
name: "podcastType",
2737
title: "Podcast Type",
@@ -125,6 +135,153 @@ export default defineType({
125135
type: "podcastRssEpisode",
126136
// validation: (rule) => [rule.required()],
127137
}),
138+
// --- New fields ---
139+
defineField({
140+
name: "thumbnail",
141+
title: "Thumbnail",
142+
type: "image",
143+
options: {
144+
hotspot: true,
145+
},
146+
group: "podcast",
147+
description:
148+
"YouTube-optimized thumbnail (1280×720). Falls back to coverImage.",
149+
}),
150+
defineField({
151+
name: "duration",
152+
title: "Duration",
153+
type: "number",
154+
group: "podcast",
155+
description: "Episode duration in seconds",
156+
}),
157+
defineField({
158+
name: "chapters",
159+
title: "Chapters",
160+
type: "array",
161+
group: "podcast",
162+
of: [
163+
{
164+
type: "object",
165+
fields: [
166+
defineField({
167+
name: "title",
168+
title: "Title",
169+
type: "string",
170+
validation: (rule) => rule.required(),
171+
}),
172+
defineField({
173+
name: "timestamp",
174+
title: "Timestamp",
175+
type: "string",
176+
validation: (rule) => rule.required(),
177+
description: "Display format e.g. 02:34",
178+
}),
179+
defineField({
180+
name: "seconds",
181+
title: "Seconds",
182+
type: "number",
183+
validation: (rule) => rule.required(),
184+
description: "Timestamp in seconds for seeking",
185+
}),
186+
],
187+
preview: {
188+
select: {
189+
title: "title",
190+
subtitle: "timestamp",
191+
},
192+
},
193+
},
194+
],
195+
}),
196+
defineField({
197+
name: "series",
198+
title: "Series",
199+
type: "reference",
200+
to: [{ type: podcastSeriesType.name }],
201+
group: "podcast",
202+
}),
203+
defineField({
204+
name: "seriesOrder",
205+
title: "Series Order",
206+
type: "number",
207+
group: "podcast",
208+
description: "Position within the series",
209+
}),
210+
defineField({
211+
name: "listenLinks",
212+
title: "Listen Links",
213+
type: "object",
214+
group: "podcast",
215+
description: "Multi-platform listen links",
216+
fields: [
217+
defineField({
218+
name: "youtube",
219+
title: "YouTube",
220+
type: "string",
221+
}),
222+
defineField({
223+
name: "spotify",
224+
title: "Spotify",
225+
type: "string",
226+
}),
227+
defineField({
228+
name: "apple",
229+
title: "Apple Podcasts",
230+
type: "string",
231+
}),
232+
defineField({
233+
name: "overcast",
234+
title: "Overcast",
235+
type: "string",
236+
}),
237+
defineField({
238+
name: "pocketCasts",
239+
title: "Pocket Casts",
240+
type: "string",
241+
}),
242+
defineField({
243+
name: "rss",
244+
title: "RSS",
245+
type: "string",
246+
}),
247+
],
248+
}),
249+
defineField({
250+
name: "transcript",
251+
title: "Transcript",
252+
type: "text",
253+
group: "podcast",
254+
description: "Full episode transcript",
255+
}),
256+
defineField({
257+
name: "contentType",
258+
title: "Content Type",
259+
type: "string",
260+
group: "podcast",
261+
description: "Episode format",
262+
options: {
263+
list: ["interview", "solo", "tutorial", "news", "review"],
264+
},
265+
}),
266+
defineField({
267+
name: "relatedShorts",
268+
title: "Related Shorts",
269+
type: "array",
270+
group: "podcast",
271+
of: [
272+
{
273+
type: "reference",
274+
to: [{ type: shortType.name }],
275+
},
276+
],
277+
}),
278+
defineField({
279+
name: "relatedBlogPost",
280+
title: "Related Blog Post",
281+
type: "reference",
282+
group: "podcast",
283+
to: [{ type: postType.name }],
284+
}),
128285
],
129286
orderings: [
130287
{
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { defineField, defineType } from "sanity";
2+
import { FaLayerGroup } from "react-icons/fa";
3+
4+
export default defineType({
5+
name: "podcastSeries",
6+
title: "Podcast Series",
7+
type: "document",
8+
icon: FaLayerGroup,
9+
fields: [
10+
defineField({
11+
name: "title",
12+
title: "Title",
13+
type: "string",
14+
validation: (rule) => rule.required(),
15+
}),
16+
defineField({
17+
name: "slug",
18+
title: "Slug",
19+
type: "slug",
20+
options: {
21+
source: "title",
22+
maxLength: 96,
23+
isUnique: (value, context) =>
24+
context.defaultIsUnique(value, context),
25+
},
26+
validation: (rule) => rule.required(),
27+
}),
28+
defineField({
29+
name: "description",
30+
title: "Description",
31+
type: "text",
32+
}),
33+
defineField({
34+
name: "coverImage",
35+
title: "Cover Image",
36+
type: "image",
37+
options: {
38+
hotspot: true,
39+
},
40+
}),
41+
defineField({
42+
name: "youtubePlaylistId",
43+
title: "YouTube Playlist ID",
44+
type: "string",
45+
description: "YouTube playlist ID for this series",
46+
}),
47+
defineField({
48+
name: "isActive",
49+
title: "Is Active",
50+
type: "boolean",
51+
initialValue: true,
52+
description: "Is this series still producing new episodes?",
53+
}),
54+
],
55+
preview: {
56+
select: {
57+
title: "title",
58+
subtitle: "description",
59+
},
60+
},
61+
});
Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
import { HiOutlinePencilAlt } from "react-icons/hi";
2-
import { defineType } from "sanity";
2+
import { defineField, defineType } from "sanity";
33

44
import contentType from "../partials/content";
5+
import {
6+
socialPreviewFields,
7+
socialPreviewGroup,
8+
} from "../partials/socialPreview";
9+
import categoryType from "./category";
510

611
export default defineType({
712
...contentType,
813
name: "post",
914
title: "Post",
1015
icon: HiOutlinePencilAlt,
1116
type: "document",
17+
groups: [...(contentType.groups || []), socialPreviewGroup],
18+
fields: [
19+
...contentType.fields,
20+
...socialPreviewFields,
21+
defineField({
22+
name: "categories",
23+
title: "Categories",
24+
type: "array",
25+
of: [
26+
{
27+
type: "reference",
28+
to: [{ type: categoryType.name }],
29+
},
30+
],
31+
}),
32+
],
1233
});

0 commit comments

Comments
 (0)