diff --git a/README.md b/README.md
index 65d5913..6070e46 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## To do
-- [ ] Add user creation in database
+- [ ] Get premium Vercel account for database
- [ ] Add middleware for authentication
- [ ] Add user profile and settings (i.e. language)
- [ ] Add user roles
diff --git a/app/api/protected/module/[id]/route.ts b/app/api/protected/customer-form/[id]/route.ts
similarity index 100%
rename from app/api/protected/module/[id]/route.ts
rename to app/api/protected/customer-form/[id]/route.ts
diff --git a/app/api/protected/customer-form/route.ts b/app/api/protected/customer-form/route.ts
new file mode 100644
index 0000000..4ac5a85
--- /dev/null
+++ b/app/api/protected/customer-form/route.ts
@@ -0,0 +1,53 @@
+import { getSession, withApiAuthRequired } from '@auth0/nextjs-auth0';
+import prisma from '@prisma/prisma';
+import { randomUUID } from 'crypto';
+import { NextResponse } from 'next/server';
+
+export const GET = withApiAuthRequired(async () => {
+ const session = await getSession();
+
+ console.log('GET', session?.user);
+
+ // const userModules = await prisma.user.findUniqueOrThrow({
+ // where: {
+ // email: session?.user.email
+ // },
+ // include: {
+ // CustomerForm: true
+ // }
+ // });
+
+ // const customerForms: CustomerForm[] = userModules.CustomerForm;
+
+ return NextResponse.json([]);
+});
+
+export const POST = withApiAuthRequired(async request => {
+ try {
+ const session = await getSession();
+
+ const body = await request.json();
+
+ console.log('POST', session?.user);
+
+ const newCustomerForm = await prisma.customerForm.create({
+ data: {
+ type: body.type,
+ text: body.text,
+ createdBy: {
+ connect: {
+ id: randomUUID(),
+ email: session?.user.email
+ }
+ }
+ }
+ });
+
+ return NextResponse.json({ success: true, data: newCustomerForm });
+ } catch (error) {
+ return NextResponse.json(
+ { success: false, message: 'Something went wrong.' },
+ { status: 500 }
+ );
+ }
+});
diff --git a/app/api/protected/module/route.ts b/app/api/protected/module/route.ts
deleted file mode 100644
index 107f3c2..0000000
--- a/app/api/protected/module/route.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-export async function GET(request: NextRequest, response: NextResponse) {
- return NextResponse.json('GET request');
-}
-
-export async function POST(request: NextRequest, response: NextResponse) {
- return NextResponse.json('POST request');
-}
diff --git a/app/costumer-form/[id]/page.tsx b/app/costumer-form/[id]/page.tsx
new file mode 100644
index 0000000..cf74286
--- /dev/null
+++ b/app/costumer-form/[id]/page.tsx
@@ -0,0 +1,9 @@
+'use client';
+
+export default function SingleCustomerForm({
+ params
+}: {
+ params: { id: string };
+}) {
+ return
Module {params.id}
;
+}
diff --git a/app/costumer-form/page.tsx b/app/costumer-form/page.tsx
new file mode 100644
index 0000000..5679702
--- /dev/null
+++ b/app/costumer-form/page.tsx
@@ -0,0 +1,106 @@
+'use client';
+
+import { Button } from '@components/Button';
+import { FormControl } from '@components/FormControl';
+import { FormMessage } from '@components/FormMessage';
+import { Input } from '@components/Input';
+import { FormField } from '@contexts/FormField/FormFieldProvider';
+import { FormItem } from '@contexts/FormItem/FormItemProvider';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { CustomerFormType } from '@prisma/client';
+import {
+ CustomerForm,
+ CustomerFormListSchema,
+ CustomerFormSchema
+} from '@utils/types';
+import axios from 'axios';
+import { useEffect, useState } from 'react';
+import { FormProvider, useForm } from 'react-hook-form';
+
+export default function Modules() {
+ const [modules, setModules] = useState([]);
+
+ const form = useForm({
+ resolver: zodResolver(CustomerFormSchema),
+ defaultValues: {
+ type: CustomerFormType.TYPE1,
+ text: ''
+ }
+ });
+
+ useEffect(() => {
+ (async () => {
+ const response = await axios.get('/api/protected/customer-form');
+
+ const validatedResponse = CustomerFormListSchema.safeParse(response.data);
+
+ if (!validatedResponse.success) {
+ console.error(validatedResponse.error);
+ return;
+ }
+
+ setModules(validatedResponse.data);
+ })();
+ }, []);
+
+ async function handleSubmit(values: CustomerForm) {
+ console.log('values', values);
+
+ try {
+ const response = await axios.post(
+ '/api/protected/customer-form',
+ {
+ type: CustomerFormType.TYPE1,
+ text: values.text
+ },
+ {
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }
+ );
+
+ const validatedResponse = CustomerFormSchema.safeParse(response.data);
+
+ if (!validatedResponse.success) {
+ console.error(validatedResponse.error);
+ return;
+ }
+
+ setModules([...modules, validatedResponse.data]);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ return (
+ <>
+ Modules
+ {modules &&
+ modules.map(module => (
+
+
{module.type}
+
{module.text}
+
+ ))}
+ Create Module
+
+
+
+ >
+ );
+}
diff --git a/app/module/[id]/page.tsx b/app/module/[id]/page.tsx
deleted file mode 100644
index ef34d7c..0000000
--- a/app/module/[id]/page.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function SpecificModule({ params }: { params: { id: string } }) {
- return Module {params.id}
;
-}
diff --git a/app/module/page.tsx b/app/module/page.tsx
deleted file mode 100644
index 2f59111..0000000
--- a/app/module/page.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function Module() {
- return Modules home
;
-}
diff --git a/app/profile/page.tsx b/app/profile/page.tsx
index 61d2ce0..6941aad 100644
--- a/app/profile/page.tsx
+++ b/app/profile/page.tsx
@@ -1,6 +1,7 @@
'use client';
import { withPageAuthRequired } from '@auth0/nextjs-auth0/client';
+import axios from 'axios';
import { useEffect, useState } from 'react';
export default withPageAuthRequired(function Profile() {
@@ -8,18 +9,16 @@ export default withPageAuthRequired(function Profile() {
useEffect(() => {
(async () => {
- const res = await fetch(
- `${window.location.origin}/api/protected/profile`
- );
- setUser(await res.json());
+ const response = await axios.get('/api/protected/profile');
+
+ setUser(response.data);
})();
}, []);
return (
-
+ <>
Profile (fetched from API)
- User
- {JSON.stringify(user, null, 2)}
-
+ {JSON.stringify(user, null, 2)}
+ >
);
});
diff --git a/components/Button.tsx b/components/Button.tsx
new file mode 100644
index 0000000..0e94c3b
--- /dev/null
+++ b/components/Button.tsx
@@ -0,0 +1,19 @@
+import { Slot } from '@radix-ui/react-slot';
+import { cn } from '@utils/cn';
+import * as React from 'react';
+
+export type ButtonProps = {
+ asChild?: boolean;
+} & React.ButtonHTMLAttributes;
+
+const Button = React.forwardRef(
+ ({ asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button';
+ return (
+
+ );
+ }
+);
+Button.displayName = 'Button';
+
+export { Button };
diff --git a/components/FormControl.tsx b/components/FormControl.tsx
new file mode 100644
index 0000000..91a5485
--- /dev/null
+++ b/components/FormControl.tsx
@@ -0,0 +1,26 @@
+import { useFormField } from '@hooks/useFormField';
+import { Slot } from '@radix-ui/react-slot';
+import * as React from 'react';
+
+export const FormControl = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ ...props }, ref) => {
+ const { error, formItemId, formDescriptionId, formMessageId } =
+ useFormField();
+
+ return (
+
+ );
+});
+FormControl.displayName = 'FormControl';
diff --git a/components/FormMessage.tsx b/components/FormMessage.tsx
new file mode 100644
index 0000000..635da3d
--- /dev/null
+++ b/components/FormMessage.tsx
@@ -0,0 +1,28 @@
+import { useFormField } from '@hooks/useFormField';
+import { cn } from '@utils/cn';
+import * as React from 'react';
+
+export const FormMessage = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, children, ...props }, ref) => {
+ const { error, formMessageId } = useFormField();
+ const body = error ? String(error?.message) : children;
+
+ return (
+ <>
+ {!body ? null : (
+
+ {body}
+
+ )}
+ >
+ );
+});
+
+FormMessage.displayName = 'FormMessage';
diff --git a/components/Input.tsx b/components/Input.tsx
new file mode 100644
index 0000000..ac87667
--- /dev/null
+++ b/components/Input.tsx
@@ -0,0 +1,22 @@
+import { cn } from '@utils/cn';
+import * as React from 'react';
+
+const Input = React.forwardRef<
+ HTMLInputElement,
+ React.InputHTMLAttributes
+>(({ className, type, ...props }, ref) => {
+ return (
+
+ );
+});
+Input.displayName = 'Input';
+
+export { Input };
diff --git a/contexts/FormField/FormFieldContext.ts b/contexts/FormField/FormFieldContext.ts
new file mode 100644
index 0000000..35793ad
--- /dev/null
+++ b/contexts/FormField/FormFieldContext.ts
@@ -0,0 +1,13 @@
+import React from 'react';
+import { FieldPath, FieldValues } from 'react-hook-form';
+
+interface FormFieldContextValue<
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath
+> {
+ name: TName;
+}
+
+export const FormFieldContext = React.createContext(
+ {} as FormFieldContextValue
+);
diff --git a/contexts/FormField/FormFieldProvider.tsx b/contexts/FormField/FormFieldProvider.tsx
new file mode 100644
index 0000000..53c3544
--- /dev/null
+++ b/contexts/FormField/FormFieldProvider.tsx
@@ -0,0 +1,20 @@
+import {
+ Controller,
+ ControllerProps,
+ FieldPath,
+ FieldValues
+} from 'react-hook-form';
+import { FormFieldContext } from './FormFieldContext';
+
+export const FormField = <
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath
+>({
+ ...props
+}: ControllerProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/contexts/FormItem/FormItemContext.ts b/contexts/FormItem/FormItemContext.ts
new file mode 100644
index 0000000..2fa8cdf
--- /dev/null
+++ b/contexts/FormItem/FormItemContext.ts
@@ -0,0 +1,9 @@
+import React from 'react';
+
+interface FormItemContextValue {
+ id: string;
+}
+
+export const FormItemContext = React.createContext(
+ {} as FormItemContextValue
+);
diff --git a/contexts/FormItem/FormItemProvider.tsx b/contexts/FormItem/FormItemProvider.tsx
new file mode 100644
index 0000000..324f877
--- /dev/null
+++ b/contexts/FormItem/FormItemProvider.tsx
@@ -0,0 +1,18 @@
+import { cn } from '@utils/cn';
+import * as React from 'react';
+import { FormItemContext } from './FormItemContext';
+
+export const FormItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const id = React.useId();
+
+ return (
+
+
+
+ );
+});
+
+FormItem.displayName = 'FormItem';
diff --git a/hooks/useFormField.tsx b/hooks/useFormField.tsx
new file mode 100644
index 0000000..90ee767
--- /dev/null
+++ b/hooks/useFormField.tsx
@@ -0,0 +1,29 @@
+import { FormFieldContext } from '@contexts/FormField/FormFieldContext';
+import { FormItemContext } from '@contexts/FormItem/FormItemContext';
+import * as React from 'react';
+import { useFormContext } from 'react-hook-form';
+
+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
+ };
+};
+
+export { useFormField };
diff --git a/middleware.ts b/middleware.ts
deleted file mode 100644
index c574192..0000000
--- a/middleware.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { withMiddlewareAuthRequired } from '@auth0/nextjs-auth0/edge';
-
-export default withMiddlewareAuthRequired();
-
-export const config = {
- matcher: ['/api/protected/:path*', '/module/:path*', '/user']
-};
diff --git a/package.json b/package.json
index 0608e4f..6e96566 100644
--- a/package.json
+++ b/package.json
@@ -19,15 +19,20 @@
},
"dependencies": {
"@auth0/nextjs-auth0": "^3.5.0",
+ "@hookform/resolvers": "^3.6.0",
"@prisma/client": "^5.6.0",
+ "@radix-ui/react-slot": "^1.1.0",
"@vercel/analytics": "^1.1.1",
+ "axios": "^1.7.2",
+ "clsx": "^2.1.1",
"next": "^14.1.0",
"postcss-nesting": "^12.0.2",
"react": "^18",
"react-dom": "^18",
- "react-hook-form": "^7.48.2",
+ "react-hook-form": "^7.52.0",
"resend": "^3.1.0",
- "zod": "^3.22.4"
+ "tailwind-merge": "^2.3.0",
+ "zod": "^3.23.8"
},
"devDependencies": {
"@commitlint/cli": "^18.4.3",
diff --git a/prisma/migrations/20240626211956_change_table_name/migration.sql b/prisma/migrations/20240626211956_change_table_name/migration.sql
new file mode 100644
index 0000000..e6fdb2f
--- /dev/null
+++ b/prisma/migrations/20240626211956_change_table_name/migration.sql
@@ -0,0 +1,36 @@
+/*
+ Warnings:
+
+ - You are about to drop the `Module` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- CreateEnum
+CREATE TYPE "CustomerFormType" AS ENUM ('TYPE1', 'TYPE2', 'TYPE3');
+
+-- DropForeignKey
+ALTER TABLE "Module" DROP CONSTRAINT "Module_createdById_fkey";
+
+-- DropTable
+DROP TABLE "Module";
+
+-- DropEnum
+DROP TYPE "ModuleType";
+
+-- CreateTable
+CREATE TABLE "CustomerForm" (
+ "id" DOUBLE PRECISION NOT NULL,
+ "type" "CustomerFormType" NOT NULL,
+ "text" TEXT NOT NULL,
+ "deleted" BOOLEAN NOT NULL DEFAULT false,
+ "createdById" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "CustomerForm_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "CustomerForm_id_key" ON "CustomerForm"("id");
+
+-- AddForeignKey
+ALTER TABLE "CustomerForm" ADD CONSTRAINT "CustomerForm_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/migrations/20240707152629_string_id_for_user/migration.sql b/prisma/migrations/20240707152629_string_id_for_user/migration.sql
new file mode 100644
index 0000000..7003068
--- /dev/null
+++ b/prisma/migrations/20240707152629_string_id_for_user/migration.sql
@@ -0,0 +1,13 @@
+/*
+ Warnings:
+
+ - The primary key for the `CustomerForm` table will be changed. If it partially fails, the table could be left without primary key constraint.
+
+*/
+-- DropIndex
+DROP INDEX "CustomerForm_id_key";
+
+-- AlterTable
+ALTER TABLE "CustomerForm" DROP CONSTRAINT "CustomerForm_pkey",
+ALTER COLUMN "id" SET DATA TYPE TEXT,
+ADD CONSTRAINT "CustomerForm_pkey" PRIMARY KEY ("id");
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 3b695cb..b9f27ad 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -8,29 +8,29 @@ datasource db {
directUrl = env("DATABASE_URL_NON_POOLING") // uses a direct connection
}
-enum ModuleType {
+enum CustomerFormType {
TYPE1
TYPE2
TYPE3
}
model User {
- id String @id @default(cuid())
- name String?
- email String @unique
- deleted Boolean @default(false)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- Module Module[]
+ id String @id @default(cuid())
+ name String?
+ email String @unique
+ deleted Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ CustomerForm CustomerForm[]
}
-model Module {
- id Float @id @unique
- type ModuleType
+model CustomerForm {
+ id String @id @default(cuid())
+ type CustomerFormType
text String
- deleted Boolean @default(false)
+ deleted Boolean @default(false)
createdById String
- createdBy User @relation(fields: [createdById], references: [id])
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
+ createdBy User @relation(fields: [createdById], references: [id])
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
}
diff --git a/tsconfig.json b/tsconfig.json
index bd52bbb..8919204 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,7 +20,11 @@
],
"paths": {
"@app/*": ["./app/*"],
- "@prisma/*": ["./prisma/*"]
+ "@prisma/*": ["./prisma/*"],
+ "@utils/*": ["./utils/*"],
+ "@contexts/*": ["./contexts/*"],
+ "@components/*": ["./components/*"],
+ "@hooks/*": ["./hooks/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
diff --git a/utils/cn.ts b/utils/cn.ts
new file mode 100644
index 0000000..9ad0df4
--- /dev/null
+++ b/utils/cn.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/utils/types.ts b/utils/types.ts
new file mode 100644
index 0000000..7d65365
--- /dev/null
+++ b/utils/types.ts
@@ -0,0 +1,24 @@
+import { z } from 'zod';
+
+export const UserSchema = z.object({
+ id: z.string(),
+ name: z.string(),
+ email: z.string().email(),
+ deleted: z.boolean(),
+ createdAt: z.date().transform(date => date.toISOString()),
+ updatedAt: z.date().transform(date => date.toISOString())
+});
+
+export type User = z.infer;
+
+export const CustomerFormSchema = z.object({
+ id: z.string(),
+ type: z.string(),
+ text: z.string(),
+ createdAt: z.date().transform(date => date.toISOString()),
+ updatedAt: z.date().transform(date => date.toISOString())
+});
+
+export const CustomerFormListSchema = z.array(CustomerFormSchema);
+
+export type CustomerForm = z.infer;
diff --git a/yarn.lock b/yarn.lock
index ce88bda..9022eda 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -60,6 +60,15 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.24.1":
+ version: 7.24.7
+ resolution: "@babel/runtime@npm:7.24.7"
+ dependencies:
+ regenerator-runtime: "npm:^0.14.0"
+ checksum: 7b77f566165dee62db3db0296e71d08cafda3f34e1b0dcefcd68427272e17c1704f4e4369bff76651b07b6e49d3ea5a0ce344818af9116e9292e4381e0918c76
+ languageName: node
+ linkType: hard
+
"@commitlint/cli@npm:^18.4.3":
version: 18.6.1
resolution: "@commitlint/cli@npm:18.6.1"
@@ -330,6 +339,15 @@ __metadata:
languageName: node
linkType: hard
+"@hookform/resolvers@npm:^3.6.0":
+ version: 3.6.0
+ resolution: "@hookform/resolvers@npm:3.6.0"
+ peerDependencies:
+ react-hook-form: ^7.0.0
+ checksum: 6dd1b7ad21ed2b171470740884e0b83982c79a0d4ceddabe60b616e53eeed2b5569cdba5e91ad844e379aeda5ffa835b0c2d2525d702fcd8b263c2194895f9b7
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/config-array@npm:^0.11.14":
version: 0.11.14
resolution: "@humanwhocodes/config-array@npm:0.11.14"
@@ -618,6 +636,34 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-compose-refs@npm:1.1.0":
+ version: 1.1.0
+ resolution: "@radix-ui/react-compose-refs@npm:1.1.0"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 047a4ed5f87cb848be475507cd62836cf5af5761484681f521ea543ea7c9d59d61d42806d6208863d5e2380bf38cdf4cff73c2bbe5f52dbbe50fb04e1a13ac72
+ languageName: node
+ linkType: hard
+
+"@radix-ui/react-slot@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "@radix-ui/react-slot@npm:1.1.0"
+ dependencies:
+ "@radix-ui/react-compose-refs": "npm:1.1.0"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 95e190868418b1c83adf6627256f6b664b0dcbea95d7215de9c64ac2c31102fc09155565d9ca27be6abd20fc63d0b0bacfe1b67d78b2de1d198244c848e1a54e
+ languageName: node
+ linkType: hard
+
"@react-email/render@npm:0.0.15":
version: 0.0.15
resolution: "@react-email/render@npm:0.0.15"
@@ -1216,6 +1262,13 @@ __metadata:
languageName: node
linkType: hard
+"asynckit@npm:^0.4.0":
+ version: 0.4.0
+ resolution: "asynckit@npm:0.4.0"
+ checksum: 3ce727cbc78f69d6a4722517a58ee926c8c21083633b1d3fdf66fd688f6c127a53a592141bd4866f9b63240a86e9d8e974b13919450bd17fa33c2d22c4558ad8
+ languageName: node
+ linkType: hard
+
"audit-ci@npm:^6.6.1":
version: 6.6.1
resolution: "audit-ci@npm:6.6.1"
@@ -1268,6 +1321,17 @@ __metadata:
languageName: node
linkType: hard
+"axios@npm:^1.7.2":
+ version: 1.7.2
+ resolution: "axios@npm:1.7.2"
+ dependencies:
+ follow-redirects: "npm:^1.15.6"
+ form-data: "npm:^4.0.0"
+ proxy-from-env: "npm:^1.1.0"
+ checksum: 6ae80dda9736bb4762ce717f1a26ff997d94672d3a5799ad9941c24d4fb019c1dff45be8272f08d1975d7950bac281f3ba24aff5ecd49ef5a04d872ec428782f
+ languageName: node
+ linkType: hard
+
"axobject-query@npm:~3.1.1":
version: 3.1.1
resolution: "axobject-query@npm:3.1.1"
@@ -1512,6 +1576,13 @@ __metadata:
languageName: node
linkType: hard
+"clsx@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "clsx@npm:2.1.1"
+ checksum: cdfb57fa6c7649bbff98d9028c2f0de2f91c86f551179541cf784b1cfdc1562dcb951955f46d54d930a3879931a980e32a46b598acaea274728dbe068deca919
+ languageName: node
+ linkType: hard
+
"color-convert@npm:^1.9.0":
version: 1.9.3
resolution: "color-convert@npm:1.9.3"
@@ -1551,6 +1622,15 @@ __metadata:
languageName: node
linkType: hard
+"combined-stream@npm:^1.0.8":
+ version: 1.0.8
+ resolution: "combined-stream@npm:1.0.8"
+ dependencies:
+ delayed-stream: "npm:~1.0.0"
+ checksum: 2e969e637d05d09fa50b02d74c83a1186f6914aae89e6653b62595cc75a221464f884f55f231b8f4df7a49537fba60bdc0427acd2bf324c09a1dbb84837e36e4
+ languageName: node
+ linkType: hard
+
"commander@npm:^10.0.0":
version: 10.0.1
resolution: "commander@npm:10.0.1"
@@ -1842,6 +1922,13 @@ __metadata:
languageName: node
linkType: hard
+"delayed-stream@npm:~1.0.0":
+ version: 1.0.0
+ resolution: "delayed-stream@npm:1.0.0"
+ checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020
+ languageName: node
+ linkType: hard
+
"didyoumean@npm:^1.2.2":
version: 1.2.2
resolution: "didyoumean@npm:1.2.2"
@@ -2647,6 +2734,16 @@ __metadata:
languageName: node
linkType: hard
+"follow-redirects@npm:^1.15.6":
+ version: 1.15.6
+ resolution: "follow-redirects@npm:1.15.6"
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ checksum: 70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7
+ languageName: node
+ linkType: hard
+
"for-each@npm:^0.3.3":
version: 0.3.3
resolution: "for-each@npm:0.3.3"
@@ -2666,6 +2763,17 @@ __metadata:
languageName: node
linkType: hard
+"form-data@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "form-data@npm:4.0.0"
+ dependencies:
+ asynckit: "npm:^0.4.0"
+ combined-stream: "npm:^1.0.8"
+ mime-types: "npm:^2.1.12"
+ checksum: 7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805
+ languageName: node
+ linkType: hard
+
"fraction.js@npm:^4.3.7":
version: 4.3.7
resolution: "fraction.js@npm:4.3.7"
@@ -4039,6 +4147,22 @@ __metadata:
languageName: node
linkType: hard
+"mime-db@npm:1.52.0":
+ version: 1.52.0
+ resolution: "mime-db@npm:1.52.0"
+ checksum: 54bb60bf39e6f8689f6622784e668a3d7f8bed6b0d886f5c3c446cb3284be28b30bf707ed05d0fe44a036f8469976b2629bbea182684977b084de9da274694d7
+ languageName: node
+ linkType: hard
+
+"mime-types@npm:^2.1.12":
+ version: 2.1.35
+ resolution: "mime-types@npm:2.1.35"
+ dependencies:
+ mime-db: "npm:1.52.0"
+ checksum: 89aa9651b67644035de2784a6e665fc685d79aba61857e02b9c8758da874a754aed4a9aced9265f5ed1171fd934331e5516b84a7f0218031b6fa0270eca1e51a
+ languageName: node
+ linkType: hard
+
"mimic-fn@npm:^2.1.0":
version: 2.1.0
resolution: "mimic-fn@npm:2.1.0"
@@ -5011,6 +5135,13 @@ __metadata:
languageName: node
linkType: hard
+"proxy-from-env@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "proxy-from-env@npm:1.1.0"
+ checksum: f0bb4a87cfd18f77bc2fba23ae49c3b378fb35143af16cc478171c623eebe181678f09439707ad80081d340d1593cd54a33a0113f3ccb3f4bc9451488780ee23
+ languageName: node
+ linkType: hard
+
"punycode@npm:^2.1.0":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
@@ -5044,7 +5175,7 @@ __metadata:
languageName: node
linkType: hard
-"react-hook-form@npm:^7.48.2":
+"react-hook-form@npm:^7.52.0":
version: 7.52.0
resolution: "react-hook-form@npm:7.52.0"
peerDependencies:
@@ -5162,6 +5293,13 @@ __metadata:
languageName: node
linkType: hard
+"regenerator-runtime@npm:^0.14.0":
+ version: 0.14.1
+ resolution: "regenerator-runtime@npm:0.14.1"
+ checksum: 5db3161abb311eef8c45bcf6565f4f378f785900ed3945acf740a9888c792f75b98ecb77f0775f3bf95502ff423529d23e94f41d80c8256e8fa05ed4b07cf471
+ languageName: node
+ linkType: hard
+
"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.2":
version: 1.5.2
resolution: "regexp.prototype.flags@npm:1.5.2"
@@ -5888,6 +6026,15 @@ __metadata:
languageName: node
linkType: hard
+"tailwind-merge@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "tailwind-merge@npm:2.3.0"
+ dependencies:
+ "@babel/runtime": "npm:^7.24.1"
+ checksum: cf3b126bee82bc9ff3f60f9601b66925418db6906544198b637487b25fba1c9c307734ca37ccb8fa2355f69ad0c5aff34c5dce460809addb459a93205d9a0abe
+ languageName: node
+ linkType: hard
+
"tailwindcss@npm:^3.4.1":
version: 3.4.4
resolution: "tailwindcss@npm:3.4.4"
@@ -6241,7 +6388,9 @@ __metadata:
"@auth0/nextjs-auth0": "npm:^3.5.0"
"@commitlint/cli": "npm:^18.4.3"
"@commitlint/config-conventional": "npm:^18.4.3"
+ "@hookform/resolvers": "npm:^3.6.0"
"@prisma/client": "npm:^5.6.0"
+ "@radix-ui/react-slot": "npm:^1.1.0"
"@types/node": "npm:^20"
"@types/react": "npm:^18"
"@types/react-dom": "npm:^18"
@@ -6250,6 +6399,8 @@ __metadata:
"@vercel/analytics": "npm:^1.1.1"
audit-ci: "npm:^6.6.1"
autoprefixer: "npm:^10.0.1"
+ axios: "npm:^1.7.2"
+ clsx: "npm:^2.1.1"
eslint: "npm:^8"
eslint-config-next: "npm:14.0.3"
eslint-config-prettier: "npm:^9.0.0"
@@ -6263,11 +6414,12 @@ __metadata:
prisma: "npm:^5.6.0"
react: "npm:^18"
react-dom: "npm:^18"
- react-hook-form: "npm:^7.48.2"
+ react-hook-form: "npm:^7.52.0"
resend: "npm:^3.1.0"
+ tailwind-merge: "npm:^2.3.0"
tailwindcss: "npm:^3.4.1"
typescript: "npm:^5"
- zod: "npm:^3.22.4"
+ zod: "npm:^3.23.8"
languageName: unknown
linkType: soft
@@ -6457,7 +6609,7 @@ __metadata:
languageName: node
linkType: hard
-"zod@npm:^3.22.4":
+"zod@npm:^3.23.8":
version: 3.23.8
resolution: "zod@npm:3.23.8"
checksum: 846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1