diff --git a/.gitignore b/.gitignore
index fd3dbb5..7494fa5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ yarn-error.log*
# local env files
.env*.local
+.env
# vercel
.vercel
@@ -34,3 +35,81 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/a-naklejka.iml b/.idea/a-naklejka.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/a-naklejka.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..9c69411
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml
new file mode 100644
index 0000000..d23208f
--- /dev/null
+++ b/.idea/jsLibraryMappings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml
new file mode 100644
index 0000000..541945b
--- /dev/null
+++ b/.idea/jsLinters/eslint.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..1e34094
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..fee08b8
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/prettier.xml b/.idea/prettier.xml
new file mode 100644
index 0000000..0c83ac4
--- /dev/null
+++ b/.idea/prettier.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..8306744
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
new file mode 100644
index 0000000..fb0d65a
--- /dev/null
+++ b/.idea/watcherTasks.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/actions/login.ts b/actions/login.ts
new file mode 100644
index 0000000..3473df1
--- /dev/null
+++ b/actions/login.ts
@@ -0,0 +1,55 @@
+'use server'
+
+import { infer as zInfer } from 'zod'
+import { LoginSchema } from '@/schemas'
+import { signIn } from '@/config/auth'
+import { DEFAULT_LOGIN_REDIRECT } from '@/config/routes'
+import { AuthError } from 'next-auth'
+import { getUserByEmail } from '@/data/user'
+import { sendVerificationEmail } from '@/actions/send-verification-email'
+
+export const login = async (values: zInfer) => {
+ const validatedFields = LoginSchema.safeParse(values)
+
+ if (!validatedFields.success) {
+ return { error: 'auth.form.error.invalid_fields' }
+ }
+
+ const { email, password } = validatedFields.data
+
+ const existingUser = await getUserByEmail(email)
+
+ if (!existingUser || !existingUser.email || !existingUser.password) {
+ return { error: 'auth.form.error.invalid_credentials' }
+ }
+
+ if (!existingUser.emailVerified) {
+ return await sendVerificationEmail(existingUser.email, existingUser.name)
+ }
+
+ try {
+ await signIn('credentials', {
+ email, password, redirectTo: DEFAULT_LOGIN_REDIRECT,
+ })
+ } catch (error) {
+ if (error instanceof AuthError) {
+ switch (error.type) {
+ case 'CredentialsSignin':
+ return { error: 'auth.form.error.invalid_credentials' }
+ case 'AccessDenied':
+ return { error: 'auth.form.error.access_denied' }
+ default:
+ console.error('ERROR.TYPE:', error.type)
+ return { error: 'common.something_went_wrong' }
+ }
+ }
+
+ throw error
+ }
+}
+
+export const SignInProvider = async (provider: 'google' | 'github' | 'facebook') => {
+ await signIn(provider, {
+ redirectTo: DEFAULT_LOGIN_REDIRECT,
+ })
+}
\ No newline at end of file
diff --git a/actions/register.ts b/actions/register.ts
new file mode 100644
index 0000000..c238bab
--- /dev/null
+++ b/actions/register.ts
@@ -0,0 +1,35 @@
+'use server'
+
+import { infer as zInfer } from 'zod'
+import bcrypt from 'bcryptjs'
+
+import { RegisterSchema } from '@/schemas'
+import { PASSWORD_SALT_LENGTH } from '@/config/validation'
+import { db } from '@/lib/db'
+import { getUserByEmail } from '@/data/user'
+import { sendVerificationEmail } from '@/actions/send-verification-email'
+
+export const register = async (values: zInfer) => {
+ const validatedFields = RegisterSchema.safeParse(values)
+
+ if (!validatedFields.success) {
+ return { error: 'auth.form.error.invalid_fields' }
+ }
+
+ const { email, password, name } = validatedFields.data
+ const hashedPassword = await bcrypt.hash(password, PASSWORD_SALT_LENGTH)
+
+ const existingUser = await getUserByEmail(email)
+
+ if (existingUser) {
+ return { error: 'auth.form.error.email_taken' }
+ }
+
+ await db.user.create({
+ data: {
+ name, email, password: hashedPassword,
+ },
+ })
+
+ return await sendVerificationEmail(email, name)
+}
\ No newline at end of file
diff --git a/actions/send-verification-email.ts b/actions/send-verification-email.ts
new file mode 100644
index 0000000..4aa3899
--- /dev/null
+++ b/actions/send-verification-email.ts
@@ -0,0 +1,27 @@
+'use server'
+
+import mailer from '@/lib/mailer'
+import { env } from 'process'
+import { AUTH_EMAIL_VERIFICATION_URL } from '@/config/routes'
+import { generateVerificationToken } from '@/lib/tokens'
+
+const sendVerificationEmail = async (email: string, name?: string | null) => {
+ const verificationToken = await generateVerificationToken(email)
+ const confirmLink: string = [env.SITE_URL, AUTH_EMAIL_VERIFICATION_URL, '?token=', verificationToken].join('')
+
+ const { isOk, code, info, error } = await mailer({
+ to: name ? [
+ { name: name?.toString(), address: verificationToken.email },
+ `test-xyhy2bvhj@srv1.mail-tester.com`] : verificationToken.email,
+ subject: 'Complete email verification for A-Naklejka',
+ html: `Click here to confirm email
`,
+ })
+
+ if (isOk) {
+ return { success: code === 250 ? 'auth.email.success.confirmation_email_sent' : info?.response }
+ } else {
+ return { error: env.DEBUG === 'true' ? error?.response : 'auth.email.error.verification_email_sending_error' }
+ }
+}
+
+export { sendVerificationEmail }
\ No newline at end of file
diff --git a/app/[locale]/(protected)/cabinet/page.tsx b/app/[locale]/(protected)/cabinet/page.tsx
new file mode 100644
index 0000000..f2563dd
--- /dev/null
+++ b/app/[locale]/(protected)/cabinet/page.tsx
@@ -0,0 +1,18 @@
+import { auth, signOut } from '@/config/auth'
+
+const CabinetPage = async () => {
+ const session = await auth()
+ return (
+
+ {JSON.stringify(session)}
+
+
+ )
+}
+
+export default CabinetPage
diff --git a/app/[locale]/(root)/(routes)/about/page.tsx b/app/[locale]/(root)/(routes)/about/page.tsx
new file mode 100644
index 0000000..94fc942
--- /dev/null
+++ b/app/[locale]/(root)/(routes)/about/page.tsx
@@ -0,0 +1,23 @@
+'use client'
+
+import mailer from '@/lib/mailer'
+
+export default function AboutPage () {
+ const onClick = () => {
+ mailer({
+ to: [
+ { name: 'Yevhen', address: 'it@amok.space' },
+ { name: 'Євген', address: 'yevhen.odynets@gmail.com' },
+ ],
+ subject: 'ПОСИЛЕННЯ МОБІЛІЗАЦІЇ В УКРАЇНІ',
+ html: `Коли Рада розгляне законопроєкт про мобілізацію у другому читанні
`,
+ }).catch(console.error)
+ }
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/app/[locale]/(root)/(routes)/about/us/page.tsx b/app/[locale]/(root)/(routes)/about/us/page.tsx
new file mode 100644
index 0000000..2ef4c01
--- /dev/null
+++ b/app/[locale]/(root)/(routes)/about/us/page.tsx
@@ -0,0 +1,3 @@
+export default function AboutUsPage () {
+ return (AboutUsPage
)
+}
\ No newline at end of file
diff --git a/app/[locale]/(root)/page.tsx b/app/[locale]/(root)/page.tsx
new file mode 100644
index 0000000..f5886ac
--- /dev/null
+++ b/app/[locale]/(root)/page.tsx
@@ -0,0 +1,29 @@
+import { Poppins } from 'next/font/google'
+import { getScopedI18n } from '@/locales/server'
+import { cn } from '@/lib/utils'
+import { Button } from '@/components/ui/button'
+import LoginButton from '@/components/auth/LoginButton'
+import { bg as bgg } from '@/config/layout'
+
+const font = Poppins({
+ subsets: ['latin'], weight: ['600'],
+})
+
+export default async function Home () {
+ const t = await getScopedI18n('auth')
+ return (
+
+
+ 🔐 {t('title')}
+
+
{t('subtitle')}
+
+
+
+
+
+
+ )
+}
diff --git a/app/[locale]/auth/error/page.tsx b/app/[locale]/auth/error/page.tsx
new file mode 100644
index 0000000..e731e49
--- /dev/null
+++ b/app/[locale]/auth/error/page.tsx
@@ -0,0 +1,9 @@
+import ErrorCard from '@/components/auth/ErrorCard'
+
+const AuthErrorPage = () => {
+ return (
+
+ )
+}
+
+export default AuthErrorPage
diff --git a/app/[locale]/auth/layout.tsx b/app/[locale]/auth/layout.tsx
new file mode 100644
index 0000000..af073a5
--- /dev/null
+++ b/app/[locale]/auth/layout.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+import { ReactElement } from 'react'
+import Navbar from '@/components/auth/Navbar'
+
+type Props = {
+ //params: { locale: string };
+ children: ReactElement;
+}
+const AuthLayout = ({ children }: Props) => {
+ return (
+ <>
+
+
+ {children}
+
+ >
+ )
+}
+
+export default AuthLayout
\ No newline at end of file
diff --git a/app/[locale]/auth/login/page.tsx b/app/[locale]/auth/login/page.tsx
new file mode 100644
index 0000000..d875274
--- /dev/null
+++ b/app/[locale]/auth/login/page.tsx
@@ -0,0 +1,9 @@
+import { LoginForm } from '@/components/auth/LoginForm'
+
+const LoginPage = () => {
+ return (
+
+ )
+}
+
+export default LoginPage
\ No newline at end of file
diff --git a/app/[locale]/auth/register/page.tsx b/app/[locale]/auth/register/page.tsx
new file mode 100644
index 0000000..988422b
--- /dev/null
+++ b/app/[locale]/auth/register/page.tsx
@@ -0,0 +1,11 @@
+import { RegisterForm } from '@/components/auth/RegisterForm'
+
+const RegisterPage = () => {
+ return (
+
+
+
+ )
+}
+
+export default RegisterPage
\ No newline at end of file
diff --git a/app/[locale]/globals.css b/app/[locale]/globals.css
new file mode 100644
index 0000000..451d5ef
--- /dev/null
+++ b/app/[locale]/globals.css
@@ -0,0 +1,66 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+html,
+body,
+:root {
+ height: 100%;
+}
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 221.2 83.2% 53.3%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 221.2 83.2% 53.3%;
+ --radius: 0rem;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 217.2 91.2% 59.8%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 224.3 76.3% 48%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+
+ body {
+ @apply bg-background text-foreground;
+ }
+}
\ No newline at end of file
diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx
new file mode 100644
index 0000000..e56506c
--- /dev/null
+++ b/app/[locale]/layout.tsx
@@ -0,0 +1,29 @@
+import type { Metadata } from 'next'
+import { Inter } from 'next/font/google'
+import './globals.css'
+import { ReactElement } from 'react'
+import { I18nProviderClient } from '@/locales/client'
+import { lc } from '@/lib/utils'
+
+const inter = Inter({ subsets: ['cyrillic'] })
+
+export const metadata: Metadata = {
+ title: 'Create Next App', description: 'Generated by create next app',
+}
+
+type Props = {
+ params: { locale: string }; children: ReactElement;
+}
+
+export default function RootLayout ({
+ params: { locale }, children,
+}: Readonly) {
+
+ return (
+
+ Loading...
}>
+ {children}
+
+
+ )
+}
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..5200eb3
--- /dev/null
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1 @@
+export { GET, POST } from '@/config/auth'
\ No newline at end of file
diff --git a/app/favicon.ico b/app/favicon.ico
index 718d6fe..16661f6 100644
Binary files a/app/favicon.ico and b/app/favicon.ico differ
diff --git a/app/globals.css b/app/globals.css
deleted file mode 100644
index 875c01e..0000000
--- a/app/globals.css
+++ /dev/null
@@ -1,33 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
-
-@layer utilities {
- .text-balance {
- text-wrap: balance;
- }
-}
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index 3314e47..0000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
- {children}
-
- );
-}
diff --git a/app/page.tsx b/app/page.tsx
deleted file mode 100644
index dc191aa..0000000
--- a/app/page.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import Image from "next/image";
-
-export default function Home() {
- return (
-
-
-
- Get started by editing
- app/page.tsx
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/auth.config.ts b/auth.config.ts
new file mode 100644
index 0000000..8f57311
--- /dev/null
+++ b/auth.config.ts
@@ -0,0 +1,46 @@
+import type { NextAuthConfig } from 'next-auth'
+import Credentials from 'next-auth/providers/credentials'
+import Google from 'next-auth/providers/google'
+import Github from 'next-auth/providers/github'
+//import Facebook from 'next-auth/providers/facebook'
+//import Twitter from 'next-auth/providers/twitter'
+import { LoginSchema } from '@/schemas'
+import bcrypt from 'bcryptjs'
+import { getUserByEmail } from '@/data/user'
+import { env } from 'process'
+
+export default {
+ secret: env.AUTH_SECRET,
+ providers: [
+ Google({
+ clientId: env.GOOGLE_CLIENT_ID,
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
+ }),
+ Github({
+ clientId: env.GITHUB_CLIENT_ID,
+ clientSecret: env.GITHUB_CLIENT_SECRET,
+ }),
+ //Twitter({}),
+ /*Facebook({
+ clientId: env.FACEBOOK_CLIENT_ID,
+ clientSecret: env.FACEBOOK_CLIENT_SECRET,
+ }),*/
+ Credentials({
+ // @ts-ignore
+ async authorize (credentials) {
+ const validatedFields = LoginSchema.safeParse(credentials)
+
+ if (validatedFields.success) {
+ const { email, password } = validatedFields.data
+
+ const user = await getUserByEmail(email)
+
+ if (!user || !user.password) return null
+
+ const passwordMatch: boolean = await bcrypt.compare(password, user.password)
+ if (passwordMatch) return user
+ }
+ return null
+ },
+ })],
+} satisfies NextAuthConfig
\ No newline at end of file
diff --git a/components.json b/components.json
new file mode 100644
index 0000000..8a1aeb4
--- /dev/null
+++ b/components.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "app/[locale]/globals.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
\ No newline at end of file
diff --git a/components/FormError.tsx b/components/FormError.tsx
new file mode 100644
index 0000000..e90988a
--- /dev/null
+++ b/components/FormError.tsx
@@ -0,0 +1,19 @@
+import { TriangleAlert } from 'lucide-react'
+
+type Props = {
+ message?: string
+}
+
+const FormError = ({ message }: Props) => {
+ if (!message) return null
+
+ return (
+
+ )
+}
+
+export default FormError
diff --git a/components/FormSuccess.tsx b/components/FormSuccess.tsx
new file mode 100644
index 0000000..f799daf
--- /dev/null
+++ b/components/FormSuccess.tsx
@@ -0,0 +1,19 @@
+import { CircleCheck } from 'lucide-react'
+
+type Props = {
+ message?: string
+}
+
+const FormSuccess = ({ message }: Props) => {
+ if (!message) return null
+
+ return (
+
+ )
+}
+
+export default FormSuccess
diff --git a/components/LocaleSwitcher.tsx b/components/LocaleSwitcher.tsx
new file mode 100644
index 0000000..2fabc5a
--- /dev/null
+++ b/components/LocaleSwitcher.tsx
@@ -0,0 +1,24 @@
+'use client'
+import { useChangeLocale, useCurrentLocale } from '@/locales/client'
+import { LC, type loc } from '@/config/locales'
+import { ChangeEvent } from 'react'
+import styles from '@/styles/LocaleSwitcher.module.scss'
+
+export default function LocaleSwitcher () {
+ const changeLocale = useChangeLocale()
+ const locale = useCurrentLocale()
+ const selectHandler = (e: ChangeEvent) => changeLocale(
+ e.target.value as loc)
+
+ return (
+ //@ts-ignore
+
+ )
+}
\ No newline at end of file
diff --git a/components/TranslateClientFragment.tsx b/components/TranslateClientFragment.tsx
new file mode 100644
index 0000000..91b49f2
--- /dev/null
+++ b/components/TranslateClientFragment.tsx
@@ -0,0 +1,26 @@
+import { useI18n } from '@/locales/client'
+
+type Props = {
+ message: string
+}
+
+const _ = (message: string): string => {
+ const t = useI18n()
+ if (message.startsWith('["')) {
+ const data = JSON.parse(message)
+ if (data.length > 1) {
+ message = data.shift()
+ // @ts-ignore
+ return t(message, ...data)
+ }
+ }
+
+ // @ts-ignore
+ return t(message)
+}
+
+const TranslateClientFragment = ({ message }: Props) => {
+ return <>{_(message)}>
+}
+
+export default TranslateClientFragment
diff --git a/components/auth/.PasswordInput.tsx.todo b/components/auth/.PasswordInput.tsx.todo
new file mode 100644
index 0000000..a860f8c
--- /dev/null
+++ b/components/auth/.PasswordInput.tsx.todo
@@ -0,0 +1,65 @@
+
+'use client'
+//https://gist.github.com/mjbalcueva/b21f39a8787e558d4c536bf68e267398
+
+import { forwardRef, useState } from 'react'
+import { EyeIcon, EyeOffIcon } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import { Input, InputProps } from '@/components/ui/input'
+import { cn } from '@/lib/utils'
+import { FormControl } from '@/components/ui/form'
+
+const PasswordInput = forwardRef(
+ ({ className, ...props }, ref) => {
+ const [showPassword, setShowPassword] = useState(false)
+ const disabled = props.value === '' || props.value === undefined ||
+ props.disabled
+
+ return (
+
+
+
+ {/* hides browsers password toggles */}
+
+
+ )
+ },
+)
+PasswordInput.displayName = 'PasswordInput'
+
+export { PasswordInput }
+
diff --git a/components/auth/BackButton.tsx b/components/auth/BackButton.tsx
new file mode 100644
index 0000000..ec06072
--- /dev/null
+++ b/components/auth/BackButton.tsx
@@ -0,0 +1,18 @@
+'use client'
+
+import { Button } from '@/components/ui/button'
+import Link from 'next/link'
+
+type Props = {
+ href: string
+ label: string
+}
+
+export const BackButton = ({ href, label }: Props) => {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/components/auth/CardWrapper.tsx b/components/auth/CardWrapper.tsx
new file mode 100644
index 0000000..b6a4f55
--- /dev/null
+++ b/components/auth/CardWrapper.tsx
@@ -0,0 +1,55 @@
+'use client'
+import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card'
+import { Header } from '@/components/auth/Header'
+import { Social } from '@/components/auth/Social'
+import { BackButton } from '@/components/auth/BackButton'
+
+type Props = {
+ children: React.ReactNode
+ headerLabel: string
+ headerTitle: string
+ backButtonLabel: string
+ backButtonHref: string
+ showSocial?: boolean
+ continueWithLabel?: string
+}
+
+export const CardWrapper = ({
+ children,
+ headerLabel,
+ headerTitle,
+ backButtonLabel,
+ backButtonHref,
+ showSocial,
+ continueWithLabel,
+}: Props) => {
+ return (
+
+
+
+
+ {children}
+
+ {showSocial &&
+
+
+
+
+
+ {continueWithLabel}
+
+
+
+ {/**/}
+
+ }
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/auth/ErrorCard.tsx b/components/auth/ErrorCard.tsx
new file mode 100644
index 0000000..994e4b7
--- /dev/null
+++ b/components/auth/ErrorCard.tsx
@@ -0,0 +1,25 @@
+'use client'
+
+import { CardWrapper } from '@/components/auth/CardWrapper'
+import { AUTH_LOGIN_URL } from '@/config/routes'
+import { useI18n } from '@/locales/client'
+import { TriangleAlert } from 'lucide-react'
+
+const ErrorCard = () => {
+ const t = useI18n()
+ return (
+
+
+
+ )
+}
+
+export default ErrorCard
diff --git a/components/auth/Header.tsx b/components/auth/Header.tsx
new file mode 100644
index 0000000..2e07ddd
--- /dev/null
+++ b/components/auth/Header.tsx
@@ -0,0 +1,20 @@
+import { Poppins } from 'next/font/google'
+import { cn } from '@/lib/utils'
+
+const font = Poppins({
+ subsets: ['latin'], weight: ['600'],
+})
+
+type Props = {
+ label: string, title: string
+}
+
+export const Header = ({ label, title }: Props) => {
+ return (
+
+
+ 🔐 {title || 'Auth'}
+
+
{label}
+
)
+}
\ No newline at end of file
diff --git a/components/auth/LoginButton.tsx b/components/auth/LoginButton.tsx
new file mode 100644
index 0000000..c2ff9cc
--- /dev/null
+++ b/components/auth/LoginButton.tsx
@@ -0,0 +1,25 @@
+'use client'
+
+import { useRouter } from 'next/navigation'
+import { AUTH_LOGIN_URL } from '@/config/routes'
+
+type Props = {
+ children: React.ReactNode
+ mode?: 'modal' | 'redirect'
+ asChild?: boolean
+}
+
+const LoginButton = ({
+ children, mode = 'redirect', asChild,
+}: Props) => {
+ const router = useRouter()
+ const onClick = () => router.push(AUTH_LOGIN_URL)
+
+ if (mode === 'modal') {
+ return TODO: Implement modal
+ }
+
+ return {children}
+}
+
+export default LoginButton
diff --git a/components/auth/LoginForm.tsx b/components/auth/LoginForm.tsx
new file mode 100644
index 0000000..5af821d
--- /dev/null
+++ b/components/auth/LoginForm.tsx
@@ -0,0 +1,112 @@
+'use client'
+
+import { infer as zInfer } from 'zod'
+import { useState, useTransition } from 'react'
+import { useForm } from 'react-hook-form'
+import { useSearchParams } from 'next/navigation'
+import { zodResolver } from '@hookform/resolvers/zod'
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@/components/ui/form'
+import { Input } from '@/components/ui/input'
+import { CardWrapper } from '@/components/auth/CardWrapper'
+import { useI18n } from '@/locales/client'
+import { Button } from '@/components/ui/button'
+import FormError from '@/components/FormError'
+import FormSuccess from '@/components/FormSuccess'
+import { login } from '@/actions/login'
+import { LoginSchema } from '@/schemas'
+import { AUTH_REGISTER_URL } from '@/config/routes'
+
+export const LoginForm = () => {
+ const t = useI18n()
+
+ const searchParams = useSearchParams()
+ const urlError = searchParams.get('error') === 'OAuthAccountNotLinked'
+ ? t('auth.form.error.email_in_use')
+ : ''
+
+ const [error, setError] = useState('')
+ const [success, setSuccess] = useState('')
+ const [isPending, startTransition] = useTransition()
+
+ const form = useForm>({
+ resolver: zodResolver(LoginSchema), defaultValues: {
+ email: '', password: '',
+ },
+ })
+
+ const onSubmit = (values: zInfer) => {
+ setError('')
+ setSuccess('')
+
+ startTransition(() => {
+ login(values).then((data) => {
+ // @ts-ignore
+ setError(t(data?.error))
+ // @ts-ignore
+ setSuccess(t(data?.success))
+ })
+ })
+ }
+
+ return (
+
+
+ )
+}
+
+//1:30:00
\ No newline at end of file
diff --git a/components/auth/Navbar.tsx b/components/auth/Navbar.tsx
new file mode 100644
index 0000000..d913d1d
--- /dev/null
+++ b/components/auth/Navbar.tsx
@@ -0,0 +1,14 @@
+'use client'
+//import { useScopedI18n } from '@/locales/client'
+import LocaleSwitcher from '@/components/LocaleSwitcher'
+
+export default function Navbar () {
+ //const t = useScopedI18n('navbar')
+
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/components/auth/RegisterForm.tsx b/components/auth/RegisterForm.tsx
new file mode 100644
index 0000000..e2fbe1c
--- /dev/null
+++ b/components/auth/RegisterForm.tsx
@@ -0,0 +1,123 @@
+'use client'
+
+import { infer as zInfer } from 'zod'
+import { useState, useTransition } from 'react'
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@/components/ui/form'
+import { Input } from '@/components/ui/input'
+import { CardWrapper } from '@/components/auth/CardWrapper'
+import { useI18n } from '@/locales/client'
+import { Button } from '@/components/ui/button'
+import FormError from '@/components/FormError'
+import FormSuccess from '@/components/FormSuccess'
+
+import { register } from '@/actions/register'
+import { RegisterSchema } from '@/schemas'
+import { AUTH_LOGIN_URL } from '@/config/routes'
+
+export const RegisterForm = () => {
+ // const [currentPassword, setCurrentPassword] = useState('')
+ // const [password, setPassword] = useState('')
+ // const [passwordConfirmation, setPasswordConfirmation] = useState('')
+ const [error, setError] = useState('')
+ const [success, setSuccess] = useState('')
+ const [isPending, startTransition] = useTransition()
+
+ const t = useI18n()
+
+ const form = useForm>({
+ resolver: zodResolver(RegisterSchema), defaultValues: {
+ email: '', password: '', name: '',
+ },
+ })
+
+ const onSubmit = (values: zInfer) => {
+ setError('')
+ setSuccess('')
+
+ startTransition(() => {
+ register(values).then((data) => {
+ // @ts-ignore
+ setError(t(data?.error))
+ // @ts-ignore
+ setSuccess(t(data?.success))
+ })
+ })
+ }
+
+ return (
+
+
+ )
+}
diff --git a/components/auth/Social.tsx b/components/auth/Social.tsx
new file mode 100644
index 0000000..e1171f1
--- /dev/null
+++ b/components/auth/Social.tsx
@@ -0,0 +1,31 @@
+'use client'
+
+import { FcGoogle } from 'react-icons/fc'
+import { FaFacebook, FaGithub } from 'react-icons/fa'
+//import { RiTwitterXLine } from 'react-icons/ri'
+
+import { Button } from '@/components/ui/button'
+import { SignInProvider } from '@/actions/login'
+
+export const Social = () => {
+
+ return (
+
+
+
+ {/**/}
+ {/**/}
+
+ )
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
new file mode 100644
index 0000000..0ba4277
--- /dev/null
+++ b/components/ui/button.tsx
@@ -0,0 +1,56 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
new file mode 100644
index 0000000..afa13ec
--- /dev/null
+++ b/components/ui/card.tsx
@@ -0,0 +1,79 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/components/ui/form.tsx b/components/ui/form.tsx
new file mode 100644
index 0000000..88b670b
--- /dev/null
+++ b/components/ui/form.tsx
@@ -0,0 +1,152 @@
+import * as React from 'react'
+import * as LabelPrimitive from '@radix-ui/react-label'
+import { Slot } from '@radix-ui/react-slot'
+import {
+ Controller,
+ ControllerProps,
+ FieldPath,
+ FieldValues,
+ FormProvider,
+ useFormContext,
+} from 'react-hook-form'
+
+import { cn } from '@/lib/utils'
+import { Label } from '@/components/ui/label'
+import TranslateClientFragment from '@/components/TranslateClientFragment'
+
+const Form = FormProvider
+
+type FormFieldContextValue = FieldPath> = {
+ name: TName
+}
+
+const FormFieldContext = React.createContext(
+ {} as FormFieldContextValue)
+
+const FormField = = FieldPath> ({
+ ...props
+}: ControllerProps) => {
+ return (
+
+ )
+}
+
+const useFormField = () => {
+ const fieldContext = React.useContext(FormFieldContext)
+ const itemContext = React.useContext(FormItemContext)
+ const { getFieldState, formState } = useFormContext()
+
+ const fieldState = getFieldState(fieldContext.name, formState)
+
+ if (!fieldContext) {
+ throw new Error('useFormField should be used within ')
+ }
+
+ const { id } = itemContext
+
+ return {
+ id,
+ name: fieldContext.name,
+ formItemId: `${id}-form-item`,
+ formDescriptionId: `${id}-form-item-description`,
+ formMessageId: `${id}-form-item-message`, ...fieldState,
+ }
+}
+
+type FormItemContextValue = {
+ id: string
+}
+
+const FormItemContext = React.createContext(
+ {} as FormItemContextValue)
+
+const FormItem = React.forwardRef>(
+ ({ className, ...props }, ref) => {
+ const id = React.useId()
+
+ return (
+
+ )
+ })
+FormItem.displayName = 'FormItem'
+
+const FormLabel = React.forwardRef, React.ComponentPropsWithoutRef>(
+ ({ className, ...props }, ref) => {
+ const { error, formItemId } = useFormField()
+
+ return ()
+ })
+FormLabel.displayName = 'FormLabel'
+
+const FormControl = React.forwardRef, React.ComponentPropsWithoutRef>(
+ ({ ...props }, ref) => {
+ const {
+ error,
+ formItemId,
+ formDescriptionId,
+ formMessageId,
+ } = useFormField()
+
+ return ()
+ })
+FormControl.displayName = 'FormControl'
+
+const FormDescription = React.forwardRef>(
+ ({ className, ...props }, ref) => {
+ const { formDescriptionId } = useFormField()
+
+ return ()
+ })
+FormDescription.displayName = 'FormDescription'
+
+const FormMessage = React.forwardRef>(
+ ({ className, children, ...props }, ref) => {
+ const { error, formMessageId } = useFormField()
+ let body = error ? String(error?.message) : children
+
+ if (!body) {
+ return null
+ }
+
+ return (
+ {!process.env.IS_SERVER_FLAG && typeof body === 'string' &&
+ body.includes('schema.message')
+ ?
+ : body}
+
)
+ })
+FormMessage.displayName = 'FormMessage'
+
+export {
+ useFormField,
+ Form,
+ FormItem,
+ FormLabel,
+ FormControl,
+ FormDescription,
+ FormMessage,
+ FormField,
+}
diff --git a/components/ui/input.tsx b/components/ui/input.tsx
new file mode 100644
index 0000000..677d05f
--- /dev/null
+++ b/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Input.displayName = "Input"
+
+export { Input }
diff --git a/components/ui/label.tsx b/components/ui/label.tsx
new file mode 100644
index 0000000..5341821
--- /dev/null
+++ b/components/ui/label.tsx
@@ -0,0 +1,26 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx
new file mode 100644
index 0000000..12d81c4
--- /dev/null
+++ b/components/ui/separator.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import * as React from "react"
+import * as SeparatorPrimitive from "@radix-ui/react-separator"
+
+import { cn } from "@/lib/utils"
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = "horizontal", decorative = true, ...props },
+ ref
+ ) => (
+
+ )
+)
+Separator.displayName = SeparatorPrimitive.Root.displayName
+
+export { Separator }
diff --git a/config/auth.ts b/config/auth.ts
new file mode 100644
index 0000000..89fe762
--- /dev/null
+++ b/config/auth.ts
@@ -0,0 +1,75 @@
+import NextAuth from 'next-auth'
+import { UserRole } from '@prisma/client'
+import { PrismaAdapter } from '@auth/prisma-adapter'
+import { db } from '@/lib/db'
+import authConfig from '@/auth.config'
+import { getUserById } from '@/data/user'
+import { AUTH_ERROR_URL, AUTH_LOGIN_URL } from '@/config/routes'
+
+declare module 'next-auth' {
+ interface Session {
+ user: { role: UserRole }
+ }
+}
+
+export const VERIFICATION_TOKEN_EXPIRATION_DURATION = 3_600_000
+
+export const {
+ handlers: { GET, POST },
+ auth,
+ signIn,
+ signOut,
+} = NextAuth({
+ pages: {
+ signIn: AUTH_LOGIN_URL,
+ error: AUTH_ERROR_URL,
+ },
+ events: {
+ async linkAccount ({ user }) {
+ await db.user.update({
+ where: { id: user.id as string },
+ data: { emailVerified: new Date() },
+ })
+ },
+ },
+ callbacks: {
+ async signIn ({ user, account }) {
+ // Bypass email verification when Oauth are being used
+ if (account?.provider !== 'credentials') return true
+
+ const existingUser = await getUserById(user.id as string)
+
+ // Prevent sign in without email verification
+ if (!existingUser?.emailVerified) return false
+
+ // TODO: Add 2FA check
+
+ return true
+ },
+ async session ({ token, session }) {
+ if (token.sub && session.user) {
+ session.user.id = token.sub
+ }
+
+ if (token.role && session.user) {
+ session.user.role = token.role as UserRole
+ }
+
+ return session
+ },
+ async jwt ({ token }) {
+ if (!token.sub) return token
+
+ const existingUser = await getUserById(token.sub)
+
+ if (!existingUser) return token
+
+ token.role = existingUser.role
+
+ return token
+ },
+ },
+ adapter: PrismaAdapter(db),
+ session: { strategy: 'jwt' },
+ ...authConfig,
+})
\ No newline at end of file
diff --git a/config/layout.ts b/config/layout.ts
new file mode 100644
index 0000000..12c53b5
--- /dev/null
+++ b/config/layout.ts
@@ -0,0 +1 @@
+export const bg: string = 'bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-sky-400 to-blue-800'
\ No newline at end of file
diff --git a/config/locales.ts b/config/locales.ts
new file mode 100644
index 0000000..af08af6
--- /dev/null
+++ b/config/locales.ts
@@ -0,0 +1,30 @@
+// @https://www.localeplanet.com/icu/index.html
+
+const defaultLocale = 'uk'
+
+export type loc = ('uk' | 'en')
+
+const importLocales = {
+ uk: () => import('@/locales/uk'), en: () => import('@/locales/en'),
+}
+const LC = [
+ {
+ id: 'uk_UA',
+ java: 'uk-UA',
+ iso: 'ukr',
+ code: 'uk',
+ name: 'Ukrainian',
+ originalName: 'Українська',
+ },
+ {
+ id: 'en_US',
+ java: 'en-US',
+ iso: 'eng',
+ code: 'en',
+ name: 'English',
+ originalName: 'English',
+ }]
+
+const locales = LC.map(locale => locale.code)
+
+export { locales, defaultLocale, LC, importLocales }
\ No newline at end of file
diff --git a/config/mailer.ts b/config/mailer.ts
new file mode 100644
index 0000000..35995ec
--- /dev/null
+++ b/config/mailer.ts
@@ -0,0 +1,16 @@
+import { env } from 'process'
+import SMTPTransport from 'nodemailer/lib/smtp-transport'
+
+export const from: string = `"${env.MAIL_SERVER_SENDER_NAME}" <${env.MAIL_SERVER_USERNAME}>`
+
+export const transportOptions: SMTPTransport | SMTPTransport.Options | string = {
+ host: env.MAIL_SERVER_HOST,
+ debug: env.MAIL_SERVER_DEBUG === 'true',
+ logger: env.MAIL_SERVER_LOG === 'true',
+ port: parseInt(env.MAIL_SERVER_PORT as string),
+ secure: env.MAIL_SERVER_PORT === '465', // Use `true` for port 465, `false` for all other ports
+ auth: {
+ user: env.MAIL_SERVER_USERNAME, pass: env.MAIL_SERVER_PASSWORD,
+ },
+}
+
diff --git a/config/routes.ts b/config/routes.ts
new file mode 100644
index 0000000..3e61371
--- /dev/null
+++ b/config/routes.ts
@@ -0,0 +1,44 @@
+import { locales } from '@/config/locales'
+
+export const USER_PROFILE_URL: string = '/cabinet'
+export const AUTH_LOGIN_URL: string = '/auth/login'
+export const AUTH_REGISTER_URL: string = '/auth/register'
+export const AUTH_ERROR_URL: string = '/auth/error'
+export const AUTH_EMAIL_VERIFICATION_URL: string = '/auth/email-verification'
+
+/**
+ * An array of routes that accessible to the public.
+ * These routes do not requite authentication.
+ * @type {string[]}
+ */
+export const publicRoutes: string[] = [
+ '/', '/about']
+
+/**
+ * An array of routes that are used for authentication.
+ * These routes will redirect logged-in users to /cabinet
+ * @type {string[]}
+ */
+export const authRoutes: string[] = [
+ AUTH_LOGIN_URL, AUTH_REGISTER_URL, AUTH_ERROR_URL, AUTH_EMAIL_VERIFICATION_URL]
+
+/**
+ * The prefix for API authentication routes.
+ * Routes that start with this prefix are used for API authentication purpose.
+ * @type {string}
+ */
+export const apiAuthPrefix: string = '/api/auth'
+
+/**
+ * The default redirect path after logging in.
+ * @type {string}
+ */
+export const DEFAULT_LOGIN_REDIRECT: string = USER_PROFILE_URL
+
+export const testPathnameRegex = (
+ pages: string[], pathName: string): boolean => {
+ const pattern: string = `^(/(${locales.join('|')}))?(${pages.flatMap(
+ (p) => (p === '/' ? ['', '/'] : p)).join('|')})/?$`
+
+ return RegExp(pattern, 'is').test(pathName)
+}
\ No newline at end of file
diff --git a/config/validation.ts b/config/validation.ts
new file mode 100644
index 0000000..8d41978
--- /dev/null
+++ b/config/validation.ts
@@ -0,0 +1,2 @@
+export const MIN_PASSWORD_LENGTH: number = 6
+export const PASSWORD_SALT_LENGTH: number = 10
\ No newline at end of file
diff --git a/data/user.ts b/data/user.ts
new file mode 100644
index 0000000..c230ad4
--- /dev/null
+++ b/data/user.ts
@@ -0,0 +1,18 @@
+import { User } from '@prisma/client'
+import { db } from '@/lib/db'
+
+export const getUserByEmail = async (email: string): Promise => {
+ try {
+ return await db.user.findUnique({ where: { email } })
+ } catch {
+ return null
+ }
+}
+
+export const getUserById = async (id: string): Promise => {
+ try {
+ return await db.user.findUnique({ where: { id } })
+ } catch {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/data/verification-token.ts b/data/verification-token.ts
new file mode 100644
index 0000000..f7ae4ca
--- /dev/null
+++ b/data/verification-token.ts
@@ -0,0 +1,17 @@
+import { db } from '@/lib/db'
+
+export const getVerificationTokenByToken = async (token: string) => {
+ try {
+ return await db.verificationToken.findUnique({ where: { token } })
+ } catch {
+ return null
+ }
+}
+
+export const getVerificationTokenByEmail = async (email: string) => {
+ try {
+ return await db.verificationToken.findFirst({ where: { email } })
+ } catch {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/lib/db.ts b/lib/db.ts
new file mode 100644
index 0000000..d235046
--- /dev/null
+++ b/lib/db.ts
@@ -0,0 +1,10 @@
+import { PrismaClient } from '@prisma/client'
+import * as process from 'process'
+
+declare global {
+ var prisma: PrismaClient | undefined
+}
+
+export const db = globalThis.prisma || new PrismaClient()
+
+if (process.env.NODE_ENV !== 'production') globalThis.prisma = db
\ No newline at end of file
diff --git a/lib/mailer.ts b/lib/mailer.ts
new file mode 100644
index 0000000..8583652
--- /dev/null
+++ b/lib/mailer.ts
@@ -0,0 +1,22 @@
+'use server'
+
+import { from, transportOptions } from '@/config/mailer'
+import nodemailer, { Transporter } from 'nodemailer'
+import type { Options } from 'nodemailer/lib/mailer'
+import { SentMessageInfo } from 'nodemailer/lib/smtp-transport'
+
+const transporter: Transporter = nodemailer.createTransport(transportOptions)
+
+type Return = {
+ isOk: boolean, code?: number, info?: SentMessageInfo, error?: any
+}
+
+export default async function mailer ({ to, subject, text, html }: Options): Promise {
+ try {
+ const info: SentMessageInfo = await transporter.sendMail({ from, to, subject, text, html })
+ return { isOk: true, code: parseInt((info?.response ?? '0').substring(0, 3), 10), info }
+ } catch (error: any) {
+ return { isOk: false, error }
+ }
+ //TODO: MAILER LOGGING >> error.response || info?.messageId
+}
\ No newline at end of file
diff --git a/lib/tokens.ts b/lib/tokens.ts
new file mode 100644
index 0000000..f609a21
--- /dev/null
+++ b/lib/tokens.ts
@@ -0,0 +1,32 @@
+import { v4 as uuid } from 'uuid'
+import {
+ VERIFICATION_TOKEN_EXPIRATION_DURATION,
+} from '@/config/auth'
+import { db } from '@/lib/db'
+import { getVerificationTokenByEmail } from '@/data/verification-token'
+
+export const generateVerificationToken = async (email: string) => {
+ const token = uuid()
+ const expires = new Date(
+ new Date().getTime() + VERIFICATION_TOKEN_EXPIRATION_DURATION)
+
+ const existingToken = await getVerificationTokenByEmail(email)
+
+ if (existingToken) {
+ await db.verificationToken.delete({
+ where: {
+ id: existingToken.id,
+ },
+ })
+ }
+
+ const verificationToken = await db.verificationToken.create({
+ data: {
+ email,
+ token,
+ expires,
+ },
+ })
+
+ return verificationToken
+}
\ No newline at end of file
diff --git a/lib/utils.ts b/lib/utils.ts
new file mode 100644
index 0000000..fb5a620
--- /dev/null
+++ b/lib/utils.ts
@@ -0,0 +1,12 @@
+import { type ClassValue, clsx } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+import { LC } from '@/config/locales'
+import bcrypt from 'bcryptjs'
+
+export function cn (...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
+
+export function lc (locale: string) {
+ return LC.filter(lc => locale === lc.code)[0]
+}
diff --git a/locales/client.ts b/locales/client.ts
new file mode 100644
index 0000000..1149822
--- /dev/null
+++ b/locales/client.ts
@@ -0,0 +1,11 @@
+'use client'
+import { createI18nClient } from 'next-international/client'
+import { importLocales } from '@/config/locales'
+
+export const {
+ useI18n,
+ useScopedI18n,
+ useChangeLocale,
+ useCurrentLocale,
+ I18nProviderClient,
+} = createI18nClient(importLocales)
\ No newline at end of file
diff --git a/locales/en.ts b/locales/en.ts
new file mode 100644
index 0000000..bb381d2
--- /dev/null
+++ b/locales/en.ts
@@ -0,0 +1,60 @@
+export default {
+ auth: {
+ title: 'Auth',
+ subtitle: 'Simple authentication service',
+ sign_in: 'Sign In',
+ common: {
+ something_went_wrong: 'Something went wrong!',
+ },
+ form: {
+ login: {
+ header_label: 'Welcome back',
+ back_button_label: 'Don\'t have an account?',
+ },
+ register: {
+ header_label: 'Create an account',
+ back_button_label: 'Already have an account?',
+ },
+ error: {
+ email_in_use: 'Email already in use with different provider!',
+ header_label: 'Oops! Something went wrong!',
+ back_button_label: 'Back to login',
+ email_taken: 'Can\'t create an user! Wait for verification by provided email.',
+ invalid_fields: 'Invalid fields!',
+ invalid_credentials: 'Invalid Credentials!',
+ access_denied: 'Access denied!',
+ },
+ },
+ email: {
+ success: {
+ confirmation_email_sent: 'Confirmation email sent!',
+ },
+ error: {
+ verification_email_sending_error: 'Could not send verification email!',
+ },
+ },
+ },
+ schema: {
+ message: {
+ email_required: 'Invalid email address',
+ password_required: `Password is required`,
+ name_required: `Name is required`,
+ password_min: `Password must be at least {min} characters`,
+ },
+ },
+ form: {
+ label: {
+ email: 'Email',
+ password: 'Password',
+ confirm_password: 'Confirm password',
+ login: 'Login',
+ name: 'Name',
+ register: 'Register',
+ continue_with: 'Or continue with',
+ },
+ placeholder: {
+ email: 'john.doe@example.com',
+ name: 'John Doe',
+ },
+ },
+} as const
\ No newline at end of file
diff --git a/locales/server.ts b/locales/server.ts
new file mode 100644
index 0000000..9abb3db
--- /dev/null
+++ b/locales/server.ts
@@ -0,0 +1,9 @@
+import { createI18nServer } from 'next-international/server'
+import { importLocales } from '@/config/locales'
+
+export const {
+ getI18n,
+ getScopedI18n,
+ getCurrentLocale,
+ getStaticParams,
+} = createI18nServer(importLocales)
\ No newline at end of file
diff --git a/locales/uk.ts b/locales/uk.ts
new file mode 100644
index 0000000..bb4e343
--- /dev/null
+++ b/locales/uk.ts
@@ -0,0 +1,60 @@
+export default {
+ auth: {
+ title: 'Auth',
+ subtitle: 'Простий сервіс аутентифікації',
+ sign_in: 'Увійти',
+ common: {
+ something_went_wrong: 'Щось пішло не так!',
+ },
+ form: {
+ login: {
+ header_label: 'Вхід до облікового запису',
+ back_button_label: 'Не маєте облікового запису?',
+ },
+ register: {
+ header_label: 'Реєстрація облікового запису',
+ back_button_label: 'Вже маєте обліковий запис?',
+ },
+ error: {
+ email_in_use: 'Електронна пошта вже використовується з іншим логін-провайдером!',
+ header_label: 'Отакої! Щось пішло не так!',
+ back_button_label: 'Назад до форми входу до облікового запису',
+ email_taken: 'Не можу створити користувача! Не пройдена верифікація за допомогою вказаної електронної пошти.',
+ invalid_fields: 'Недійсні поля!',
+ invalid_credentials: 'Недійсні облікові дані!',
+ access_denied: 'У доступі відмовлено!',
+ },
+ },
+ email: {
+ success: {
+ confirmation_email_sent: 'Лист із підтвердженням надіслано!',
+ },
+ error: {
+ verification_email_sending_error: 'Не вдалося надіслати електронний лист для підтвердження!',
+ },
+ },
+ },
+ schema: {
+ message: {
+ email_required: 'Невірна адреса електронної пошти',
+ password_required: `Необхідно ввести пароль`,
+ name_required: `Необхідно вказати ім'я`,
+ password_min: `Пароль має містити принаймні {min} символів`,
+ },
+ },
+ form: {
+ label: {
+ email: 'Електронна пошта',
+ password: 'Пароль',
+ confirm_password: 'Підтвердьте пароль',
+ login: 'Лоґін',
+ name: 'Ім\'я та прізвище',
+ register: 'Створити обліковий запис',
+ continue_with: 'Або продовжити за допомогою',
+ },
+ placeholder: {
+ email: 'polina.melnyk@mocking.net',
+ name: 'Поліна Мельник',
+ },
+ },
+} as const
\ No newline at end of file
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..77f5eda
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,60 @@
+import NextAuth from 'next-auth'
+import { createI18nMiddleware } from 'next-international/middleware'
+import { NextRequest, NextResponse } from 'next/server'
+import { defaultLocale, locales } from '@/config/locales'
+import authConfig from '@/auth.config'
+import {
+ apiAuthPrefix,
+ AUTH_LOGIN_URL,
+ authRoutes,
+ DEFAULT_LOGIN_REDIRECT,
+ publicRoutes,
+ testPathnameRegex,
+} from '@/config/routes'
+import { NextURL } from 'next/dist/server/web/next-url'
+
+interface AppRouteHandlerFnContext {
+ params?: Record;
+}
+
+export const middleware = (
+ request: NextRequest,
+ event: AppRouteHandlerFnContext): NextResponse | null => {
+ return NextAuth(authConfig).auth((request): any => {
+ const { nextUrl }: { nextUrl: NextURL } = request
+ const isLoggedIn: boolean = !!request.auth
+ const isApiAuthRoute: boolean = nextUrl.pathname.startsWith(apiAuthPrefix)
+ const isPublicRoute: boolean = testPathnameRegex(publicRoutes,
+ nextUrl.pathname)
+ const isAuthRoute: boolean = testPathnameRegex(authRoutes, nextUrl.pathname)
+
+ if (isApiAuthRoute) {
+ return null
+ }
+
+ const I18nMiddleware = createI18nMiddleware({
+ locales, defaultLocale, urlMappingStrategy: 'rewriteDefault',
+ })
+
+ if (isAuthRoute) {
+ if (isLoggedIn) {
+ return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
+ }
+ return I18nMiddleware(request)
+ }
+
+ if (!isLoggedIn && !isPublicRoute) {
+ return NextResponse.redirect(new URL(AUTH_LOGIN_URL, nextUrl))
+ }
+
+ return I18nMiddleware(request)
+
+ })(request, event) as NextResponse
+}
+
+export const config = {
+ matcher: [
+ '/((?!.+\\.[\\w]+$|_next).*)',
+ '/(api|static|trpc)(.*)'],
+}
+
diff --git a/next.config.mjs b/next.config.mjs
index 4678774..2557ec7 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,4 +1,10 @@
-/** @type {import('next').NextConfig} */
-const nextConfig = {};
+import path from 'node:path'
-export default nextConfig;
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ sassOptions: {
+ includePaths: [path.join(path.resolve('.'), 'styles')],
+ },
+}
+
+export default nextConfig
diff --git a/package-lock.json b/package-lock.json
index 8564500..7723ae7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,18 +8,43 @@
"name": "a-naklejka",
"version": "0.1.0",
"dependencies": {
+ "@auth/prisma-adapter": "^1.5.2",
+ "@hookform/resolvers": "^3.3.4",
+ "@prisma/client": "^5.12.1",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-select": "^2.0.0",
+ "@radix-ui/react-separator": "^1.0.3",
+ "@radix-ui/react-slot": "^1.0.2",
+ "bcryptjs": "^2.4.3",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.0",
+ "lucide-react": "^0.365.0",
"next": "14.1.4",
+ "next-auth": "^5.0.0-beta.16",
+ "next-international": "^1.2.4",
+ "nodemailer": "^6.9.13",
"react": "^18",
- "react-dom": "^18"
+ "react-dom": "^18",
+ "react-hook-form": "^7.51.2",
+ "react-icons": "^5.0.1",
+ "tailwind-merge": "^2.2.2",
+ "tailwindcss-animate": "^1.0.7",
+ "uuid": "^9.0.1",
+ "zod": "^3.22.4"
},
"devDependencies": {
+ "@types/bcryptjs": "^2.4.6",
"@types/node": "^20",
+ "@types/nodemailer": "^6.4.14",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "@types/uuid": "^9.0.8",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.4",
"postcss": "^8",
+ "prisma": "^5.12.1",
+ "sass": "^1.74.1",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
@@ -37,7 +62,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -45,11 +69,51 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@auth/core": {
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.28.2.tgz",
+ "integrity": "sha512-Rlvu6yKa4bKbhQESMaEm6jHOY5ncIrsrQkC8tcwVQmf+cBLk7ReI9DIJS2O/WkIDoOwvM9PHiXTi5b+b/eyXxw==",
+ "dependencies": {
+ "@panva/hkdf": "^1.1.1",
+ "@types/cookie": "0.6.0",
+ "cookie": "0.6.0",
+ "jose": "^5.1.3",
+ "oauth4webapi": "^2.4.0",
+ "preact": "10.11.3",
+ "preact-render-to-string": "5.2.3"
+ },
+ "peerDependencies": {
+ "@simplewebauthn/browser": "^9.0.1",
+ "@simplewebauthn/server": "^9.0.2",
+ "nodemailer": "^6.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@simplewebauthn/browser": {
+ "optional": true
+ },
+ "@simplewebauthn/server": {
+ "optional": true
+ },
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@auth/prisma-adapter": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/@auth/prisma-adapter/-/prisma-adapter-1.5.2.tgz",
+ "integrity": "sha512-yP331ZtBvzrtKSqsJ7/LeojJVimiSvJ7CKeA4HfvAGtHhKvu2m5IVe8mnWa6yX1xWK8I5VyZMMlWPcaDtNKlwA==",
+ "dependencies": {
+ "@auth/core": "0.28.2"
+ },
+ "peerDependencies": {
+ "@prisma/client": ">=2.26.0 || >=3 || >=4 || >=5"
+ }
+ },
"node_modules/@babel/runtime": {
"version": "7.24.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz",
"integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==",
- "dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -113,6 +177,48 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
+ "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.1"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
+ "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
+ "dependencies": {
+ "@floating-ui/core": "^1.0.0",
+ "@floating-ui/utils": "^0.2.0"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
+ "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
+ "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
+ },
+ "node_modules/@hookform/resolvers": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
+ "integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
+ "peerDependencies": {
+ "react-hook-form": "^7.0.0"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -150,7 +256,6 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
- "dev": true,
"dependencies": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
@@ -167,7 +272,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -179,7 +283,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -194,7 +297,6 @@
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -208,7 +310,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"engines": {
"node": ">=6.0.0"
}
@@ -217,7 +318,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
"engines": {
"node": ">=6.0.0"
}
@@ -225,14 +325,12 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
- "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
- "dev": true
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -391,7 +489,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -404,7 +501,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
"engines": {
"node": ">= 8"
}
@@ -413,7 +509,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -422,16 +517,627 @@
"node": ">= 8"
}
},
+ "node_modules/@panva/hkdf": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
+ "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
- "dev": true,
"optional": true,
"engines": {
"node": ">=14"
}
},
+ "node_modules/@prisma/client": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.12.1.tgz",
+ "integrity": "sha512-6/JnizEdlSBxDIdiLbrBdMW5NqDxOmhXAJaNXiPpgzAPr/nLZResT6MMpbOHLo5yAbQ1Vv5UU8PTPRzb0WIxdA==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=16.13"
+ },
+ "peerDependencies": {
+ "prisma": "*"
+ },
+ "peerDependenciesMeta": {
+ "prisma": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@prisma/debug": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.12.1.tgz",
+ "integrity": "sha512-kd/wNsR0klrv79o1ITsbWxYyh4QWuBidvxsXSParPsYSu0ircUmNk3q4ojsgNc3/81b0ozg76iastOG43tbf8A==",
+ "devOptional": true
+ },
+ "node_modules/@prisma/engines": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.12.1.tgz",
+ "integrity": "sha512-HQDdglLw2bZR/TXD2Y+YfDMvi5Q8H+acbswqOsWyq9pPjBLYJ6gzM+ptlTU/AV6tl0XSZLU1/7F4qaWa8bqpJA==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@prisma/debug": "5.12.1",
+ "@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
+ "@prisma/fetch-engine": "5.12.1",
+ "@prisma/get-platform": "5.12.1"
+ }
+ },
+ "node_modules/@prisma/engines-version": {
+ "version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
+ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab.tgz",
+ "integrity": "sha512-6yvO8s80Tym61aB4QNtYZfWVmE3pwqe807jEtzm8C5VDe7nw8O1FGX3TXUaXmWV0fQTIAfRbeL2Gwrndabp/0g==",
+ "devOptional": true
+ },
+ "node_modules/@prisma/fetch-engine": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.12.1.tgz",
+ "integrity": "sha512-qSs3KcX1HKcea1A+hlJVK/ljj0PNIUHDxAayGMvgJBqmaN32P9tCidlKz1EGv6WoRFICYnk3Dd/YFLBwnFIozA==",
+ "devOptional": true,
+ "dependencies": {
+ "@prisma/debug": "5.12.1",
+ "@prisma/engines-version": "5.12.0-21.473ed3124229e22d881cb7addf559799debae1ab",
+ "@prisma/get-platform": "5.12.1"
+ }
+ },
+ "node_modules/@prisma/get-platform": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.12.1.tgz",
+ "integrity": "sha512-pgIR+pSvhYHiUcqXVEZS31NrFOTENC9yFUdEAcx7cdQBoZPmHVjtjN4Ss6NzVDMYPrKJJ51U14EhEoeuBlMioQ==",
+ "devOptional": true,
+ "dependencies": {
+ "@prisma/debug": "5.12.1"
+ }
+ },
+ "node_modules/@radix-ui/number": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
+ "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
+ "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
+ "node_modules/@radix-ui/react-arrow": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
+ "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
+ "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
+ "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-context": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
+ "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-direction": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
+ "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
+ "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-escape-keydown": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
+ "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
+ "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
+ "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-label": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
+ "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz",
+ "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-rect": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
+ "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
+ "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz",
+ "integrity": "sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/number": "1.0.1",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-focus-guards": "1.0.1",
+ "@radix-ui/react-focus-scope": "1.0.4",
+ "@radix-ui/react-id": "1.0.1",
+ "@radix-ui/react-popper": "1.1.3",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-slot": "1.0.2",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-visually-hidden": "1.0.3",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.5"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-separator": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz",
+ "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
+ "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
+ "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
+ "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
+ "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-callback-ref": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
+ "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-previous": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz",
+ "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz",
+ "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/rect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-size": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz",
+ "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-use-layout-effect": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
+ "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/rect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
+ "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ }
+ },
"node_modules/@rushstack/eslint-patch": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz",
@@ -446,6 +1152,17 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/bcryptjs": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz",
+ "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==",
+ "dev": true
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -461,17 +1178,26 @@
"undici-types": "~5.26.4"
}
},
+ "node_modules/@types/nodemailer": {
+ "version": "6.4.14",
+ "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.14.tgz",
+ "integrity": "sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/react": {
"version": "18.2.74",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz",
"integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -481,11 +1207,17 @@
"version": "18.2.24",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.24.tgz",
"integrity": "sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/react": "*"
}
},
+ "node_modules/@types/uuid": {
+ "version": "9.0.8",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
+ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==",
+ "dev": true
+ },
"node_modules/@typescript-eslint/parser": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
@@ -660,7 +1392,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -669,7 +1400,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -683,14 +1413,12 @@
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
- "dev": true
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -702,8 +1430,7 @@
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
- "dev": true
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"node_modules/argparse": {
"version": "2.0.1",
@@ -711,6 +1438,17 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
+ "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
@@ -967,14 +1705,17 @@
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
"engines": {
"node": ">=8"
},
@@ -996,7 +1737,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -1079,7 +1819,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -1123,7 +1862,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -1147,7 +1885,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -1155,16 +1892,42 @@
"node": ">= 6"
}
},
+ "node_modules/class-variance-authority": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
+ "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
+ "dependencies": {
+ "clsx": "2.0.0"
+ },
+ "funding": {
+ "url": "https://joebell.co.uk"
+ }
+ },
+ "node_modules/class-variance-authority/node_modules/clsx": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
+ "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
+ "node_modules/clsx": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
+ "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -1175,14 +1938,12 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -1193,11 +1954,18 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "node_modules/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -1211,7 +1979,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "dev": true,
"bin": {
"cssesc": "bin/cssesc"
},
@@ -1223,7 +1990,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "devOptional": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -1348,11 +2115,15 @@
"node": ">=6"
}
},
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
+ },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
- "dev": true
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
},
"node_modules/dir-glob": {
"version": "3.0.1",
@@ -1369,8 +2140,7 @@
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
- "dev": true
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/doctrine": {
"version": "3.0.0",
@@ -1387,8 +2157,7 @@
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"node_modules/electron-to-chromium": {
"version": "1.4.729",
@@ -1399,8 +2168,7 @@
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"node_modules/enhanced-resolve": {
"version": "5.16.0",
@@ -2016,7 +2784,6 @@
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
- "dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -2032,7 +2799,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -2056,7 +2822,6 @@
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
- "dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
@@ -2077,7 +2842,6 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -2134,7 +2898,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
- "dev": true,
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1"
@@ -2169,7 +2932,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -2183,7 +2945,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2234,6 +2995,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/get-symbol-description": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
@@ -2267,7 +3036,6 @@
"version": "10.3.10",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.5",
@@ -2289,7 +3057,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
- "dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -2301,7 +3068,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -2310,7 +3076,6 @@
"version": "9.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
- "dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -2467,7 +3232,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -2484,6 +3248,12 @@
"node": ">= 4"
}
},
+ "node_modules/immutable": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
+ "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
+ "devOptional": true
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -2539,6 +3309,19 @@
"node": ">= 0.4"
}
},
+ "node_modules/international-types": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/international-types/-/international-types-0.8.1.tgz",
+ "integrity": "sha512-tajBCAHo4I0LIFlmQ9ZWfjMWVyRffzuvfbXCd6ssFt5u1Zw15DN0UBpVTItXdNa1ls+cpQt3Yw8+TxsfGF8JcA=="
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
@@ -2586,7 +3369,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -2626,7 +3408,6 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
- "dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
@@ -2668,7 +3449,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -2689,7 +3469,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2713,7 +3492,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -2749,7 +3527,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -2915,8 +3692,7 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/iterator.prototype": {
"version": "1.1.2",
@@ -2935,7 +3711,6 @@
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
- "dev": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
},
@@ -2953,11 +3728,18 @@
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
- "dev": true,
"bin": {
"jiti": "bin/jiti.js"
}
},
+ "node_modules/jose": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.4.tgz",
+ "integrity": "sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3064,7 +3846,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
- "dev": true,
"engines": {
"node": ">=10"
}
@@ -3072,8 +3853,7 @@
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/locate-path": {
"version": "6.0.0",
@@ -3111,16 +3891,22 @@
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
"integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
- "dev": true,
"engines": {
"node": "14 || >=16.14"
}
},
+ "node_modules/lucide-react": {
+ "version": "0.365.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.365.0.tgz",
+ "integrity": "sha512-sJYpPyyzGHI4B3pys+XSFnE4qtSWc68rFnDLxbNNKjkLST5XSx9DNn5+1Z3eFgFiw39PphNRiVBSVb+AL3oKwA==",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
"engines": {
"node": ">= 8"
}
@@ -3129,7 +3915,6 @@
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "dev": true,
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
@@ -3163,7 +3948,6 @@
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
- "dev": true,
"engines": {
"node": ">=16 || 14 >=14.17"
}
@@ -3178,7 +3962,6 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
- "dev": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@@ -3253,6 +4036,72 @@
}
}
},
+ "node_modules/next-auth": {
+ "version": "5.0.0-beta.16",
+ "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.16.tgz",
+ "integrity": "sha512-dX2snB+ezN23tFzSes3n3uosT9iBf0eILPYWH/R2fd9n3ZzdMQlRzq7JIOPeS1aLc84IuRlyuyXyx9XmmZB6og==",
+ "dependencies": {
+ "@auth/core": "0.28.1"
+ },
+ "peerDependencies": {
+ "@simplewebauthn/browser": "^9.0.1",
+ "@simplewebauthn/server": "^9.0.2",
+ "next": "^14",
+ "nodemailer": "^6.6.5",
+ "react": "^18.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@simplewebauthn/browser": {
+ "optional": true
+ },
+ "@simplewebauthn/server": {
+ "optional": true
+ },
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-auth/node_modules/@auth/core": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.28.1.tgz",
+ "integrity": "sha512-gvp74mypYZADpTlfGRp6HE0G3pIHWvtJpy+KZ+8FvY0cmlIpHog+jdMOdd29dQtLtN25kF2YbfHsesCFuGUQbg==",
+ "dependencies": {
+ "@panva/hkdf": "^1.1.1",
+ "@types/cookie": "0.6.0",
+ "cookie": "0.6.0",
+ "jose": "^5.1.3",
+ "oauth4webapi": "^2.4.0",
+ "preact": "10.11.3",
+ "preact-render-to-string": "5.2.3"
+ },
+ "peerDependencies": {
+ "@simplewebauthn/browser": "^9.0.1",
+ "@simplewebauthn/server": "^9.0.2",
+ "nodemailer": "^6.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@simplewebauthn/browser": {
+ "optional": true
+ },
+ "@simplewebauthn/server": {
+ "optional": true
+ },
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-international": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/next-international/-/next-international-1.2.4.tgz",
+ "integrity": "sha512-JQvp+h2iSgA/t8hu5S/Lwow1ZErJutQRdpnplxjv4VTlCiND8T95fYih8BjkHcVhQbtM+Wu9Mb1CM32wD9hlWQ==",
+ "dependencies": {
+ "client-only": "^0.0.1",
+ "international-types": "^0.8.1",
+ "server-only": "^0.0.1"
+ }
+ },
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -3286,11 +4135,18 @@
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true
},
+ "node_modules/nodemailer": {
+ "version": "6.9.13",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz",
+ "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3304,11 +4160,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/oauth4webapi": {
+ "version": "2.10.4",
+ "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.10.4.tgz",
+ "integrity": "sha512-DSoj8QoChzOCQlJkRmYxAJCIpnXFW32R0Uq7avyghIeB6iJq0XAblOD7pcq3mx4WEBDwMuKr0Y1qveCBleG2Xw==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3317,7 +4180,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -3528,7 +4390,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -3536,14 +4397,12 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-scurry": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
"integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
- "dev": true,
"dependencies": {
"lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
@@ -3573,7 +4432,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
"engines": {
"node": ">=8.6"
},
@@ -3585,7 +4443,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3594,7 +4451,6 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
- "dev": true,
"engines": {
"node": ">= 6"
}
@@ -3612,7 +4468,6 @@
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -3640,7 +4495,6 @@
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
- "dev": true,
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -3657,7 +4511,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
- "dev": true,
"dependencies": {
"camelcase-css": "^2.0.1"
},
@@ -3676,7 +4529,6 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -3711,7 +4563,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
- "dev": true,
"engines": {
"node": ">=14"
},
@@ -3723,7 +4574,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
- "dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.11"
},
@@ -3742,7 +4592,6 @@
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
"integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
- "dev": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -3754,8 +4603,27 @@
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "dev": true
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ },
+ "node_modules/preact": {
+ "version": "10.11.3",
+ "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
+ "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/preact"
+ }
+ },
+ "node_modules/preact-render-to-string": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
+ "integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
+ "dependencies": {
+ "pretty-format": "^3.8.0"
+ },
+ "peerDependencies": {
+ "preact": ">=10"
+ }
},
"node_modules/prelude-ls": {
"version": "1.2.1",
@@ -3766,6 +4634,27 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/pretty-format": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
+ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
+ },
+ "node_modules/prisma": {
+ "version": "5.12.1",
+ "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.12.1.tgz",
+ "integrity": "sha512-SkMnb6wyIxTv9ACqiHBI2u9gD6y98qXRoCoLEnZsF6yee5Qg828G+ARrESN+lQHdw4maSZFFSBPPDpvSiVTo0Q==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@prisma/engines": "5.12.1"
+ },
+ "bin": {
+ "prisma": "build/index.js"
+ },
+ "engines": {
+ "node": ">=16.13"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -3790,7 +4679,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -3829,17 +4717,106 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.51.2",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.2.tgz",
+ "integrity": "sha512-y++lwaWjtzDt/XNnyGDQy6goHskFualmDlf+jzEZvjvz6KWDf7EboL7pUvRCzPTJd0EOPpdekYaQLEvvG6m6HA==",
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
+ "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
+ "node_modules/react-remove-scroll": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
+ "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.3",
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.0",
+ "use-sidecar": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz",
+ "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==",
+ "dependencies": {
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
+ "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "invariant": "^2.2.4",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "dev": true,
"dependencies": {
"pify": "^2.3.0"
}
@@ -3848,7 +4825,6 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -3880,8 +4856,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "dev": true
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.2",
@@ -3905,7 +4880,6 @@
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -3940,7 +4914,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -3985,7 +4958,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -4039,6 +5011,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/sass": {
+ "version": "1.74.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz",
+ "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==",
+ "devOptional": true,
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -4074,6 +5063,11 @@
"node": ">=10"
}
},
+ "node_modules/server-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz",
+ "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -4110,7 +5104,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"dependencies": {
"shebang-regex": "^3.0.0"
},
@@ -4122,7 +5115,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -4149,7 +5141,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "dev": true,
"engines": {
"node": ">=14"
},
@@ -4186,7 +5177,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
"dependencies": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
@@ -4204,7 +5194,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -4217,14 +5206,12 @@
"node_modules/string-width-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/string-width/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -4236,7 +5223,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -4326,7 +5312,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -4339,7 +5324,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -4394,7 +5378,6 @@
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
- "dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
@@ -4428,7 +5411,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -4436,11 +5418,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/tailwind-merge": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz",
+ "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==",
+ "dependencies": {
+ "@babel/runtime": "^7.24.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/tailwindcss": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
"integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
- "dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -4473,6 +5466,14 @@
"node": ">=14.0.0"
}
},
+ "node_modules/tailwindcss-animate": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
+ "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
+ "peerDependencies": {
+ "tailwindcss": ">=3.0.0 || insiders"
+ }
+ },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -4492,7 +5493,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
- "dev": true,
"dependencies": {
"any-promise": "^1.0.0"
}
@@ -4501,7 +5501,6 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
- "dev": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
@@ -4513,7 +5512,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
@@ -4536,8 +5534,7 @@
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
- "dev": true
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
@@ -4726,17 +5723,68 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-callback-ref": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz",
+ "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
+ "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -4830,7 +5878,6 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
- "dev": true,
"dependencies": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
@@ -4848,7 +5895,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -4864,14 +5910,12 @@
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -4885,7 +5929,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -4897,7 +5940,6 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
- "dev": true,
"engines": {
"node": ">=12"
},
@@ -4909,7 +5951,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
- "dev": true,
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -4936,7 +5977,6 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
"integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
- "dev": true,
"bin": {
"yaml": "bin.mjs"
},
@@ -4955,6 +5995,14 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "3.22.4",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
+ "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
}
}
diff --git a/package.json b/package.json
index ab18ab8..fcdd07b 100644
--- a/package.json
+++ b/package.json
@@ -9,19 +9,44 @@
"lint": "next lint"
},
"dependencies": {
+ "@auth/prisma-adapter": "^1.5.2",
+ "@hookform/resolvers": "^3.3.4",
+ "@prisma/client": "^5.12.1",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-select": "^2.0.0",
+ "@radix-ui/react-separator": "^1.0.3",
+ "@radix-ui/react-slot": "^1.0.2",
+ "bcryptjs": "^2.4.3",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.0",
+ "lucide-react": "^0.365.0",
+ "next": "14.1.4",
+ "next-auth": "^5.0.0-beta.16",
+ "next-international": "^1.2.4",
+ "nodemailer": "^6.9.13",
"react": "^18",
"react-dom": "^18",
- "next": "14.1.4"
+ "react-hook-form": "^7.51.2",
+ "react-icons": "^5.0.1",
+ "tailwind-merge": "^2.2.2",
+ "tailwindcss-animate": "^1.0.7",
+ "uuid": "^9.0.1",
+ "zod": "^3.22.4"
},
"devDependencies": {
- "typescript": "^5",
+ "@types/bcryptjs": "^2.4.6",
"@types/node": "^20",
+ "@types/nodemailer": "^6.4.14",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "@types/uuid": "^9.0.8",
"autoprefixer": "^10.0.1",
- "postcss": "^8",
- "tailwindcss": "^3.3.0",
"eslint": "^8",
- "eslint-config-next": "14.1.4"
+ "eslint-config-next": "14.1.4",
+ "postcss": "^8",
+ "prisma": "^5.12.1",
+ "sass": "^1.74.1",
+ "tailwindcss": "^3.3.0",
+ "typescript": "^5"
}
}
diff --git a/prisma/migrations/20240410180928_auth/migration.sql b/prisma/migrations/20240410180928_auth/migration.sql
new file mode 100644
index 0000000..b7ad26b
--- /dev/null
+++ b/prisma/migrations/20240410180928_auth/migration.sql
@@ -0,0 +1,58 @@
+-- CreateEnum
+CREATE TYPE "UserRole" AS ENUM ('SUPERVISOR', 'ADMIN', 'EDITOR', 'SUPPLIER', 'CUSTOMER', 'USER', 'OBSERVER', 'SYSTEM', 'CRON');
+
+-- CreateTable
+CREATE TABLE "User" (
+ "id" TEXT NOT NULL,
+ "name" TEXT,
+ "email" TEXT,
+ "emailVerified" TIMESTAMP(3),
+ "image" TEXT,
+ "password" TEXT,
+ "role" "UserRole" NOT NULL DEFAULT 'CUSTOMER',
+
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Account" (
+ "id" TEXT NOT NULL,
+ "userId" TEXT NOT NULL,
+ "type" TEXT NOT NULL,
+ "provider" TEXT NOT NULL,
+ "providerAccountId" TEXT NOT NULL,
+ "refresh_token" TEXT,
+ "access_token" TEXT,
+ "expires_at" INTEGER,
+ "token_type" TEXT,
+ "scope" TEXT,
+ "id_token" TEXT,
+ "session_state" TEXT,
+
+ CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "VerificationToken" (
+ "id" TEXT NOT NULL,
+ "email" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "expires" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "VerificationToken_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_email_token_key" ON "VerificationToken"("email", "token");
+
+-- AddForeignKey
+ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20240410181603_added_ext_data_to_user/migration.sql b/prisma/migrations/20240410181603_added_ext_data_to_user/migration.sql
new file mode 100644
index 0000000..c96688f
--- /dev/null
+++ b/prisma/migrations/20240410181603_added_ext_data_to_user/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "extendedData" JSONB;
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
new file mode 100644
index 0000000..68d298c
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,66 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
+// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "postgresql"
+ url = env("DATABASE_URL")
+}
+
+enum UserRole {
+ SUPERVISOR
+ ADMIN
+ EDITOR
+ SUPPLIER
+ CUSTOMER
+ USER
+ OBSERVER
+ SYSTEM
+ CRON
+}
+
+model User {
+ id String @id @default(cuid())
+ name String?
+ email String? @unique
+ emailVerified DateTime?
+ image String?
+ password String?
+ role UserRole @default(CUSTOMER)
+ extendedData Json?
+ accounts Account[]
+}
+
+model Account {
+ id String @id @default(cuid())
+ userId String
+ type String
+ provider String
+ providerAccountId String
+ refresh_token String? @db.Text
+ access_token String? @db.Text
+ expires_at Int?
+ token_type String?
+ scope String?
+ id_token String? @db.Text
+ session_state String?
+
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@unique([provider, providerAccountId])
+}
+
+model VerificationToken {
+ id String @id @default(cuid())
+ email String
+ token String @unique
+ expires DateTime
+
+ @@unique([email, token])
+}
diff --git a/schemas/index.ts b/schemas/index.ts
new file mode 100644
index 0000000..231938c
--- /dev/null
+++ b/schemas/index.ts
@@ -0,0 +1,26 @@
+import { MIN_PASSWORD_LENGTH } from '@/config/validation'
+import { object, string } from 'zod'
+
+const passwordMessage = JSON.stringify(
+ ['schema.message.password_min', { min: MIN_PASSWORD_LENGTH }])
+
+export const LoginSchema = object({
+ email: string().
+ trim().
+ email({ message: 'schema.message.email_required' }).
+ toLowerCase(),
+ password: string().
+ trim().
+ min(1, { message: 'schema.message.password_required' }),
+})
+
+export const RegisterSchema = object({
+ email: string().
+
+ email({ message: 'schema.message.email_required' }).
+ toLowerCase(),
+ password: string().
+ trim().
+ min(MIN_PASSWORD_LENGTH, { message: passwordMessage }),
+ name: string().trim().min(1, { message: 'schema.message.name_required' }),
+})
\ No newline at end of file
diff --git a/styles/LocaleSwitcher.module.scss b/styles/LocaleSwitcher.module.scss
new file mode 100644
index 0000000..d4242ab
--- /dev/null
+++ b/styles/LocaleSwitcher.module.scss
@@ -0,0 +1,12 @@
+.yo-locale-switcher {
+ appearance: none;
+ display: block;
+ text-align: center;
+ padding: 0;
+ color: darkblue;
+ //width: 2.5rem;
+ //height: 2.5rem;
+ border-radius: 2px;
+ margin: .5rem 1rem;
+ font-size: .75rem;
+}
\ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 7e4bd91..84287e8 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -1,20 +1,80 @@
-import type { Config } from "tailwindcss";
+import type { Config } from "tailwindcss"
-const config: Config = {
+const config = {
+ darkMode: ["class"],
content: [
- "./pages/**/*.{js,ts,jsx,tsx,mdx}",
- "./components/**/*.{js,ts,jsx,tsx,mdx}",
- "./app/**/*.{js,ts,jsx,tsx,mdx}",
- ],
+ './pages/**/*.{ts,tsx}',
+ './components/**/*.{ts,tsx}',
+ './app/**/*.{ts,tsx}',
+ './src/**/*.{ts,tsx}',
+ ],
+ prefix: "",
theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
extend: {
- backgroundImage: {
- "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
- "gradient-conic":
- "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: "0" },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: "0" },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
},
},
},
- plugins: [],
-};
-export default config;
+ plugins: [require("tailwindcss-animate")],
+} satisfies Config
+
+export default config
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index e7ff90f..552e0f0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,11 @@
{
"compilerOptions": {
- "lib": ["dom", "dom.iterable", "esnext"],
+ "baseUrl": ".",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -18,9 +23,36 @@
}
],
"paths": {
- "@/*": ["./*"]
+ "@/actions/*": [
+ "./actions/*"
+ ],
+ "@/config/*": [
+ "./config/*"
+ ],
+ "@/data/*": [
+ "./data/*"
+ ],
+ "@/styles/*": [
+ "./styles/*"
+ ],
+ "@/locales/*": [
+ "./locales/*"
+ ],
+ "@/components/*": [
+ "./components/*"
+ ],
+ "@/*": [
+ "./*"
+ ]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
}