Discussion about a Next.js project and seeking a front-end developer for a Sanity/Next.js/Supabase project.

11 replies
Last updated: Aug 31, 2023
😿 anybody keen to help me in my useFormValue quest, i’ve posted two code files showing what i’m trying to do and failing to do: https://sanity-io-land.slack.com/archives/C9Z7RC3V1/p1692749534746789?thread_ts=1692411026.271349&cid=C9Z7RC3V1
Aug 23, 2023, 12:16 AM
👋 What does the schema for this document look like?
Aug 23, 2023, 4:33 PM
good morning. it’s a bit messy atm. the component I am bringing in:
{
      title: '🚧 Newsletter Tool: Nerd Edition',
      name: 'NewsletterSend',
      type: 'sendButton',
      components: {
        input: SendEmail
      },
      options: {
        title: 'title', // Pass the title field value to the component
        body: 'body' // Pass the body field value to the component
      }
    }
the full schema:

// /lib/sanity/schemas/post.ts

// import SendButtonInput from './SendButtonInput';
import SendEmail from './SendEmail';

const schema = {
  name: 'post',
  title: 'Post',
  type: 'document',
  initialValue: () => ({
    publishedAt: new Date().toISOString()
  }),
  fields: [
    {
      name: 'featured',
      title: 'Mark as Featured',
      type: 'boolean',
      layout: 'radio'
    },
    {
      name: 'title',
      placeholder: 'Enter title here',
      title: 'Title',
      type: 'string'
    },
    {
      name: 'publishedAt',
      title: 'Published at',
      type: 'datetime'
    },
    {
      name: 'excerpt',
      title: 'Excerpt / Subtitle',
      description:
        'The excerpt is used in blog feeds and as the subtitle in Newsletters, and also for search results',
      type: 'text',
      rows: 3,
      validation: (Rule) => Rule.max(200)
    },
    {
      name: 'preface',
      title: 'Preface',
      description: 'Optional note from Solana/Editor preceding the article. ',
      type: 'text'
    },
    {
      name: 'body',
      title: 'Body',
      type: 'blockContent'
    },
    {
      name: 'author',
      title: 'Author',
      type: 'reference',
      to: { type: 'author' }
    },
    {
      name: 'section',
      title: 'Section',
      type: 'string',
      options: {
        list: [
          { title: 'The Wire', value: 'the-wire' },
          { title: 'The Industry', value: 'the-industry' },
          { title: 'The White Pill', value: 'the-white-pill' }
          // Add more predefined sections as needed
        ]
      }
    },
    {
      name: 'categories',
      title: 'Categories',
      type: 'array',
      of: [{ type: 'reference', to: { type: 'category' } }]
    },
    {
      name: 'mainImage',
      title: 'Main image',
      type: 'image',
      fields: [
        {
          name: 'alt',
          type: 'string',
          title: 'Alternative text',
          description: 'Important for SEO accessiblity.'
        }
      ],
      options: {
        hotspot: true
      }
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96
      }
    },
    {
      title: '🚧 Newsletter Tool: Nerd Edition',
      name: 'NewsletterSend',
      type: 'sendButton',
      components: {
        input: SendEmail
      },
      options: {
        title: 'title', // Pass the title field value to the component
        body: 'body' // Pass the body field value to the component
      }
    }
    // {
    //   title: '🚧 Newsletter Tool: Nerd Edition',
    //   name: 'NewsletterSend',
    //   type: 'sendButton',
    //   components: {
    //     input: SendButtonInput
    //   },
    //   options: {
    //     title: 'title', // Pass the title field value to the component
    //     body: 'body' // Pass the body field value to the component
    //   }
    // }
  ],

  preview: {
    select: {
      title: 'title',
      author: 'author.name',
      media: 'mainImage'
    },

    prepare(selection) {
      const { author } = selection;
      return Object.assign({}, selection, {
        subtitle: author && `by ${author}`
      });
    }
  }
};

export default schema;
Aug 23, 2023, 4:48 PM
Ah, I think I just saw the problem. I don’t believe you need to put the brackets around the variable you’re assigning to what’s returned from
UseFormValue
. For example, this should work:
const title = useFormValue(['title']);
Aug 23, 2023, 4:57 PM
Presently trying this:
// lib/sanity/schemas/SendEmail.jsx
import { toHTML } from '@portabletext/to-html';
import React from 'react';
import { useMemo } from 'react';
import { useFormValue } from 'sanity';

const components = {
  types: {
    image: ({ value }) => {
      if (value && value.asset && value.asset._ref) {
        return `<img src="${value.asset._ref}" />`;
      } else {
        return '';
      }
    },
    embed: ({ value }) => {
      if (value && value.url) {
        return `<iframe src="${value.url}" />`;
      } else {
        return '';
      }
    }
  }
};

export default function SendEmail(props) {
  const form = useFormValue([]);
  const title = useFormValue(['title']);
  const section = useFormValue(['section']);
  const subTitle = useFormValue(['excerpt']);
  const preface = useFormValue(['preface']);
  const body = useFormValue(['body']);
  const bodyHtml = toHTML(body, { components });

  // const authorName = 'Author Name';
  // const authorSlug = 'author-slug';
  // const authorImageUrl = 'author-image';

  console.log('form', form);
  console.log('title', title);
  console.log('section', section);
  console.log('subTitle', subTitle);
  console.log('preface', preface);
  console.log('bodyHtml', bodyHtml);

  const [formValues, setFormValues] = React.useState({
    title: '',
    section: '',
    subTitle: '',
    preface: '',
    bodyHtml: ''
  });

  React.useEffect(() => {
    setFormValues({
      title: title,
      section: section,
      subTitle: subTitle,
      preface: preface,
      bodyHtml: bodyHtml
    });
  }, [title, section, subTitle, preface, bodyHtml]);

  const handleSendEmail = async () => {
    // Prepare the request body
    // I only want to send the title, section, and subTitle right now
    const requestBody = {
      title: formValues.title,
      subTitle: formValues.subTitle,
      section: formValues.section
    };

    console.log('Request body:', requestBody);

    try {
      const response = await fetch('/api/send-email', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestBody)
      });

      if (response.ok) {
        setEmailSent(true);
      } else {
        console.error('Email sending failed');
      }
    } catch (error) {
      console.error('An error occurred:', error);
    }
  };

  return (
    <div className="space-y-2 border p-4">
      <h2 className="text-xl font-bold">Send Email</h2>
      <div className="flex flex-col space-y-2">
        <pre>{title}</pre>
        <button onClick={handleSendEmail}>Send Email</button>
      </div>
    </div>
  );
}
Aug 23, 2023, 5:01 PM
i just put up a commit of the three important files in this puzzle. maybe easier to see the whole picture: https://github.com/Pirate-Wires/pirate-wires/commit/c09c58d3e6a1d1ef54de4605534af3d3a9c55f08
Aug 23, 2023, 5:09 PM
the actual passing of values to the template isn’t so much the problem, as i am hardcoding those values in the route presently. the email fires off and that is fine. i’m meanwhile just trying to get the values in the right shape in the component and pass the req body successfully. no values log in the route and my req res are all a mess. getting 500 while the email actually does get sent. kind of dealing with an assortment of problems, some more next.js 13 api route stuff, but then the confusing aspect of, “are my useFormValues getting put into the proper for (req body) and can the route even consume them if they were”.
Aug 23, 2023, 5:15 PM
user M
I shared this screen-share a moment ago with
user S
who has been helping me try to figure this out: https://www.loom.com/share/f2a33f455fec405b85c9a04250e6007e?sid=091352b7-7502-4c86-833e-0422e578fcfd
Aug 23, 2023, 5:55 PM
Hmm; it seems congruent with any way I am used to these things being handled. That said, I am not privy to the app router yet and there seem to be some discrepancies between your route as-written and some of the doc patterns in terms of things like import resolution, some syntax, etc.
Not saying that's the case but mostly because I wouldn't know one way or another from experience.

Seems very Next-y though to seemingly work but not-quite-work silently. If you have a grip on the new router might be worth double-checking that aspect of it if you haven't yet.
Aug 23, 2023, 6:29 PM
Ah, it seems I am getting warmer. Thanks to some clues in the ‘doc pattern’ you pointed out. I am logging everything I need now in the route successfully. I’ll post the updated code here in a moment. For those following along, you have my sympathy. It was largely a matter of NextRequest, NextResponse methods being written as for NextApiRequest, NextApiResponse, and a handful of other next-y stuff.
Aug 23, 2023, 8:19 PM
Looking for a front-end dev if y’all know anyone with some time in the next month or more. We have a sanity/next.js/supabase project with a fair amount of complexity and are looking for someone comfortable with the “full stack”: qroq, servless functions, vercel, tailwind, state management in next.js 13 / react, etc… quite a lot of api work on the front too.
Aug 31, 2023, 8:37 PM
I think it's unlikely that there will be high visibility deep into this one thread, so you might want to make sure that goes in jobs , unless you meant to narrow it to people either of us know 🙂
That said, aside from Tailwind knowledge that's my stack, Supabase included, so I may find myself around people who'll fit well and accordingly will keep my eyes open and ears peeled!

I am happy to answer any integration questions between the two platforms here in the open where others could benefit, otherwise, as a courtesy, if you have Supabase-only questions feel free to DM me and I'll see if I can help (aside from what comes from, and until you get, the professional help, that is)
Aug 31, 2023, 9:08 PM

Sanity– build remarkable experiences at scale

The Sanity Composable Content Cloud is the headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?