levrops-contracts 1.3.1 → 1.3.2
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/README.md +83 -0
- package/contracts/backend/schema/migration_discipline.schema.yaml +220 -0
- package/contracts/content/heirloom/lead-capture.ts +212 -0
- package/contracts/content/index.ts +8 -0
- package/creative/README.md +21 -0
- package/creative/creative_cluster.schema.json +44 -0
- package/creative/creative_edge_alignment.schema.json +59 -0
- package/creative/creative_signal_profile.schema.json +71 -0
- package/creative/creative_whisper.schema.json +70 -0
- package/creative/models.py +86 -0
- package/creative/requirements.txt +2 -0
- package/package.json +6 -4
- package/sanity/schema/generated/blocks/cta.ts +68 -0
- package/sanity/schema/generated/blocks/editorial_grid.ts +58 -0
- package/sanity/schema/generated/blocks/hero.ts +72 -0
- package/sanity/schema/generated/blocks/product.ts +66 -0
- package/sanity/schema/generated/blocks/story.ts +59 -0
- package/sanity/schema/generated/index.ts +16 -0
- package/sanity/schema/generated/page.ts +74 -0
- package/sanity/schema/levropsStructure/blocks/cta.ts +77 -0
- package/sanity/schema/levropsStructure/blocks/editorialGrid.ts +66 -0
- package/sanity/schema/levropsStructure/blocks/hero.ts +71 -0
- package/sanity/schema/levropsStructure/blocks/product.ts +69 -0
- package/sanity/schema/levropsStructure/blocks/ritual.ts +59 -0
- package/sanity/schema/levropsStructure/blocks/story.ts +57 -0
- package/sanity/schema/levropsStructure/index.ts +21 -0
- package/sanity/schema/levropsStructure/page.ts +118 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineType, defineField, string, text, url } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CTA Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "cta"
|
|
7
|
+
* Sanity type: "block.cta"
|
|
8
|
+
*/
|
|
9
|
+
export const blockCta = defineType({
|
|
10
|
+
name: "block.cta",
|
|
11
|
+
title: "CTA Block",
|
|
12
|
+
type: "object",
|
|
13
|
+
description: "Call to action block",
|
|
14
|
+
fields: [
|
|
15
|
+
defineField({
|
|
16
|
+
name: "_key",
|
|
17
|
+
type: "string",
|
|
18
|
+
title: "Key",
|
|
19
|
+
description: "Unique key for array item (auto-generated)",
|
|
20
|
+
readOnly: true,
|
|
21
|
+
}),
|
|
22
|
+
defineField({
|
|
23
|
+
name: "headline",
|
|
24
|
+
type: string(),
|
|
25
|
+
title: "Headline",
|
|
26
|
+
validation: (Rule) => Rule.required().max(100),
|
|
27
|
+
description: "CTA headline text",
|
|
28
|
+
}),
|
|
29
|
+
defineField({
|
|
30
|
+
name: "description",
|
|
31
|
+
type: text(),
|
|
32
|
+
title: "Description",
|
|
33
|
+
description: "Supporting description text",
|
|
34
|
+
}),
|
|
35
|
+
defineField({
|
|
36
|
+
name: "buttonText",
|
|
37
|
+
type: string(),
|
|
38
|
+
title: "Button Text",
|
|
39
|
+
validation: (Rule) => Rule.required().max(50),
|
|
40
|
+
description: "CTA button text",
|
|
41
|
+
}),
|
|
42
|
+
defineField({
|
|
43
|
+
name: "buttonLink",
|
|
44
|
+
type: url(),
|
|
45
|
+
title: "Button Link",
|
|
46
|
+
validation: (Rule) => Rule.required(),
|
|
47
|
+
description: "CTA button URL",
|
|
48
|
+
}),
|
|
49
|
+
defineField({
|
|
50
|
+
name: "variant",
|
|
51
|
+
type: string(),
|
|
52
|
+
title: "Variant",
|
|
53
|
+
description: "CTA style variant",
|
|
54
|
+
options: {
|
|
55
|
+
list: [
|
|
56
|
+
{ title: "Primary", value: "primary" },
|
|
57
|
+
{ title: "Secondary", value: "secondary" },
|
|
58
|
+
{ title: "Outline", value: "outline" },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
initialValue: "primary",
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
preview: {
|
|
65
|
+
select: {
|
|
66
|
+
headline: "headline",
|
|
67
|
+
buttonText: "buttonText",
|
|
68
|
+
},
|
|
69
|
+
prepare({ headline, buttonText }) {
|
|
70
|
+
return {
|
|
71
|
+
title: headline || "CTA Block",
|
|
72
|
+
subtitle: buttonText || "No button text",
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineType, defineField, string, array, reference } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Editorial Grid Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "editorial_grid"
|
|
7
|
+
* Sanity type: "block.editorialGrid"
|
|
8
|
+
*/
|
|
9
|
+
export const blockEditorialGrid = defineType({
|
|
10
|
+
name: "block.editorialGrid",
|
|
11
|
+
title: "Editorial Grid Block",
|
|
12
|
+
type: "object",
|
|
13
|
+
description: "Grid of editorial content",
|
|
14
|
+
fields: [
|
|
15
|
+
defineField({
|
|
16
|
+
name: "_key",
|
|
17
|
+
type: "string",
|
|
18
|
+
title: "Key",
|
|
19
|
+
description: "Unique key for array item (auto-generated)",
|
|
20
|
+
readOnly: true,
|
|
21
|
+
}),
|
|
22
|
+
defineField({
|
|
23
|
+
name: "title",
|
|
24
|
+
type: string(),
|
|
25
|
+
title: "Title",
|
|
26
|
+
validation: (Rule) => Rule.required().max(200),
|
|
27
|
+
description: "Grid section title",
|
|
28
|
+
}),
|
|
29
|
+
defineField({
|
|
30
|
+
name: "items",
|
|
31
|
+
type: array({
|
|
32
|
+
of: [
|
|
33
|
+
{
|
|
34
|
+
type: "reference",
|
|
35
|
+
to: [{ type: "blog-post" }],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
39
|
+
title: "Items",
|
|
40
|
+
validation: (Rule) => Rule.min(1),
|
|
41
|
+
description: "Editorial content items (blog posts, articles, etc.)",
|
|
42
|
+
}),
|
|
43
|
+
defineField({
|
|
44
|
+
name: "columns",
|
|
45
|
+
type: "number",
|
|
46
|
+
title: "Columns",
|
|
47
|
+
description: "Number of columns in grid",
|
|
48
|
+
validation: (Rule) => Rule.min(1).max(4).integer(),
|
|
49
|
+
initialValue: 3,
|
|
50
|
+
}),
|
|
51
|
+
],
|
|
52
|
+
preview: {
|
|
53
|
+
select: {
|
|
54
|
+
title: "title",
|
|
55
|
+
itemCount: "items.length",
|
|
56
|
+
columns: "columns",
|
|
57
|
+
},
|
|
58
|
+
prepare({ title, itemCount, columns }) {
|
|
59
|
+
return {
|
|
60
|
+
title: title || "Editorial Grid Block",
|
|
61
|
+
subtitle: `${itemCount || 0} item(s) • ${columns || 3} column(s)`,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { defineType, defineField, string, text, url, image } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hero Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "hero"
|
|
7
|
+
* Sanity type: "block.hero"
|
|
8
|
+
*
|
|
9
|
+
* Fields map directly from Structure DSL block definition.
|
|
10
|
+
* Constraints from Structure DSL are enforced via validation rules.
|
|
11
|
+
*/
|
|
12
|
+
export const blockHero = defineType({
|
|
13
|
+
name: "block.hero",
|
|
14
|
+
title: "Hero Block",
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "Large headline with supporting text and CTA",
|
|
17
|
+
fields: [
|
|
18
|
+
defineField({
|
|
19
|
+
name: "_key",
|
|
20
|
+
type: "string",
|
|
21
|
+
title: "Key",
|
|
22
|
+
description: "Unique key for array item (auto-generated)",
|
|
23
|
+
readOnly: true,
|
|
24
|
+
}),
|
|
25
|
+
defineField({
|
|
26
|
+
name: "headline",
|
|
27
|
+
type: string(),
|
|
28
|
+
title: "Headline",
|
|
29
|
+
validation: (Rule) => Rule.required().max(100),
|
|
30
|
+
description: "Main headline text",
|
|
31
|
+
}),
|
|
32
|
+
defineField({
|
|
33
|
+
name: "subheadline",
|
|
34
|
+
type: text(),
|
|
35
|
+
title: "Subheadline",
|
|
36
|
+
description: "Supporting text below headline",
|
|
37
|
+
}),
|
|
38
|
+
defineField({
|
|
39
|
+
name: "backgroundImage",
|
|
40
|
+
type: image(),
|
|
41
|
+
title: "Background Image",
|
|
42
|
+
validation: (Rule) => Rule.required(),
|
|
43
|
+
description: "Hero background image",
|
|
44
|
+
}),
|
|
45
|
+
defineField({
|
|
46
|
+
name: "ctaText",
|
|
47
|
+
type: string(),
|
|
48
|
+
title: "CTA Button Text",
|
|
49
|
+
description: "Call-to-action button text",
|
|
50
|
+
}),
|
|
51
|
+
defineField({
|
|
52
|
+
name: "ctaLink",
|
|
53
|
+
type: url(),
|
|
54
|
+
title: "CTA Link",
|
|
55
|
+
description: "Call-to-action button URL",
|
|
56
|
+
}),
|
|
57
|
+
],
|
|
58
|
+
preview: {
|
|
59
|
+
select: {
|
|
60
|
+
headline: "headline",
|
|
61
|
+
subheadline: "subheadline",
|
|
62
|
+
},
|
|
63
|
+
prepare({ headline, subheadline }) {
|
|
64
|
+
return {
|
|
65
|
+
title: headline || "Hero Block",
|
|
66
|
+
subtitle: subheadline || "No subheadline",
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineType, defineField, string, text, array, image, reference } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Product Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "product"
|
|
7
|
+
* Sanity type: "block.product"
|
|
8
|
+
*/
|
|
9
|
+
export const blockProduct = defineType({
|
|
10
|
+
name: "block.product",
|
|
11
|
+
title: "Product Block",
|
|
12
|
+
type: "object",
|
|
13
|
+
description: "Product showcase block",
|
|
14
|
+
fields: [
|
|
15
|
+
defineField({
|
|
16
|
+
name: "_key",
|
|
17
|
+
type: "string",
|
|
18
|
+
title: "Key",
|
|
19
|
+
description: "Unique key for array item (auto-generated)",
|
|
20
|
+
readOnly: true,
|
|
21
|
+
}),
|
|
22
|
+
defineField({
|
|
23
|
+
name: "title",
|
|
24
|
+
type: string(),
|
|
25
|
+
title: "Title",
|
|
26
|
+
validation: (Rule) => Rule.required().max(200),
|
|
27
|
+
description: "Product block title",
|
|
28
|
+
}),
|
|
29
|
+
defineField({
|
|
30
|
+
name: "description",
|
|
31
|
+
type: text(),
|
|
32
|
+
title: "Description",
|
|
33
|
+
description: "Product block description",
|
|
34
|
+
}),
|
|
35
|
+
defineField({
|
|
36
|
+
name: "products",
|
|
37
|
+
type: array({
|
|
38
|
+
of: [
|
|
39
|
+
{
|
|
40
|
+
type: "reference",
|
|
41
|
+
to: [{ type: "product" }],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
}),
|
|
45
|
+
title: "Products",
|
|
46
|
+
validation: (Rule) => Rule.min(1),
|
|
47
|
+
description: "Referenced products to showcase",
|
|
48
|
+
}),
|
|
49
|
+
defineField({
|
|
50
|
+
name: "image",
|
|
51
|
+
type: image(),
|
|
52
|
+
title: "Image",
|
|
53
|
+
description: "Optional product block image",
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
preview: {
|
|
57
|
+
select: {
|
|
58
|
+
title: "title",
|
|
59
|
+
productCount: "products.length",
|
|
60
|
+
},
|
|
61
|
+
prepare({ title, productCount }) {
|
|
62
|
+
return {
|
|
63
|
+
title: title || "Product Block",
|
|
64
|
+
subtitle: `${productCount || 0} product(s)`,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { defineType, defineField, string, array, reference } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ritual Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "ritual"
|
|
7
|
+
* Sanity type: "block.ritual"
|
|
8
|
+
*
|
|
9
|
+
* Optional block type for ritual/ceremony content.
|
|
10
|
+
*/
|
|
11
|
+
export const blockRitual = defineType({
|
|
12
|
+
name: "block.ritual",
|
|
13
|
+
title: "Ritual Block",
|
|
14
|
+
type: "object",
|
|
15
|
+
description: "Ritual or ceremony content block",
|
|
16
|
+
fields: [
|
|
17
|
+
defineField({
|
|
18
|
+
name: "_key",
|
|
19
|
+
type: "string",
|
|
20
|
+
title: "Key",
|
|
21
|
+
description: "Unique key for array item (auto-generated)",
|
|
22
|
+
readOnly: true,
|
|
23
|
+
}),
|
|
24
|
+
defineField({
|
|
25
|
+
name: "title",
|
|
26
|
+
type: string(),
|
|
27
|
+
title: "Title",
|
|
28
|
+
validation: (Rule) => Rule.required().max(200),
|
|
29
|
+
description: "Ritual block title",
|
|
30
|
+
}),
|
|
31
|
+
defineField({
|
|
32
|
+
name: "rituals",
|
|
33
|
+
type: array({
|
|
34
|
+
of: [
|
|
35
|
+
{
|
|
36
|
+
type: "reference",
|
|
37
|
+
to: [{ type: "ritual" }],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
}),
|
|
41
|
+
title: "Rituals",
|
|
42
|
+
validation: (Rule) => Rule.min(1),
|
|
43
|
+
description: "Referenced ritual documents",
|
|
44
|
+
}),
|
|
45
|
+
],
|
|
46
|
+
preview: {
|
|
47
|
+
select: {
|
|
48
|
+
title: "title",
|
|
49
|
+
ritualCount: "rituals.length",
|
|
50
|
+
},
|
|
51
|
+
prepare({ title, ritualCount }) {
|
|
52
|
+
return {
|
|
53
|
+
title: title || "Ritual Block",
|
|
54
|
+
subtitle: `${ritualCount || 0} ritual(s)`,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { defineType, defineField, string, array, image } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Story Block
|
|
5
|
+
*
|
|
6
|
+
* Maps to Structure DSL block type: "story"
|
|
7
|
+
* Sanity type: "block.story"
|
|
8
|
+
*/
|
|
9
|
+
export const blockStory = defineType({
|
|
10
|
+
name: "block.story",
|
|
11
|
+
title: "Story Block",
|
|
12
|
+
type: "object",
|
|
13
|
+
description: "Narrative content block",
|
|
14
|
+
fields: [
|
|
15
|
+
defineField({
|
|
16
|
+
name: "_key",
|
|
17
|
+
type: "string",
|
|
18
|
+
title: "Key",
|
|
19
|
+
description: "Unique key for array item (auto-generated)",
|
|
20
|
+
readOnly: true,
|
|
21
|
+
}),
|
|
22
|
+
defineField({
|
|
23
|
+
name: "title",
|
|
24
|
+
type: string(),
|
|
25
|
+
title: "Title",
|
|
26
|
+
validation: (Rule) => Rule.required().max(200),
|
|
27
|
+
description: "Story section title",
|
|
28
|
+
}),
|
|
29
|
+
defineField({
|
|
30
|
+
name: "content",
|
|
31
|
+
type: array({
|
|
32
|
+
of: [{ type: "block" }],
|
|
33
|
+
}),
|
|
34
|
+
title: "Content",
|
|
35
|
+
validation: (Rule) => Rule.required(),
|
|
36
|
+
description: "Story content (portable text)",
|
|
37
|
+
}),
|
|
38
|
+
defineField({
|
|
39
|
+
name: "image",
|
|
40
|
+
type: image(),
|
|
41
|
+
title: "Image",
|
|
42
|
+
description: "Optional story image",
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
preview: {
|
|
46
|
+
select: {
|
|
47
|
+
title: "title",
|
|
48
|
+
},
|
|
49
|
+
prepare({ title }) {
|
|
50
|
+
return {
|
|
51
|
+
title: title || "Story Block",
|
|
52
|
+
subtitle: "Narrative content",
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LevrOps Structure Schema
|
|
3
|
+
*
|
|
4
|
+
* This schema defines the Sanity document and object types for Expression Studio Structure.
|
|
5
|
+
*
|
|
6
|
+
* Mapping:
|
|
7
|
+
* - Page templates → page document with pageType discriminant
|
|
8
|
+
* - Block types → block.* object types
|
|
9
|
+
*
|
|
10
|
+
* This schema is deterministic and stable across tenants/properties.
|
|
11
|
+
* Tenant-specific configuration is stored in LevrOps backend, not in Sanity schema.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export { page } from './page';
|
|
15
|
+
export { blockHero } from './blocks/hero';
|
|
16
|
+
export { blockStory } from './blocks/story';
|
|
17
|
+
export { blockProduct } from './blocks/product';
|
|
18
|
+
export { blockEditorialGrid } from './blocks/editorialGrid';
|
|
19
|
+
export { blockCta } from './blocks/cta';
|
|
20
|
+
export { blockRitual } from './blocks/ritual';
|
|
21
|
+
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { defineType, defineField, string, slug, array } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Page Document
|
|
5
|
+
*
|
|
6
|
+
* Single document type for all page templates, discriminated by pageType.
|
|
7
|
+
*
|
|
8
|
+
* Rationale for single document type:
|
|
9
|
+
* - More flexible: can add new page types without schema changes
|
|
10
|
+
* - Simpler maintenance: one document type to manage
|
|
11
|
+
* - Better tenant isolation: shared schema, tenant-specific config in LevrOps
|
|
12
|
+
* - Aligns with Sanity's pattern of using discriminators
|
|
13
|
+
*
|
|
14
|
+
* Mapping:
|
|
15
|
+
* - pageType enum → page template IDs from Structure DSL
|
|
16
|
+
* - blocks array → ordered composition of block.* object types
|
|
17
|
+
*/
|
|
18
|
+
export const page = defineType({
|
|
19
|
+
name: "page",
|
|
20
|
+
title: "Page",
|
|
21
|
+
type: "document",
|
|
22
|
+
description: "A page composed of ordered blocks, discriminated by pageType",
|
|
23
|
+
fields: [
|
|
24
|
+
defineField({
|
|
25
|
+
name: "title",
|
|
26
|
+
type: string(),
|
|
27
|
+
title: "Title",
|
|
28
|
+
validation: (Rule) => Rule.required().max(200),
|
|
29
|
+
description: "Page title",
|
|
30
|
+
}),
|
|
31
|
+
defineField({
|
|
32
|
+
name: "slug",
|
|
33
|
+
type: slug({
|
|
34
|
+
source: "title",
|
|
35
|
+
}),
|
|
36
|
+
title: "Slug",
|
|
37
|
+
validation: (Rule) => Rule.required(),
|
|
38
|
+
description: "URL-friendly identifier",
|
|
39
|
+
}),
|
|
40
|
+
defineField({
|
|
41
|
+
name: "pageType",
|
|
42
|
+
type: string(),
|
|
43
|
+
title: "Page Type",
|
|
44
|
+
validation: (Rule) => Rule.required(),
|
|
45
|
+
description: "Template type (maps to Structure DSL page template ID)",
|
|
46
|
+
options: {
|
|
47
|
+
list: [
|
|
48
|
+
{ title: "Homepage", value: "homepage" },
|
|
49
|
+
{ title: "Product Page", value: "product_page" },
|
|
50
|
+
{ title: "Editorial Page", value: "editorial_page" },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
defineField({
|
|
55
|
+
name: "blocks",
|
|
56
|
+
type: array({
|
|
57
|
+
of: [
|
|
58
|
+
{ type: "block.hero" },
|
|
59
|
+
{ type: "block.story" },
|
|
60
|
+
{ type: "block.product" },
|
|
61
|
+
{ type: "block.editorialGrid" },
|
|
62
|
+
{ type: "block.cta" },
|
|
63
|
+
{ type: "block.ritual" },
|
|
64
|
+
],
|
|
65
|
+
}),
|
|
66
|
+
title: "Blocks",
|
|
67
|
+
description: "Ordered composition of content blocks",
|
|
68
|
+
validation: (Rule) => Rule.required().min(1),
|
|
69
|
+
}),
|
|
70
|
+
defineField({
|
|
71
|
+
name: "metadata",
|
|
72
|
+
type: "object",
|
|
73
|
+
title: "Metadata",
|
|
74
|
+
description: "Hidden metadata fields for LevrOps integration",
|
|
75
|
+
fields: [
|
|
76
|
+
defineField({
|
|
77
|
+
name: "structureId",
|
|
78
|
+
type: string(),
|
|
79
|
+
title: "Structure ID",
|
|
80
|
+
description: "Reference to Structure DSL block/template IDs",
|
|
81
|
+
hidden: true,
|
|
82
|
+
}),
|
|
83
|
+
defineField({
|
|
84
|
+
name: "tenantId",
|
|
85
|
+
type: string(),
|
|
86
|
+
title: "Tenant ID",
|
|
87
|
+
description: "Tenant identifier for isolation",
|
|
88
|
+
hidden: true,
|
|
89
|
+
}),
|
|
90
|
+
defineField({
|
|
91
|
+
name: "propertyId",
|
|
92
|
+
type: string(),
|
|
93
|
+
title: "Property ID",
|
|
94
|
+
description: "Property identifier for isolation",
|
|
95
|
+
hidden: true,
|
|
96
|
+
}),
|
|
97
|
+
],
|
|
98
|
+
options: {
|
|
99
|
+
collapsible: true,
|
|
100
|
+
collapsed: true,
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
],
|
|
104
|
+
preview: {
|
|
105
|
+
select: {
|
|
106
|
+
title: "title",
|
|
107
|
+
pageType: "pageType",
|
|
108
|
+
slug: "slug.current",
|
|
109
|
+
},
|
|
110
|
+
prepare({ title, pageType, slug }) {
|
|
111
|
+
return {
|
|
112
|
+
title: title || "Untitled Page",
|
|
113
|
+
subtitle: `${pageType || "unknown"} • /${slug || ""}`,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|