Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need help with Astro (zod) collection conversion to Keystatic #1115

Open
neo-art opened this issue May 2, 2024 · 4 comments
Open

Need help with Astro (zod) collection conversion to Keystatic #1115

neo-art opened this issue May 2, 2024 · 4 comments

Comments

@neo-art
Copy link

neo-art commented May 2, 2024

Hello all,
I am new to Keystatic and Astro and hope to get some help. I am trying to add Keystatic to an existing Astro project but I am stuck with converting the existing ZOD collections to Keystatic.
This is the original theme Astro schema (config.ts):

import { defineCollection, z } from 'astro:content';

const seoSchema = z.object({
    title: z.string().min(5).max(120).optional(),
    description: z.string().min(15).max(160).optional(),
    image: z
        .object({
            src: z.string(),
            alt: z.string().optional()
        })
        .optional(),
    pageType: z.enum(['website', 'article']).default('website')
});

const blog = defineCollection({
    schema: z.object({
        title: z.string(),
        excerpt: z.string().optional(),
        publishDate: z.coerce.date(),
        updatedDate: z.coerce.date().optional(),
        isFeatured: z.boolean().default(false),
        tags: z.array(z.string()).default([]),
        seo: seoSchema.optional()
    })
});

const pages = defineCollection({
    schema: z.object({
        title: z.string(),
        seo: seoSchema.optional()
    })
});

const projects = defineCollection({
    schema: z.object({
        title: z.string(),
        description: z.string().optional(),
        publishDate: z.coerce.date(),
        isFeatured: z.boolean().default(false),
        seo: seoSchema.optional()
    })
});

export const collections = { blog, pages, projects };

And this is what I have done so far to the keystatic.config.ts

// keystatic.config.ts
import { config, fields, collection, singleton } from '@keystatic/core';

import { z } from 'zod';

