diff --git a/.env b/.env index 5c7a024..2c27af1 100644 --- a/.env +++ b/.env @@ -1,16 +1,26 @@ -POSTGRES_URL="postgresql://postgres:PASSOWRD@localhost:5433/next-pizza?schema=public" +POSTGRES_URL="postgres://default:RKIHbVqs74ni@ep-round-violet-a2drsspn-pooler.eu-central-1.aws.neon.tech:5432/verceldb?sslmode=require" +POSTGRES_PRISMA_URL="postgres://default:RKIHbVqs74ni@ep-round-violet-a2drsspn-pooler.eu-central-1.aws.neon.tech:5432/verceldb?sslmode=require&pgbouncer=true&connect_timeout=15" +POSTGRES_URL_NO_SSL="postgres://default:RKIHbVqs74ni@ep-round-violet-a2drsspn-pooler.eu-central-1.aws.neon.tech:5432/verceldb" +POSTGRES_URL_NON_POOLING="postgres://default:RKIHbVqs74ni@ep-round-violet-a2drsspn.eu-central-1.aws.neon.tech:5432/verceldb?sslmode=require" +POSTGRES_USER="default" +POSTGRES_HOST="ep-round-violet-a2drsspn-pooler.eu-central-1.aws.neon.tech" +POSTGRES_PASSWORD="RKIHbVqs74ni" +POSTGRES_DATABASE="verceldb" + NEXT_PUBLIC_API_URL=/api -RESEND_API_KEY= -NEXTAUTH_SECRET= +RESEND_API_KEY=re_MaQpzHJj_4THr88nyQqhVxSmFsunBh5pF +NEXTAUTH_SECRET=5bNhbW+OgyVX7gMAZh3CalP7BJlYyXgtl7LPOveuHuQ= -YOOKASSA_STORE_ID= -YOOKASSA_API_KEY= +YOOKASSA_STORE_ID=464198 +YOOKASSA_API_KEY=test_DCclawG3sFjXeCKmweaJ3xjVSpSCkqquxCjdYxVzcZE YOOKASSA_CALLBACK_URL=http://localhost:3000/?paid -GITHUB_ID= -GITHUB_SECRET= +GITHUB_ID=Ov23liwDEw73WA1f6Y8L +GITHUB_SECRET=630df005edd445626f18427d61e639b18660d66a + +# когда будем пушить продакшен, надо указать другой URL в developer settings GOOGLE_CLIENT_ID="" GOOGLE_CLIENT_SECRET="" diff --git a/app/(checkout)/layout.tsx b/app/(checkout)/layout.tsx index bb5b1fd..aedbb33 100644 --- a/app/(checkout)/layout.tsx +++ b/app/(checkout)/layout.tsx @@ -3,7 +3,7 @@ import { Metadata } from 'next'; import { Suspense } from 'react'; export const metadata: Metadata = { - title: 'Next Pizza | Корзина', + title: 'Ножи СПБ | Корзина', description: 'Generated by create next app', }; diff --git a/app/(root)/layout.tsx b/app/(root)/layout.tsx index 9064557..09b36a7 100644 --- a/app/(root)/layout.tsx +++ b/app/(root)/layout.tsx @@ -3,7 +3,7 @@ import type { Metadata } from 'next'; import { Suspense } from 'react'; export const metadata: Metadata = { - title: 'Next Pizza | Главная', + title: 'Ножи СПБ | Главная', }; export default function HomeLayout({ diff --git a/app/(root)/page.tsx b/app/(root)/page.tsx index 4fff96f..947780c 100644 --- a/app/(root)/page.tsx +++ b/app/(root)/page.tsx @@ -15,7 +15,7 @@ export default async function Home({ searchParams }: { searchParams: GetSearchPa return ( <> - + category.products.length > 0)} /> diff --git a/app/actions.ts b/app/actions.ts index 715328e..e742a71 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,7 +1,8 @@ 'use server'; import { prisma } from '@/prisma/prisma-client'; -import { PayOrderTemplate } from '@/shared/components'; +import { PayOrderTemplate } from '@/shared/components/shared/email-temapltes'; + import { VerificationUserTemplate } from '@/shared/components/shared/email-temapltes/verification-user'; import { CheckoutFormValues } from '@/shared/constants'; import { createPayment, sendEmail } from '@/shared/lib'; @@ -103,7 +104,7 @@ export async function createOrder(data: CheckoutFormValues) { await sendEmail( data.email, - 'Next Pizza / Оплатите заказ #' + order.id, + 'Hoжы СПБ / Оплатите заказ #' + order.id, PayOrderTemplate({ orderId: order.id, totalAmount: order.totalAmount, @@ -156,13 +157,39 @@ export async function registerUser(body: Prisma.UserCreateInput) { }); if (user) { + // Проверяем, если пользователь существует и не верифицирован if (!user.verified) { - throw new Error('Почта не подтверждена'); + // Генерируем новый код подтверждения + const code = Math.floor(100000 + Math.random() * 900000).toString(); + + await prisma.verificationCode.upsert({ + where: { + userId: user.id, + }, + update: { + code, + }, + create: { + code, + userId: user.id, + }, + }); + + await sendEmail( + user.email, + 'Ножи СПБ / 📝 Повторное подтверждение регистрации', + VerificationUserTemplate({ + code, + }), + ); + + throw new Error('Почта не подтверждена. Новый код подтверждения отправлен.'); } - throw new Error('Пользователь уже существует'); + throw new Error('Пользователь уже существует и верифицирован.'); } + // Если пользователь не найден, создаем нового const createdUser = await prisma.user.create({ data: { fullName: body.fullName, @@ -182,7 +209,7 @@ export async function registerUser(body: Prisma.UserCreateInput) { await sendEmail( createdUser.email, - 'Next Pizza / 📝 Подтверждение регистрации', + 'Ножи СПБ / 📝 Подтверждение регистрации', VerificationUserTemplate({ code, }), diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index c7d7e30..bac6794 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -1,6 +1,8 @@ import NextAuth from 'next-auth'; import { authOptions } from '@/shared/constants/auth-options'; +//настройки выносим в отдельную переменнную + const handler = NextAuth(authOptions); export { handler as GET, handler as POST }; diff --git a/app/api/auth/verify/route.ts b/app/api/auth/verify/route.ts index a4de662..25def3a 100644 --- a/app/api/auth/verify/route.ts +++ b/app/api/auth/verify/route.ts @@ -3,13 +3,14 @@ import { NextRequest, NextResponse } from 'next/server'; export async function GET(req: NextRequest) { try { - // const code = req.nextUrl.searchParams.get('code'); - const code = ''; + // Получаем параметр 'code' из URL + const code = req.nextUrl.searchParams.get('code'); if (!code) { return NextResponse.json({ error: 'Неверный код' }, { status: 400 }); } + // Ищем verificationCode в базе данных по коду const verificationCode = await prisma.verificationCode.findFirst({ where: { code, @@ -20,24 +21,28 @@ export async function GET(req: NextRequest) { return NextResponse.json({ error: 'Неверный код' }, { status: 400 }); } + // Обновляем статус пользователя как верифицированный await prisma.user.update({ where: { id: verificationCode.userId, }, data: { - verified: new Date(), + verified: true, // Устанавливаем флаг верификации }, }); + // Удаляем использованный verificationCode await prisma.verificationCode.delete({ where: { id: verificationCode.id, }, }); + // Редиректим на главную с параметром ?verified return NextResponse.redirect(new URL('/?verified', req.url)); } catch (error) { console.error(error); console.log('[VERIFY_GET] Server error', error); + return NextResponse.json({ error: 'Server error' }, { status: 500 }); } } diff --git a/app/api/checkout/callback/route.ts b/app/api/checkout/callback/route.ts index 08d433f..ae884b4 100644 --- a/app/api/checkout/callback/route.ts +++ b/app/api/checkout/callback/route.ts @@ -36,7 +36,7 @@ export async function POST(req: NextRequest) { if (isSucceeded) { await sendEmail( order.email, - 'Next Pizza / Ваш заказ успешно оформлен 🎉', + 'Ножи СПБ / Ваш заказ успешно оформлен 🎉', OrderSuccessTemplate({ orderId: order.id, items }), ); } else { diff --git a/app/globals.css b/app/globals.css index b44f896..272e337 100644 --- a/app/globals.css +++ b/app/globals.css @@ -14,33 +14,58 @@ html { @layer base { :root { - --foreground: 20 14.3% 4.1%; - + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 20 14.3% 4.1%; - + --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 20 14.3% 4.1%; - - --primary: 22 100% 50%; - --primary-foreground: 60 9.1% 97.8%; - - --secondary: 32 100% 98%; - --secondary-foreground: 24 9.8% 10%; - - --muted: 60 4.8% 95.9%; - --muted-foreground: 25 5.3% 44.7%; - - --accent: 60 4.8% 95.9%; - --accent-foreground: 24 9.8% 10%; - + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 60 9.1% 97.8%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } - --border: 20 5.9% 90%; - --input: 0 0% 90%; - --ring: 24.6 95% 53.1%; - --radius: 18px; + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; } } @@ -70,25 +95,4 @@ html { .text-balance { text-wrap: balance; } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -#nprogress .bar { - @apply bg-primary !important; -} - -#nprogress .peg { - @apply shadow-md shadow-primary !important; -} - -#nprogress .spinner-icon { - @apply border-t-primary border-l-primary !important; -} +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 9018b22..1510ccb 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,7 +17,7 @@ export default function RootLayout({ return ( - + {children} diff --git a/next-pizza b/next-pizza new file mode 160000 index 0000000..03acc36 --- /dev/null +++ b/next-pizza @@ -0,0 +1 @@ +Subproject commit 03acc36ef9c8b84087affd78ce2d92ecbd25e9fe diff --git a/package-lock.json b/package-lock.json index 643e4d6..1ad8a7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "next-pizza", "version": "0.1.0", + "hasInstallScript": true, "dependencies": { "@hookform/resolvers": "^3.4.2", "@prisma/client": "^5.13.0", @@ -17,25 +18,24 @@ "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.0.2", "@types/bcrypt": "^5.0.2", - "@types/qs": "^6.9.15", + "@types/qs": "^6.9.16", "axios": "^1.6.8", "bcrypt": "^5.1.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.376.0", "next": "14.2.3", - "next-auth": "^4.24.7", + "next-auth": "^4.24.8", "nextjs-toploader": "^1.6.12", - "prisma": "^5.13.0", - "qs": "^6.12.1", + "qs": "^6.13.0", "react": "^18", "react-dadata": "^2.23.1", "react-dom": "^18", - "react-hook-form": "^7.51.5", + "react-hook-form": "^7.53.0", "react-hot-toast": "^2.4.1", "react-insta-stories": "^2.7.0", "react-use": "^17.5.0", - "resend": "^3.2.0", + "resend": "^3.5.0", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", @@ -50,6 +50,7 @@ "eslint": "^8", "eslint-config-next": "14.2.3", "postcss": "^8", + "prisma": "^5.19.1", "tailwindcss": "^3.4.1", "typescript": "^5" } @@ -551,43 +552,48 @@ } }, "node_modules/@prisma/debug": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", - "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==" + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.1.tgz", + "integrity": "sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==", + "devOptional": true }, "node_modules/@prisma/engines": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", - "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.1.tgz", + "integrity": "sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==", + "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/debug": "5.13.0", - "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "@prisma/fetch-engine": "5.13.0", - "@prisma/get-platform": "5.13.0" + "@prisma/debug": "5.19.1", + "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "@prisma/fetch-engine": "5.19.1", + "@prisma/get-platform": "5.19.1" } }, "node_modules/@prisma/engines-version": { - "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", - "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==" + "version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3.tgz", + "integrity": "sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==", + "devOptional": true }, "node_modules/@prisma/fetch-engine": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", - "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.1.tgz", + "integrity": "sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==", + "devOptional": true, "dependencies": { - "@prisma/debug": "5.13.0", - "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", - "@prisma/get-platform": "5.13.0" + "@prisma/debug": "5.19.1", + "@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3", + "@prisma/get-platform": "5.19.1" } }, "node_modules/@prisma/get-platform": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", - "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.1.tgz", + "integrity": "sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==", + "devOptional": true, "dependencies": { - "@prisma/debug": "5.13.0" + "@prisma/debug": "5.19.1" } }, "node_modules/@radix-ui/number": { @@ -1247,40 +1253,20 @@ } }, "node_modules/@react-email/render": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-0.0.12.tgz", - "integrity": "sha512-S8WRv/PqECEi6x0QJBj0asnAb5GFtJaHlnByxLETLkgJjc76cxMYDH4r9wdbuJ4sjkcbpwP3LPnVzwS+aIjT7g==", + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-0.0.16.tgz", + "integrity": "sha512-wDaMy27xAq1cJHtSFptp0DTKPuV2GYhloqia95ub/DH9Dea1aWYsbdM918MOc/b/HvVS3w1z8DWzfAk13bGStQ==", "dependencies": { "html-to-text": "9.0.5", "js-beautify": "^1.14.11", - "react": "18.2.0", - "react-dom": "18.2.0" + "react-promise-suspense": "0.3.4" }, "engines": { "node": ">=18.0.0" - } - }, - "node_modules/@react-email/render/node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@react-email/render/node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.2.0", + "react-dom": "^18.2.0" } }, "node_modules/@rushstack/eslint-patch": { @@ -1369,9 +1355,9 @@ "devOptional": true }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" }, "node_modules/@types/react": { "version": "18.3.1", @@ -4753,9 +4739,9 @@ } }, "node_modules/next-auth": { - "version": "4.24.7", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz", - "integrity": "sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==", + "version": "4.24.8", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.8.tgz", + "integrity": "sha512-SLt3+8UCtklsotnz2p+nB4aN3IHNmpsQFAZ24VLxGotWGzSxkBh192zxNhm/J5wgkcrDWVp0bwqvW0HksK/Lcw==", "dependencies": { "@babel/runtime": "^7.20.13", "@panva/hkdf": "^1.0.2", @@ -4768,12 +4754,16 @@ "uuid": "^8.3.2" }, "peerDependencies": { + "@auth/core": "0.34.2", "next": "^12.2.5 || ^13 || ^14", "nodemailer": "^6.6.5", "react": "^17.0.2 || ^18", "react-dom": "^17.0.2 || ^18" }, "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, "nodemailer": { "optional": true } @@ -5420,18 +5410,22 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, "node_modules/prisma": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", - "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.1.tgz", + "integrity": "sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==", + "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.13.0" + "@prisma/engines": "5.19.1" }, "bin": { "prisma": "build/index.js" }, "engines": { "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" } }, "node_modules/prop-types": { @@ -5464,9 +5458,9 @@ } }, "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { "side-channel": "^1.0.6" }, @@ -5535,18 +5529,18 @@ } }, "node_modules/react-hook-form": { - "version": "7.51.5", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.5.tgz", - "integrity": "sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==", + "version": "7.53.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", + "integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==", "engines": { - "node": ">=12.22.0" + "node": ">=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/react-hook-form" }, "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18" + "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "node_modules/react-hot-toast": { @@ -5577,6 +5571,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-promise-suspense": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz", + "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" + }, "node_modules/react-remove-scroll": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", @@ -5755,11 +5762,11 @@ } }, "node_modules/resend": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/resend/-/resend-3.2.0.tgz", - "integrity": "sha512-lDHhexiFYPoLXy7zRlJ8D5eKxoXy6Tr9/elN3+Vv7PkUoYuSSD1fpiIfa/JYXEWyiyN2UczkCTLpkT8dDPJ4Pg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/resend/-/resend-3.5.0.tgz", + "integrity": "sha512-bKu4LhXSecP6krvhfDzyDESApYdNfjirD5kykkT1xO0Cj9TKSiGh5Void4pGTs3Am+inSnp4dg0B5XzdwHBJOQ==", "dependencies": { - "@react-email/render": "0.0.12" + "@react-email/render": "0.0.16" }, "engines": { "node": ">=18" diff --git a/package.json b/package.json index bad8d78..19ea5ec 100644 --- a/package.json +++ b/package.json @@ -25,25 +25,24 @@ "@radix-ui/react-slider": "^1.1.2", "@radix-ui/react-slot": "^1.0.2", "@types/bcrypt": "^5.0.2", - "@types/qs": "^6.9.15", + "@types/qs": "^6.9.16", "axios": "^1.6.8", "bcrypt": "^5.1.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.376.0", "next": "14.2.3", - "next-auth": "^4.24.7", + "next-auth": "^4.24.8", "nextjs-toploader": "^1.6.12", - "prisma": "^5.13.0", - "qs": "^6.12.1", + "qs": "^6.13.0", "react": "^18", "react-dadata": "^2.23.1", "react-dom": "^18", - "react-hook-form": "^7.51.5", + "react-hook-form": "^7.53.0", "react-hot-toast": "^2.4.1", "react-insta-stories": "^2.7.0", "react-use": "^17.5.0", - "resend": "^3.2.0", + "resend": "^3.5.0", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "ts-node": "^10.9.2", @@ -58,6 +57,7 @@ "eslint": "^8", "eslint-config-next": "14.2.3", "postcss": "^8", + "prisma": "^5.19.1", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/prisma/constants.ts b/prisma/constants.ts index 4c06a2d..1bc9b6d 100644 --- a/prisma/constants.ts +++ b/prisma/constants.ts @@ -1,198 +1,146 @@ export const categories = [ { - name: 'Пиццы', + name: 'Складные', }, { - name: 'Завтрак', + name: 'Охотничьи', }, { - name: 'Закуски', + name: 'Мультитулы', }, { - name: 'Коктейли', + name: 'Метательные', }, { - name: 'Напитки', + name: 'Кухонные', }, ]; export const _ingredients = [ { - name: 'Сырный бортик', - price: 179, + name: 'Чехол', + price: 2000, imageUrl: - 'https://cdn.dodostatic.net/static/Img/Ingredients/99f5cb91225b4875bd06a26d2e842106.png', + 'https://kizlyar-moscow.ru/image/cache/catalog/li/default/e78d6167f0ebad9fbfd9f2ebcd2678ee-710x475-product_thumb.jpeg', }, { - name: 'Сливочная моцарелла', - price: 79, + name: 'Масло для ножей "Смажь Нож"', + price: 690, imageUrl: - 'https://cdn.dodostatic.net/static/Img/Ingredients/cdea869ef287426386ed634e6099a5ba.png', + 'https://img.nozhikov.ru/images/products/1/505/850027001/DSC05933-Edit.jpg', }, { - name: 'Сыры чеддер и пармезан', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA69C1FE796', + name: 'Заточка', + price: 1290, + imageUrl: 'https://profi-knife.ru/wp-content/uploads/2020/05/4342rb-800x793.jpg', }, { - name: 'Острый перец халапеньо', - price: 59, + name: 'Огниво', + price: 990, imageUrl: - 'https://cdn.dodostatic.net/static/Img/Ingredients/11ee95b6bfdf98fb88a113db92d7b3df.png', + 'https://ir.ozone.ru/s3/multimedia-1-g/c1000/6968271508.jpg', }, { - name: 'Нежный цыпленок', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A39D824A82E11E9AFA5B328D35A', + name: 'Компас', + price: 1390, + imageUrl: 'https://cdn1.ozone.ru/s3/multimedia-5/c600/6617448077.jpg', }, { - name: 'Шампиньоны', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA67259A324', + name: 'Компактный алмазный Брусок 100х16х10, зерно 50/40-20/14', + price: 1590, + imageUrl: 'https://img.nozhikov.ru/images/products/1/5476/514970980/DSC09432.jpg', }, { - name: 'Ветчина', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A39D824A82E11E9AFA61B9A8D61', - }, - { - name: 'Пикантная пепперони', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA6258199C3', - }, - { - name: 'Острая чоризо', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA62D5D6027', - }, - { - name: 'Маринованные огурчики', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A21DA51A81211E9EA89958D782B', - }, - { - name: 'Свежие томаты', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A39D824A82E11E9AFA7AC1A1D67', - }, - { - name: 'Красный лук', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA60AE6464C', - }, - { - name: 'Сочные ананасы', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A21DA51A81211E9AFA6795BA2A0', - }, - { - name: 'Итальянские травы', - price: 39, - imageUrl: - 'https://cdn.dodostatic.net/static/Img/Ingredients/370dac9ed21e4bffaf9bc2618d258734.png', - }, - { - name: 'Сладкий перец', - price: 59, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A22FA54A81411E9AFA63F774C1B', - }, - { - name: 'Кубики брынзы', - price: 79, - imageUrl: 'https://cdn.dodostatic.net/static/Img/Ingredients/000D3A39D824A82E11E9AFA6B0FFC349', - }, - { - name: 'Митболы', - price: 79, - imageUrl: - 'https://cdn.dodostatic.net/static/Img/Ingredients/b2f3a5d5afe44516a93cfc0d2ee60088.png', + name: 'Темляк малый с бусиной "Руна", зеленый', + price: 1110, + imageUrl: 'https://img.nozhikov.ru/images/products/1/3073/772451329/%D1%80%D1%83%D0%BD%D0%B01.jpg', }, ].map((obj, index) => ({ id: index + 1, ...obj })); export const products = [ { - name: 'Омлет с ветчиной и грибами', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7970321044479C1D1085457A36EB.webp', + name: 'Нож Honor Prime X, D2', + imageUrl: 'https://img.nozhikov.ru/images/products/1/2843/865028891/DSC08178-Edit.jpg', categoryId: 2, }, { - name: 'Омлет с пепперони', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE94ECF33B0C46BA410DEC1B1DD6F8.webp', + name: 'Нож Финский Ромб', + imageUrl: 'https://img.nozhikov.ru/images/products/1/2773/616057557/DSC09364-Edit.jpg', categoryId: 2, }, { - name: 'Кофе Латте', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61B0C26A3F85D97A78FEEE00AD.webp', + name: 'Нож "Басенджи"', + imageUrl: 'https://img.nozhikov.ru/images/products/1/2677/821463669/DSC00711-Edit.jpg', categoryId: 2, }, { - name: 'Дэнвич ветчина и сыр', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE796FF0059B799A17F57A9E64C725.webp', + name: 'Нож перочинный Victorinox Spartan', + imageUrl: 'https://img.nozhikov.ru/images/products/1/6199/286595127/nozh-perochinnyj-victorinox-spartan-stal-x55crmo14-rukoyat-cellidor-krasnyj-1.jpg', categoryId: 3, }, { - name: 'Куриные наггетсы', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D618B5C7EC29350069AE9532C6E.webp', + name: 'Нож перочинный Victorinox Huntsman', + imageUrl: 'https://img.nozhikov.ru/images/products/1/4981/285766517/nozh-perochinnyj-victorinox-huntsman-stal-x55crmo14-rukoyat-cellidor-kamuflyazh-4.jpg', categoryId: 3, }, { - name: 'Картофель из печи с соусом 🌱', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EED646A9CD324C962C6BEA78124F19.webp', + name: 'Мультитул HX OUTDOORS TD-03', + imageUrl: 'https://img.nozhikov.ru/images/products/1/847/839902031/DSC02665.jpg', categoryId: 3, }, { - name: 'Додстер', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE796F96D11392A2F6DD73599921B9.webp', + name: 'Мультитул MQ065, 13 функций', + imageUrl: 'https://img.nozhikov.ru/images/products/1/4460/878276972/DSC00131-Edit.jpg', categoryId: 3, }, { - name: 'Острый Додстер 🌶️🌶️', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE796FD3B594068F7A752DF8161D04.webp', + name: 'Нож перочинный Victorinox Climber', + imageUrl: 'https://img.nozhikov.ru/images/products/1/1269/284476661/nozh-perochinnyj-victorinox-climber-stal-x55crmo14-rukoyat-cellidor-krasnyj-2.jpg', categoryId: 3, }, { - name: 'Банановый молочный коктейль', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EEE20B8772A72A9B60CFB20012C185.webp', + name: 'Спортивный нож «Профессионал-4»', + imageUrl: 'https://img.nozhikov.ru/images/products/1/741/669115109/DSC04930-Edit-2.jpg', categoryId: 4, }, { - name: 'Карамельное яблоко молочный коктейль', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE79702E2A22E693D96133906FB1B8.webp', + name: 'Набор из 3 Спортивных ножей Кунай', + imageUrl: 'https://img.nozhikov.ru/images/products/1/4739/724316803/DSC04970-Edit.jpg', categoryId: 4, }, { - name: 'Молочный коктейль с печеньем Орео', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE796FA1F50F8F8111A399E4C1A1E3.webp', + name: 'Спортивный нож Импульс, Kizlyar Supreme', + imageUrl: 'https://img.nozhikov.ru/images/products/1/7504/729824592/DSC03609.jpg', categoryId: 4, }, { - name: 'Классический молочный коктейль 👶', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE796F93FB126693F96CB1D3E403FB.webp', + name: 'Спортивный нож Лидер', + imageUrl: 'https://img.nozhikov.ru/images/products/1/1716/736093876/DSC01677.jpg', categoryId: 4, }, { - name: 'Ирландский Капучино', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61999EBDA59C10E216430A6093.webp', + name: 'Кухонный нож универсал Tuotown', + imageUrl: 'https://img.nozhikov.ru/images/products/1/3382/908463414/185011.jpg', categoryId: 5, }, { - name: 'Кофе Карамельный капучино', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61AED6B6D4BFDAD4E58D76CF56.webp', + name: 'Сербский нож Fissman', + imageUrl: 'https://img.nozhikov.ru/images/products/1/391/660029831/DSC03404-Edit.jpg', categoryId: 5, }, { - name: 'Кофе Кокосовый латте', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61B19FA07090EE88B0ED347F42.webp', + name: 'Нож Овощной-2, сталь AUS-8', + imageUrl: 'https://img.nozhikov.ru/images/products/1/2728/772090536/DSC01088-Edit.jpg', categoryId: 5, }, { - name: 'Кофе Американо', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61B044583596548A59078BBD33.webp', + name: 'Нож "Шеф-де-Беф"', + imageUrl: 'https://img.nozhikov.ru/images/products/1/5389/583947533/DSC07598.jpg', categoryId: 5, }, { - name: 'Кофе Латте', - imageUrl: 'https://media.dodostatic.net/image/r:292x292/11EE7D61B0C26A3F85D97A78FEEE00AD.webp', + name: 'Нож кухонный Kanetsune Usabagata 165 мм', + imageUrl: 'https://img.nozhikov.ru/images/products/1/3966/545443710/kc-361-_2_.png', categoryId: 5, }, ]; diff --git a/prisma/migrations/20240928094222_change_verified_to_boolean/migration.sql b/prisma/migrations/20240928094222_change_verified_to_boolean/migration.sql new file mode 100644 index 0000000..02dba01 --- /dev/null +++ b/prisma/migrations/20240928094222_change_verified_to_boolean/migration.sql @@ -0,0 +1,214 @@ +-- CreateEnum +CREATE TYPE "OrderStatus" AS ENUM ('PENDING', 'SUCCEEDED', 'CANCELLED'); + +-- CreateEnum +CREATE TYPE "UserRole" AS ENUM ('USER', 'ADMIN'); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "fullName" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "role" "UserRole" NOT NULL DEFAULT 'USER', + "verified" BOOLEAN, + "provider" TEXT, + "providerId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Category" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Category_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Product" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "imageUrl" TEXT NOT NULL, + "categoryId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ProductItem" ( + "id" SERIAL NOT NULL, + "price" INTEGER NOT NULL, + "size" INTEGER, + "pizzaType" INTEGER, + "productId" INTEGER NOT NULL, + + CONSTRAINT "ProductItem_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Ingredient" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "price" INTEGER NOT NULL, + "imageUrl" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Ingredient_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Cart" ( + "id" SERIAL NOT NULL, + "userId" INTEGER, + "token" TEXT NOT NULL, + "totalAmount" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Cart_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CartItem" ( + "id" SERIAL NOT NULL, + "cartId" INTEGER NOT NULL, + "productItemId" INTEGER NOT NULL, + "quantity" INTEGER NOT NULL DEFAULT 1, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "CartItem_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Order" ( + "id" SERIAL NOT NULL, + "userId" INTEGER, + "token" TEXT NOT NULL, + "totalAmount" INTEGER NOT NULL, + "status" "OrderStatus" NOT NULL, + "paymentId" TEXT, + "items" JSONB NOT NULL, + "fullName" TEXT NOT NULL, + "email" TEXT NOT NULL, + "phone" TEXT NOT NULL, + "address" TEXT NOT NULL, + "comment" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Order_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "VerificationCode" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "code" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "VerificationCode_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Story" ( + "id" SERIAL NOT NULL, + "previewImageUrl" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Story_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "StoryItem" ( + "id" SERIAL NOT NULL, + "storyId" INTEGER NOT NULL, + "sourceUrl" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "StoryItem_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_IngredientToProduct" ( + "A" INTEGER NOT NULL, + "B" INTEGER NOT NULL +); + +-- CreateTable +CREATE TABLE "_CartItemToIngredient" ( + "A" INTEGER NOT NULL, + "B" INTEGER NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Category_name_key" ON "Category"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "Cart_userId_key" ON "Cart"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationCode_userId_key" ON "VerificationCode"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationCode_userId_code_key" ON "VerificationCode"("userId", "code"); + +-- CreateIndex +CREATE UNIQUE INDEX "_IngredientToProduct_AB_unique" ON "_IngredientToProduct"("A", "B"); + +-- CreateIndex +CREATE INDEX "_IngredientToProduct_B_index" ON "_IngredientToProduct"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_CartItemToIngredient_AB_unique" ON "_CartItemToIngredient"("A", "B"); + +-- CreateIndex +CREATE INDEX "_CartItemToIngredient_B_index" ON "_CartItemToIngredient"("B"); + +-- AddForeignKey +ALTER TABLE "Product" ADD CONSTRAINT "Product_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProductItem" ADD CONSTRAINT "ProductItem_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Cart" ADD CONSTRAINT "Cart_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CartItem" ADD CONSTRAINT "CartItem_cartId_fkey" FOREIGN KEY ("cartId") REFERENCES "Cart"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CartItem" ADD CONSTRAINT "CartItem_productItemId_fkey" FOREIGN KEY ("productItemId") REFERENCES "ProductItem"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Order" ADD CONSTRAINT "Order_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "VerificationCode" ADD CONSTRAINT "VerificationCode_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "StoryItem" ADD CONSTRAINT "StoryItem_storyId_fkey" FOREIGN KEY ("storyId") REFERENCES "Story"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_IngredientToProduct" ADD CONSTRAINT "_IngredientToProduct_A_fkey" FOREIGN KEY ("A") REFERENCES "Ingredient"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_IngredientToProduct" ADD CONSTRAINT "_IngredientToProduct_B_fkey" FOREIGN KEY ("B") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CartItemToIngredient" ADD CONSTRAINT "_CartItemToIngredient_A_fkey" FOREIGN KEY ("A") REFERENCES "CartItem"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CartItemToIngredient" ADD CONSTRAINT "_CartItemToIngredient_B_fkey" FOREIGN KEY ("B") REFERENCES "Ingredient"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fe97a4c..cdb48cc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -4,8 +4,8 @@ generator client { datasource db { provider = "postgresql" - url = env("POSTGRES_PRISMA_URL") // uses connection pooling - directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection + url = env("POSTGRES_PRISMA_URL") + directUrl = env("POSTGRES_URL_NON_POOLING") } model User { @@ -15,7 +15,7 @@ model User { email String @unique password String role UserRole @default(USER) - verified DateTime? + verified Boolean? @default(false) provider String? providerId String? @@ -64,6 +64,9 @@ model ProductItem { product Product @relation(fields: [productId], references: [id]) productId Int + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Ingredient { @@ -144,7 +147,6 @@ model VerificationCode { userId Int @unique code String - createdAt DateTime @default(now()) @@unique([userId, code]) diff --git a/prisma/seed.ts b/prisma/seed.ts index bf09cf8..a28d8f1 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -31,14 +31,14 @@ async function up() { fullName: 'User Test', email: 'user@test.ru', password: hashSync('111111', 10), - verified: new Date(), + verified: true, // Здесь теперь булевое значение role: 'USER', }, { fullName: 'Admin Admin', email: 'admin@test.ru', password: hashSync('111111', 10), - verified: new Date(), + verified: true, // Булевое значение role: 'ADMIN', }, ], @@ -58,36 +58,48 @@ async function up() { const pizza1 = await prisma.product.create({ data: { - name: 'Пепперони фреш', + name: 'Складной нож Honor Ajax', imageUrl: - 'https://media.dodostatic.net/image/r:233x233/11EE7D61304FAF5A98A6958F2BB2D260.webp', + 'https://img.nozhikov.ru/images/products/1/2925/789998445/DSC05498-Edit-Edit.jpg', categoryId: 1, ingredients: { - connect: _ingredients.slice(0, 5), + connect: _ingredients.slice(0, 7), }, }, }); const pizza2 = await prisma.product.create({ data: { - name: 'Сырная', + name: 'Нож Dagger', imageUrl: - 'https://media.dodostatic.net/image/r:233x233/11EE7D610CF7E265B7C72BE5AE757CA7.webp', + 'https://img.nozhikov.ru/images/products/1/4611/629502467/203678.2048x2048.jpg', categoryId: 1, ingredients: { - connect: _ingredients.slice(5, 10), + connect: _ingredients.slice(0, 7), }, }, }); const pizza3 = await prisma.product.create({ data: { - name: 'Чоризо фреш', + name: 'Складной нож Five Knives 15', imageUrl: - 'https://media.dodostatic.net/image/r:584x584/11EE7D61706D472F9A5D71EB94149304.webp', + 'https://img.nozhikov.ru/images/products/1/608/855433824/DSC09909-Edit.jpg', categoryId: 1, ingredients: { - connect: _ingredients.slice(10, 40), + connect: _ingredients.slice(0, 7), + }, + }, + }); + + const pizza4 = await prisma.product.create({ + data: { + name: 'Складной нож Five Knives 15', + imageUrl: + 'https://img.nozhikov.ru/images/products/1/608/855433824/DSC09909-Edit.jpg', + categoryId: 1, + ingredients: { + connect: _ingredients.slice(0, 7), }, }, }); @@ -163,27 +175,27 @@ async function up() { data: [ { previewImageUrl: - 'https://cdn.inappstory.ru/story/xep/xzh/zmc/cr4gcw0aselwvf628pbmj3j/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=3101815496', + 'https://static.insales-cdn.com/r/Igx7a8HKYP0/rs:fit:1000:0:1/q:100/plain/images/products/1/6970/904469306/DSC08191.jpg@webp', }, { previewImageUrl: - 'https://cdn.inappstory.ru/story/km2/9gf/jrn/sb7ls1yj9fe5bwvuwgym73e/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=3074015640', + 'https://static.insales-cdn.com/images/products/1/7133/880278493/DSC08293__2_.jpg', }, { previewImageUrl: - 'https://cdn.inappstory.ru/story/quw/acz/zf5/zu37vankpngyccqvgzbohj1/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=1336215020', + 'https://static.insales-cdn.com/images/products/1/7431/835755271/3__9_.jpg', }, { previewImageUrl: - 'https://cdn.inappstory.ru/story/7oc/5nf/ipn/oznceu2ywv82tdlnpwriyrq/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=38903958', + 'https://static.insales-cdn.com/r/AThmZSL1O1I/rs:fit:1000:0:1/q:100/plain/images/products/1/3196/915991676/DSC09476.jpg@webp', }, { previewImageUrl: - 'https://cdn.inappstory.ru/story/q0t/flg/0ph/xt67uw7kgqe9bag7spwkkyw/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=2941222737', + 'https://static.insales-cdn.com/r/qh75IE5DBks/rs:fit:1000:0:1/q:100/plain/images/products/1/6158/618502158/DSC03422.jpg@webp', }, { previewImageUrl: - 'https://cdn.inappstory.ru/story/lza/rsp/2gc/xrar8zdspl4saq4uajmso38/custom_cover/logo-350x440.webp?k=IgAAAAAAAAAE&v=4207486284', + 'https://static.insales-cdn.com/r/qK4L2mJ0YZI/rs:fit:1000:0:1/q:100/plain/images/products/1/4808/736613064/DSC09448__2_.jpg@webp', }, ], }); @@ -193,27 +205,32 @@ async function up() { { storyId: 1, sourceUrl: - 'https://cdn.inappstory.ru/file/dd/yj/sx/oqx9feuljibke3mknab7ilb35t.webp?k=IgAAAAAAAAAE', + 'https://static.insales-cdn.com/r/Igx7a8HKYP0/rs:fit:1000:0:1/q:100/plain/images/products/1/6970/904469306/DSC08191.jpg@webp', + }, + { + storyId: 1, + sourceUrl: + 'https://static.insales-cdn.com/images/products/1/7133/880278493/DSC08293__2_.jpg', }, { storyId: 1, sourceUrl: - 'https://cdn.inappstory.ru/file/jv/sb/fh/io7c5zarojdm7eus0trn7czdet.webp?k=IgAAAAAAAAAE', + 'https://static.insales-cdn.com/images/products/1/7431/835755271/3__9_.jpg', }, { storyId: 1, sourceUrl: - 'https://cdn.inappstory.ru/file/ts/p9/vq/zktyxdxnjqbzufonxd8ffk44cb.webp?k=IgAAAAAAAAAE', + 'https://static.insales-cdn.com/r/AThmZSL1O1I/rs:fit:1000:0:1/q:100/plain/images/products/1/3196/915991676/DSC09476.jpg@webp', }, { storyId: 1, sourceUrl: - 'https://cdn.inappstory.ru/file/ur/uq/le/9ufzwtpdjeekidqq04alfnxvu2.webp?k=IgAAAAAAAAAE', + 'https://static.insales-cdn.com/r/qh75IE5DBks/rs:fit:1000:0:1/q:100/plain/images/products/1/6158/618502158/DSC03422.jpg@webp', }, { storyId: 1, sourceUrl: - 'https://cdn.inappstory.ru/file/sy/vl/c7/uyqzmdojadcbw7o0a35ojxlcul.webp?k=IgAAAAAAAAAE', + 'https://static.insales-cdn.com/r/qK4L2mJ0YZI/rs:fit:1000:0:1/q:100/plain/images/products/1/4808/736613064/DSC09448__2_.jpg@webp', }, ], }); @@ -227,6 +244,8 @@ async function down() { await prisma.$executeRaw`TRUNCATE TABLE "Ingredient" RESTART IDENTITY CASCADE`; await prisma.$executeRaw`TRUNCATE TABLE "Product" RESTART IDENTITY CASCADE`; await prisma.$executeRaw`TRUNCATE TABLE "ProductItem" RESTART IDENTITY CASCADE`; + await prisma.$executeRaw`TRUNCATE TABLE "Story" RESTART IDENTITY CASCADE`; + await prisma.$executeRaw`TRUNCATE TABLE "StoryItem" RESTART IDENTITY CASCADE`; } async function main() { diff --git a/public/logo1.png b/public/logo1.png new file mode 100644 index 0000000..436953f Binary files /dev/null and b/public/logo1.png differ diff --git a/shared/components/shared/filters.tsx b/shared/components/shared/filters.tsx index b2ee464..1fcc1ef 100644 --- a/shared/components/shared/filters.tsx +++ b/shared/components/shared/filters.tsx @@ -30,7 +30,7 @@ export const Filters: React.FC = ({ className }) => { {/* Верхние чекбоксы */} - = ({ className }) => { { text: '30 см', value: '30' }, { text: '40 см', value: '40' }, ]} - /> + /> */} {/* Фильтр цен */} @@ -63,15 +63,15 @@ export const Filters: React.FC = ({ className }) => { type="number" placeholder="0" min={0} - max={1000} + max={10000} value={String(filters.prices.priceFrom)} onChange={(e) => filters.setPrices('priceFrom', Number(e.target.value))} /> filters.setPrices('priceTo', Number(e.target.value))} /> @@ -79,7 +79,7 @@ export const Filters: React.FC = ({ className }) => { = ({ className }) => { = ({ hasSearch = true, hasCart = true, clas {/* Левая часть */} - + - Next Pizza - вкусней уже некуда + Ножи СПБ + Доставка по РФ diff --git a/shared/components/shared/modals/auth-modal/auth-modal.tsx b/shared/components/shared/modals/auth-modal/auth-modal.tsx index 931e60a..d14a334 100644 --- a/shared/components/shared/modals/auth-modal/auth-modal.tsx +++ b/shared/components/shared/modals/auth-modal/auth-modal.tsx @@ -44,7 +44,7 @@ export const AuthModal: React.FC = ({ open, onClose }) => { } type="button" className="gap-2 h-12 p-2 flex-1"> - + GitHub diff --git a/shared/components/shared/pizza-image.tsx b/shared/components/shared/pizza-image.tsx index fbca741..f3b804f 100644 --- a/shared/components/shared/pizza-image.tsx +++ b/shared/components/shared/pizza-image.tsx @@ -20,8 +20,8 @@ export const PizzaImage: React.FC = ({ imageUrl, size, className }) => { })} /> - - + {/* + */} ); }; diff --git a/shared/components/shared/profile-button.tsx b/shared/components/shared/profile-button.tsx index 9a3d3b3..5b4bb36 100644 --- a/shared/components/shared/profile-button.tsx +++ b/shared/components/shared/profile-button.tsx @@ -13,7 +13,7 @@ export const ProfileButton: React.FC = ({ className, onClickSignIn }) => const { data: session } = useSession(); return ( - + {!session ? ( diff --git a/shared/components/shared/providers.tsx b/shared/components/shared/providers.tsx index 06bba26..aa00a46 100644 --- a/shared/components/shared/providers.tsx +++ b/shared/components/shared/providers.tsx @@ -8,9 +8,10 @@ import NextTopLoader from 'nextjs-toploader'; export const Providers: React.FC = ({ children }) => { return ( <> - {children} + {children} > ); }; +// SessionProvider внутри себя будет хранить контекст, который мы уже будем прокидыать в то место куда нам нужно \ No newline at end of file diff --git a/shared/components/shared/search-input.tsx b/shared/components/shared/search-input.tsx index 42e103a..84a4b2e 100644 --- a/shared/components/shared/search-input.tsx +++ b/shared/components/shared/search-input.tsx @@ -52,7 +52,7 @@ export const SearchInput: React.FC = ({ className }) => { setFocused(true)} value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} diff --git a/shared/constants/auth-options.ts b/shared/constants/auth-options.ts index 431be51..dac59bb 100644 --- a/shared/constants/auth-options.ts +++ b/shared/constants/auth-options.ts @@ -111,7 +111,7 @@ export const authOptions: AuthOptions = { email: user.email, fullName: user.name || 'User #' + user.id, password: hashSync(user.id.toString(), 10), - verified: new Date(), + verified: true, provider: account?.provider, providerId: account?.providerAccountId, }, diff --git a/shared/constants/pizza.ts b/shared/constants/pizza.ts index fcaf784..3abaca2 100644 --- a/shared/constants/pizza.ts +++ b/shared/constants/pizza.ts @@ -1,12 +1,12 @@ export const mapPizzaSize = { - 20: 'Маленькая', - 30: 'Средняя', - 40: 'Большая', + 20: 'Обычный', + 30: 'Средний', + 40: 'Большой', } as const; export const mapPizzaType = { - 1: 'традиционная', - 2: 'тонкая', + 1: 'стандартный', + 2: 'кастомный', } as const; export const pizzaSizes = Object.entries(mapPizzaSize).map(([value, name]) => ({ diff --git a/shared/lib/get-pizza-details.ts b/shared/lib/get-pizza-details.ts index 558551e..7b11428 100644 --- a/shared/lib/get-pizza-details.ts +++ b/shared/lib/get-pizza-details.ts @@ -10,7 +10,7 @@ export const getPizzaDetails = ( selectedIngredients: Set, ) => { const totalPrice = calcTotalPizzaPrice(type, size, items, ingredients, selectedIngredients); - const textDetaills = `${size} см, ${mapPizzaType[type]} пицца`; + const textDetaills = `${size} см, ${mapPizzaType[type]} нож`; - return { totalPrice, textDetaills }; + return { totalPrice, textDetaills}; };
вкусней уже некуда
Доставка по РФ