diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..3d533f7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,22 @@ { - "extends": "next/core-web-vitals" + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "next/core-web-vitals", + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/consistent-type-definitions": ["error", "type"] + } } diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..1a089f4 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx --no-install commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..8806a7c --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +yarn audit +yarn format +yarn lint +yarn typecheck \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..15cf6fb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "none", + "singleQuote": true, + "printWidth": 80, + "jsxSingleQuote": true, + "tabWidth": 2, + "arrowParens": "avoid", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/app/api/cron/route.ts b/app/api/cron/route.ts index bf32048..b68936b 100644 --- a/app/api/cron/route.ts +++ b/app/api/cron/route.ts @@ -14,22 +14,22 @@ export async function GET(request: Request) { const response = await hackernewsApi(); return new NextResponse(JSON.stringify(response), { - status: 200, + status: 200 }); } async function hackernewsApi() { - const topstories: number[] = await fetch(topNews).then((res) => res.json()); + const topstories: number[] = await fetch(topNews).then(res => res.json()); console.log('topstories', topstories); const newsPromises = topstories .splice(0, Number(process.env.NEWS_LIMIT)) - .map(async (id) => { + .map(async id => { console.log('id', id); const sourceNews: z.infer = await fetch( singleNews(id) - ).then((res) => res.json()); + ).then(res => res.json()); console.log('sourceNews', sourceNews); @@ -42,7 +42,7 @@ async function hackernewsApi() { by: sourceNews.by, time: sourceNews.time, url: sourceNews.url, - score: sourceNews.score, + score: sourceNews.score }, update: { title: sourceNews.title, @@ -51,18 +51,18 @@ async function hackernewsApi() { by: sourceNews.by, time: sourceNews.time, url: sourceNews.url, - score: sourceNews.score, + score: sourceNews.score }, where: { - id, + id }, select: { - id: true, - }, + id: true + } }); }); const newsIds = await Promise.all(newsPromises); - return newsIds.map((news) => news.id); + return newsIds.map(news => news.id); } diff --git a/app/api/subscribe/route.ts b/app/api/subscribe/route.ts index 1c922cd..0313fa6 100644 --- a/app/api/subscribe/route.ts +++ b/app/api/subscribe/route.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { fromZodError } from 'zod-validation-error'; import prisma from '../../../prisma/prisma'; import { ResponseSchema, SubscribeFormSchema } from '../../utils/types'; @@ -7,7 +8,8 @@ export async function POST(request: Request) { const body = await request.json(); const validation = SubscribeFormSchema.safeParse(body); if (!validation.success) { - return new Response('Bad request', { status: 400 }); + const message = fromZodError(validation.error); + return new Response(message.message, { status: 400 }); } const { email, targetingAllowed } = validation.data; @@ -15,18 +17,18 @@ export async function POST(request: Request) { await prisma.user.upsert({ create: { email, - targetingAllowed, + targetingAllowed }, update: { - targetingAllowed, + targetingAllowed }, where: { - email, - }, + email + } }); const message: z.infer = { - message: `${email} subscribed!`, + message: `${email} subscribed!` }; return new Response(JSON.stringify(message), { status: 200 }); } diff --git a/app/api/unsubscribe/route.ts b/app/api/unsubscribe/route.ts index dc46f6c..68798a8 100644 --- a/app/api/unsubscribe/route.ts +++ b/app/api/unsubscribe/route.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { fromZodError } from 'zod-validation-error'; import prisma from '../../../prisma/prisma'; import { ResponseSchema, UnsubscribeFormSchema } from '../../utils/types'; @@ -7,7 +8,8 @@ export async function POST(request: Request) { const body = await request.json(); const validation = UnsubscribeFormSchema.safeParse(body); if (!validation.success) { - return new Response('Bad request', { status: 400 }); + const message = fromZodError(validation.error); + return new Response(message.message, { status: 400 }); } const { email } = validation.data; @@ -15,15 +17,15 @@ export async function POST(request: Request) { try { await prisma.user.delete({ where: { - email, - }, + email + } }); } catch (err) { console.log(err); } const message: z.infer = { - message: `${email} unsubscribe!`, + message: `${email} unsubscribe!` }; return new Response(JSON.stringify(message), { status: 200 }); } diff --git a/app/globals.css b/app/globals.css index 81326e6..b3195ae 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,8 +2,18 @@ html, body { padding: 0; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, - Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; + font-family: + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Roboto, + Oxygen, + Ubuntu, + Cantarell, + Fira Sans, + Droid Sans, + Helvetica Neue, + sans-serif; background: #1e1e1e; min-height: 100vh; display: flex; @@ -28,8 +38,11 @@ body { border-radius: 10px; width: 85%; padding: 50px; - box-shadow: 0 54px 55px rgb(78 78 78 / 25%), 0 -12px 30px rgb(78 78 78 / 25%), - 0 4px 6px rgb(78 78 78 / 25%), 0 12px 13px rgb(78 78 78 / 25%), + box-shadow: + 0 54px 55px rgb(78 78 78 / 25%), + 0 -12px 30px rgb(78 78 78 / 25%), + 0 4px 6px rgb(78 78 78 / 25%), + 0 12px 13px rgb(78 78 78 / 25%), 0 -3px 5px rgb(78 78 78 / 25%); } diff --git a/app/layout.tsx b/app/layout.tsx index 40e027f..bbd36fc 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,22 +1,22 @@ -import type { Metadata } from 'next' -import { Inter } from 'next/font/google' -import './globals.css' +import type { Metadata } from 'next'; +import { Inter } from 'next/font/google'; +import './globals.css'; -const inter = Inter({ subsets: ['latin'] }) +const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { title: 'Create Next App', - description: 'Generated by create next app', -} + description: 'Generated by create next app' +}; export default function RootLayout({ - children, + children }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( - + {children} - ) + ); } diff --git a/app/page.tsx b/app/page.tsx index 29ca0c4..45955b2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -9,8 +9,8 @@ export default function Home() { return (

Home

- +
+
- +
+
-