From 6985ba68a0e442926fb54faaa2ae3e46201d8569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20B=C3=B6hm?= Date: Sat, 22 Jun 2024 12:55:42 +0200 Subject: [PATCH] feat(studio): add `aboutUsPage` page closes: #19 --- apps/studio/plugins/index.ts | 3 ++ apps/studio/schemas/index.ts | 4 ++ apps/studio/schemas/objects/image-card.ts | 29 +++++++++++ .../schemas/single-pages/about-us/_groups.ts | 24 ++++++++++ .../single-pages/about-us/chronicle.ts | 23 +++++++++ .../schemas/single-pages/about-us/gallery.ts | 25 ++++++++++ .../schemas/single-pages/about-us/index.ts | 48 +++++++++++++++++++ .../schemas/single-pages/groups/_groups.ts | 6 +-- .../schemas/single-pages/home/_groups.ts | 10 ++-- .../studio/schemas/single-pages/home/index.ts | 2 +- .../single-pages/single-group/_groups.ts | 2 +- .../single-group/{groups.ts => gallery.ts} | 0 .../single-pages/single-group/index.ts | 3 +- .../home => shared/sections}/vision.ts | 0 apps/studio/shared/validation-rules.ts | 30 ++++++++++-- 15 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 apps/studio/schemas/objects/image-card.ts create mode 100644 apps/studio/schemas/single-pages/about-us/_groups.ts create mode 100644 apps/studio/schemas/single-pages/about-us/chronicle.ts create mode 100644 apps/studio/schemas/single-pages/about-us/gallery.ts create mode 100644 apps/studio/schemas/single-pages/about-us/index.ts rename apps/studio/schemas/single-pages/single-group/{groups.ts => gallery.ts} (100%) rename apps/studio/{schemas/single-pages/home => shared/sections}/vision.ts (100%) diff --git a/apps/studio/plugins/index.ts b/apps/studio/plugins/index.ts index 9264a0a..c2b1fff 100644 --- a/apps/studio/plugins/index.ts +++ b/apps/studio/plugins/index.ts @@ -6,6 +6,7 @@ import type { PluginOptions } from 'sanity'; import { structureTool } from 'sanity/structure'; import { media } from 'sanity-plugin-media'; +import aboutUsPage from '@/schemas/single-pages/about-us'; import contactPage from '@/schemas/single-pages/contact'; import groupsPage from '@/schemas/single-pages/groups'; import homePage from '@/schemas/single-pages/home'; @@ -26,6 +27,7 @@ export function getPlugins() { structureTool({ structure: pageStructure([ homePage, + aboutUsPage, contactPage, membershipPage, newsArticlePage, @@ -41,6 +43,7 @@ export function getPlugins() { // to suit the Settings document singleton singletonPlugin([ homePage.name, + aboutUsPage.name, contactPage.name, membershipPage.name, newsArticlePage.name, diff --git a/apps/studio/schemas/index.ts b/apps/studio/schemas/index.ts index ec6990a..db0d8ed 100644 --- a/apps/studio/schemas/index.ts +++ b/apps/studio/schemas/index.ts @@ -8,6 +8,7 @@ import columns from './objects/columns'; import contactTo from './objects/contact-to'; import extendedImage from './objects/extended-image'; import externalLink from './objects/external-link'; +import imageCard from './objects/image-card'; import internalLink from './objects/internal-link'; import link from './objects/link'; import metFields from './objects/meta'; @@ -18,6 +19,7 @@ import blockContent from './sections/block-content'; import grid from './sections/grid'; import mainImage from './sections/main-image'; import spacer from './sections/spacer'; +import aboutUsPage from './single-pages/about-us'; import contactPage from './single-pages/contact'; import groupsPage from './single-pages/groups'; import homePage from './single-pages/home'; @@ -47,6 +49,7 @@ export const schemaTypes = [ contactTo, extendedImage, externalLink, + imageCard, internalLink, link, metFields, @@ -61,6 +64,7 @@ export const schemaTypes = [ spacer, // Single Pages + aboutUsPage, contactPage, groupsPage, homePage, diff --git a/apps/studio/schemas/objects/image-card.ts b/apps/studio/schemas/objects/image-card.ts new file mode 100644 index 0000000..656cd5d --- /dev/null +++ b/apps/studio/schemas/objects/image-card.ts @@ -0,0 +1,29 @@ +import { RiExternalLinkLine } from 'react-icons/ri'; +import { defineField } from 'sanity'; + +const imageCard = defineField({ + title: 'Kachel mit Bild', + name: 'imageCard', + type: 'object', + description: 'Die Abschnitte der Chronik.', + icon: RiExternalLinkLine, + fields: [ + defineField({ + title: 'Titel', + name: 'title', + type: 'string', + }), + defineField({ + title: 'Beschreibung', + name: 'description', + type: 'text', + }), + defineField({ + title: 'Bild', + name: 'image', + type: 'extendedImage', + }), + ], +}); + +export default imageCard; diff --git a/apps/studio/schemas/single-pages/about-us/_groups.ts b/apps/studio/schemas/single-pages/about-us/_groups.ts new file mode 100644 index 0000000..fcbbe66 --- /dev/null +++ b/apps/studio/schemas/single-pages/about-us/_groups.ts @@ -0,0 +1,24 @@ +export const chronicle = { + title: 'Chronik', + name: 'chronicle', +}; + +export const contactPersons = { + title: 'Ansprechpartner', + name: 'contactPersons', +}; + +export const gallery = { + title: 'Galerie', + name: 'gallery', +}; + +export const stats = { + title: 'Stats', + name: 'stats', +}; + +export const vision = { + title: 'Vision', + name: 'vision', +}; diff --git a/apps/studio/schemas/single-pages/about-us/chronicle.ts b/apps/studio/schemas/single-pages/about-us/chronicle.ts new file mode 100644 index 0000000..f8e4c27 --- /dev/null +++ b/apps/studio/schemas/single-pages/about-us/chronicle.ts @@ -0,0 +1,23 @@ +import { RiLinksLine } from 'react-icons/ri'; +import { defineField } from 'sanity'; + +import { getDefaultPageFieldsWithGroup } from '@/shared/fields/general'; + +export const chronicleField = defineField({ + title: 'Chronik', + name: 'chronicleSection', + type: 'object', + icon: RiLinksLine, + group: 'chronicle', + fields: [ + ...getDefaultPageFieldsWithGroup(), + + defineField({ + title: 'Chronik', + name: 'chronicle', + type: 'array', + of: [{ type: 'imageCard' }], + description: 'Die Abschnitte der Chronik.', + }), + ], +}); diff --git a/apps/studio/schemas/single-pages/about-us/gallery.ts b/apps/studio/schemas/single-pages/about-us/gallery.ts new file mode 100644 index 0000000..c71f073 --- /dev/null +++ b/apps/studio/schemas/single-pages/about-us/gallery.ts @@ -0,0 +1,25 @@ +import { RiLinksLine } from 'react-icons/ri'; +import { defineField } from 'sanity'; + +import { getDefaultPageFieldsWithGroup } from '@/shared/fields/general'; +import { getLengthRule } from '@/shared/validation-rules'; + +export const galleryField = defineField({ + title: 'Galerie', + name: 'gallerySection', + type: 'object', + icon: RiLinksLine, + group: 'gallery', + fields: [ + ...getDefaultPageFieldsWithGroup(), + + defineField({ + title: 'Bilder', + name: 'venues', + type: 'array', + of: [{ type: 'extendedImage' }], + description: 'Diese gewählten Bilder werden in der gewünschten Reihenfolge angezeigt.', + validation: rule => [getLengthRule(rule, 3, 'Es müssen genau 3 Bilder ausgewählt werden.')], + }), + ], +}); diff --git a/apps/studio/schemas/single-pages/about-us/index.ts b/apps/studio/schemas/single-pages/about-us/index.ts new file mode 100644 index 0000000..1130ce5 --- /dev/null +++ b/apps/studio/schemas/single-pages/about-us/index.ts @@ -0,0 +1,48 @@ +// cSpell:words verein +import { RiBookletLine, RiLinksLine } from 'react-icons/ri'; +import { defineField, defineType } from 'sanity'; + +import { content, general, meta } from '@/shared/field-groups'; +import { defaultPageFields, getHiddenSlugField } from '@/shared/fields/general'; +import { metaField } from '@/shared/fields/meta'; +import { contactPersonsSectionField } from '@/shared/sections/contact-persons'; +import { statsField } from '@/shared/sections/stats'; +import { visionField } from '@/shared/sections/vision'; + +import { chronicle, contactPersons, gallery, stats, vision } from './_groups'; +import { chronicleField } from './chronicle'; +import { galleryField } from './gallery'; + +const aboutUsPage = defineType({ + title: 'Über uns', + name: 'aboutUs', + type: 'document', + icon: RiBookletLine, + groups: [general, meta, content], + fields: [ + // (hidden) + getHiddenSlugField('verein'), + + // general + ...defaultPageFields, + + // meta + metaField, + + // content + defineField({ + title: 'Inhalte', + name: 'content', + type: 'object', + icon: RiLinksLine, + group: 'content', + groups: [gallery, chronicle, vision, stats, contactPersons], + fields: [galleryField, chronicleField, visionField, statsField, contactPersonsSectionField], + }), + ], + preview: { + prepare: () => ({ title: 'Über uns' }), + }, +}); + +export default aboutUsPage; diff --git a/apps/studio/schemas/single-pages/groups/_groups.ts b/apps/studio/schemas/single-pages/groups/_groups.ts index 8791f6d..50f55eb 100644 --- a/apps/studio/schemas/single-pages/groups/_groups.ts +++ b/apps/studio/schemas/single-pages/groups/_groups.ts @@ -1,15 +1,15 @@ export const contactPersons = { - title: 'ContactPersons', + title: 'Ansprechpartner', name: 'contactPersons', }; export const groups = { - title: 'Groups', + title: 'Gruppen', name: 'groups', }; export const venues = { - title: 'venues', + title: 'Sportstätten', name: 'venues', }; diff --git a/apps/studio/schemas/single-pages/home/_groups.ts b/apps/studio/schemas/single-pages/home/_groups.ts index 0031303..a2a26ce 100644 --- a/apps/studio/schemas/single-pages/home/_groups.ts +++ b/apps/studio/schemas/single-pages/home/_groups.ts @@ -1,15 +1,15 @@ export const contactPersons = { - title: 'ContactPersons', + title: 'Ansprechpartner', name: 'contactPersons', }; export const features = { - title: 'Features', + title: 'Merkmale', name: 'features', }; export const groups = { - title: 'Groups', + title: 'Gruppen', name: 'groups', }; @@ -19,7 +19,7 @@ export const news = { }; export const pricing = { - title: 'Pricing', + title: 'Preise', name: 'pricing', }; @@ -29,7 +29,7 @@ export const stats = { }; export const testimonial = { - title: 'Testimonial', + title: 'Referenz', name: 'testimonial', }; diff --git a/apps/studio/schemas/single-pages/home/index.ts b/apps/studio/schemas/single-pages/home/index.ts index 5ca08ce..fc1527a 100644 --- a/apps/studio/schemas/single-pages/home/index.ts +++ b/apps/studio/schemas/single-pages/home/index.ts @@ -6,6 +6,7 @@ import { defaultPageFields, getHiddenSlugField } from '@/shared/fields/general'; import { metaField } from '@/shared/fields/meta'; import { contactPersonsSectionField } from '@/shared/sections/contact-persons'; import { statsField } from '@/shared/sections/stats'; +import { visionField } from '@/shared/sections/vision'; import { contactPersons, @@ -22,7 +23,6 @@ import { groupsField } from './groups'; import { newsField } from './news'; import { pricingField } from './pricing'; import { testimonialField } from './testimonial'; -import { visionField } from './vision'; const homePage = defineType({ title: 'Home', diff --git a/apps/studio/schemas/single-pages/single-group/_groups.ts b/apps/studio/schemas/single-pages/single-group/_groups.ts index 27e7068..b1e5a33 100644 --- a/apps/studio/schemas/single-pages/single-group/_groups.ts +++ b/apps/studio/schemas/single-pages/single-group/_groups.ts @@ -1,5 +1,5 @@ export const contactPersons = { - title: 'ContactPersons', + title: 'Ansprechpartner', name: 'contactPersons', }; diff --git a/apps/studio/schemas/single-pages/single-group/groups.ts b/apps/studio/schemas/single-pages/single-group/gallery.ts similarity index 100% rename from apps/studio/schemas/single-pages/single-group/groups.ts rename to apps/studio/schemas/single-pages/single-group/gallery.ts diff --git a/apps/studio/schemas/single-pages/single-group/index.ts b/apps/studio/schemas/single-pages/single-group/index.ts index 3732f48..2257c87 100644 --- a/apps/studio/schemas/single-pages/single-group/index.ts +++ b/apps/studio/schemas/single-pages/single-group/index.ts @@ -1,4 +1,3 @@ -// cSpell:words angebot import { RiBookletLine, RiLinksLine } from 'react-icons/ri'; import { defineField, defineType } from 'sanity'; @@ -8,7 +7,7 @@ import { metaField } from '@/shared/fields/meta'; import { contactPersonsSectionField } from '@/shared/sections/contact-persons'; import { contactPersons, gallery } from './_groups'; -import { galleryField } from './groups'; +import { galleryField } from './gallery'; const singleGroupPage = defineType({ title: 'Einzel-Gruppe', diff --git a/apps/studio/schemas/single-pages/home/vision.ts b/apps/studio/shared/sections/vision.ts similarity index 100% rename from apps/studio/schemas/single-pages/home/vision.ts rename to apps/studio/shared/sections/vision.ts diff --git a/apps/studio/shared/validation-rules.ts b/apps/studio/shared/validation-rules.ts index 57eb309..13b0681 100644 --- a/apps/studio/shared/validation-rules.ts +++ b/apps/studio/shared/validation-rules.ts @@ -1,11 +1,12 @@ import type { StringRule } from 'sanity'; interface RuleOptions { + inputType?: 'array' | 'text'; message?: string; type?: 'error' | 'warning'; } -const defaultRuleOptions: RuleOptions = { type: 'error' }; +const defaultRuleOptions: RuleOptions = { inputType: 'text', type: 'error' }; interface BaseRule { error: (message: string) => Rule; @@ -16,6 +17,10 @@ interface RequiredRule extends BaseRule { required: () => Rule; } +interface LengthRule extends BaseRule, RequiredRule { + length: (length: number) => Rule; +} + interface MaxLengthRule extends BaseRule { max: (length: number) => Rule; } @@ -24,6 +29,23 @@ interface MinLengthRule extends BaseRule, RequiredRule { min: (length: number) => Rule; } +function getTextByInputType(inputType: RuleOptions['inputType'], length: number) { + const arrayOutput = length === 1 ? 'Eintrag' : 'Einträge'; + return inputType === 'array' ? arrayOutput : 'Zeichen'; +} + +export function getLengthRule>( + rule: Rule, + length: number, + title: string, + options = { ...defaultRuleOptions, type: 'warning' }, +) { + const validationRule = rule.length(length); + const itemText = getTextByInputType(options.inputType, length); + const message = options.message ?? `${title} muss genau ${length} ${itemText} lang sein`; + return options.type === 'error' ? validationRule.error(message) : validationRule.warning(message); +} + export function getMaxLengthRule>( rule: Rule, length: number, @@ -31,7 +53,8 @@ export function getMaxLengthRule>( options = { ...defaultRuleOptions, type: 'warning' }, ) { const validationRule = rule.max(length); - const message = options.message ?? `${title} sollte maximal ${length} Zeichen lang sein`; + const itemText = getTextByInputType(options.inputType, length); + const message = options.message ?? `${title} sollte maximal ${length} ${itemText} lang sein`; return options.type === 'error' ? validationRule.error(message) : validationRule.warning(message); } @@ -42,7 +65,8 @@ export function getMinLengthRule>( options = defaultRuleOptions, ) { const validationRule = rule.required().min(length); - const message = options.message ?? `${title} muss mindestens ${length} Zeichen lang sein`; + const itemText = getTextByInputType(options.inputType, length); + const message = options.message ?? `${title} muss mindestens ${length} ${itemText} lang sein`; return options.type === 'error' ? validationRule.error(message) : validationRule.warning(message); }