diff --git a/app/[locale]/[...slug]/page.tsx b/app/(site)/[locale]/[...slug]/page.tsx
similarity index 100%
rename from app/[locale]/[...slug]/page.tsx
rename to app/(site)/[locale]/[...slug]/page.tsx
diff --git a/app/[locale]/[...slug]/pages/category-page.tsx b/app/(site)/[locale]/[...slug]/pages/category-page.tsx
similarity index 100%
rename from app/[locale]/[...slug]/pages/category-page.tsx
rename to app/(site)/[locale]/[...slug]/pages/category-page.tsx
diff --git a/app/[locale]/[...slug]/pages/product-page.tsx b/app/(site)/[locale]/[...slug]/pages/product-page.tsx
similarity index 100%
rename from app/[locale]/[...slug]/pages/product-page.tsx
rename to app/(site)/[locale]/[...slug]/pages/product-page.tsx
diff --git a/app/[locale]/[...slug]/pages/single-page.tsx b/app/(site)/[locale]/[...slug]/pages/single-page.tsx
similarity index 100%
rename from app/[locale]/[...slug]/pages/single-page.tsx
rename to app/(site)/[locale]/[...slug]/pages/single-page.tsx
diff --git a/app/[locale]/error.tsx b/app/(site)/[locale]/error.tsx
similarity index 100%
rename from app/[locale]/error.tsx
rename to app/(site)/[locale]/error.tsx
diff --git a/app/[locale]/layout.tsx b/app/(site)/[locale]/layout.tsx
similarity index 91%
rename from app/[locale]/layout.tsx
rename to app/(site)/[locale]/layout.tsx
index 7c8240522..21e7b80ab 100644
--- a/app/[locale]/layout.tsx
+++ b/app/(site)/[locale]/layout.tsx
@@ -4,8 +4,8 @@ import { NextIntlClientProvider } from 'next-intl';
import { Inter } from 'next/font/google';
import { notFound } from 'next/navigation';
import { ReactNode, Suspense } from 'react';
-import { supportedLanguages } from '../../i18n-config';
-import './globals.css';
+import { supportedLanguages } from '../../../i18n-config';
+import './../../globals.css';
export const metadata = {
title: {
@@ -48,7 +48,7 @@ export default async function LocaleLayout({ children, params: { locale } }: Loc
let messages;
try {
- messages = (await import(`../../messages/${locale}.json`)).default;
+ messages = (await import(`../../../messages/${locale}.json`)).default;
} catch (error) {
notFound();
}
diff --git a/app/[locale]/not-found.tsx b/app/(site)/[locale]/not-found.tsx
similarity index 100%
rename from app/[locale]/not-found.tsx
rename to app/(site)/[locale]/not-found.tsx
diff --git a/app/[locale]/page.tsx b/app/(site)/[locale]/page.tsx
similarity index 100%
rename from app/[locale]/page.tsx
rename to app/(site)/[locale]/page.tsx
diff --git a/app/[locale]/sok/page.tsx b/app/(site)/[locale]/sok/page.tsx
similarity index 100%
rename from app/[locale]/sok/page.tsx
rename to app/(site)/[locale]/sok/page.tsx
diff --git a/app/(studio)/studio/[[...index]]/page.tsx b/app/(studio)/studio/[[...index]]/page.tsx
new file mode 100644
index 000000000..665afa1ef
--- /dev/null
+++ b/app/(studio)/studio/[[...index]]/page.tsx
@@ -0,0 +1,8 @@
+'use client';
+
+import { NextStudio } from 'next-sanity/studio';
+import config from '@/sanity.config';
+
+export default function AdminPage() {
+ return ;
+}
diff --git a/app/(studio)/studio/layout.tsx b/app/(studio)/studio/layout.tsx
new file mode 100644
index 000000000..4877d0dc6
--- /dev/null
+++ b/app/(studio)/studio/layout.tsx
@@ -0,0 +1,14 @@
+import './../../globals.css';
+
+export const metadata = {
+ title: `Studio | ${process.env.SITE_NAME}`,
+ description: 'KM Storefront studio admin interface.'
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/app/api/revalidate/sanity/route.ts b/app/api/revalidate/sanity/route.ts
deleted file mode 100644
index 63146640a..000000000
--- a/app/api/revalidate/sanity/route.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { isValidSignature, SIGNATURE_HEADER_NAME } from '@sanity/webhook';
-import { revalidatePath } from 'next/cache';
-import { headers } from 'next/headers';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SANITY_WEBHOOK_SECRET = `${process.env.SANITY_WEBHOOK_SECRET}`;
-
-export async function POST(request: NextRequest) {
- // Await the response from our request.
- const requestData = await request.json();
-
- // Get headers.
- const headersList = headers();
-
- // Get Sanity webhook signature header name.
- const signature = `${headersList.get(SIGNATURE_HEADER_NAME)}`;
- const isValid = isValidSignature(JSON.stringify(requestData), signature, SANITY_WEBHOOK_SECRET);
-
- // Log out validity of request.
- console.log(`Webhook request valid? ${isValid}`);
-
- // If not valid, return.
- if (!isValid) {
- NextResponse.json({ success: false, message: 'Invalid signature' });
- return;
- }
-
- const slug = requestData.slug;
- const type = requestData.type;
-
- if (type === 'home') {
- revalidatePath(`${slug}`)
- } else {
- revalidatePath(`${slug}`)
- }
-
- console.log(`Revalidated path: ${slug}`);
- return NextResponse.json({ revalidated: true, now: Date.now() });
-}
\ No newline at end of file
diff --git a/app/[locale]/globals.css b/app/globals.css
similarity index 100%
rename from app/[locale]/globals.css
rename to app/globals.css
diff --git a/lib/sanity/components/hotspots/ProductTooltip.tsx b/lib/sanity/components/hotspots/ProductTooltip.tsx
new file mode 100644
index 000000000..4b39100c8
--- /dev/null
+++ b/lib/sanity/components/hotspots/ProductTooltip.tsx
@@ -0,0 +1,38 @@
+import styled from 'styled-components'
+import {PreviewLayoutKey, SchemaType, useSchema} from 'sanity'
+import {Box} from '@sanity/ui'
+import {HotspotTooltipProps} from 'sanity-plugin-hotspot-array'
+import {useMemo} from 'react'
+
+interface HotspotFields {
+ productWithVariant?: {
+ product: {
+ _ref: string
+ }
+ }
+}
+
+const StyledBox = styled(Box)`
+ width: 200px;
+`
+
+export default function ProductPreview(props: HotspotTooltipProps) {
+ const {value, renderPreview} = props
+ const productSchemaType = useSchema().get('product')
+ const hasProduct = value?.productWithVariant?.product?._ref && productSchemaType
+
+ const previewProps = useMemo(
+ () => ({
+ value: value?.productWithVariant?.product,
+ schemaType: productSchemaType as SchemaType,
+ layout: 'default' as PreviewLayoutKey,
+ }),
+ [productSchemaType, value?.productWithVariant?.product]
+ )
+
+ return (
+
+ {hasProduct && previewProps ? renderPreview(previewProps) : `No product selected`}
+
+ )
+}
diff --git a/lib/sanity/components/icons/kodamera.tsx b/lib/sanity/components/icons/kodamera.tsx
new file mode 100644
index 000000000..51af6859b
--- /dev/null
+++ b/lib/sanity/components/icons/kodamera.tsx
@@ -0,0 +1,27 @@
+const Kodamera = () => {
+ return (
+
+ )
+}
+
+export default Kodamera
\ No newline at end of file
diff --git a/lib/sanity/components/inputs/PlaceholderString.tsx b/lib/sanity/components/inputs/PlaceholderString.tsx
new file mode 100644
index 000000000..22e96526d
--- /dev/null
+++ b/lib/sanity/components/inputs/PlaceholderString.tsx
@@ -0,0 +1,20 @@
+import {StringInputProps, useFormValue, SanityDocument, StringSchemaType} from 'sanity'
+import get from 'lodash.get'
+
+type Props = StringInputProps
+
+const PlaceholderStringInput = (props: Props) => {
+ const {schemaType} = props
+
+ const path = schemaType?.options?.field
+ const doc = useFormValue([]) as SanityDocument
+
+ const proxyValue = path ? (get(doc, path) as string) : ''
+
+ return props.renderDefault({
+ ...props,
+ elementProps: {placeholder: proxyValue, ...props.elementProps},
+ })
+}
+
+export default PlaceholderStringInput
diff --git a/lib/sanity/components/inputs/ProxyString.tsx b/lib/sanity/components/inputs/ProxyString.tsx
new file mode 100644
index 000000000..3129370c7
--- /dev/null
+++ b/lib/sanity/components/inputs/ProxyString.tsx
@@ -0,0 +1,32 @@
+import {LockIcon} from '@sanity/icons'
+import {Box, Text, TextInput, Tooltip} from '@sanity/ui'
+import {StringInputProps, useFormValue, SanityDocument, StringSchemaType} from 'sanity'
+import get from 'lodash.get'
+
+type Props = StringInputProps
+
+const ProxyString = (props: Props) => {
+ const {schemaType} = props
+
+ const path = schemaType?.options?.field
+ const doc = useFormValue([]) as SanityDocument
+
+ const proxyValue = path ? (get(doc, path) as string) : ''
+
+ return (
+
+
+ This value is set in Shopify ({path}
)
+
+
+ }
+ portal
+ >
+
+
+ )
+}
+
+export default ProxyString
diff --git a/lib/sanity/constants.ts b/lib/sanity/constants.ts
new file mode 100644
index 000000000..9551f2347
--- /dev/null
+++ b/lib/sanity/constants.ts
@@ -0,0 +1,48 @@
+// Currency code (ISO 4217) to use when displaying prices in the studio
+// https://en.wikipedia.org/wiki/ISO_4217
+export const DEFAULT_CURRENCY_CODE = 'SEK'
+
+// Document types which:
+// - cannot be created in the 'new document' menu
+// - cannot be duplicated, unpublished or deleted
+export const LOCKED_DOCUMENT_TYPES = ['settings', 'home', 'media.tag']
+
+// Document types which:
+// - cannot be created in the 'new document' menu
+// - cannot be duplicated, unpublished or deleted
+// - are from the KM-COMMERCE connect app.
+export const STORM_DOCUMENT_TYPES = ['product', 'productVariant', 'category']
+
+// References to include in 'internal' links
+export const PAGE_REFERENCES = [
+ {type: 'category'},
+ {type: 'home'},
+ {type: 'page'},
+ {type: 'product'},
+ {type: 'productVariant'},
+]
+
+// Objects to include in page building arrays.
+export const COMPONENT_REFERENCES = [
+ {type: 'hero'},
+ {type: 'slider'},
+ {type: 'filteredProductList'},
+ {type: 'blurbSection'},
+ {type: 'uspSection'},
+ {type: 'reusableSection'},
+]
+
+// API version to use when using the Sanity client within the studio
+// https://www.sanity.io/help/studio-client-specify-api-version
+export const SANITY_API_VERSION = '2022-10-25'
+
+// Your Shopify store ID.
+// This is your unique store URL (e.g. 'my-store-name.myshopify.com').
+// Set this to enable helper links in document status banners and shortcut links on products and collections.
+export const STORM_STORE_ID = ''
+
+// Project preview URLs
+export const localStorefrontUrl = 'http://localhost:3000';
+export const localStorefrontPreviewUrl = 'http://localhost:3000/api/draft';
+export const publicStorefrontUrl = 'https://km-storefront.vercel.app';
+export const publicStorefrontPreviewUrl = 'https://km-storefront.vercel.app/api/draft';
\ No newline at end of file
diff --git a/lib/sanity/desk/blurbStructure.ts b/lib/sanity/desk/blurbStructure.ts
new file mode 100644
index 000000000..3d9dd8d39
--- /dev/null
+++ b/lib/sanity/desk/blurbStructure.ts
@@ -0,0 +1,15 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Blurbs')
+ .schemaType('blurb')
+ .child (
+ S.documentTypeList('blurb')
+ .child (
+ S.document()
+ .schemaType("blurb")
+ )
+ )
+)
diff --git a/lib/sanity/desk/categoryStructure.ts b/lib/sanity/desk/categoryStructure.ts
new file mode 100644
index 000000000..35892698a
--- /dev/null
+++ b/lib/sanity/desk/categoryStructure.ts
@@ -0,0 +1,33 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+import Iframe from 'sanity-plugin-iframe-pane'
+import {SanityDocument} from 'sanity'
+import {EyeOpenIcon, MasterDetailIcon} from '@sanity/icons'
+import getPreviewUrl from '../utils/getPreviewUrl'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Categories')
+ .schemaType('category')
+ .child (
+ S.documentTypeList('category')
+ .child (id =>
+ S.document()
+ .schemaType("category")
+ .id(id)
+ .views([
+ S.view
+ .form()
+ .icon(MasterDetailIcon),
+ S.view
+ .component(Iframe)
+ .icon(EyeOpenIcon)
+ .options({
+ url: (doc: SanityDocument) => getPreviewUrl(doc),
+ })
+ .title('Preview')
+ ])
+ )
+
+ )
+)
diff --git a/lib/sanity/desk/homeStructure.ts b/lib/sanity/desk/homeStructure.ts
new file mode 100644
index 000000000..3b7aacf4b
--- /dev/null
+++ b/lib/sanity/desk/homeStructure.ts
@@ -0,0 +1,34 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+import Iframe from 'sanity-plugin-iframe-pane'
+import {SanityDocument} from 'sanity'
+import {EyeOpenIcon, MasterDetailIcon} from '@sanity/icons'
+import getPreviewUrl from '../utils/getPreviewUrl'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Home')
+ .schemaType('home')
+ .child (
+ S.documentList()
+ .title('Home pages')
+ .filter('_type == "home"')
+ .child(id =>
+ S.document()
+ .schemaType("home")
+ .id(id)
+ .views([
+ S.view
+ .form()
+ .icon(MasterDetailIcon),
+ S.view
+ .component(Iframe)
+ .icon(EyeOpenIcon)
+ .options({
+ url: (doc: SanityDocument) => getPreviewUrl(doc),
+ })
+ .title('Preview')
+ ])
+ )
+ )
+)
diff --git a/lib/sanity/desk/index.ts b/lib/sanity/desk/index.ts
new file mode 100644
index 000000000..f6a82cecc
--- /dev/null
+++ b/lib/sanity/desk/index.ts
@@ -0,0 +1,73 @@
+/**
+ * Desk structure overrides
+ */
+import {ListItemBuilder, StructureResolver} from 'sanity/desk'
+import categories from './categoryStructure'
+import home from './homeStructure'
+import pages from './pageStructure'
+import products from './productStructure'
+import settings from './settingStructure'
+import blurbs from './blurbStructure'
+import sections from './sectionStructure'
+import usps from './uspStructure'
+import navigation from './navigationStructure'
+
+/**
+ * Desk structure overrides
+ *
+ * Sanity Studio automatically lists document types out of the box.
+ * With this custom desk structure we achieve things like showing the `home`
+ * and `settings` document types as singletons, and grouping product details
+ * and variants for easy editorial access.
+ *
+ * You can customize this even further as your schemas progress.
+ * To learn more about structure builder, visit our docs:
+ * https://www.sanity.io/docs/overview-structure-builder
+ */
+
+// If you add document types to desk structure manually, you can add them to this function to prevent duplicates in the root pane
+const hiddenDocTypes = (listItem: ListItemBuilder) => {
+ const id = listItem.getId()
+
+ if (!id) {
+ return false
+ }
+
+ return ![
+ 'category',
+ 'home',
+ 'media.tag',
+ 'page',
+ 'product',
+ 'productVariant',
+ 'settings',
+ 'blurb',
+ 'section',
+ 'usp',
+ 'navigation',
+ 'footerMenu',
+ 'utilityMenu'
+ ].includes(id)
+}
+
+export const structure: StructureResolver = (S, context) =>
+ S.list()
+ .title('Content')
+ .items([
+ home(S, context),
+ pages(S, context),
+ S.divider(),
+ products(S, context),
+ categories(S, context),
+ S.divider(),
+ blurbs(S, context),
+ usps(S, context),
+ sections(S, context),
+ S.divider(),
+ navigation(S, context),
+ S.divider(),
+ settings(S, context),
+ S.divider(),
+ ...S.documentTypeListItems().filter(hiddenDocTypes),
+ S.divider(),
+ ])
diff --git a/lib/sanity/desk/navigationStructure.ts b/lib/sanity/desk/navigationStructure.ts
new file mode 100644
index 000000000..155c23260
--- /dev/null
+++ b/lib/sanity/desk/navigationStructure.ts
@@ -0,0 +1,31 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+import {MenuIcon} from '@sanity/icons'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Navigation')
+ .icon(MenuIcon)
+ .child(
+ S.list()
+ // Sets a title for our new list
+ .title('Navigation Documents')
+ // Add items to the array
+ // Each will pull one of our new documents/singletons
+ .items([
+ S.listItem()
+ .title('Utility menu')
+ .child(S.document().schemaType('utilityMenu').documentId('utilityMenu')),
+ S.listItem()
+ .title('Footer menus')
+ .child(
+ S.documentTypeList('footerMenu')
+ .title('Footer menus')
+ .child (
+ S.document()
+ .schemaType("footerMenu")
+ )
+ ),
+ ])
+ ),
+)
\ No newline at end of file
diff --git a/lib/sanity/desk/pageStructure.ts b/lib/sanity/desk/pageStructure.ts
new file mode 100644
index 000000000..a2ae46e4b
--- /dev/null
+++ b/lib/sanity/desk/pageStructure.ts
@@ -0,0 +1,35 @@
+ import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+import {DocumentsIcon} from '@sanity/icons'
+import Iframe from 'sanity-plugin-iframe-pane'
+import {SanityDocument} from 'sanity'
+import {EyeOpenIcon, MasterDetailIcon} from '@sanity/icons'
+import getPreviewUrl from '../utils/getPreviewUrl'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Pages')
+ .schemaType('page')
+ .icon(DocumentsIcon)
+ .child (
+ S.documentTypeList('page')
+ .child (id =>
+ S.document()
+ .schemaType("page")
+ .id(id)
+ .views([
+ S.view
+ .form()
+ .icon(MasterDetailIcon),
+ S.view
+ .component(Iframe)
+ .icon(EyeOpenIcon)
+ .options({
+ url: (doc: SanityDocument) => getPreviewUrl(doc),
+ })
+ .title('Preview')
+ ])
+ )
+
+ )
+)
\ No newline at end of file
diff --git a/lib/sanity/desk/productStructure.ts b/lib/sanity/desk/productStructure.ts
new file mode 100644
index 000000000..6f985dc4f
--- /dev/null
+++ b/lib/sanity/desk/productStructure.ts
@@ -0,0 +1,73 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+import Iframe from 'sanity-plugin-iframe-pane'
+import {SanityDocument} from 'sanity'
+import {EyeOpenIcon, MasterDetailIcon} from '@sanity/icons'
+import getPreviewUrl from '../utils/getPreviewUrl'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Products')
+ .schemaType('product')
+ .child (
+ S.documentTypeList('product')
+ .child (id =>
+ S.document()
+ .schemaType("product")
+ .id(id)
+ .views([
+ S.view
+ .form()
+ .icon(MasterDetailIcon),
+ S.view
+ .component(Iframe)
+ .icon(EyeOpenIcon)
+ .options({
+ url: (doc: SanityDocument) => getPreviewUrl(doc),
+ })
+ .title('Preview')
+ ])
+ )
+
+ )
+)
+
+// @TODO - FIX THIS STRUCTURE.
+// export default defineStructure((S) =>
+// S.listItem()
+// .title('Products')
+// .schemaType('product')
+// .child(
+// S.documentTypeList('product')
+// // .defaultLayout('detail')
+// .child(async (id) =>
+// S.list()
+// .title('Product')
+// .items([
+// // Details
+// S.listItem()
+// .title('Details')
+// .icon(InfoOutlineIcon)
+// .child(S.document().schemaType('product').documentId(id)),
+// // Product variants
+// S.listItem()
+// .title('Variants')
+// .schemaType('productVariant')
+// .child(
+// S.documentList()
+// .title('Variants')
+// .schemaType('productVariant')
+// .filter(
+// `
+// _type == "productVariant"
+// && store.productId == $productId
+// `
+// )
+// .params({
+// productId: Number(id.replace('shopifyProduct-', '')),
+// })
+// ),
+// ])
+// )
+// )
+// )
diff --git a/lib/sanity/desk/sectionStructure.ts b/lib/sanity/desk/sectionStructure.ts
new file mode 100644
index 000000000..7a03a3010
--- /dev/null
+++ b/lib/sanity/desk/sectionStructure.ts
@@ -0,0 +1,15 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Sections')
+ .schemaType('section')
+ .child (
+ S.documentTypeList('section')
+ .child (
+ S.document()
+ .schemaType("section")
+ )
+ )
+)
diff --git a/lib/sanity/desk/settingStructure.ts b/lib/sanity/desk/settingStructure.ts
new file mode 100644
index 000000000..881a2b769
--- /dev/null
+++ b/lib/sanity/desk/settingStructure.ts
@@ -0,0 +1,15 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('Settings')
+ .schemaType('settings')
+ .child (
+ S.documentTypeList('settings')
+ .child (
+ S.document()
+ .schemaType("settings")
+ )
+ )
+)
diff --git a/lib/sanity/desk/uspStructure.ts b/lib/sanity/desk/uspStructure.ts
new file mode 100644
index 000000000..e4e0af8eb
--- /dev/null
+++ b/lib/sanity/desk/uspStructure.ts
@@ -0,0 +1,15 @@
+import {ListItemBuilder} from 'sanity/desk'
+import defineStructure from '../utils/defineStructure'
+
+export default defineStructure((S) =>
+ S.listItem()
+ .title('USPs')
+ .schemaType('usp')
+ .child (
+ S.documentTypeList('usp')
+ .child (
+ S.document()
+ .schemaType("usp")
+ )
+ )
+)
\ No newline at end of file
diff --git a/lib/sanity/languages.ts b/lib/sanity/languages.ts
new file mode 100644
index 000000000..d92d13230
--- /dev/null
+++ b/lib/sanity/languages.ts
@@ -0,0 +1,21 @@
+
+export const languages = [
+ {
+ id: 'sv',
+ title: 'Swedish',
+ flag: '🇸🇪'
+ },
+ {
+ id: 'en',
+ title: 'English',
+ flag: '🇬🇧'
+ },
+]
+
+const i18n = {
+ languages: languages,
+ base: 'sv'
+}
+
+// For v3 studio
+export {i18n}
\ No newline at end of file
diff --git a/lib/sanity/localizedTypes.ts b/lib/sanity/localizedTypes.ts
new file mode 100644
index 000000000..0b93d8b1b
--- /dev/null
+++ b/lib/sanity/localizedTypes.ts
@@ -0,0 +1,23 @@
+const product = {
+ type: 'product',
+ sv: 'produkt',
+ en: 'product'
+}
+
+const category = {
+ type: 'category',
+ 'sv': 'kategori',
+ 'en': 'category'
+}
+
+const page = {
+ type: 'page',
+ 'sv': '',
+ 'en': ''
+}
+
+export const localizedTypes = [
+ page,
+ product,
+ category,
+]
\ No newline at end of file
diff --git a/lib/sanity/schemas/blocks/body.tsx b/lib/sanity/schemas/blocks/body.tsx
new file mode 100644
index 000000000..94626c058
--- /dev/null
+++ b/lib/sanity/schemas/blocks/body.tsx
@@ -0,0 +1,54 @@
+import {defineField} from 'sanity'
+
+export default defineField({
+ name: 'body',
+ title: 'Body',
+ type: 'array',
+ of: [
+ {
+ lists: [
+ {title: 'Bullet', value: 'bullet'},
+ {title: 'Numbered', value: 'number'},
+ ],
+ marks: {
+ decorators: [
+ {
+ title: 'Italic',
+ value: 'em',
+ },
+ {
+ title: 'Strong',
+ value: 'strong',
+ },
+ ],
+ },
+ // Paragraphs
+ type: 'block',
+ },
+ // Custom blocks
+ // {
+ // name: 'blockAccordion',
+ // type: 'module.accordion',
+ // },
+ // {
+ // name: 'blockCallout',
+ // type: 'module.callout',
+ // },
+ // {
+ // name: 'blockGrid',
+ // type: 'module.grid',
+ // },
+ // {
+ // name: 'blockImages',
+ // type: 'module.images',
+ // },
+ // {
+ // name: 'blockInstagram',
+ // type: 'module.instagram',
+ // },
+ // {
+ // name: 'blockProducts',
+ // type: 'module.products',
+ // },
+ ],
+})
diff --git a/lib/sanity/schemas/documents/blurb.tsx b/lib/sanity/schemas/documents/blurb.tsx
new file mode 100644
index 000000000..eead7f8dc
--- /dev/null
+++ b/lib/sanity/schemas/documents/blurb.tsx
@@ -0,0 +1,122 @@
+import {CommentIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+import {languages} from '../../languages'
+import {validateImage} from '../../utils/validation'
+
+export default defineField({
+ name: 'blurb',
+ title: 'Blurb',
+ type: 'document',
+ icon: CommentIcon,
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'What do you want to convey?',
+ validation: (Rule) => Rule.required(),
+ }),
+ // Image
+ defineField({
+ name: 'image',
+ title: 'Image',
+ type: 'mainImage',
+ validation: (Rule) => validateImage(Rule, true),
+ }),
+ // Text
+ defineField({
+ name: 'text',
+ title: 'Text',
+ type: 'text',
+ description: 'Small text displayed below title.',
+ rows: 5,
+ }),
+ // Link
+ {
+ name: 'link',
+ title: 'Link',
+ type: 'object',
+ fields: [
+ {
+ name: 'linkType',
+ type: 'string',
+ title: 'Link type',
+ initialValue: 'internal',
+ description: 'Link to internal or external content.',
+ validation: (Rule) => Rule.required(),
+ options: {
+ list: ['internal', 'external'],
+ layout: 'radio',
+ },
+ },
+ {
+ name: 'internalLink',
+ type: 'linkInternal',
+ title: 'Internal link',
+ hidden: ({parent}) => parent?.linkType !== 'internal',
+ options: {
+ collapsible: false,
+ },
+ validation: (Rule) =>
+ Rule.custom((value: any, context: any) => {
+ if (context.parent.linkType == 'internal') {
+ const currentLink = value && value.reference
+ if (!currentLink) {
+ return 'Reference is required'
+ }
+ }
+ return true
+ }),
+ },
+ {
+ name: 'externalLink',
+ type: 'linkExternal',
+ title: 'External link',
+ hidden: ({parent}) => parent?.linkType !== 'external',
+ options: {
+ collapsible: false,
+ },
+ validation: (Rule) =>
+ Rule.custom((value: any, context: any) => {
+ if (context.parent.linkType == 'external') {
+ const currentTitle = value?.title
+ const currentUrl = value?.url
+ if (!currentTitle) {
+ return 'Title is required'
+ } else if (!currentUrl) {
+ return 'URL is required'
+ }
+ }
+ return true
+ }),
+ },
+ ],
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ image: 'image',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {image, title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ media: image,
+ title,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/category.tsx b/lib/sanity/schemas/documents/category.tsx
new file mode 100644
index 000000000..493df701c
--- /dev/null
+++ b/lib/sanity/schemas/documents/category.tsx
@@ -0,0 +1,126 @@
+import {TagIcon} from '@sanity/icons'
+import {defineField, defineType} from 'sanity'
+import {slugWithLocalizedType} from './slugWithLocalizedType'
+import {languages} from '../../languages'
+import {validateImage} from '../../utils/validation'
+
+const GROUPS = [
+ {
+ name: 'editorial',
+ title: 'Editorial',
+ },
+ {
+ name: 'seo',
+ title: 'SEO',
+ },
+]
+
+export default defineType({
+ name: 'category',
+ title: 'Category',
+ type: 'document',
+ icon: TagIcon,
+ groups: GROUPS,
+ fields: [
+ // Language
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'Category title.',
+ }),
+ // Slug
+ slugWithLocalizedType('category', 'title'),
+ // // Show banner
+ // defineField({
+ // name: 'showBanner',
+ // title: 'Show banner',
+ // type: 'boolean',
+ // description: 'If disabled, category title will be displayed instead.',
+ // group: 'editorial',
+ // }),
+ // // Banner
+ // defineField({
+ // name: 'banner',
+ // title: 'Banner',
+ // type: 'banner',
+ // hidden: ({document}) => !document?.showBanner,
+ // group: 'editorial',
+ // }),
+ // Image
+ defineField({
+ name: 'image',
+ type: 'mainImage',
+ title: 'Image',
+ validation: (Rule) => validateImage(Rule, true),
+ }),
+ defineField({
+ name: 'description',
+ title: 'Description',
+ type: 'text',
+ rows: 5,
+ description: 'Description of this category.',
+ }),
+ defineField({
+ name: 'id',
+ title: 'ID',
+ type: 'number',
+ description: 'Unique ID.',
+ }),
+ defineField({
+ name: 'categoryId',
+ title: 'Category ID',
+ type: 'number',
+ description: 'Unique category ID.',
+ }),
+ defineField({
+ name: 'parentId',
+ title: 'Parent ID',
+ type: 'number',
+ description: 'Unique parent category ID.',
+ }),
+ // SEO
+ defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'seo',
+ group: 'seo',
+ }),
+ ],
+ orderings: [
+ {
+ name: 'titleAsc',
+ title: 'Title (A-Z)',
+ by: [{field: 'title', direction: 'asc'}],
+ },
+ {
+ name: 'titleDesc',
+ title: 'Title (Z-A)',
+ by: [{field: 'title', direction: 'desc'}],
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {title, language} = selection
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ title,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ media: TagIcon,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/footerMenu.tsx b/lib/sanity/schemas/documents/footerMenu.tsx
new file mode 100644
index 000000000..b57dda013
--- /dev/null
+++ b/lib/sanity/schemas/documents/footerMenu.tsx
@@ -0,0 +1,48 @@
+import {MenuIcon} from '@sanity/icons'
+import {defineType, defineField} from 'sanity'
+import {languages} from '../../languages'
+
+export default defineType({
+ name: 'footerMenu',
+ title: 'Footer menu',
+ type: 'document',
+ icon: MenuIcon,
+ groups: [],
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ defineField({
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Menu title or designation for menu.',
+ }),
+ // Menu
+ defineField({
+ name: 'menu',
+ title: 'Menu',
+ type: 'menu',
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ title: `${title}`,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/page.tsx b/lib/sanity/schemas/documents/page.tsx
new file mode 100644
index 000000000..7b040327b
--- /dev/null
+++ b/lib/sanity/schemas/documents/page.tsx
@@ -0,0 +1,75 @@
+import {DocumentIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+import {languages} from '../../languages'
+import {COMPONENT_REFERENCES} from '../../constants'
+import {slugWithLocalizedType} from './slugWithLocalizedType'
+
+export default defineField({
+ name: 'page',
+ title: 'Page',
+ type: 'document',
+ icon: DocumentIcon,
+ groups: [
+ {
+ name: 'editorial',
+ title: 'Editorial',
+ },
+ {
+ name: 'seo',
+ title: 'SEO',
+ },
+ ],
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'Page title.',
+ validation: (Rule) => Rule.required(),
+ }),
+ // Slug
+ slugWithLocalizedType('page', 'title'),
+ // Content
+ defineField({
+ name: 'content',
+ title: 'Page sections',
+ type: 'array',
+ group: 'editorial',
+ description: 'Add, reorder, edit or delete page sections.',
+ of: COMPONENT_REFERENCES,
+ }),
+ // SEO
+ defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'seo',
+ group: 'seo',
+ }),
+ ],
+ preview: {
+ select: {
+ seoImage: 'seo.image',
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {seoImage, title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ media: seoImage,
+ title,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/product.tsx b/lib/sanity/schemas/documents/product.tsx
new file mode 100644
index 000000000..0e773cf41
--- /dev/null
+++ b/lib/sanity/schemas/documents/product.tsx
@@ -0,0 +1,167 @@
+import {PackageIcon} from '@sanity/icons'
+import {defineField, defineType} from 'sanity'
+import {slugWithLocalizedType} from './slugWithLocalizedType'
+import {languages} from '../../languages'
+import {validateImage} from '../../utils/validation'
+
+const GROUPS = [
+ {
+ name: 'editorial',
+ title: 'Editorial',
+ },
+ {
+ name: 'seo',
+ title: 'SEO',
+ },
+]
+
+export default defineType({
+ name: 'product',
+ title: 'Product',
+ type: 'document',
+ icon: PackageIcon,
+ groups: GROUPS,
+ fields: [
+ // Language
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // ID
+ defineField({
+ name: 'id',
+ title: 'ID',
+ type: 'number',
+ description: 'Unique product ID.',
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'Product title/name.',
+ validation: (Rule) => Rule.required(),
+ }),
+ // Slug
+ slugWithLocalizedType('product', 'title'),
+ defineField({
+ name: 'images',
+ title: 'Images',
+ description: 'Images of this product, the first image will be used as main image.',
+ type: 'array',
+ of: [
+ {
+ title: 'Image',
+ type: 'mainImage',
+ validation: (Rule) => validateImage(Rule, true),
+ },
+ ],
+ validation: (Rule) => Rule.required().min(1).max(5),
+ }),
+ defineField({
+ name: 'description',
+ title: 'Description',
+ type: 'text',
+ description: 'Product description.',
+ }),
+ defineField({
+ name: 'price',
+ title: 'Price',
+ type: 'object',
+ description: 'Product price information.',
+ fields: [
+ defineField({
+ name: 'value',
+ title: 'Value',
+ type: 'number',
+ description: 'Product price.',
+ }),
+ defineField({
+ name: 'currencyCode',
+ title: 'Currency code',
+ type: 'string',
+ description: 'Product currency code.',
+ options: {
+ list: [
+ {title: 'SEK', value: 'SEK'},
+ {title: 'GBP', value: 'GBP'},
+ {title: 'EUR', value: 'EUR'},
+ ],
+ layout: 'radio',
+ },
+ initialValue: 'SEK',
+ }),
+ defineField({
+ name: 'retailPrice',
+ title: 'Retail price',
+ type: 'number',
+ description: 'Product retail price.',
+ }),
+ ],
+ }),
+ defineField({
+ name: 'options',
+ title: 'Product options',
+ type: 'array',
+ description: 'What product options are available?',
+ of: [{type: 'productOptions'}],
+ }),
+ defineField({
+ name: 'categories',
+ title: 'Categories',
+ type: 'array',
+ description: 'What category/categories does this product belong to?',
+ of: [{type: 'reference', to: {type: 'category'}}],
+ }),
+ defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'seo',
+ group: 'seo',
+ }),
+ ],
+ orderings: [
+ {
+ name: 'titleAsc',
+ title: 'Title (A-Z)',
+ by: [{field: 'title', direction: 'asc'}],
+ },
+ {
+ name: 'titleDesc',
+ title: 'Title (Z-A)',
+ by: [{field: 'title', direction: 'desc'}],
+ },
+ {
+ name: 'priceDesc',
+ title: 'Price (Highest first)',
+ by: [{field: 'price', direction: 'desc'}],
+ },
+ {
+ name: 'priceAsc',
+ title: 'Title (Lowest first)',
+ by: [{field: 'price', direction: 'asc'}],
+ },
+ ],
+ preview: {
+ select: {
+ images: 'images',
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {images, title, language} = selection
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ const firstImage = images[0]
+
+ return {
+ title,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ media: firstImage ? firstImage : PackageIcon,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/productVariant.tsx b/lib/sanity/schemas/documents/productVariant.tsx
new file mode 100644
index 000000000..74a6f9769
--- /dev/null
+++ b/lib/sanity/schemas/documents/productVariant.tsx
@@ -0,0 +1,30 @@
+import {CopyIcon} from '@sanity/icons'
+import {defineField, defineType} from 'sanity'
+
+export default defineType({
+ name: 'productVariant',
+ title: 'Product variant',
+ type: 'document',
+ icon: CopyIcon,
+ fields: [
+ // Title
+ defineField({
+ title: 'Title',
+ name: 'title',
+ type: 'string',
+ description: 'Product variant title/name.'
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ },
+ prepare(selection) {
+ const {title} = selection
+
+ return {
+ title,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/section.tsx b/lib/sanity/schemas/documents/section.tsx
new file mode 100644
index 000000000..3de3cada1
--- /dev/null
+++ b/lib/sanity/schemas/documents/section.tsx
@@ -0,0 +1,64 @@
+import {defineField} from 'sanity'
+import {BlockElementIcon} from '@sanity/icons'
+import {languages} from '../../languages'
+
+export default defineField({
+ name: 'section',
+ type: 'document',
+ title: 'Reusable section',
+ icon: BlockElementIcon,
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ }),
+ defineField({
+ name: 'section',
+ title: 'Section',
+ type: 'object',
+ description: 'Reusable section to refer to from other pages.',
+ fields: [
+ defineField({
+ name: 'sectionType',
+ type: 'array',
+ title: 'Section type',
+ description: 'Select reusable component (only 1 allowed).',
+ of: [
+ {type: 'hero'},
+ {type: 'filteredProductList'},
+ {type: 'slider'},
+ {type: 'blurbSection'},
+ {type: 'uspSection'},
+ ],
+ validation: (Rule) => Rule.length(1),
+ }),
+ ],
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ title: `${title}`,
+ media: BlockElementIcon,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/documents/slugWithLocalizedType.ts b/lib/sanity/schemas/documents/slugWithLocalizedType.ts
new file mode 100644
index 000000000..d2880d440
--- /dev/null
+++ b/lib/sanity/schemas/documents/slugWithLocalizedType.ts
@@ -0,0 +1,56 @@
+import {Rule, Slug} from 'sanity'
+import slugify from "slugify";
+import { i18n } from "../../languages";
+import { localizedTypes } from "../../localizedTypes";
+
+const MAX_LENGTH = 96
+
+function formatSlug(input: string, docType: string, context: any, schemaType: object | any) {
+ const locale = schemaType?.parent?.language ? schemaType?.parent?.language : i18n.base;
+
+ let currentDocType: any;
+
+ currentDocType = localizedTypes.find(item => item.type === docType);
+ const currentDocTypeLocalized = currentDocType[locale];
+
+ const slugStart = currentDocTypeLocalized ? `/${currentDocTypeLocalized}/` : `/`;
+ const slug = slugify(input, { lower: true });
+
+ return slugStart + slug;
+}
+
+export function slugWithLocalizedType(documentType = '', source = `title`) {
+ const docType = documentType;
+
+ return {
+ name: `slug`,
+ type: `slug`,
+ options: {
+ source,
+ slugify: (value: any, context: any, schemaType: object | any) => formatSlug(value, docType, context, schemaType),
+ },
+ validation: (Rule: Rule) => {
+ return Rule.required().custom(async (value: Slug) => {
+
+ const currentSlug = value && value.current
+
+ if (!currentSlug) {
+ return true
+ }
+
+ if (currentSlug.length >= MAX_LENGTH) {
+ return `Must be less than ${MAX_LENGTH} characters`
+ }
+
+ if (currentSlug.length === 0) {
+ return 'Slug cannot be empty'
+ }
+
+ if (currentSlug.endsWith("/")) {
+ return 'Slug cannot end with "/"'
+ }
+ return true
+ })
+ }
+ };
+}
\ No newline at end of file
diff --git a/lib/sanity/schemas/documents/usp.tsx b/lib/sanity/schemas/documents/usp.tsx
new file mode 100644
index 000000000..b372884e9
--- /dev/null
+++ b/lib/sanity/schemas/documents/usp.tsx
@@ -0,0 +1,62 @@
+import {StarIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+import {languages} from '../../languages'
+import {validateImage} from '../../utils/validation'
+
+export default defineField({
+ name: 'usp',
+ title: 'USPs',
+ type: 'document',
+ icon: StarIcon,
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'USP title',
+ validation: (Rule) => Rule.required(),
+ }),
+ // Image
+ defineField({
+ name: 'image',
+ title: 'Image',
+ type: 'mainImage',
+ description: 'USP icon',
+ validation: (Rule) => validateImage(Rule, true),
+ }),
+ // Text
+ defineField({
+ name: 'text',
+ title: 'Text',
+ type: 'text',
+ description: 'Small text displayed below title.',
+ rows: 5,
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ image: 'image',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {image, title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ media: image,
+ title,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/index.ts b/lib/sanity/schemas/index.ts
new file mode 100644
index 000000000..b5492acb2
--- /dev/null
+++ b/lib/sanity/schemas/index.ts
@@ -0,0 +1,91 @@
+// Rich text annotations used in the block content editor
+// import annotationLinkEmail from './annotations/linkEmail'
+// import annotationLinkExternal from './annotations/linkExternal'
+// import annotationLinkInternal from './annotations/linkInternal'
+// import annotationProduct from './annotations/product'
+
+// const annotations = [
+// annotationLinkEmail,
+// annotationLinkExternal,
+// annotationLinkInternal,
+// annotationProduct,
+// ]
+
+// Document types
+import category from './documents/category'
+import page from './documents/page'
+import product from './documents/product'
+import productVariant from './documents/productVariant'
+import blurb from './documents/blurb'
+import section from './documents/section'
+import usp from './documents/usp'
+import footerMenu from './documents/footerMenu'
+
+const documents = [
+ category,
+ page,
+ product,
+ productVariant,
+ blurb,
+ section,
+ usp,
+ footerMenu
+]
+
+// Singleton document types
+import home from './singletons/home'
+import settings from './singletons/settings'
+import utilityMenu from './singletons/utilityMenu'
+// import navigation from './singletons/navigation'
+
+const singletons = [home, settings, utilityMenu]
+
+// Block content
+import body from './blocks/body'
+
+const blocks = [body]
+
+// Object types
+import banner from './objects/banner'
+import linkExternal from './objects/linkExternal'
+import linkInternal from './objects/linkInternal'
+import hero from './objects/hero'
+import placeholderString from './objects/placeholderString'
+import proxyString from './objects/proxyString'
+import seo from './objects/seo'
+import mainImage from './objects/mainImage'
+import slider from './objects/slider'
+import productOption from './objects/productOption'
+import productOptions from './objects/productOptions'
+import blurbSection from './objects/blurbSection'
+import filteredProductList from './objects/filteredProductList'
+import uspSection from './objects/uspSection'
+import reusableSection from './objects/reusableSection'
+import menu from './objects/menu'
+
+const objects = [
+ linkExternal,
+ linkInternal,
+ hero,
+ placeholderString,
+ proxyString,
+ seo,
+ mainImage,
+ slider,
+ productOption,
+ productOptions,
+ filteredProductList,
+ banner,
+ blurbSection,
+ uspSection,
+ reusableSection,
+ menu
+]
+
+export const schemaTypes = [
+ // ...annotations,
+ ...documents,
+ ...singletons,
+ ...objects,
+ ...blocks
+]
diff --git a/lib/sanity/schemas/objects/banner.ts b/lib/sanity/schemas/objects/banner.ts
new file mode 100644
index 000000000..fe950b1a9
--- /dev/null
+++ b/lib/sanity/schemas/objects/banner.ts
@@ -0,0 +1,50 @@
+import {defineField} from 'sanity'
+import { validateImage } from '../../utils/validation'
+
+export default defineField({
+ name: 'banner',
+ type: 'object',
+ title: 'Banner',
+ description: 'Normally used in the top of a page to display current page information.',
+ fields: [
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'What do you want to convey?',
+ validation: Rule => [
+ Rule.required(),
+ Rule.max(50).warning('Shorter titles are usually better.')
+ ]
+ },
+ {
+ name: 'text',
+ type: 'text',
+ title: 'Text',
+ rows: 5,
+ description: 'Small text below title.'
+ },
+ {
+ name: 'image',
+ type: 'mainImage',
+ title: 'Image',
+ validation: (Rule) => validateImage(Rule, true)
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ image: 'image',
+ text: 'text'
+ },
+ prepare(selection) {
+ const {title, image, text} = selection
+
+ return {
+ title: `${title}`,
+ subtitle: `Banner`,
+ media: image
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/blurbSection.tsx b/lib/sanity/schemas/objects/blurbSection.tsx
new file mode 100644
index 000000000..c946c8b78
--- /dev/null
+++ b/lib/sanity/schemas/objects/blurbSection.tsx
@@ -0,0 +1,132 @@
+import {defineField} from 'sanity'
+import {CommentIcon} from '@sanity/icons'
+
+export default defineField({
+ name: 'blurbSection',
+ type: 'object',
+ title: 'Blurb section',
+ icon: CommentIcon,
+ fieldsets: [
+ {
+ name: 'layoutSettings',
+ title: 'Layout settings',
+ },
+ ],
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: 'false',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Text displayed above blurbs.',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'mobileLayout',
+ type: 'string',
+ title: 'Mobile layout',
+ initialValue: 'stacked',
+ fieldset: 'layoutSettings',
+ description: 'Display blurbs stacked on top of each other or in a slider.',
+ validation: (Rule) => Rule.required(),
+ options: {
+ list: [
+ {
+ title: 'Vertical (1 column)',
+ value: 'vertical',
+ },
+ {
+ title: 'Horizontal (1 row with scroll)',
+ value: 'horizontal',
+ },
+ ],
+ layout: 'radio',
+ direction: 'horizontal',
+ },
+ },
+ {
+ name: 'desktopLayout',
+ type: 'string',
+ title: 'Desktop layout',
+ initialValue: '3-column',
+ fieldset: 'layoutSettings',
+ description: 'Display blurbs in a 2- 3- or 4-column layout.',
+ validation: (Rule) => Rule.required(),
+ options: {
+ list: [
+ {
+ title: '2 columns',
+ value: '2-column',
+ },
+ {
+ title: '3 columns',
+ value: '3-column',
+ },
+ {
+ title: '4 columns',
+ value: '4-column',
+ },
+ ],
+ layout: 'radio',
+ direction: 'horizontal',
+ },
+ },
+ {
+ name: 'imageFormat',
+ type: 'string',
+ title: 'Blurb image format',
+ initialValue: 'square',
+ description: 'Choose format to display blurb images in.',
+ validation: (Rule) => Rule.required(),
+ fieldset: 'layoutSettings',
+ options: {
+ list: [
+ {title: 'Square (1:1)', value: 'square'},
+ {title: 'Portrait (3:4)', value: 'portrait'},
+ {title: 'Landscape (16:9)', value: 'landscape'},
+ ],
+ layout: 'radio',
+ },
+ },
+ {
+ name: 'blurbs',
+ type: 'array',
+ title: 'Blurbs',
+ description: 'Create blurbs or refer to existing blurbs.',
+ of: [
+ {
+ type: 'reference',
+ to: [
+ {
+ type: 'blurb',
+ },
+ ],
+ },
+ ],
+ validation: (Rule) => Rule.required(),
+ },
+ ],
+
+ preview: {
+ select: {
+ title: 'title',
+ disabled: 'disabled',
+ },
+ prepare(selection) {
+ const {title, disabled} = selection
+
+ return {
+ title: `${title}`,
+ subtitle: `Blurb section ${disabled ? '(⚠️ Disabled)' : ''}`,
+ media: CommentIcon,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/filteredProductList.ts b/lib/sanity/schemas/objects/filteredProductList.ts
new file mode 100644
index 000000000..fd2172c47
--- /dev/null
+++ b/lib/sanity/schemas/objects/filteredProductList.ts
@@ -0,0 +1,70 @@
+import {defineField} from 'sanity'
+import {FilterIcon} from '@sanity/icons'
+
+export default defineField({
+ name: 'filteredProductList',
+ type: 'object',
+ title: 'Filtered product list',
+ icon: FilterIcon,
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: 'false',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Text displayed above product list.'
+ },
+ {
+ name: 'productCategories',
+ type: 'array',
+ title: 'Product categories',
+ description: 'Select category/categories to display products from.',
+ of: [
+ {
+ type: 'reference',
+ to: [{
+ type: 'category',
+ }],
+ options: {
+ disableNew: true,
+ },
+ },
+ ],
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'itemsToShow',
+ type: 'number',
+ title: 'Number of products',
+ initialValue: 4,
+ description: 'Amount of products to be displayed.',
+ validation: (Rule) => Rule.required(),
+ options: {
+ list: [4, 8, 12],
+ layout: 'radio',
+ },
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ disabled: 'disabled'
+ },
+ prepare(selection) {
+ const {title, disabled} = selection
+
+ return {
+ title: `${title}`,
+ subtitle: `Filtered product list ${disabled ? '(⚠️ Disabled)' : ''}`,
+ media: FilterIcon
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/hero.ts b/lib/sanity/schemas/objects/hero.ts
new file mode 100644
index 000000000..c60d9c5b0
--- /dev/null
+++ b/lib/sanity/schemas/objects/hero.ts
@@ -0,0 +1,125 @@
+import {defineField} from 'sanity'
+import {StarIcon} from '@sanity/icons'
+import { validateImage } from '../../utils/validation'
+
+export default defineField({
+ name: 'hero',
+ type: 'object',
+ title: 'Hero',
+ icon: StarIcon,
+ fieldsets: [
+ {
+ name: 'settings',
+ title: 'Hero settings',
+ description: 'Hero layout and semantic settings.',
+ options: {
+ collapsed: true,
+ collapsible: true,
+ },
+ }
+ ],
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: false,
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'variant',
+ type: 'string',
+ title: 'Hero variant',
+ initialValue: 'fullScreen',
+ description: 'Choose display for larger screens: Full or half screen height.',
+ validation: Rule => Rule.required(),
+ fieldset: 'settings',
+ options: {
+ list: [
+ {title: 'Full screen', value: 'fullScreen'},
+ {title: '50% height', value: 'halfScreen'},
+ ],
+ layout: 'radio',
+ }
+ },
+ {
+ name: 'headingLevel',
+ type: 'string',
+ title: 'Heading level',
+ initialValue: 'h1',
+ fieldset: 'settings',
+ description: 'Set appropriate heading level depending on the current document structure.',
+ options: {
+ list: [
+ {title: 'H1', value: 'h1'},
+ {title: 'H2', value: 'h2'},
+ ],
+ layout: 'radio',
+ },
+ },
+ {
+ name: 'label',
+ type: 'string',
+ title: 'Label',
+ description: 'Small text displayed above title.'
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'What you want to convey.',
+ validation: Rule => [
+ Rule.required(),
+ Rule.max(50).warning('Shorter titles are usually better.')
+ ]
+ },
+ {
+ name: 'text',
+ type: 'text',
+ title: 'Text',
+ rows: 5,
+ description: 'Short text displayed below title.',
+ validation: Rule => [
+ Rule.max(100).warning('Strive to be short, precise and on point.')
+ ]
+ },
+ {
+ name: 'image',
+ type: 'mainImage',
+ title: 'Image',
+ validation: Rule => validateImage(Rule, true),
+ options: {
+ hotspot: true,
+ collapsed: false,
+ collapsible: true,
+ },
+ },
+ {
+ name: 'link',
+ type: 'linkInternal',
+ title: 'Link',
+ description: 'Link to internal page.',
+ options: {
+ collapsed: true,
+ collapsible: true,
+ },
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ image: 'image',
+ disabled: 'disabled'
+ },
+ prepare(selection) {
+ const {title, image, disabled} = selection
+
+ return {
+ title: `${title}`,
+ media: image?.asset ? image : StarIcon,
+ subtitle: `Hero ${disabled ? '(⚠️ Disabled)' : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/linkExternal.ts b/lib/sanity/schemas/objects/linkExternal.ts
new file mode 100644
index 000000000..0f9becb8e
--- /dev/null
+++ b/lib/sanity/schemas/objects/linkExternal.ts
@@ -0,0 +1,54 @@
+import {EarthGlobeIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+
+export default defineField({
+ title: 'External Link',
+ name: 'linkExternal',
+ type: 'object',
+ icon: EarthGlobeIcon,
+ description: 'Link to content on external site.',
+ fields: [
+ // Title
+ defineField({
+ title: 'Title',
+ name: 'title',
+ type: 'string',
+ description: 'Descriptive text for the content on this link.'
+ }),
+ // URL
+ defineField({
+ name: 'url',
+ title: 'URL',
+ type: 'url',
+ description: 'Link to websites, e-mail address or phone number.',
+ validation: (Rule) => Rule.required().uri({scheme: ['http', 'https', 'mailto', 'tel']})
+ }),
+ // Open in a new window?
+ defineField({
+ title: 'Open in a new window?',
+ name: 'newWindow',
+ type: 'boolean',
+ description: 'If set to true, opens the link in a new window.',
+ initialValue: false,
+ })
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ url: 'url',
+ },
+ prepare(selection) {
+ const {title, url} = selection
+
+ let subtitle = []
+ if (url) {
+ subtitle.push(`→ ${url}`)
+ }
+
+ return {
+ title,
+ subtitle: subtitle.join(' '),
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/linkInternal.ts b/lib/sanity/schemas/objects/linkInternal.ts
new file mode 100644
index 000000000..eef2225fc
--- /dev/null
+++ b/lib/sanity/schemas/objects/linkInternal.ts
@@ -0,0 +1,56 @@
+import {LinkIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+import {PAGE_REFERENCES} from '../../constants'
+
+export default defineField({
+ title: 'Internal Link',
+ name: 'linkInternal',
+ type: 'object',
+ description: 'Link to content on this site.',
+ icon: LinkIcon,
+ fields: [
+ // Title
+ defineField({
+ title: 'Title',
+ name: 'title',
+ type: 'string',
+ description: 'If empty, displays the current reference title.'
+ }),
+ // Reference
+ defineField({
+ name: 'reference',
+ type: 'reference',
+ title: 'Content reference',
+ description: 'Link to already created, internal content.',
+ weak: true,
+ to: PAGE_REFERENCES,
+ }),
+ ],
+ preview: {
+ select: {
+ reference: 'reference',
+ referenceTitle: 'reference.title',
+ referenceType: 'reference._type',
+ title: 'title',
+ },
+ prepare(selection) {
+ const {
+ reference,
+ referenceTitle,
+ title,
+ } = selection
+
+ let subtitle = []
+ if (reference) {
+ subtitle.push([`→ ${referenceTitle || reference?._id}`])
+ } else {
+ subtitle.push('(Nonexistent document reference)')
+ }
+
+ return {
+ title,
+ subtitle: subtitle.join(' '),
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/mainImage.ts b/lib/sanity/schemas/objects/mainImage.ts
new file mode 100644
index 000000000..ff16b6b77
--- /dev/null
+++ b/lib/sanity/schemas/objects/mainImage.ts
@@ -0,0 +1,26 @@
+import { defineField, defineType } from "sanity"
+
+export default defineType({
+ name: 'mainImage',
+ type: 'image',
+ title: 'Main image',
+ description: 'Select or upload image. Edit by using the `menu` icon. Modify by using the `crop` icon.',
+ options: {
+ hotspot: true,
+ metadata: [
+ 'blurhash', // Default: included
+ 'lqip', // Default: included
+ 'palette', // Default: included
+ 'exif', // Default: not included
+ 'location', // Default: not included
+ ],
+ },
+ fields: [
+ defineField({
+ name: 'alt',
+ type: 'string',
+ title: 'Alternative text',
+ description: 'Note: Important for SEO and accessibility.',
+ }),
+ ],
+})
\ No newline at end of file
diff --git a/lib/sanity/schemas/objects/menu.ts b/lib/sanity/schemas/objects/menu.ts
new file mode 100644
index 000000000..0d5613081
--- /dev/null
+++ b/lib/sanity/schemas/objects/menu.ts
@@ -0,0 +1,34 @@
+import {MenuIcon} from '@sanity/icons'
+import {defineType, defineField} from 'sanity'
+
+export default defineType({
+ name: 'menu',
+ title: 'Menu',
+ type: 'object',
+ icon: MenuIcon,
+ groups: [],
+ fields: [
+ // Links
+ defineField({
+ name: 'links',
+ title: 'Links',
+ type: 'array',
+ of: [
+ {type: 'linkInternal'},
+ {type: 'linkExternal'},
+ ],
+ })
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ },
+ prepare(selection) {
+ const {title} = selection;
+
+ return {
+ title: `${title}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/placeholderString.ts b/lib/sanity/schemas/objects/placeholderString.ts
new file mode 100644
index 000000000..c8c2ff209
--- /dev/null
+++ b/lib/sanity/schemas/objects/placeholderString.ts
@@ -0,0 +1,10 @@
+import PlaceholderStringInput from '../../components/inputs/PlaceholderString'
+
+export default {
+ name: 'placeholderString',
+ title: 'Title',
+ type: 'string',
+ components: {
+ input: PlaceholderStringInput,
+ },
+}
diff --git a/lib/sanity/schemas/objects/productOption.ts b/lib/sanity/schemas/objects/productOption.ts
new file mode 100644
index 000000000..47eae289a
--- /dev/null
+++ b/lib/sanity/schemas/objects/productOption.ts
@@ -0,0 +1,25 @@
+import {defineField} from 'sanity'
+
+export default defineField({
+ name: 'productOption',
+ title: 'Product option',
+ type: 'object',
+ fields: [
+ defineField({
+ name: 'label',
+ title: 'Label',
+ type: 'string',
+ validation: Rule => Rule.required(),
+ description: 'Product option label.'
+ }),
+ defineField({
+ name: 'hexColors',
+ title: 'Color hex code',
+ type: 'color',
+ description: 'Hex color code for product option.',
+ options: {
+ disableAlpha: true
+ }
+ })
+ ],
+})
diff --git a/lib/sanity/schemas/objects/productOptions.ts b/lib/sanity/schemas/objects/productOptions.ts
new file mode 100644
index 000000000..88a5066d0
--- /dev/null
+++ b/lib/sanity/schemas/objects/productOptions.ts
@@ -0,0 +1,32 @@
+import {defineField} from 'sanity'
+
+export default defineField({
+ name: 'productOptions',
+ title: 'Product options',
+ type: 'object',
+ fields: [
+ defineField({
+ name: 'id',
+ title: 'ID (string)',
+ type: 'string',
+ validation: Rule => Rule.required(),
+ description: 'Unique product option ID.'
+ }),
+ defineField({
+ name: 'displayName',
+ title: 'Display name',
+ type: 'string',
+ description: 'Name displayed for this collection of product options.',
+ validation: Rule => Rule.required(),
+ }),
+ defineField({
+ name: 'values',
+ title: 'Values',
+ type: 'array',
+ description: 'What kind of values are available?',
+ of: [{type: 'productOption'}],
+ options: {},
+ validation: Rule => Rule.required(),
+ }),
+ ],
+})
diff --git a/lib/sanity/schemas/objects/proxyString.ts b/lib/sanity/schemas/objects/proxyString.ts
new file mode 100644
index 000000000..f5e839e17
--- /dev/null
+++ b/lib/sanity/schemas/objects/proxyString.ts
@@ -0,0 +1,11 @@
+import {defineField} from 'sanity'
+import ProxyStringInput from '../../components/inputs/ProxyString'
+
+export default defineField({
+ name: 'proxyString',
+ title: 'Title',
+ type: 'string',
+ components: {
+ input: ProxyStringInput,
+ },
+})
diff --git a/lib/sanity/schemas/objects/reusableSection.tsx b/lib/sanity/schemas/objects/reusableSection.tsx
new file mode 100644
index 000000000..d9c8535c3
--- /dev/null
+++ b/lib/sanity/schemas/objects/reusableSection.tsx
@@ -0,0 +1,54 @@
+import {defineField} from 'sanity'
+import {BlockElementIcon} from '@sanity/icons'
+
+export default defineField({
+ name: 'reusableSection',
+ type: 'object',
+ title: 'Reusable section',
+ icon: BlockElementIcon,
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: 'false',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ },
+ {
+ name: 'section',
+ type: 'object',
+ title: 'Section',
+ description: 'Reference to an existing section (only 1 allowed).',
+ fields: [
+ {
+ title: 'Existing section',
+ name: 'existingSection',
+ type: 'reference',
+ to: [{type: 'section'}],
+ },
+ ],
+ },
+ ],
+
+ preview: {
+ select: {
+ title: 'title',
+ disabled: 'disabled',
+ },
+ prepare(selection) {
+ const {title, disabled} = selection
+
+ return {
+ title: `${title}`,
+ subtitle: `Reusable section ${disabled ? '(⚠️ Disabled)' : ''}`,
+ media: BlockElementIcon,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/objects/seo.tsx b/lib/sanity/schemas/objects/seo.tsx
new file mode 100644
index 000000000..c0a1c2555
--- /dev/null
+++ b/lib/sanity/schemas/objects/seo.tsx
@@ -0,0 +1,45 @@
+import {defineField} from 'sanity'
+import { validateImage } from '../../utils/validation'
+
+export default defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'object',
+ description: 'Optimise content for search engines.',
+ options: {
+ collapsed: false,
+ collapsible: true,
+ },
+ fields: [
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ validation: (Rule) =>
+ Rule.max(50).warning('Longer titles may be truncated by search engines'),
+ description: (
+ <>
+ A short and accurate title representative of the content on this page.
+ If empty, displays the current document title (title).
+ >
+ ),
+ }),
+ defineField({
+ name: 'description',
+ title: 'Description',
+ type: 'text',
+ rows: 2,
+ validation: (Rule) =>
+ Rule.max(150).warning('Longer descriptions may be truncated by search engines'),
+ description: 'A brief description of the content on this page.'
+ }),
+ defineField({
+ name: 'image',
+ title: 'Image',
+ type: 'mainImage',
+ validation: Rule => validateImage(Rule, false),
+ description: 'A representative image of the content on this page.'
+ }),
+ ],
+ validation: (Rule) => Rule.required(),
+})
diff --git a/lib/sanity/schemas/objects/slider.ts b/lib/sanity/schemas/objects/slider.ts
new file mode 100644
index 000000000..3cff3c286
--- /dev/null
+++ b/lib/sanity/schemas/objects/slider.ts
@@ -0,0 +1,97 @@
+
+import {defineField} from 'sanity'
+import {PackageIcon} from '@sanity/icons'
+import {TagIcon, NumberIcon} from '@sanity/icons'
+
+export default defineField({
+ name: 'slider',
+ type: 'object',
+ title: 'Slider',
+ icon: NumberIcon,
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: 'false',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Title displayed above slider items.',
+ validation: Rule => Rule.required(),
+ },
+ {
+ name: 'sliderType',
+ type: 'string',
+ title: 'Slider type',
+ initialValue: 'products',
+ description: 'Select content type to display.',
+ validation: Rule => Rule.required(),
+ options: {
+ list: [
+ {title: 'Products', value: 'products'},
+ {title: 'Categories', value: 'categories'},
+ ],
+ layout: 'radio'
+ }
+ },
+ {
+ title: 'Products',
+ name: 'products',
+ type: 'array',
+ description: 'Select products to display.',
+ of: [
+ {
+ type: 'reference',
+ to: [{type: 'product'}],
+ },
+ ],
+ validation: Rule => Rule.custom((x:any, context:any) => {
+ if (context.parent.sliderType == 'products' && context?.parent?.products?.length < 3 || context?.parent?.products?.length > 8) {
+ return 'Must have between 3 and 8 items'
+ }
+ return true
+ }),
+ hidden: ({ parent }) => parent?.sliderType !== "products"
+ },
+ {
+ title: 'Categories',
+ name: 'categories',
+ type: 'array',
+ description: 'Select categories to display.',
+ of: [
+ {
+ type: 'reference',
+ to: [{type: 'category'}],
+ },
+ ],
+ validation: Rule => Rule.custom((x:any, context:any) => {
+ if (context.parent.sliderType == 'categories' && context?.parent?.categories?.length < 3 || context?.parent?.categories?.length > 8) {
+ return 'Must have between 3 and 8 items'
+ }
+ return true
+ }),
+ hidden: ({ parent }) => parent?.sliderType !== "categories"
+ },
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ sliderType: 'sliderType',
+ disabled: 'disabled'
+ },
+ prepare(selection) {
+ const {title, sliderType, disabled} = selection
+
+ return {
+ title: `${title}`,
+ media: sliderType === 'products' ? PackageIcon : TagIcon,
+ subtitle: `${sliderType === 'products' ? 'Product' : 'Category'} slider ${disabled ? '(⚠️ Disabled)' : ''}`,
+ }
+ },
+ },
+})
\ No newline at end of file
diff --git a/lib/sanity/schemas/objects/uspSection.ts b/lib/sanity/schemas/objects/uspSection.ts
new file mode 100644
index 000000000..9232e252e
--- /dev/null
+++ b/lib/sanity/schemas/objects/uspSection.ts
@@ -0,0 +1,63 @@
+import {defineField} from 'sanity'
+import {StarIcon} from '@sanity/icons'
+
+export default defineField({
+ name: 'uspSection',
+ type: 'object',
+ title: 'USP section',
+ icon: StarIcon,
+ fieldsets: [
+ {
+ name: 'layoutSettings',
+ title: 'Layout settings'
+ },
+ ],
+ fields: [
+ {
+ name: 'disabled',
+ type: 'boolean',
+ title: 'Disabled?',
+ description: 'Set to true to disable this section.',
+ initialValue: 'false',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Title will only be used internally.',
+ validation: (Rule) => Rule.required(),
+ },
+ {
+ name: 'usps',
+ type: 'array',
+ title: 'USPs',
+ description: 'Create USPs or refer to existing USP.',
+ of: [
+ {
+ type: 'reference',
+ to: [{
+ type: 'usp',
+ }],
+ },
+ ],
+ validation: (Rule) => Rule.required().min(2).max(4),
+ },
+ ],
+
+ preview: {
+ select: {
+ title: 'title',
+ disabled: 'disabled'
+ },
+ prepare(selection) {
+ const {title, disabled} = selection
+
+ return {
+ title: `${title}`,
+ subtitle: `USP section ${disabled ? '(⚠️ Disabled)' : ''}`,
+ media: StarIcon,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/singletons/home.tsx b/lib/sanity/schemas/singletons/home.tsx
new file mode 100644
index 000000000..c17aa29ad
--- /dev/null
+++ b/lib/sanity/schemas/singletons/home.tsx
@@ -0,0 +1,69 @@
+import {HomeIcon} from '@sanity/icons'
+import {defineField} from 'sanity'
+import {languages} from '../../languages'
+import {COMPONENT_REFERENCES} from '../../constants'
+
+export default defineField({
+ name: 'home',
+ title: 'Home',
+ type: 'document',
+ icon: HomeIcon,
+ groups: [
+ {
+ name: 'editorial',
+ title: 'Editorial',
+ },
+ {
+ name: 'seo',
+ title: 'SEO',
+ },
+ ],
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.',
+ // hidden: true,
+ }),
+ // Title
+ {
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ description: 'Page title.',
+ validation: (Rule) => Rule.required(),
+ },
+ defineField({
+ name: 'content',
+ title: 'Page sections',
+ type: 'array',
+ group: 'editorial',
+ description: 'Add, reorder, edit or delete page sections.',
+ of: COMPONENT_REFERENCES,
+ }),
+ // SEO
+ defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'seo',
+ group: 'seo',
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language',
+ },
+ prepare(selection) {
+ const {title, language} = selection
+
+ const currentLang = languages.find((lang) => lang.id === language)
+
+ return {
+ title: `${title}`,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/singletons/settings.ts b/lib/sanity/schemas/singletons/settings.ts
new file mode 100644
index 000000000..5c1a20f3e
--- /dev/null
+++ b/lib/sanity/schemas/singletons/settings.ts
@@ -0,0 +1,158 @@
+import {CogIcon} from '@sanity/icons'
+import {defineType, defineField} from 'sanity'
+import { languages } from '../../languages'
+
+const TITLE = 'Settings'
+
+export default defineType({
+ name: 'settings',
+ title: TITLE,
+ type: 'document',
+ icon: CogIcon,
+ groups: [
+ {
+ name: 'notFoundPage',
+ title: '404 page',
+ },
+ {
+ name: 'socialMedia',
+ title: 'Social media',
+ },
+ {
+ name: 'usps',
+ title: 'USPs',
+ },
+ {
+ name: 'contact',
+ title: 'Contact',
+ },
+ {
+ name: 'seo',
+ title: 'SEO',
+ },
+ ],
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.'
+ // hidden: true,
+ }),
+ defineField({
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Document title.',
+ }),
+ // Not found page
+ defineField({
+ name: 'notFoundPage',
+ title: '404 page',
+ type: 'object',
+ group: 'notFoundPage',
+ description: 'Information displayed on 404 page.',
+ fields: [
+ defineField({
+ name: 'title',
+ title: 'Title',
+ type: 'string',
+ validation: (Rule) => Rule.required(),
+ description: 'Page title displayed on 404 error page.',
+ }),
+ defineField({
+ name: 'body',
+ title: 'Body',
+ type: 'text',
+ rows: 5,
+ description: 'Text displayed adjacent to the title on 404 error page.'
+ }),
+ defineField({
+ name: 'category',
+ title: 'Category',
+ type: 'reference',
+ description: 'Category of products displayed on 404 error page.',
+ weak: true,
+ to: [
+ {
+ name: 'category',
+ type: 'category',
+ },
+ ],
+ }),
+ ]
+ }),
+ // Contact
+ defineField({
+ name: 'contact',
+ title: 'Contact options',
+ type: 'object',
+ group: 'contact',
+ description: 'Contact options for your business.',
+ options: {
+ collapsed: false,
+ collapsible: true,
+ },
+ fields: [
+ // Selling points
+ defineField({
+ name: 'contactOption',
+ title: 'Options (links)',
+ description: 'Links, e-mail address and phone numbers.',
+ type: 'array',
+ of: [
+ {type: 'linkExternal'},
+ ],
+ }),
+ ],
+ }),
+ // Social media links
+ defineField({
+ name: 'socialMedia',
+ title: 'Social Media',
+ type: 'object',
+ group: 'socialMedia',
+ description: "Links to your business's social media accounts",
+ options: {
+ collapsed: false,
+ collapsible: true,
+ },
+ fields: [
+ // Links
+ defineField({
+ name: 'links',
+ title: 'Links',
+ type: 'array',
+ description: 'Facebook, Twitter and Instgram etc.',
+ of: [
+ {type: 'linkExternal'},
+ ],
+ }),
+ ],
+ }),
+ // SEO
+ defineField({
+ name: 'seo',
+ title: 'SEO',
+ type: 'seo',
+ group: 'seo',
+ description: 'Default SEO displayed for every page unless overwritten on page/document level.',
+ }),
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language'
+ },
+ prepare(selection) {
+ const {title, language} = selection;
+
+ const currentLang = languages.find(lang => lang.id === language);
+
+ return {
+ title: `${title}`,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/schemas/singletons/utilityMenu.ts b/lib/sanity/schemas/singletons/utilityMenu.ts
new file mode 100644
index 000000000..637087b62
--- /dev/null
+++ b/lib/sanity/schemas/singletons/utilityMenu.ts
@@ -0,0 +1,48 @@
+import {MenuIcon} from '@sanity/icons'
+import {defineType, defineField} from 'sanity'
+import { languages } from '../../languages'
+
+export default defineType({
+ name: 'utilityMenu',
+ title: 'Utility menu',
+ type: 'document',
+ icon: MenuIcon,
+ groups: [],
+ fields: [
+ defineField({
+ name: 'language',
+ type: 'string',
+ readOnly: true,
+ description: 'Language of this document.'
+ // hidden: true,
+ }),
+ defineField({
+ name: 'title',
+ type: 'string',
+ title: 'Title',
+ description: 'Menu title or designation for menu.',
+ }),
+ // Menu
+ defineField({
+ name: 'menu',
+ title: 'Menu',
+ type: 'menu',
+ })
+ ],
+ preview: {
+ select: {
+ title: 'title',
+ language: 'language'
+ },
+ prepare(selection) {
+ const {title, language} = selection;
+
+ const currentLang = languages.find(lang => lang.id === language);
+
+ return {
+ title: `${title}`,
+ subtitle: `${currentLang ? currentLang.title : ''}`,
+ }
+ },
+ },
+})
diff --git a/lib/sanity/utils/defineStructure.ts b/lib/sanity/utils/defineStructure.ts
new file mode 100644
index 000000000..da1f86ad7
--- /dev/null
+++ b/lib/sanity/utils/defineStructure.ts
@@ -0,0 +1,11 @@
+import {ConfigContext} from 'sanity'
+import {StructureBuilder} from 'sanity/desk'
+
+/**
+ * Helper for creating and typing composable desk structure parts.
+ */
+export default function defineStructure(
+ factory: (S: StructureBuilder, context: ConfigContext) => StructureType
+) {
+ return factory
+}
diff --git a/lib/sanity/utils/getPreviewUrl.ts b/lib/sanity/utils/getPreviewUrl.ts
new file mode 100644
index 000000000..564a0c324
--- /dev/null
+++ b/lib/sanity/utils/getPreviewUrl.ts
@@ -0,0 +1,19 @@
+import {isDev, SanityDocument} from 'sanity'
+import { localStorefrontPreviewUrl, publicStorefrontPreviewUrl } from '../constants'
+
+// Customise this function to show the correct URL based on the current document
+export default function getPreviewUrl(doc: SanityDocument) {
+ if (isDev) {
+ if (!doc.slug) {
+ return
+ }
+
+ return `${localStorefrontPreviewUrl}?slug=${doc.slug.current}&locale=${doc.language}&secret=secret&type=${doc._type}`
+ } else {
+ if (!doc.slug) {
+ return
+ }
+
+ return `${publicStorefrontPreviewUrl}?slug=${doc.slug.current}&locale=${doc.language}&secret=secret&type=${doc._type}`
+ }
+}
\ No newline at end of file
diff --git a/lib/sanity/utils/validation.ts b/lib/sanity/utils/validation.ts
new file mode 100644
index 000000000..0a74d5d75
--- /dev/null
+++ b/lib/sanity/utils/validation.ts
@@ -0,0 +1,59 @@
+import {Rule, Slug} from 'sanity'
+import slug from 'slug'
+
+// SLUG VALIDATION
+export const validateSlug = (Rule: Rule) => {
+ const MAX_LENGTH = 96
+
+ return Rule.required().custom(async(value: Slug) => {
+ const currentSlug = value && value.current
+
+ if (!currentSlug) {
+ return true
+ }
+
+ if (currentSlug.length >= MAX_LENGTH) {
+ return `Must be less than ${MAX_LENGTH} characters.`
+ }
+
+ if (currentSlug !== slug(currentSlug, {lower: true})) {
+ return 'Must be a valid slug.'
+ }
+ return true
+ })
+}
+
+// IMAGE VALIDATION
+export const validateImage = (Rule: Rule, isRequired: boolean = false) => {
+ if (isRequired) {
+ return Rule.required().custom((value: object | any) => {
+ const currentImage = value && value.asset;
+ const currentImageAlt = value && value.alt;
+
+ if (!currentImage) {
+ return true
+ }
+
+ if (!currentImageAlt) {
+ return "Image and alt text is required."
+ }
+
+ return true
+ })
+ } else {
+ return Rule.custom((value: object | any) => {
+ const currentImage = value && value.asset;
+ const currentImageAlt = value && value.alt;
+
+ if (!currentImage) {
+ return true
+ }
+
+ if (currentImage && !currentImageAlt) {
+ return "Alt text is required."
+ }
+
+ return true
+ })
+ }
+}
\ No newline at end of file
diff --git a/middleware.ts b/middleware.ts
index 2a1cb0d55..554afd35b 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -12,5 +12,5 @@ export default createMiddleware({
export const config = {
// Skip all paths that should not be internationalized
- matcher: ['/((?!api|_next|.*\\..*).*)']
+ matcher: ['/((?!api|studio|_next|.*\\..*).*)']
};
\ No newline at end of file
diff --git a/package.json b/package.json
index 21c8a53a1..c78568af8 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,9 @@
"@sanity/types": "^3.11.1",
"@sanity/ui": "^1.3.3",
"@sanity/webhook": "^2.0.0",
+ "@sanity/color-input": "^3.0.2",
+ "@sanity/document-internationalization": "^2.0.1",
+ "@sanity/vision": "^3.0.0",
"@types/styled-components": "^5.1.26",
"@vercel/og": "^0.1.0",
"algoliasearch": "^4.19.1",
@@ -47,7 +50,11 @@
"react-glider": "^4.0.2",
"react-instantsearch": "^7.0.1",
"sanity": "^3.11.1",
+ "sanity-plugin-iframe-pane": "^2.3.0",
+ "sanity-plugin-media": "^2.0.4",
"sharp": "^0.32.1",
+ "slug": "^8.2.2",
+ "slugify": "^1.6.5",
"styled-components": "^5.3.10",
"tailwind-merge": "^1.12.0",
"tailwindcss-animate": "^1.0.5"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7b6c4df46..4e98498c8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,12 @@ dependencies:
'@sanity/client':
specifier: ^6.4.4
version: 6.4.4
+ '@sanity/color-input':
+ specifier: ^3.0.2
+ version: 3.0.2(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11)
+ '@sanity/document-internationalization':
+ specifier: ^2.0.1
+ version: 2.0.1(@babel/core@7.22.10)(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-fast-compare@3.2.2)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(sanity@3.15.0)(styled-components@5.3.11)
'@sanity/image-url':
specifier: ^1.0.2
version: 1.0.2
@@ -38,6 +44,9 @@ dependencies:
'@sanity/ui':
specifier: ^1.3.3
version: 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ '@sanity/vision':
+ specifier: ^3.0.0
+ version: 3.0.0(@babel/runtime@7.22.10)(@codemirror/lint@6.4.0)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.0.3)(@sanity/client@6.4.4)(codemirror@6.0.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(styled-components@5.3.11)
'@sanity/webhook':
specifier: ^2.0.0
version: 2.0.0
@@ -89,9 +98,21 @@ dependencies:
sanity:
specifier: ^3.11.1
version: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ sanity-plugin-iframe-pane:
+ specifier: ^2.3.0
+ version: 2.3.0(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11)
+ sanity-plugin-media:
+ specifier: ^2.0.4
+ version: 2.0.4(@sanity/color@2.2.5)(@sanity/icons@2.4.1)(@types/react@18.2.19)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11)
sharp:
specifier: ^0.32.1
version: 0.32.4
+ slug:
+ specifier: ^8.2.2
+ version: 8.2.2
+ slugify:
+ specifier: ^1.6.5
+ version: 1.6.5
styled-components:
specifier: ^5.3.10
version: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
@@ -524,6 +545,89 @@ packages:
to-fast-properties: 2.0.0
dev: false
+ /@codemirror/autocomplete@6.9.0(@codemirror/language@6.8.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.3):
+ resolution: {integrity: sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==}
+ peerDependencies:
+ '@codemirror/language': ^6.0.0
+ '@codemirror/state': ^6.0.0
+ '@codemirror/view': ^6.0.0
+ '@lezer/common': ^1.0.0
+ dependencies:
+ '@codemirror/language': 6.8.0
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ '@lezer/common': 1.0.3
+ dev: false
+
+ /@codemirror/commands@6.2.4:
+ resolution: {integrity: sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==}
+ dependencies:
+ '@codemirror/language': 6.8.0
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ '@lezer/common': 1.0.3
+ dev: false
+
+ /@codemirror/lang-javascript@6.1.9:
+ resolution: {integrity: sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w==}
+ dependencies:
+ '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.8.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.3)
+ '@codemirror/language': 6.8.0
+ '@codemirror/lint': 6.4.0
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ '@lezer/common': 1.0.3
+ '@lezer/javascript': 1.4.5
+ dev: false
+
+ /@codemirror/language@6.8.0:
+ resolution: {integrity: sha512-r1paAyWOZkfY0RaYEZj3Kul+MiQTEbDvYqf8gPGaRvNneHXCmfSaAVFjwRUPlgxS8yflMxw2CTu6uCMp8R8A2g==}
+ dependencies:
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ '@lezer/common': 1.0.3
+ '@lezer/highlight': 1.1.6
+ '@lezer/lr': 1.3.9
+ style-mod: 4.0.3
+ dev: false
+
+ /@codemirror/lint@6.4.0:
+ resolution: {integrity: sha512-6VZ44Ysh/Zn07xrGkdtNfmHCbGSHZzFBdzWi0pbd7chAQ/iUcpLGX99NYRZTa7Ugqg4kEHCqiHhcZnH0gLIgSg==}
+ dependencies:
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ crelt: 1.0.6
+ dev: false
+
+ /@codemirror/search@6.5.1:
+ resolution: {integrity: sha512-4jupk4JwkeVbrN2pStY74q6OJEYqwosB4koA66nyLeVedadtX9MHI38j2vbYmnfDGurDApP3OZO46MrWalcjiQ==}
+ dependencies:
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ crelt: 1.0.6
+ dev: false
+
+ /@codemirror/state@6.2.1:
+ resolution: {integrity: sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw==}
+ dev: false
+
+ /@codemirror/theme-one-dark@6.1.2:
+ resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
+ dependencies:
+ '@codemirror/language': 6.8.0
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ '@lezer/highlight': 1.1.6
+ dev: false
+
+ /@codemirror/view@6.16.0:
+ resolution: {integrity: sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==}
+ dependencies:
+ '@codemirror/state': 6.2.1
+ style-mod: 4.0.3
+ w3c-keyname: 2.2.8
+ dev: false
+
/@dnd-kit/accessibility@3.0.1(react@18.2.0):
resolution: {integrity: sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==}
peerDependencies:
@@ -579,6 +683,36 @@ packages:
tslib: 2.6.1
dev: false
+ /@emotion/babel-plugin@11.11.0:
+ resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==}
+ dependencies:
+ '@babel/helper-module-imports': 7.22.5
+ '@babel/runtime': 7.22.10
+ '@emotion/hash': 0.9.1
+ '@emotion/memoize': 0.8.1
+ '@emotion/serialize': 1.1.2
+ babel-plugin-macros: 3.1.0
+ convert-source-map: 1.9.0
+ escape-string-regexp: 4.0.0
+ find-root: 1.1.0
+ source-map: 0.5.7
+ stylis: 4.2.0
+ dev: false
+
+ /@emotion/cache@11.11.0:
+ resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==}
+ dependencies:
+ '@emotion/memoize': 0.8.1
+ '@emotion/sheet': 1.2.2
+ '@emotion/utils': 1.2.1
+ '@emotion/weak-memoize': 0.3.1
+ stylis: 4.2.0
+ dev: false
+
+ /@emotion/hash@0.9.1:
+ resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
+ dev: false
+
/@emotion/is-prop-valid@0.8.8:
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
requiresBuild: true
@@ -603,6 +737,41 @@ packages:
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
dev: false
+ /@emotion/react@11.11.1(@types/react@18.2.19)(react@18.2.0):
+ resolution: {integrity: sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: '>=16.8.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.22.10
+ '@emotion/babel-plugin': 11.11.0
+ '@emotion/cache': 11.11.0
+ '@emotion/serialize': 1.1.2
+ '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
+ '@emotion/utils': 1.2.1
+ '@emotion/weak-memoize': 0.3.1
+ '@types/react': 18.2.19
+ hoist-non-react-statics: 3.3.2
+ react: 18.2.0
+ dev: false
+
+ /@emotion/serialize@1.1.2:
+ resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==}
+ dependencies:
+ '@emotion/hash': 0.9.1
+ '@emotion/memoize': 0.8.1
+ '@emotion/unitless': 0.8.1
+ '@emotion/utils': 1.2.1
+ csstype: 3.1.2
+ dev: false
+
+ /@emotion/sheet@1.2.2:
+ resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
+ dev: false
+
/@emotion/stylis@0.8.5:
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
dev: false
@@ -611,6 +780,26 @@ packages:
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
dev: false
+ /@emotion/unitless@0.8.1:
+ resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
+ dev: false
+
+ /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0):
+ resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /@emotion/utils@1.2.1:
+ resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==}
+ dev: false
+
+ /@emotion/weak-memoize@0.3.1:
+ resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==}
+ dev: false
+
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
@@ -946,6 +1135,14 @@ packages:
react: 18.2.0
dev: false
+ /@hookform/resolvers@2.0.0-beta.3(react-hook-form@6.15.8):
+ resolution: {integrity: sha512-sOP+IX7TglN34WbMVt8eRqWgnXAQcvFc4XUU6x3bIiIjTkjV5L3N7sBjff2Ln4QPTMYnmdXaZV2Yf5WxOiC5YQ==}
+ peerDependencies:
+ react-hook-form: '>=6.6.0'
+ dependencies:
+ react-hook-form: 6.15.8(react@18.2.0)
+ dev: false
+
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@@ -970,6 +1167,14 @@ packages:
resolution: {integrity: sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==}
dev: false
+ /@icons/material@0.2.4(react@18.2.0):
+ resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==}
+ peerDependencies:
+ react: '*'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
engines: {node: '>=6.0.0'}
@@ -999,6 +1204,29 @@ packages:
resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
dev: false
+ /@lezer/common@1.0.3:
+ resolution: {integrity: sha512-JH4wAXCgUOcCGNekQPLhVeUtIqjH0yPBs7vvUdSjyQama9618IOKFJwkv2kcqdhF0my8hQEgCTEJU0GIgnahvA==}
+ dev: false
+
+ /@lezer/highlight@1.1.6:
+ resolution: {integrity: sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==}
+ dependencies:
+ '@lezer/common': 1.0.3
+ dev: false
+
+ /@lezer/javascript@1.4.5:
+ resolution: {integrity: sha512-FmBUHz8K1V22DgjTd6SrIG9owbzOYZ1t3rY6vGEmw+e2RVBd7sqjM8uXEVRFmfxKFn1Mx2ABJehHjrN3G2ZpmA==}
+ dependencies:
+ '@lezer/highlight': 1.1.6
+ '@lezer/lr': 1.3.9
+ dev: false
+
+ /@lezer/lr@1.3.9:
+ resolution: {integrity: sha512-XPz6dzuTHlnsbA5M2DZgjflNQ+9Hi5Swhic0RULdp3oOs3rh6bqGZolosVqN/fQIT8uNiepzINJDnS39oweTHQ==}
+ dependencies:
+ '@lezer/common': 1.0.3
+ dev: false
+
/@motionone/animation@10.15.1:
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
dependencies:
@@ -1841,6 +2069,25 @@ packages:
'@babel/runtime': 7.22.10
dev: false
+ /@reduxjs/toolkit@1.9.5(react-redux@7.2.9)(react@18.2.0):
+ resolution: {integrity: sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==}
+ peerDependencies:
+ react: ^16.9.0 || ^17.0.0 || ^18
+ react-redux: ^7.2.1 || ^8.0.2
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-redux:
+ optional: true
+ dependencies:
+ immer: 9.0.21
+ react: 18.2.0
+ react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0)
+ redux: 4.2.1
+ redux-thunk: 2.4.2(redux@4.2.1)
+ reselect: 4.1.8
+ dev: false
+
/@resvg/resvg-wasm@2.0.0-alpha.4:
resolution: {integrity: sha512-pWIG9a/x1ky8gXKRhPH1OPKpHFoMN1ISLbJ+O+gPXQHIAKhNd5I28RlWf7q576hAOQA9JZTlo3p/M2uyLzJmmw==}
engines: {node: '>= 10'}
@@ -1909,6 +2156,27 @@ packages:
- supports-color
dev: false
+ /@sanity/color-input@3.0.2(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-jeNLxuVmt2P7LhBKCPwXL/SSjA5uryIje3AdqhJT0nO/9l7Ghmd7h11GaXwMcm5GntUbm/AtLVyQtV3/v686WQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: ^18
+ sanity: ^3
+ styled-components: ^5.2
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ lodash: 4.17.21
+ react: 18.2.0
+ react-color: 2.19.3(react@18.2.0)
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - react-dom
+ - react-is
+ dev: false
+
/@sanity/color@2.2.5:
resolution: {integrity: sha512-tTi22KoKuER3sldXYl4c1Dq2zU7tMLDkljFiaUKVkBbu4PBvRGCFw75kXZnD2b4Bsp6vin+7sI+AKdCKRhfRuw==}
dev: false
@@ -1920,6 +2188,33 @@ packages:
diff-match-patch: 1.0.5
dev: false
+ /@sanity/document-internationalization@2.0.1(@babel/core@7.22.10)(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-fast-compare@3.2.2)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-pfLx7rfu2rfTn9xRm4hTpKGSK2HwE8oTA91CNfxWrRjvfgF/pgJ2nzTTEbLZ1FfIn/EEQ+MgQwrS2GqbE3uhfw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@sanity/ui': ^1.2.2
+ react: ^18
+ sanity: ^3.0.0
+ styled-components: ^5.3.6
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ '@sanity/uuid': 3.0.2
+ react: 18.2.0
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ sanity-plugin-internationalized-array: 1.10.1(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11)
+ sanity-plugin-utils: 1.6.2(@babel/core@7.22.10)(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-fast-compare@3.2.2)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(sanity@3.15.0)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-dom
+ - react-fast-compare
+ - react-is
+ - rxjs
+ - supports-color
+ dev: false
+
/@sanity/eventsource@5.0.0:
resolution: {integrity: sha512-0ewT+BDzfiamHwitUfRcwsl/RREHjWv6VNZvQ8Q4OnnNKXfEEGXbWmqzof0okOTkp4XELgyliht4Qj28o9AU2g==}
dependencies:
@@ -1983,6 +2278,14 @@ packages:
- supports-color
dev: false
+ /@sanity/icons@1.3.10(react@18.2.0):
+ resolution: {integrity: sha512-5wVG/vIiGuGrSmq+Bl3PY7XDgQrGv0fyHdJI64FSulnr2wH3NMqZ6C59UFxnrZ93sr7kOt0zQFoNv2lkPBi0Cg==}
+ peerDependencies:
+ react: ^16.9 || ^17 || ^18
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/@sanity/icons@2.4.1(react@18.2.0):
resolution: {integrity: sha512-/yxcIT0k1RxStI/pP/oHM44fHI6Oxiygf0jPcdV06Ce0xfFo+51UEqJSfE8hQ3fh+uFkat8ZZObZjq5v1ceJzw==}
engines: {node: '>=14.0.0'}
@@ -2023,6 +2326,39 @@ packages:
- supports-color
dev: false
+ /@sanity/incompatible-plugin@1.0.4(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-2z39G9PTM8MXOF4fJNx3TG4tH0RrTjtH6dVLW93DSjCPbIS7FgCY5yWjZfQ+HVkwhLsF7ATDAGLA/jp65pFjAg==}
+ peerDependencies:
+ react: ^16.9 || ^17 || ^18
+ react-dom: ^16.9 || ^17 || ^18
+ dependencies:
+ '@sanity/icons': 1.3.10(react@18.2.0)
+ react: 18.2.0
+ react-copy-to-clipboard: 5.1.0(react@18.2.0)
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@sanity/language-filter@3.2.1(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-QSPUZ3frdUF+v2WQHwBu/27ivD1Oja4AYSphtpqwyIftxMVcSyiX8x4aqlBw+jisvquypqgMDbzVP4z87o6OoA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: ^18
+ sanity: ^3
+ styled-components: ^5.2
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ '@sanity/util': 3.15.0
+ react: 18.2.0
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - react-dom
+ - react-is
+ - supports-color
+ dev: false
+
/@sanity/logos@2.1.2(@sanity/color@2.2.5)(react@18.2.0):
resolution: {integrity: sha512-nxJUQQzEEG8EqjiOEswQQpBUuFc3iSxTVF9D9Memg/tlOChX76dStNHoa1RWuvSPu895aqJV+9zxijAa0kF9Vg==}
engines: {node: '>=14.0.0'}
@@ -2196,6 +2532,47 @@ packages:
uuid: 8.3.2
dev: false
+ /@sanity/vision@3.0.0(@babel/runtime@7.22.10)(@codemirror/lint@6.4.0)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@lezer/common@1.0.3)(@sanity/client@6.4.4)(codemirror@6.0.1)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(styled-components@5.3.11):
+ resolution: {integrity: sha512-Dr18Iugz2xHEavag4asOzYBIjwnj+5SEB7DlbyNow0mQ5fQWYh8eRES2OBDvrOLbBJ5rVuZMIzrcokQ+nzmrBw==}
+ peerDependencies:
+ '@sanity/client': ^3.4.1
+ react: ^18
+ rxjs: ^6.5.3
+ styled-components: ^5.2
+ dependencies:
+ '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.8.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.3)
+ '@codemirror/commands': 6.2.4
+ '@codemirror/lang-javascript': 6.1.9
+ '@codemirror/language': 6.8.0
+ '@codemirror/search': 6.5.1
+ '@codemirror/view': 6.16.0
+ '@juggle/resize-observer': 3.4.0
+ '@lezer/highlight': 1.1.6
+ '@rexxars/react-json-inspector': 8.0.1(react@18.2.0)
+ '@sanity/client': 6.4.4
+ '@sanity/color': 2.2.5
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ '@uiw/react-codemirror': 4.21.9(@babel/runtime@7.22.10)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.8.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0)
+ hashlru: 2.3.0
+ is-hotkey: 0.1.8
+ json5: 1.0.2
+ lodash: 4.17.21
+ react: 18.2.0
+ react-split-pane: 0.1.92(react-dom@18.2.0)(react@18.2.0)
+ rxjs: 7.8.1
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - '@babel/runtime'
+ - '@codemirror/lint'
+ - '@codemirror/state'
+ - '@codemirror/theme-one-dark'
+ - '@lezer/common'
+ - codemirror
+ - react-dom
+ - react-is
+ dev: false
+
/@sanity/webhook@2.0.0:
resolution: {integrity: sha512-KusHeWVy6yW3hcgVHbGx6qVv//bUj3CD9Nr0EOw086+mo/ESrUV21HjOCrNQ+AYqXvJ9H5qhqmMiuc8H5dXUGA==}
engines: {node: '>=12.0.0'}
@@ -2231,6 +2608,18 @@ packages:
tailwindcss: 3.3.3
dev: true
+ /@tanem/react-nprogress@5.0.47(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-DIW4bkXxPa2Mc8WKAoxdJudLsl2N4QinMeoG3Z+bQa4RNdV97FYVGcryie9ZjJU+ZUxvjT5eq504wnW/xzHlmA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ '@babel/runtime': 7.22.10
+ hoist-non-react-statics: 3.3.2
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@tanstack/react-virtual@3.0.0-beta.54(react@18.2.0):
resolution: {integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ==}
peerDependencies:
@@ -2323,6 +2712,10 @@ packages:
/@types/normalize-package-data@2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
+ /@types/parse-json@4.0.0:
+ resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
+ dev: false
+
/@types/prop-types@15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
@@ -2347,6 +2740,21 @@ packages:
'@types/react': 18.2.19
dev: false
+ /@types/react-redux@7.1.25:
+ resolution: {integrity: sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==}
+ dependencies:
+ '@types/hoist-non-react-statics': 3.3.1
+ '@types/react': 18.2.19
+ hoist-non-react-statics: 3.3.2
+ redux: 4.2.1
+ dev: false
+
+ /@types/react-transition-group@4.4.6:
+ resolution: {integrity: sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==}
+ dependencies:
+ '@types/react': 18.2.19
+ dev: false
+
/@types/react@18.2.19:
resolution: {integrity: sha512-e2S8wmY1ePfM517PqCG80CcE48Xs5k0pwJzuDZsfE8IZRRBfOMCF+XqnFxu6mWtyivum1MQm4aco+WIt6Coimw==}
dependencies:
@@ -2448,6 +2856,53 @@ packages:
eslint-visitor-keys: 3.4.2
dev: true
+ /@uiw/codemirror-extensions-basic-setup@4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.8.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0):
+ resolution: {integrity: sha512-TQT6aF8brxZpFnk/K4fm/K/9k9eF3PMav/KKjHlYrGUT8BTNk/qL+ximLtIzvTUhmBFchjM1lrqSJdvpVom7/w==}
+ peerDependencies:
+ '@codemirror/autocomplete': '>=6.0.0'
+ '@codemirror/commands': '>=6.0.0'
+ '@codemirror/language': '>=6.0.0'
+ '@codemirror/lint': '>=6.0.0'
+ '@codemirror/search': '>=6.0.0'
+ '@codemirror/state': '>=6.0.0'
+ '@codemirror/view': '>=6.0.0'
+ dependencies:
+ '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.8.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.3)
+ '@codemirror/commands': 6.2.4
+ '@codemirror/language': 6.8.0
+ '@codemirror/lint': 6.4.0
+ '@codemirror/search': 6.5.1
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ dev: false
+
+ /@uiw/react-codemirror@4.21.9(@babel/runtime@7.22.10)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.8.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-aeLegPz2iCvqJjhzXp2WUMqpMZDqxsTnF3rX9kGRlfY6vQLsrjoctj0cQ29uxEtFYJChOVjtCOtnQUlyIuNAHQ==}
+ peerDependencies:
+ '@babel/runtime': '>=7.11.0'
+ '@codemirror/state': '>=6.0.0'
+ '@codemirror/theme-one-dark': '>=6.0.0'
+ '@codemirror/view': '>=6.0.0'
+ codemirror: '>=6.0.0'
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@babel/runtime': 7.22.10
+ '@codemirror/commands': 6.2.4
+ '@codemirror/state': 6.2.1
+ '@codemirror/theme-one-dark': 6.1.2
+ '@codemirror/view': 6.16.0
+ '@uiw/codemirror-extensions-basic-setup': 4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.8.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)
+ codemirror: 6.0.1(@lezer/common@1.0.3)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ transitivePeerDependencies:
+ - '@codemirror/autocomplete'
+ - '@codemirror/language'
+ - '@codemirror/lint'
+ - '@codemirror/search'
+ dev: false
+
/@vercel/error-utils@1.0.10:
resolution: {integrity: sha512-nsKy2sy+pjUWyKI1V/XXKspVzHMYgSalmj5+EsKWFXZbnNZicqxNtMR94J8Hs7SB4TQxh0s4KhczJtL59AVGMg==}
dev: false
@@ -2502,6 +2957,20 @@ packages:
resolution: {integrity: sha512-5b0PkOJsFBX5alChuIO3qpkt5vIZBevzLPhUQ1UP8UzVjL3F1VllnZxp/thfD8R5ol7D7WHkgZHIjdUBX4tDpQ==}
dev: false
+ /@virtuoso.dev/react-urx@0.2.13(react@18.2.0):
+ resolution: {integrity: sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: '>=16'
+ dependencies:
+ '@virtuoso.dev/urx': 0.2.13
+ react: 18.2.0
+ dev: false
+
+ /@virtuoso.dev/urx@0.2.13:
+ resolution: {integrity: sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw==}
+ dev: false
+
/@vitejs/plugin-react@4.0.4(vite@4.4.9):
resolution: {integrity: sha512-7wU921ABnNYkETiMaZy7XqpueMnpu5VxvVps13MjmCo+utBdD79sZzrApHawHtVX66cCJQQTXFcjH0y9dSUK8g==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -2796,6 +3265,11 @@ packages:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
+ /attr-accept@2.2.2:
+ resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==}
+ engines: {node: '>=4'}
+ dev: false
+
/autoprefixer@10.4.14(postcss@8.4.27):
resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==}
engines: {node: ^10 || ^12 || >=14}
@@ -2832,6 +3306,15 @@ packages:
resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
dev: false
+ /babel-plugin-macros@3.1.0:
+ resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
+ engines: {node: '>=10', npm: '>=6'}
+ dependencies:
+ '@babel/runtime': 7.22.10
+ cosmiconfig: 7.1.0
+ resolve: 1.22.4
+ dev: false
+
/babel-plugin-styled-components@2.1.4(@babel/core@7.22.10)(styled-components@5.3.11):
resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==}
peerDependencies:
@@ -3091,6 +3574,20 @@ packages:
engines: {node: '>=6'}
dev: false
+ /codemirror@6.0.1(@lezer/common@1.0.3):
+ resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
+ dependencies:
+ '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.8.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.3)
+ '@codemirror/commands': 6.2.4
+ '@codemirror/language': 6.8.0
+ '@codemirror/lint': 6.4.0
+ '@codemirror/search': 6.5.1
+ '@codemirror/state': 6.2.1
+ '@codemirror/view': 6.16.0
+ transitivePeerDependencies:
+ - '@lezer/common'
+ dev: false
+
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@@ -3127,6 +3624,10 @@ packages:
color-string: 1.9.1
dev: false
+ /colord@2.9.3:
+ resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
+ dev: false
+
/colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
dev: true
@@ -3229,6 +3730,17 @@ packages:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: false
+ /cosmiconfig@7.1.0:
+ resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@types/parse-json': 4.0.0
+ import-fresh: 3.3.0
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ yaml: 1.10.2
+ dev: false
+
/crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
@@ -3250,6 +3762,10 @@ packages:
object-assign: 4.1.1
dev: false
+ /crelt@1.0.6:
+ resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
+ dev: false
+
/cross-env@7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
@@ -3507,6 +4023,13 @@ packages:
esutils: 2.0.3
dev: true
+ /dom-helpers@5.2.1:
+ resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
+ dependencies:
+ '@babel/runtime': 7.22.10
+ csstype: 3.1.2
+ dev: false
+
/dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
dev: false
@@ -3710,7 +4233,6 @@ packages:
/escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
- dev: true
/escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
@@ -4144,6 +4666,13 @@ packages:
flat-cache: 3.0.4
dev: true
+ /file-selector@0.4.0:
+ resolution: {integrity: sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==}
+ engines: {node: '>= 10'}
+ dependencies:
+ tslib: 2.6.1
+ dev: false
+
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
dev: false
@@ -4153,12 +4682,21 @@ packages:
engines: {node: '>=4'}
dev: false
+ /filesize@8.0.7:
+ resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==}
+ engines: {node: '>= 0.4.0'}
+ dev: false
+
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
+ /find-root@1.1.0:
+ resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
+ dev: false
+
/find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -4559,6 +5097,11 @@ packages:
engines: {node: '>= 14'}
dev: false
+ /groq@2.33.2:
+ resolution: {integrity: sha512-5pf4c91JESCS28IJCgolJq/WBw4Xvf2m8FgZVUlrCig17I+qERosALAHLyyutXu403EYnyCD3DdCLaPb3aGneA==}
+ engines: {node: '>=6'}
+ dev: false
+
/groq@3.15.0:
resolution: {integrity: sha512-OjdYDrAHZC6Ku7LOgXUuh8DjcITJ7piz6WsuzrHUCiZEg3nEHWy2n3lU/INglVrIVp5o6QqqUh3fVlrBjp6X7A==}
engines: {node: '>=14'}
@@ -4942,6 +5485,10 @@ packages:
resolution: {integrity: sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==}
dev: false
+ /is-hotkey@0.2.0:
+ resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==}
+ dev: false
+
/is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'}
@@ -5180,7 +5727,6 @@ packages:
hasBin: true
dependencies:
minimist: 1.2.8
- dev: true
/json5@2.2.2:
resolution: {integrity: sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==}
@@ -5295,6 +5841,10 @@ packages:
dependencies:
p-locate: 5.0.0
+ /lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+ dev: false
+
/lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
dev: true
@@ -5326,6 +5876,10 @@ packages:
resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
dev: false
+ /lodash.uniqueid@4.0.1:
+ resolution: {integrity: sha512-GQQWaIeGlL6DIIr06kj1j6sSmBxyNMwI8kaX9aKpHR/XsMTiaXDVPNPAkiboOTK9OJpTJF/dXT3xYoFQnj386Q==}
+ dev: false
+
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
@@ -5376,10 +5930,18 @@ packages:
semver: 6.3.1
dev: false
+ /material-colors@1.2.6:
+ resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==}
+ dev: false
+
/md5-o-matic@0.1.1:
resolution: {integrity: sha512-QBJSFpsedXUl/Lgs4ySdB2XCzUEcJ3ujpbagdZCkRaYIaC0kFnID8jhc84KEiVv6dNFtIrmW7bqow0lDxgJi6A==}
dev: false
+ /memoize-one@6.0.0:
+ resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+ dev: false
+
/memoize-resolver@1.0.0:
resolution: {integrity: sha512-mXfNXte0RSWl0rEIsQhXutfM2R2Oa7UyKDD7XoZMEbKeucTRms04y5y41U8gLqPzRx7ViN/QyYnTR2TX/5tawA==}
dev: false
@@ -5519,6 +6081,10 @@ packages:
resolution: {integrity: sha512-RWgGP2TdeKZLx+guR5a7/BzYs85sj6yrXXyj0o/znbgzPlz/Ez9wQuKDpwUZ8q+u2RxXpqZ1iTkPXCIU+GHhpA==}
dev: false
+ /nanoclone@0.2.1:
+ resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==}
+ dev: false
+
/nanoid@3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -6010,7 +6576,6 @@ packages:
/pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
- dev: true
/polished@4.2.2:
resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==}
@@ -6220,6 +6785,10 @@ packages:
object-assign: 4.1.1
react-is: 16.13.1
+ /property-expr@2.0.5:
+ resolution: {integrity: sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==}
+ dev: false
+
/property-information@5.6.0:
resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
dependencies:
@@ -6297,6 +6866,21 @@ packages:
react: 18.2.0
dev: false
+ /react-color@2.19.3(react@18.2.0):
+ resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==}
+ peerDependencies:
+ react: '*'
+ dependencies:
+ '@icons/material': 0.2.4(react@18.2.0)
+ lodash: 4.17.21
+ lodash-es: 4.17.21
+ material-colors: 1.2.6
+ prop-types: 15.8.1
+ react: 18.2.0
+ reactcss: 1.2.3(react@18.2.0)
+ tinycolor2: 1.6.0
+ dev: false
+
/react-cookie@4.1.1(react@18.2.0):
resolution: {integrity: sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==}
peerDependencies:
@@ -6328,10 +6912,35 @@ packages:
scheduler: 0.23.0
dev: false
+ /react-dropzone@11.7.1(react@18.2.0):
+ resolution: {integrity: sha512-zxCMwhfPy1olUEbw3FLNPLhAm/HnaYH5aELIEglRbqabizKAdHs0h+WuyOpmA+v1JXn0++fpQDdNfUagWt5hJQ==}
+ engines: {node: '>= 10.13'}
+ peerDependencies:
+ react: '>= 16.8'
+ dependencies:
+ attr-accept: 2.2.2
+ file-selector: 0.4.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ dev: false
+
/react-fast-compare@3.2.2:
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
dev: false
+ /react-file-icon@1.3.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-wxl/WwSX5twQKVXloPHbS71iZQUKO84KgZ44Kh7vYZGu1qH2kagx+RSTNfk/+IHtXfjPWPNIHPGi2Y8S94N1CQ==}
+ peerDependencies:
+ react: ^18.0.0 || ^17.0.0 || ^16.2.0
+ react-dom: ^18.0.0 || ^17.0.0 || ^16.2.0
+ dependencies:
+ colord: 2.9.3
+ lodash.uniqueid: 4.0.1
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/react-focus-lock@2.9.5(@types/react@18.2.19)(react@18.2.0):
resolution: {integrity: sha512-h6vrdgUbsH2HeD5I7I3Cx1PPrmwGuKYICS+kB9m+32X/9xHRrAbxgvaBpG7BFBN9h3tO+C3qX1QAVESmi4CiIA==}
peerDependencies:
@@ -6362,6 +6971,14 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /react-hook-form@6.15.8(react@18.2.0):
+ resolution: {integrity: sha512-prq82ofMbnRyj5wqDe8hsTRcdR25jQ+B8KtCS7BLCzjFHAwNuCjRwzPuP4eYLsEBjEIeYd6try+pdLdw0kPkpg==}
+ peerDependencies:
+ react: ^16.8.0 || ^17
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/react-instantsearch-core@7.0.1(algoliasearch@4.19.1)(react@18.2.0):
resolution: {integrity: sha512-qKe8sV03kyoc3HjEYmpGF2k038uaBV5Mzdxgf3ZUP1B7OyCPnzeC7YPvjZFZV89rvcfxr0lwzdExKVFV1GMR7g==}
peerDependencies:
@@ -6394,10 +7011,40 @@ packages:
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ /react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+ dev: false
+
/react-is@18.2.0:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
dev: false
+ /react-lifecycles-compat@3.0.4:
+ resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
+ dev: false
+
+ /react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
+ peerDependencies:
+ react: ^16.8.3 || ^17 || ^18
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.22.10
+ '@types/react-redux': 7.1.25
+ hoist-non-react-statics: 3.3.2
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-is: 17.0.2
+ dev: false
+
/react-refractor@2.1.7(react@18.2.0):
resolution: {integrity: sha512-avNxSSsnjYg+BKpO8LVCK14KRn5pLZ+8DInMiUEeZPL6hs0SN0zafl3mJIxavGQPKyihqbXqzq4CYNflJQjaaw==}
peerDependencies:
@@ -6462,6 +7109,46 @@ packages:
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
+ /react-select@5.7.4(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-NhuE56X+p9QDFh4BgeygHFIvJJszO1i1KSkg/JPcIJrbovyRtI+GuOEa4XzFCEpZRAEoEI8u/cAHK+jG/PgUzQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ '@babel/runtime': 7.22.10
+ '@emotion/cache': 11.11.0
+ '@emotion/react': 11.11.1(@types/react@18.2.19)(react@18.2.0)
+ '@floating-ui/dom': 1.5.1
+ '@types/react-transition-group': 4.4.6
+ memoize-one: 6.0.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
+ use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.19)(react@18.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ dev: false
+
+ /react-split-pane@0.1.92(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-GfXP1xSzLMcLJI5BM36Vh7GgZBpy+U/X0no+VM3fxayv+p1Jly5HpMofZJraeaMl73b3hvlr+N9zJKvLB/uz9w==}
+ peerDependencies:
+ react: ^16.0.0-0
+ react-dom: ^16.0.0-0
+ dependencies:
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-lifecycles-compat: 3.0.4
+ react-style-proptype: 3.2.2
+ dev: false
+
+ /react-style-proptype@3.2.2:
+ resolution: {integrity: sha512-ywYLSjNkxKHiZOqNlso9PZByNEY+FTyh3C+7uuziK0xFXu9xzdyfHwg4S9iyiRRoPCR4k2LqaBBsWVmSBwCWYQ==}
+ dependencies:
+ prop-types: 15.8.1
+ dev: false
+
/react-style-singleton@2.2.1(@types/react@18.2.19)(react@18.2.0):
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
@@ -6479,6 +7166,33 @@ packages:
tslib: 2.6.1
dev: false
+ /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
+ peerDependencies:
+ react: '>=16.6.0'
+ react-dom: '>=16.6.0'
+ dependencies:
+ '@babel/runtime': 7.22.10
+ dom-helpers: 5.2.1
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /react-virtuoso@2.19.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-zF6MAwujNGy2nJWCx/Df92ay/RnV2Kj4glUZfdyadI4suAn0kAZHB1BeI7yPFVp2iSccLzFlszhakWyr+fJ4Dw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: '>=16 || >=17 || >= 18'
+ react-dom: '>=16 || >=17 || >= 18'
+ dependencies:
+ '@virtuoso.dev/react-urx': 0.2.13(react@18.2.0)
+ '@virtuoso.dev/urx': 0.2.13
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -6486,6 +7200,15 @@ packages:
loose-envify: 1.4.0
dev: false
+ /reactcss@1.2.3(react@18.2.0):
+ resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==}
+ peerDependencies:
+ react: '*'
+ dependencies:
+ lodash: 4.17.21
+ react: 18.2.0
+ dev: false
+
/read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
dependencies:
@@ -6550,6 +7273,30 @@ packages:
dependencies:
picomatch: 2.3.1
+ /redux-observable@2.0.0(redux@4.2.1):
+ resolution: {integrity: sha512-FJz4rLXX+VmDDwZS/LpvQsKnSanDOe8UVjiLryx1g3seZiS69iLpMrcvXD5oFO7rtkPyRdo/FmTqldnT3X3m+w==}
+ peerDependencies:
+ redux: '>=4 <5'
+ dependencies:
+ redux: 4.2.1
+ rxjs: 7.8.1
+ tslib: 2.1.0
+ dev: false
+
+ /redux-thunk@2.4.2(redux@4.2.1):
+ resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==}
+ peerDependencies:
+ redux: ^4
+ dependencies:
+ redux: 4.2.1
+ dev: false
+
+ /redux@4.2.1:
+ resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
+ dependencies:
+ '@babel/runtime': 7.22.10
+ dev: false
+
/refractor@3.6.0:
resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
dependencies:
@@ -6591,6 +7338,10 @@ packages:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: false
+ /reselect@4.1.8:
+ resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
+ dev: false
+
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -6728,6 +7479,119 @@ packages:
diff-match-patch: 1.0.5
dev: false
+ /sanity-plugin-iframe-pane@2.3.0(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-bTq1Fnj1AQv5FXOE/88z41tSlJkfd7Ra/+19CvFOkOdopr7nnCWtztaG+phlqm8cndIs+CMnSxaBByLf/RGcPQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: ^18
+ sanity: ^3.0.0
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ react: 18.2.0
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ usehooks-ts: 2.9.1(react-dom@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - react-dom
+ - react-is
+ - styled-components
+ dev: false
+
+ /sanity-plugin-internationalized-array@1.10.1(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-rGf96PdLkj00jC7/UlRVVk7Z9MpN9399bNby0wGm3CLxYYrQjj4A6oZChYHWTwc4RIhBUQqXJXNe9K1xjlVa6Q==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@sanity/ui': ^1.2.2
+ react: ^18
+ sanity: ^3.0.0
+ styled-components: ^5.3.6
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/language-filter': 3.2.1(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ fast-deep-equal: 3.1.3
+ lodash.get: 4.4.2
+ react: 18.2.0
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ suspend-react: 0.0.8(react@18.2.0)
+ transitivePeerDependencies:
+ - react-dom
+ - react-is
+ - supports-color
+ dev: false
+
+ /sanity-plugin-media@2.0.4(@sanity/color@2.2.5)(@sanity/icons@2.4.1)(@types/react@18.2.19)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(sanity@3.15.0)(styled-components@5.3.11):
+ resolution: {integrity: sha512-cLJOa1LZKHWCCJJdjf3j/STiiNj6WtYNQCwF52FTV1zI/PQBhVClRsBBEw5FJM6ru9c+MHlv09PPh+HRpGxQVw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@sanity/color': ^2.1.20
+ '@sanity/icons': ^2.0.0
+ react: ^18
+ react-dom: ^18
+ sanity: ^3.0.0
+ styled-components: ^5.3.3
+ dependencies:
+ '@hookform/resolvers': 2.0.0-beta.3(react-hook-form@6.15.8)
+ '@reduxjs/toolkit': 1.9.5(react-redux@7.2.9)(react@18.2.0)
+ '@sanity/color': 2.2.5
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ '@tanem/react-nprogress': 5.0.47(react-dom@18.2.0)(react@18.2.0)
+ copy-to-clipboard: 3.3.3
+ date-fns: 2.30.0
+ filesize: 8.0.7
+ groq: 2.33.2
+ is-hotkey: 0.2.0
+ nanoid: 3.3.6
+ pluralize: 8.0.0
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-dropzone: 11.7.1(react@18.2.0)
+ react-file-icon: 1.3.0(react-dom@18.2.0)(react@18.2.0)
+ react-hook-form: 6.15.8(react@18.2.0)
+ react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0)
+ react-select: 5.7.4(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)
+ react-virtuoso: 2.19.1(react-dom@18.2.0)(react@18.2.0)
+ redux: 4.2.1
+ redux-observable: 2.0.0(redux@4.2.1)
+ rxjs: 7.8.1
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ yup: 0.32.11
+ transitivePeerDependencies:
+ - '@types/react'
+ - react-is
+ - react-native
+ dev: false
+
+ /sanity-plugin-utils@1.6.2(@babel/core@7.22.10)(@sanity/ui@1.7.4)(react-dom@18.2.0)(react-fast-compare@3.2.2)(react-is@18.2.0)(react@18.2.0)(rxjs@7.8.1)(sanity@3.15.0):
+ resolution: {integrity: sha512-zn4sKLaLG5ZcibPNB8RfTjU32sear2XIHPxIoIWJCHXO3yD+JEq+/MvkwW7CnfdEcZeXPfb5DEx1CGjIQwO3Iw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@sanity/ui': ^1.2.2
+ react: ^18
+ react-fast-compare: ^3.2.0
+ rxjs: ^7.0.0
+ sanity: ^3
+ dependencies:
+ '@sanity/icons': 2.4.1(react@18.2.0)
+ '@sanity/incompatible-plugin': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+ '@sanity/ui': 1.7.4(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ react: 18.2.0
+ react-fast-compare: 3.2.2
+ rxjs: 7.8.1
+ sanity: 3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11)
+ styled-components: 5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-dom
+ - react-is
+ dev: false
+
/sanity@3.15.0(@types/node@18.13.0)(@types/react@18.2.19)(react-dom@18.2.0)(react@18.2.0)(styled-components@5.3.11):
resolution: {integrity: sha512-b0T71nzwOrMonnvZ1Jt3Jc2dY4hTT+T99cpdBjFERBH+m5w2FtEJhbdzmAyz7WgabcnhHwiZ/rzzAkXPmdtnLw==}
engines: {node: '>=14.18.0'}
@@ -7037,10 +7901,25 @@ packages:
is-fullwidth-code-point: 4.0.0
dev: true
+ /slug@8.2.2:
+ resolution: {integrity: sha512-5ByW6qXqPeG0Tmlkh24JhdXhvQsbaJSjVr3GgGxUV0BSskZKKBZZfFWxezap8+fh1vxBN9GVbqI1V6nqAFxlBg==}
+ hasBin: true
+ dev: false
+
+ /slugify@1.6.5:
+ resolution: {integrity: sha512-8mo9bslnBO3tr5PEVFzMPIWwWnipGS0xVbYf65zxDqfNwmzYn1LpiKNrR6DlClusuvo+hDHd1zKpmfAe83NQSQ==}
+ engines: {node: '>=8.0.0'}
+ dev: false
+
/source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
+ /source-map@0.5.7:
+ resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
@@ -7241,6 +8120,10 @@ packages:
engines: {node: '>=8'}
dev: true
+ /style-mod@4.0.3:
+ resolution: {integrity: sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw==}
+ dev: false
+
/styled-components@5.3.11(@babel/core@7.22.10)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==}
engines: {node: '>=10'}
@@ -7284,6 +8167,10 @@ packages:
react: 18.2.0
dev: false
+ /stylis@4.2.0:
+ resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
+ dev: false
+
/sucrase@3.34.0:
resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
engines: {node: '>=8'}
@@ -7321,6 +8208,14 @@ packages:
react: 18.2.0
dev: false
+ /suspend-react@0.0.8(react@18.2.0):
+ resolution: {integrity: sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==}
+ peerDependencies:
+ react: '>=17.0'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: false
@@ -7466,6 +8361,10 @@ packages:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: false
+ /tinycolor2@1.6.0:
+ resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
+ dev: false
+
/titleize@3.0.0:
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
engines: {node: '>=12'}
@@ -7486,6 +8385,10 @@ packages:
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
dev: false
+ /toposort@2.0.2:
+ resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
+ dev: false
+
/totalist@1.1.0:
resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==}
engines: {node: '>=6'}
@@ -7529,6 +8432,10 @@ packages:
strip-bom: 3.0.0
dev: true
+ /tslib@2.1.0:
+ resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==}
+ dev: false
+
/tslib@2.6.1:
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
@@ -7731,6 +8638,19 @@ packages:
react: 18.2.0
dev: false
+ /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.19)(react@18.2.0):
+ resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.19
+ react: 18.2.0
+ dev: false
+
/use-sidecar@1.1.2(@types/react@18.2.19)(react@18.2.0):
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
@@ -7755,6 +8675,17 @@ packages:
react: 18.2.0
dev: false
+ /usehooks-ts@2.9.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==}
+ engines: {node: '>=16.15.0', npm: '>=8'}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -7805,6 +8736,10 @@ packages:
fsevents: 2.3.2
dev: false
+ /w3c-keyname@2.2.8:
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+ dev: false
+
/w3c-xmlserializer@4.0.0:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
engines: {node: '>=14'}
@@ -7982,6 +8917,11 @@ packages:
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ /yaml@1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+ dev: false
+
/yaml@2.3.1:
resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
engines: {node: '>= 14'}
@@ -8012,6 +8952,19 @@ packages:
resolution: {integrity: sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ==}
dev: false
+ /yup@0.32.11:
+ resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@babel/runtime': 7.22.10
+ '@types/lodash': 4.14.196
+ lodash: 4.17.21
+ lodash-es: 4.17.21
+ nanoclone: 0.2.1
+ property-expr: 2.0.5
+ toposort: 2.0.2
+ dev: false
+
/zip-stream@4.1.0:
resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==}
engines: {node: '>= 10'}
diff --git a/sanity.config.ts b/sanity.config.ts
new file mode 100644
index 000000000..8709e903d
--- /dev/null
+++ b/sanity.config.ts
@@ -0,0 +1,75 @@
+import {defineConfig, isDev} from 'sanity'
+import {deskTool} from 'sanity/desk'
+import {visionTool} from '@sanity/vision'
+import { colorInput } from "@sanity/color-input";
+import {media} from 'sanity-plugin-media'
+import {schemaTypes} from '@/lib/sanity/schemas'
+import {structure} from '@/lib/sanity/desk'
+import Kodamera from '@/lib/sanity/components/icons/kodamera'
+import {documentInternationalization} from '@sanity/document-internationalization'
+
+const devOnlyPlugins = [visionTool()]
+
+// Define the actions that should be available for singleton documents
+const singletonActions = new Set(["publish", "discardChanges", "restore"])
+
+// Define the singleton document types
+const singletonTypes = new Set(["settings", "home", "utilityMenu", "media.tag"])
+
+export default defineConfig({
+ name: 'default',
+ title: 'KM Storefront CMS',
+ projectId: 'opfmivlh',
+ basePath: '/studio',
+ dataset: 'production',
+ plugins: [
+ deskTool({structure}),
+ media(),
+ ...(isDev ? devOnlyPlugins : []),
+ documentInternationalization({
+ // Required, either:
+ // An array of supported languages
+ supportedLanguages: [
+ {id: 'sv', title: 'Swedish'},
+ {id: 'en', title: 'English'}
+ ],
+ // Required
+ schemaTypes: [
+ 'home',
+ 'page',
+ 'product',
+ 'category',
+ 'settings',
+ 'blurb',
+ 'section',
+ 'usp',
+ 'footerMenu',
+ 'utilityMenu'
+ ],
+ // Optional
+ // languageField: `language`, // defauts to "language"
+ // Optional, requires access to the Publishing API
+ // bulkPublish: true // defaults to false
+ }),
+ colorInput(),
+ ],
+ schema: {
+ types: schemaTypes,
+ // Filter out singleton types from the global “New document” menu options
+ templates: (templates) =>
+ templates.filter(({ schemaType }) => !singletonTypes.has(schemaType)),
+ },
+ document: {
+ // For singleton types, filter out actions that are not explicitly included
+ // in the `singletonActions` list defined above
+ actions: (input, context) =>
+ singletonTypes.has(context.schemaType)
+ ? input.filter(({ action }) => action && singletonActions.has(action))
+ : input,
+ },
+ studio: {
+ components: {
+ logo: Kodamera,
+ }
+ },
+})