├── .env ├── .eslintrc.json ├── .gitignore ├── example-zod.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── prisma ├── dev.db ├── migrations │ ├── 20221124224943_create_feedbacks │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── src ├── assets │ ├── bug.svg │ ├── idea.svg │ └── thought.svg ├── components │ ├── CloseButton.tsx │ ├── Loading.tsx │ └── WidgetForm │ │ ├── Steps │ │ ├── FeedbackContentStep.tsx │ │ ├── FeedbackSuccessStep.tsx │ │ └── FeedbackTypeStep.tsx │ │ └── index.tsx ├── pages │ ├── _app.tsx │ ├── api │ │ └── trpc │ │ │ └── [trpc].ts │ └── index.tsx ├── server │ ├── context.ts │ ├── routers │ │ └── _app.ts │ └── trpc.ts ├── styles │ └── global.css └── utils │ ├── prisma.ts │ └── trpc.ts ├── tailwind.config.js └── tsconfig.json /.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./dev.db" -------------------------------------------------------------------------------- /.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 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /example-zod.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | const personSchema = z.object({ 4 | name: z.string(), 5 | age: z.string().transform(age => Number(age)), 6 | email: z.string().email().nullable() 7 | }) 8 | 9 | const person = { 10 | name: 'Diego', 11 | age: '21', 12 | email: 'diego@rocketseat.com.br', 13 | } 14 | 15 | type PersonSchemaInput = z.input 16 | type PersonSchemaOutput = z.output 17 | 18 | const { name, email, age } = personSchema.parse(person) 19 | 20 | function createPersonInDatabase(person: PersonSchemaOutput) { 21 | 22 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-fullstack", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@prisma/client": "^4.6.1", 13 | "@radix-ui/react-popover": "^1.0.2", 14 | "@tanstack/react-query": "^4.16.1", 15 | "@trpc/client": "^10.1.0", 16 | "@trpc/next": "^10.1.0", 17 | "@trpc/react-query": "^10.1.0", 18 | "@trpc/server": "^10.1.0", 19 | "@types/node": "18.11.9", 20 | "@types/react": "18.0.25", 21 | "@types/react-dom": "18.0.9", 22 | "axios": "^1.2.0", 23 | "eslint": "8.28.0", 24 | "eslint-config-next": "13.0.5", 25 | "next": "13.0.5", 26 | "phosphor-react": "^1.4.1", 27 | "react": "18.2.0", 28 | "react-dom": "18.2.0", 29 | "tailwind-scrollbar": "^2.0.1", 30 | "typescript": "4.9.3", 31 | "zod": "^3.19.1" 32 | }, 33 | "devDependencies": { 34 | "@tailwindcss/forms": "^0.5.3", 35 | "autoprefixer": "^10.4.13", 36 | "postcss": "^8.4.19", 37 | "prisma": "^4.6.1", 38 | "tailwindcss": "^3.2.4" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prisma/dev.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocketseat-content/next-trpc-typescript-zod/3c8154d9324393ecc913655211de2de855f3bd36/prisma/dev.db -------------------------------------------------------------------------------- /prisma/migrations/20221124224943_create_feedbacks/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Feedback" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "type" TEXT NOT NULL, 5 | "content" TEXT NOT NULL, 6 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP 7 | ); 8 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "sqlite" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model Feedback { 14 | id String @id @default(cuid()) 15 | type String 16 | content String 17 | createdAt DateTime @default(now()) 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/idea.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/thought.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | import * as Popover from '@radix-ui/react-popover'; 2 | import { X } from 'phosphor-react' 3 | 4 | export function CloseButton() { 5 | return ( 6 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import { CircleNotch } from 'phosphor-react' 2 | 3 | export function Loading() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/WidgetForm/Steps/FeedbackContentStep.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image' 2 | import { ArrowLeft } from 'phosphor-react' 3 | import { FormEvent, KeyboardEvent, useRef, useState } from 'react' 4 | import { FeedbackType, feedbackTypes } from '..' 5 | import { CloseButton } from '../../CloseButton' 6 | import { Loading } from '../../Loading' 7 | import * as z from 'zod' 8 | 9 | const feedbackFormSchema = z.object({ 10 | type: z.enum(['BUG', 'IDEA', 'OTHER']), 11 | comment: z.string(), 12 | }) 13 | 14 | export type FeedbackFormSchema = z.infer 15 | 16 | export interface FeedbackContentStepProps { 17 | feedbackType: FeedbackType 18 | onFeedbackRestartRequested: () => void 19 | onFeedbackSubmitted: (data: FeedbackFormSchema) => void | Promise 20 | } 21 | 22 | export function FeedbackContentStep({ 23 | feedbackType, 24 | onFeedbackRestartRequested, 25 | onFeedbackSubmitted, 26 | }: FeedbackContentStepProps) { 27 | const formRef = useRef(null) 28 | const [comment, setComment] = useState('') 29 | const [isSendingFeedback, setIsSendingFeedback] = useState(false) 30 | 31 | const feedbackTypeInfo = feedbackTypes[feedbackType] 32 | 33 | function handleSubmitFromTextarea(e: KeyboardEvent) { 34 | if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { 35 | formRef.current?.requestSubmit() 36 | } 37 | } 38 | 39 | async function handleSubmitFeedback(event: FormEvent) { 40 | event.preventDefault() 41 | 42 | setIsSendingFeedback(true) 43 | 44 | const data = feedbackFormSchema.parse({ 45 | type: feedbackType, 46 | comment, 47 | }) 48 | 49 | await onFeedbackSubmitted(data) 50 | 51 | setIsSendingFeedback(false) 52 | } 53 | 54 | return ( 55 | <> 56 |
57 | 65 | 66 | {feedbackTypeInfo.image.alt} 71 | {feedbackTypeInfo.title} 72 | 73 | 74 | 75 |
76 | 77 |
82 |