export default config({
    storage: {
        kind: 'local'
    },
    ui: {
        brand: {
            name: '2 Aquarius' // NAME OF YOUR SITE
        }
    },
    collections: {
        blog: collection({
            label: 'Blog',
            slugField: 'title',
            path: 'src/content/blog/*',
            entryLayout: 'content',
            columns: ['title', 'publishDate'],
            format: { contentField: 'content' },
            schema: {
                title: fields.slug({ name: { label: 'Title' } }),
                excerpt: fields.text({
                    label: 'Excerpt',
                    multiline: true
                }),
                description: fields.text({
                    label: 'Description',
                    multiline: true
                }),
                publishDate: fields.date({
                    defaultValue: { kind: 'today' },
                    label: 'Date of the publication'
                }),
                updatedDate: fields.date({
                    label: 'Updated date',
                    description: 'Date when the article was updated',
                    validation: {
                        isRequired: false
                    }
                }),
                isFeatured: fields.checkbox({
                    label: 'Is featured?',
                    defaultValue: false
                }),
                tags: fields.array(
                    fields.text({ label: 'Tags' }),
                    // Labelling options
                    {
                        label: 'Tags',
                        itemLabel: (props) => props.value
                    }
                ),
                // seoSchema: fields.text({
                //     label: 'seoSchema',
                //     multiline: true,
                //     description: 'seoSchema',
                //     validation: {
                //         isRequired: true,
                //         length: {
                //             min: 5,
                //             max: 120
                //         }
                //     }
                // }),
                seo: fields.relationship({
                    label: 'Seo',
                    collection: 'seoSchema',
                    validation: {
                        isRequired: false
                    }
                }),
                content: fields.markdoc({
                    label: 'Content',
                    extension: 'md'
                    // formatting: true,
                    // dividers: true,
                    // links: true,
                    // images: true,
                })
            }
        }),

        seoSchema: collection({
            label: 'seoSchema',
            slugField: 'title',
            path: 'src/content/seoSchema/*',
            schema: {
                title: fields.slug({ name: { label: 'Title' } }),
                description: fields.text({
                    label: 'seoSchema Description',
                    multiline: true,
                    validation: {
                        isRequired: false,
                        length: {
                            min: 5,
                            max: 120
                        }
                    }
                }),
                image: fields.image({
                    label: 'Image',
                    directory: 'src/assets/images/pages',
                    publicPath: '../../assets/images/pages/'
                }),
                imageAlt: fields.text({
                    label: 'ImageAlt',
                    validation: {
                        isRequired: false
                    }
                }),
                pageType: fields.select({
                    label: 'Page Type',
                    description: 'Type of this page',
                    options: [
                        { label: 'Website', value: 'website' },
                        { label: 'Article', value: 'article' }
                    ],
                    defaultValue: 'website'
                })
            }
        }),

        projects: collection({
            label: 'Projects',
            slugField: 'title',
            path: 'src/content/projects/*',
            format: { contentField: 'content' },
            schema: {
                title: fields.text({ label: 'Projects headline' }),
                description: fields.text({
                    label: 'Description',
                    multiline: true
                }),
                publishDate: fields.date({
                    defaultValue: { kind: 'today' },
                    label: 'Date of the publication'
                }),
                isFeatured: fields.checkbox({
                    label: 'Is featured?',
                    defaultValue: false
                }),
                content: fields.markdoc({
                    label: 'Content',
                    extension: 'md'
                    // formatting: true,
                    // dividers: true,
                    // links: true,
                    // images: true,
                }),
                seo: fields.relationship({
                    label: 'SEO',
                    collection: 'seoSchema'
                })
            }
        }),

        pages: collection({
            label: 'Pages',
            slugField: 'title',
            path: 'src/content/pages/*',
            entryLayout: 'content',
            columns: ['title', 'description'],
            format: { contentField: 'content' },
            schema: {
                title: fields.slug({ name: { label: 'SEO Title' } }),
                description: fields.text({
                    label: 'SEO Description',
                    multiline: true
                }),
                seo: fields.relationship({
                    label: 'SEO',
                    collection: 'seoSchema',
                    validation: {
                        isRequired: false
                    }
                }),
                ogImage: fields.image({
                    label: 'Image',
                    directory: 'src/assets/images/pages',
                    publicPath: '../../assets/images/pages/'
                }),
                // noIndex: fields.checkbox({
                //   label: "Don't index the page",
                //   defaultValue: false,
                // }),
                content: fields.markdoc({
                    label: 'Content',
                    extension: 'md',
                    options: {
                        image: {
                            directory: 'src/assets/images/pages',
                            publicPath: '../../assets/images/pages/'
                        }
                    }
                })
            }
        })
    }
});
```

@neo-art
Copy link
Author

neo-art commented May 2, 2024

Unfortunately, I am not able to edit the old posts or pages. I got the following error message when I tried to access the relevant pages/posts in the Admin UI:

Field validation failed: seo: Must be a string

Any help is appreciated.

Link to the repo: https://github.com/neo-art/dante-astro-theme-2AQ.git

@neo-art
Copy link
Author

neo-art commented May 8, 2024

Any help guys? Or some workaround?

@Matthijz98
Copy link

I think the problem is with the front matter of the page.

If i look at your repo some pages have this front matter

---
title: About
seo:
  title: About Me
  description: Learn more about the person behind the website and embark on a journey of inspiration and shared experiences.
  image:
    src: '/about.jpeg'
    alt: A person sitting at a desk in front of a computer
---

And other:

---
title: Page test 1
description: SEO DESC
ogImage: ../../assets/images/pages/page-test-1/ogImage.jpg
---

Your config says that the seo if a relation so it should be something like this:

---
title: About
seo: test-set
---

Then a test-set.yaml should exist in your seoSchema content collection

@neo-art
Copy link
Author

neo-art commented May 14, 2024

@Matthijz98 thank you for the comment and I appreciate your taking the time to review it.
Unfortunately, I couldn't get how to fix it :(
The file "Page test 1" was left from previous tests and I removed it.

Anyway a few days ago I made some changes to the config (changed it to . TSX) and edited the Keystatic configuration,
you can see it here:
keystatic. config.tsx

Still the same error message, no matter what I try and now can't even add a new post in Keystatic admin (blank page).

I guess the problem is that I can't match the ZOD schema in Keystatic config file but not sure how to fix it.
More specifically this part of Astro config.ts:

const seoSchema = z.object({
    title: z.string().min(5).max(120).optional(),
    description: z.string().min(15).max(160).optional(),
    image: z
        .object({
            src: z.string(),
            alt: z.string().optional()
        })
        .optional(),
    pageType: z.enum(['website', 'article']).default('website')
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants