├── .eslintrc.json
├── .gitignore
├── README.md
├── app
├── (site)
│ ├── api
│ │ └── submit-message
│ │ │ └── route.js
│ ├── layout.jsx
│ └── page.jsx
├── (studio)
│ ├── layout.jsx
│ └── studio
│ │ └── [[...index]]
│ │ └── page.jsx
├── favicon.ico
└── globals.css
├── components
└── form.jsx
├── jsconfig.json
├── lib
└── utils.js
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── next.svg
└── vercel.svg
├── sanity.config.js
├── sanity
├── config
│ └── sanity.client.js
└── plugins
│ └── inbox
│ ├── components
│ ├── inbox-tool.jsx
│ ├── message-card.jsx
│ ├── message-dialog.jsx
│ └── message-list.jsx
│ ├── plugin.js
│ └── schema.js
└── tailwind.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Inbox Tool for Sanity Studio
2 |
3 | I've been working on a fun little project recently and thought I would share it with the community! A Sanity plugin that lets you view and manage form submissions inside a handy Studio Tool.
4 |
5 |
6 |
7 | ## Features ⚡️
8 |
9 | - Studio Tool to view submissions.
10 | - Custom dialog to view a message and all its fields.
11 | - Star important messages.
12 | - Delete spam and unwanted messages.
13 | - New messages are moved out of unread folder once viewed.
14 |
15 | ## Installation
16 |
17 | To give this Studio Tool a try, you'll need to clone this GitHub repository locally, open it up in your code editor and then run `npm install` in the terminal to install the required dependencies.
18 |
19 | ## Environment Settings
20 |
21 | Create a `.env.local` file at the root of the project and add the following environment variables.
22 |
23 | ```
24 | NEXT_PUBLIC_SANITY_PROJECT_ID=""
25 | NEXT_PUBLIC_SANITY_DATASET=""
26 | NEXT_PUBLIC_SANITY_API_VERSION=""
27 | SANITY_API_TOKEN=""
28 | ```
29 |
30 | Once you have added your environment variables, open your terminal again and run `npm run dev` to start the development server.
31 |
32 | ## Usage
33 |
34 | Open your browser and navigate to `http://localhost:3000`. Here you will see a basic form, fill out the fields, hit submit and then navigate to `http://localhost:3000/studio/inbox-tool` to see your form submission.
35 |
36 | ## Important Details
37 |
38 | ### Creating documents using the HTTP API
39 |
40 | To create a document using the Sanity HTTP API requests must be authenticated with an API Token. This token should be kept safe and never exposed to the client and so we make the request in the `/api/submit-message` route handler.
41 |
42 | You can learn more about the HTTP API [here](https://www.sanity.io/docs/http-api).
43 |
44 | ### Keeping data safe
45 |
46 | By default Sanity gives unauthenticated users read access to published documents in public datasets. Our `message` document is going to contain personal information that needs to be kept private. To disable this behavior we add a `message.` prefix to the `_id` when defining our `create` mutation in `form.jsx`. This will generate a new, random, unique `_id` such as `message.s4tZYDUyXCCef1YpYu6Js5`.
47 |
48 | The important thing to know here is that documents under a sub-path (i.e. containing a `.` in the `_id`) are not publicly available and can only be read with a Token.
49 |
50 | You can learn more about IDs, paths and sub-paths [here](https://www.sanity.io/docs/ids).
51 |
52 | ```javascript
53 | // components/form.jsx
54 |
55 | const mutations = [{
56 | create: {
57 | _id: 'message.',
58 | _type: 'message',
59 | read: false,
60 | starred: false,
61 | name: name,
62 | email: email,
63 | subject: subject,
64 | fields: [
65 | {
66 | _key: generateID(),
67 | name: 'Occupation',
68 | value: occupation
69 | },
70 | {
71 | _key: generateID(),
72 | name: 'Message',
73 | value: message
74 | }
75 | ]
76 | }
77 | }]
78 | ```
79 |
80 | ## Author
81 |
82 | #### James Rea
83 |
84 | - Twitter ([@jamesreaco](https://twitter.com/jamesreaco))
85 | - Website ([jamesrea.co](https://jamesrea.co))
86 |
87 | For business enquiries, you can email me at hello@jamesrea.co.
88 |
89 |
--------------------------------------------------------------------------------
/app/(site)/api/submit-message/route.js:
--------------------------------------------------------------------------------
1 | export async function POST(req) {
2 |
3 | const body = await req.json()
4 |
5 | const API_KEY = process.env.SANITY_API_TOKEN
6 | const PROJECT_ID = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
7 | const DATASET = process.env.NEXT_PUBLIC_SANITY_DATASET
8 |
9 | const options = {
10 | method: 'POST',
11 | headers: {
12 | 'Content-Type': 'application/json',
13 | Authorization: `Bearer ${API_KEY}`
14 | },
15 | body: JSON.stringify(body)
16 | };
17 |
18 | const url = `https://${PROJECT_ID}.api.sanity.io/v2021-06-07/data/mutate/${DATASET}`
19 |
20 | try {
21 | const response = await fetch(url, options)
22 | const data = await response.json()
23 | return new Response(JSON.stringify({ success: true, data }))
24 | } catch (error) {
25 | return new Response(JSON.stringify({ error: 'Failed to save document.' }))
26 | }
27 | }
--------------------------------------------------------------------------------
/app/(site)/layout.jsx:
--------------------------------------------------------------------------------
1 | import '../globals.css'
2 | import { Inter } from 'next/font/google'
3 |
4 | const inter = Inter({ subsets: ['latin'] })
5 |
6 | export const metadata = {
7 | title: 'Messages Plugin | Sanity',
8 | }
9 |
10 | export default function RootLayout({ children }) {
11 | return (
12 |
13 |