navbar schema

This commit is contained in:
2025-08-20 00:52:21 +02:00
parent f1afd47ba3
commit 519eb1ff2a
13 changed files with 892 additions and 71 deletions

View File

@@ -0,0 +1 @@
<p>Second page</p>

View File

@@ -1 +0,0 @@
<p>Test</p>

View File

@@ -20,10 +20,10 @@
"@repo/sanity-connection": "workspace:*",
"@repo/ui": "workspace:*",
"@sanity/document-internationalization": "^4.0.0",
"@sanity/vision": "^4.3.0",
"@sanity/vision": "^4.5.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"sanity": "^4.3.0",
"sanity": "^4.5.0",
"sanity-plugin-link-field": "^1.4.0",
"sanity-plugin-seo": "^1.3.3",
"sanity-plugin-simpler-color-input": "^3.1.1",

View File

@@ -1,13 +1,13 @@
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {ClipboardIcon, HomeIcon, WrenchIcon} from '@sanity/icons'
import {ClipboardIcon, HomeIcon, MenuIcon, WrenchIcon} from '@sanity/icons'
import {schemaTypes} from './schemaTypes'
import {presentationTool} from 'sanity/presentation'
import {linkField} from 'sanity-plugin-link-field'
import {seoMetaFields} from 'sanity-plugin-seo'
import {simplerColorInput} from 'sanity-plugin-simpler-color-input'
import {createColorList} from './lib/colorUtils'
import { sanityConnection } from '@repo/sanity-connection'
import {sanityConnection} from '@repo/sanity-connection'
import Logo from './components/logo'
export default defineConfig({
@@ -23,6 +23,15 @@ export default defineConfig({
S.list()
.title('Content')
.items([
S.listItem()
.title('Settings')
.icon(WrenchIcon)
.child(S.document().schemaType('settings').documentId('settings').title('Settings')),
S.listItem()
.title('Navbar')
.icon(MenuIcon)
.child(S.document().schemaType('navbar').documentId('navbar').title('Navbar')),
S.divider(),
S.listItem()
.title('Landing Page')
.icon(HomeIcon)
@@ -32,11 +41,6 @@ export default defineConfig({
.title('Custom pages')
.icon(ClipboardIcon)
.child(S.documentTypeList('custom').title('Content')),
S.divider(),
S.listItem()
.title('Settings')
.icon(WrenchIcon)
.child(S.document().schemaType('settings').documentId('settings').title('Settings')),
]),
}),
// @ts-ignore

View File

@@ -4,11 +4,13 @@ import settings from "./settings";
import * as objects from './objects'
import * as sections from './sections'
import * as pages from './pages'
import navbar from "./navbar";
export const schemaTypes = [
blockContent,
settings,
navbar,
...Object.values(objects),
...Object.values(sections),
...Object.values(pages),

View File

@@ -0,0 +1,250 @@
import { LinkIcon, MenuIcon } from "@sanity/icons"
import { defineField, defineType, type Rule, type StringRule } from "sanity"
import { requiredLinkField } from "sanity-plugin-link-field"
export default defineType({
name: 'navbar',
title: 'Navigation',
type: 'document',
icon: MenuIcon,
fields: [
defineField({
name: 'title',
title: 'Titel',
type: 'string',
initialValue: 'Navigation',
readOnly: true,
hidden: true,
}),
defineField({
name: 'links',
title: 'Links',
icon: LinkIcon,
type: 'array',
initialValue: [
{
text: 'Home',
link: {
type: 'url',
value: '/',
}
},
{
text: 'Second Page',
link: {
type: 'url',
value: '/second',
}
},
{
text: 'Blog',
link: {
type: 'url',
value: '/blog',
},
sublinks: [
{
type: 'auto',
pageType: 'blog',
autoTitle: 'Latest Blog Posts'
}
]
}
],
of: [
{
type: 'object',
title: 'Link',
icon: LinkIcon,
fields: [
defineField({
name: 'text',
title: 'Text',
type: 'string',
validation: (Rule: StringRule) => Rule.required().error('Text is required'),
}),
defineField({
name: 'link',
title: 'Link',
type: 'link',
validation: (rule: Rule) => rule.custom((field: Rule) => requiredLinkField(field))
}),
defineField({
name: 'sublinks',
title: 'Sublinks',
type: 'array',
validation: (Rule) => Rule.max(5).error('Maximum 5 sublinks allowed'),
of: [
{
type: 'object',
title: 'Sublink',
icon: LinkIcon,
fields: [
defineField({
name: 'type',
title: 'Link Type',
type: 'string',
initialValue: 'auto',
options: {
list: [
{
title: 'Last Pages',
value: 'auto'
},
{
title: 'Pages by Tag',
value: 'tag'
},
{
title: 'Manual Link',
value: 'manual'
},
],
layout: 'radio',
},
validation: (Rule: StringRule) => Rule.required(),
}),
defineField({
name: 'pageType',
title: 'Page Type',
type: 'string',
initialValue: 'custom',
description: 'Automatically displays the 5 most recently published pages from the selected type',
options: {
list: [
{ title: 'Custom Pages', value: 'custom' },
{ title: 'Blog Posts', value: 'blog' },
],
},
hidden: ({ parent }) => parent?.type !== 'auto',
validation: (Rule: StringRule) =>
Rule.custom((value, context) => {
const parent = context.parent as { type?: string }
if (parent?.type === 'auto' && !value) {
return 'A page type must be selected'
}
return true
}),
}),
defineField({
name: 'text',
title: 'Text',
type: 'string',
hidden: ({ parent }) => parent?.type !== 'manual',
validation: (Rule: StringRule) =>
Rule.custom((value, context) => {
const parent = context.parent as { type?: string }
if (parent?.type === 'manual' && !value) {
return 'Text is required for manual links'
}
return true
}),
}),
defineField({
name: 'link',
title: 'Link',
type: 'link',
hidden: ({ parent }) => parent?.type !== 'manual',
validation: (rule: Rule) =>
rule.custom((field, context) => {
const parent = context.parent as { type?: string }
if (parent?.type === 'manual') {
return requiredLinkField(field)
}
return true
}),
}),
defineField({
name: 'tagFilter',
title: 'Tag Filter',
type: 'reference',
to: [{ type: 'tag' }],
description: 'Select a tag to filter pages by. The last 5 published pages with this tag will be shown.',
hidden: ({ parent }) => parent?.type !== 'tag',
validation: (Rule) =>
Rule.custom((value, context) => {
const parent = context.parent as { type?: string }
if (parent?.type === 'tag' && (!value)) {
return 'A tag is required when using tag-based filtering'
}
return true
}),
}),
defineField({
name: 'tagPageType',
title: 'Page Type for Tag Filter',
type: 'string',
description: 'Select which type of pages to search for the tag',
options: {
list: [
{ title: 'Custom Pages', value: 'custom' },
{ title: 'Blog Posts', value: 'blog' },
],
},
hidden: ({ parent }) => parent?.type !== 'tag',
initialValue: 'custom',
validation: (Rule: StringRule) =>
Rule.custom((value, context) => {
const parent = context.parent as { type?: string }
if (parent?.type === 'tag' && !value) {
return 'A page type must be selected for tag filtering'
}
return true
}),
}),
],
preview: {
select: {
title: 'text',
type: 'type',
tagTitles: 'tagTitles',
autoTitle: 'autoTitle',
pageType: 'pageType',
tagPageType: 'tagPageType',
},
prepare({ title, type, tagTitles, autoTitle, pageType, tagPageType }) {
let displayTitle = title
let subtitle = ''
const formatPageType = (pageType: string) => {
const typeMap: Record<string, string> = {
home: 'Home',
custom: 'Custom',
blog: 'Blog'
}
return typeMap[pageType] || pageType.charAt(0).toUpperCase() + pageType.slice(1)
}
switch (type) {
case 'auto':
displayTitle = autoTitle || 'Last Pages'
subtitle = `Auto: Latest 5 from ${formatPageType(pageType)}`
break
case 'tag':
const tagNames = tagTitles?.filter(Boolean).join(', ') || 'No tags'
displayTitle = autoTitle || `Pages tagged: ${tagNames}`
subtitle = `Tag: "${tagNames}" in ${formatPageType(tagPageType)}`
break
case 'manual':
subtitle = 'Manual link'
break
default:
displayTitle = 'Unconfigured link'
}
return {
title: displayTitle || 'Untitled Sublink',
subtitle,
media: LinkIcon,
}
},
},
},
],
}),
],
},
],
}),
],
})

View File

@@ -1,3 +1,4 @@
export { default as faq } from './faq'
export { default as imageWithAlt } from './imageWithAlt'
export { default as button } from './button'
export { default as tag } from './tag'

View File

@@ -0,0 +1,46 @@
import { TagIcon } from '@sanity/icons'
import { defineField, defineType, type StringRule } from 'sanity'
export default defineType({
name: 'tag',
title: 'Tag',
type: 'document',
icon: TagIcon,
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule: StringRule) => Rule.required().error('Tag title is required'),
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
validation: (Rule) => Rule.required(),
}),
defineField({
name: 'description',
title: 'Description',
type: 'text',
rows: 3,
}),
],
preview: {
select: {
title: 'title',
description: 'description',
},
prepare({ title, description }) {
return {
title: title || 'Untitled Tag',
subtitle: description || 'No description',
media: TagIcon,
}
},
},
})

View File

@@ -0,0 +1,78 @@
import { DocumentIcon } from '@sanity/icons'
import { defineField, defineType, type SlugRule, type StringRule } from 'sanity'
export default defineType({
name: 'blog',
title: 'Blog Post',
type: 'document',
icon: DocumentIcon,
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule: StringRule) => Rule.required().error('Title is required')
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
validation: (Rule: SlugRule) => Rule.required(),
}),
defineField({
name: 'author',
title: 'Author',
type: 'string',
}),
defineField({
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
}),
defineField({
name: 'tags',
title: 'Tags',
type: 'array',
of: [{ type: 'string' }],
description: 'Add tags to categorize this post. Tags can be used to filter and group related content in navigation menus.',
options: {
layout: 'tags',
},
}),
defineField({
name: 'excerpt',
title: 'Excerpt',
type: 'text',
rows: 4,
}),
defineField({
name: 'mainImage',
title: 'Main image',
type: 'imageWithAlt',
}),
defineField({
title: 'Content',
name: 'body',
type: 'blockContent',
}),
],
preview: {
select: {
title: 'title',
author: 'author',
media: 'mainImage',
},
prepare({ title, author, media }) {
return {
title: title || 'Untitled',
subtitle: author && `by ${author}`,
media,
}
},
},
})

View File

@@ -21,6 +21,22 @@ export default defineType({
},
validation: (Rule: SlugRule) => Rule.required(),
}),
defineField({
name: 'tags',
title: 'Tags',
type: 'array',
of: [{ type: 'string' }],
description: 'Add tags to categorize this page. Tags can be used to filter and group related content in navigation menus.',
options: {
layout: 'tags',
},
}),
defineField({
name: 'publishedAt',
title: 'Published At',
type: 'datetime',
initialValue: () => new Date().toISOString(),
}),
defineField({
title: 'Content',
name: 'body',

View File

@@ -21,6 +21,13 @@ export default defineType({
validation: (Rule) => Rule.required(),
group: 'header',
}),
defineField({
name: 'publishedAt',
title: 'Published At',
type: 'datetime',
initialValue: () => new Date().toISOString(),
group: 'header',
}),
// Page sections
defineField({

View File

@@ -1,3 +1,4 @@
// Page types
export { default as homePage } from './home'
export { default as custom } from "./custom";
export { default as blog } from "./blog";