├── .env ├── .gitignore ├── README.md ├── components ├── AddCampaignModal.tsx ├── AddPostModal.tsx ├── AddProviderModal.tsx ├── Button.tsx ├── CalendarEvent.tsx ├── Card.tsx ├── Dropdown.tsx ├── FormGroup.tsx ├── ProviderOptionLabel.tsx ├── ResourceAction.tsx ├── ResourceCard.tsx ├── SideModal.tsx ├── TimeInput.tsx ├── UpdateCampaignModal.tsx ├── UpdatePostModal.tsx └── UpdateProviderModal.tsx ├── db └── index.ts ├── layouts └── AppLayout.tsx ├── lib ├── data │ ├── campaigns.json │ └── providers.json ├── providers.ts ├── types.ts └── util.ts ├── next-env.d.ts ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── api │ ├── campaigns │ │ ├── [id].tsx │ │ └── index.ts │ ├── posts │ │ ├── [id].tsx │ │ └── index.ts │ └── providers │ │ ├── [id].tsx │ │ └── index.ts ├── campaigns.tsx ├── index.tsx └── providers.tsx ├── postcss.config.js ├── prettier.config.js ├── prisma ├── schema.prisma └── seed.ts ├── public ├── Facebook.png ├── Instagram.png ├── LinkedIn.png ├── Twitter.png ├── favicon.ico ├── logo.svg └── vercel.svg ├── styles ├── calendar.css └── styles.css ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.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#using-environment-variables 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server and MongoDB (Preview). 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | TAILWIND_MODE=watch 8 | DATABASE_URL="file:./dev.db" -------------------------------------------------------------------------------- /.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 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SocialCue 2 | 3 | Welcome to SocialCue! 👋 4 | 5 | This is the demo app used in [**How To Prisma**](https://howtoprisma.com). It's built with Next.js, TypeScript, Tailwind, and a bunch of other great libraries. 6 | 7 | It's a social media scheduling app that does just about everything except actually publishing the posts 😄 8 | 9 | ![app screenshot](https://res.cloudinary.com/dkpz9r2q7/image/upload/v1642022319/Screen_Shot_2022-01-12_at_4.18.10_PM_dijbo4.png) 10 | 11 | ## Installation and Running the App 12 | 13 | Clone the repo and install dependencies. 14 | 15 | ```bash 16 | # npm 17 | npm install 18 | 19 | # yarn 20 | yarn 21 | ``` 22 | 23 | ## Sync Prisma Schema into `dev.db` 24 | 25 | This project uses [SQLite](https://www.sqlite.org/index.html), a small and fast database engine that is great for local development and testing. 26 | 27 | The project doesn't come with the SQLite database file included. Instead, the file gets created on the fly by Prisma. 28 | 29 | The database file is called `dev.db` and it's found in the `prisma` directory. 30 | 31 | Use Prisma's `db push` command to generate the database file and sync the schema found in `prisma/schema.prisma`. 32 | 33 | ```bash 34 | # npm 35 | npx prisma db push 36 | 37 | # yarn 38 | yarn prisma db push 39 | ``` 40 | 41 | ## Seed the Database 42 | 43 | The project comes with a file to seed the database with some initial data. It's found in `prisma/seed.ts`. 44 | 45 | Use Prisma's `db seed` command to get some initial data in place. 46 | 47 | ```bash 48 | # npm 49 | npx prisma db seed 50 | 51 | # yarn 52 | yarn prisma db seed 53 | ``` 54 | 55 | ## Run the App 56 | 57 | Use Next.js's devleopment server to start the app. 58 | 59 | ```bash 60 | # npm 61 | npm run dev 62 | 63 | # yarn 64 | yarn run dev 65 | ``` 66 | 67 | ## License 68 | 69 | MIT 70 | -------------------------------------------------------------------------------- /components/AddCampaignModal.tsx: -------------------------------------------------------------------------------- 1 | import { CheckIcon } from '@heroicons/react/outline'; 2 | import { useForm } from 'react-hook-form'; 3 | import { useMutation } from 'react-query'; 4 | import Button from '../components/Button'; 5 | import FormGroup from '../components/FormGroup'; 6 | import SideModal from '../components/SideModal'; 7 | import { CampaignResult } from '../lib/types'; 8 | 9 | interface AddCampaignModalProps { 10 | isOpen: boolean; 11 | onClose: () => void; 12 | onCampaignAddSuccess: (result: CampaignResult) => void; 13 | onCampaignAddError: (err: string) => void; 14 | } 15 | 16 | const AddCampaignModal = (props: AddCampaignModalProps) => { 17 | const { isOpen, onClose, onCampaignAddSuccess, onCampaignAddError } = props; 18 | 19 | const { 20 | register, 21 | handleSubmit, 22 | formState: { errors } 23 | } = useForm(); 24 | 25 | const addCampaignMutation = useMutation( 26 | 'addCampaign', 27 | async ({ name, description }: { name: string; description: string }) => { 28 | try { 29 | const response = await fetch('./api/campaigns', { 30 | method: 'POST', 31 | body: JSON.stringify({ name, description }) 32 | }); 33 | 34 | if (!response.ok) { 35 | throw new Error('Something went wrong'); 36 | } 37 | 38 | return await response.json(); 39 | } catch (err) { 40 | throw new Error(err); 41 | } 42 | }, 43 | { 44 | onSuccess: (result) => onCampaignAddSuccess(result), 45 | onError: (err: { message: string }) => onCampaignAddError(err.message) 46 | } 47 | ); 48 | 49 | const onSubmit = (values) => addCampaignMutation.mutate(values); 50 | 51 | return ( 52 | 53 |
54 |
55 | 56 | 57 | 63 | {errors.name &&

Name is required

} 64 |
65 | 66 | 67 | 68 |