From 0b644f79873268d80e9a1499c2d0181ba0e1ab26 Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Tue, 17 Dec 2024 19:14:18 +0100 Subject: [PATCH 1/2] Agent Key Card. --- .../dashboard/agents/[agentId]/page.tsx | 24 ++++++++++++------ .../auth/login/loginForm/LoginForm.tsx | 4 --- .../socialAuthButtons/SocialAuthButton.tsx | 21 +++++++++------- .../wrappers/common/button/copy-button.tsx | 3 ++- .../agent/AgentCardKey/AgentCardKey.tsx | 25 +++++++++++++++++++ src/env.mjs | 3 +++ src/features/auth/auth.action.ts | 2 -- 7 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 src/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey.tsx diff --git a/app/(customer)/dashboard/agents/[agentId]/page.tsx b/app/(customer)/dashboard/agents/[agentId]/page.tsx index e5cc6f70..4a2eef98 100644 --- a/app/(customer)/dashboard/agents/[agentId]/page.tsx +++ b/app/(customer)/dashboard/agents/[agentId]/page.tsx @@ -11,6 +11,7 @@ import {Card, CardContent, CardHeader} from "@/components/ui/card"; import {AgentModalKey} from "@/components/wrappers/dashboard/agent/AgentModalKey/AgentModalKey"; import {CardsWithPagination} from "@/components/wrappers/common/cards-with-pagination"; import {DatabaseCard} from "@/components/wrappers/dashboard/projects/ProjectCard/ProjectDatabaseCard"; +import {AgentCardKey} from "@/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey"; export default async function RoutePage(props: PageParams<{ agentId: string }>) { @@ -53,15 +54,13 @@ export default async function RoutePage(props: PageParams<{ agentId: string }>) href={`/dashboard/agents/${agent.id}/edit`}> - - - + {/**/} + {/* */} + {/**/} - - - + {agent.description} @@ -88,7 +87,16 @@ export default async function RoutePage(props: PageParams<{ agentId: string }>) {formatDateLastContact(agent.lastContact)} + + + + Edge Key + + + + + diff --git a/src/components/wrappers/auth/login/loginForm/LoginForm.tsx b/src/components/wrappers/auth/login/loginForm/LoginForm.tsx index 01228ff8..a41b46e2 100644 --- a/src/components/wrappers/auth/login/loginForm/LoginForm.tsx +++ b/src/components/wrappers/auth/login/loginForm/LoginForm.tsx @@ -38,10 +38,6 @@ export const LoginForm = (props: loginFormProps) => {
- {/*
*/} - {/* Logo Portabase*/} - {/*
*/}

Login

Enter your informations bellow to login diff --git a/src/components/wrappers/auth/login/socialAuth/socialAuthButtons/SocialAuthButton.tsx b/src/components/wrappers/auth/login/socialAuth/socialAuthButtons/SocialAuthButton.tsx index 21fe9cf8..3cfc43a5 100644 --- a/src/components/wrappers/auth/login/socialAuth/socialAuthButtons/SocialAuthButton.tsx +++ b/src/components/wrappers/auth/login/socialAuth/socialAuthButtons/SocialAuthButton.tsx @@ -2,21 +2,24 @@ import {signInAction} from "@/features/auth/auth.action"; import {Button} from "@/components/ui/button"; import Image from "next/image"; +import {env} from "@/env.mjs"; export type SocialAuthButtonProps = {} export const SocialAuthButton = (props: SocialAuthButtonProps) => { return (

- + {env.NEXT_PUBLIC_GOOGLE_AUTH === "true" ? + + :null}
) } \ No newline at end of file diff --git a/src/components/wrappers/common/button/copy-button.tsx b/src/components/wrappers/common/button/copy-button.tsx index 96e5fd5e..84a45b92 100644 --- a/src/components/wrappers/common/button/copy-button.tsx +++ b/src/components/wrappers/common/button/copy-button.tsx @@ -13,7 +13,8 @@ export async function copyToClipboardWithMeta(value: string) { export type CopyButtonProps = { - value: string + value: string, + className: string } diff --git a/src/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey.tsx b/src/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey.tsx new file mode 100644 index 00000000..c31be231 --- /dev/null +++ b/src/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey.tsx @@ -0,0 +1,25 @@ +"use client" +import {generateEdgeKey} from "@/utils/edge_key"; +import {getServerUrl} from "@/utils/get-server-url"; +import {Agent} from "@prisma/client"; +import {PasswordInput} from "@/components/wrappers/auth/PaswordInput/password-input"; +import {useState} from "react"; +import {CopyButton} from "@/components/wrappers/common/button/copy-button"; + +export type AgentCardKeyProps = { + agent: Agent + +} + +export const AgentCardKey = (props: AgentCardKeyProps) => { + const edge_key = generateEdgeKey(getServerUrl(), props.agent.id); + const [code, setCode] = useState(`${edge_key}`); + + + return( + <> + {setCode(edge_key)} }/> + + + ) +} \ No newline at end of file diff --git a/src/env.mjs b/src/env.mjs index 4ea717f8..c555fea2 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -31,6 +31,7 @@ export const env = createEnv({ S3_USE_SSL: z.string().optional(), STORAGE_TYPE: z.string().optional(), + NEXT_PUBLIC_GOOGLE_AUTH: z.string().optional(), }, /* @@ -40,6 +41,7 @@ export const env = createEnv({ */ client: { NEXT_PUBLIC_DOMAIN_NAME: z.string(), + NEXT_PUBLIC_GOOGLE_AUTH: z.string().optional(), }, /* * Due to how Next.js bundles environment variables on Edge and Client, @@ -62,6 +64,7 @@ export const env = createEnv({ AUTH_GOOGLE_ID: process.env.AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET: process.env.AUTH_GOOGLE_SECRET, + NEXT_PUBLIC_GOOGLE_AUTH: process.env.NEXT_PUBLIC_GOOGLE_AUTH, S3_ENDPOINT: process.env.S3_ENDPOINT, S3_ACCESS_KEY: process.env.S3_ACCESS_KEY, diff --git a/src/features/auth/auth.action.ts b/src/features/auth/auth.action.ts index 9a183dd4..40928f98 100644 --- a/src/features/auth/auth.action.ts +++ b/src/features/auth/auth.action.ts @@ -18,8 +18,6 @@ export const signInAction = async (type: string, formData?: any) => { email: formData.email }); } catch (error) { - // const signInError = error as CredentialsSignin - console.error(error); return {error: "Error loging in, please try again or check your credentials !"}; } redirect("/") From 43161b8523730381868e4d0d7355230c144c9c0a Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Tue, 17 Dec 2024 19:42:02 +0100 Subject: [PATCH 2/2] Project delete. --- .../dashboard/projects/[projectId]/page.tsx | 1 + .../20241217183424_2024_12_17/migration.sql | 8 ++++ .../20241217183746_2024_12_17/migration.sql | 2 + prisma/schema.prisma | 10 ++-- .../ButtonDeleteProject.tsx | 27 +++++++---- .../delete-project.action.ts | 47 +++++++++++++++++++ .../ProjectsForm/project-form.action.ts | 4 +- 7 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 prisma/migrations/20241217183424_2024_12_17/migration.sql create mode 100644 prisma/migrations/20241217183746_2024_12_17/migration.sql create mode 100644 src/components/wrappers/dashboard/projects/ButtonDeleteProject/delete-project.action.ts diff --git a/app/(customer)/dashboard/projects/[projectId]/page.tsx b/app/(customer)/dashboard/projects/[projectId]/page.tsx index 5c40708e..d7a24ec5 100644 --- a/app/(customer)/dashboard/projects/[projectId]/page.tsx +++ b/app/(customer)/dashboard/projects/[projectId]/page.tsx @@ -39,6 +39,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }> diff --git a/prisma/migrations/20241217183424_2024_12_17/migration.sql b/prisma/migrations/20241217183424_2024_12_17/migration.sql new file mode 100644 index 00000000..a2dd6863 --- /dev/null +++ b/prisma/migrations/20241217183424_2024_12_17/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `isArchived` to the `projects` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "projects" ADD COLUMN "isArchived" BOOLEAN NOT NULL; diff --git a/prisma/migrations/20241217183746_2024_12_17/migration.sql b/prisma/migrations/20241217183746_2024_12_17/migration.sql new file mode 100644 index 00000000..87dae96b --- /dev/null +++ b/prisma/migrations/20241217183746_2024_12_17/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "projects" ALTER COLUMN "isArchived" SET DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3eb37225..4553f48b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -102,11 +102,11 @@ model UserOrganization { } model Project { - id String @id @default(cuid()) - slug String @unique - name String - createdAt DateTime @default(now()) @map("created_at") - + id String @id @default(cuid()) + slug String @unique + name String + createdAt DateTime @default(now()) @map("created_at") + isArchived Boolean @default(false) organizationId String @map("organization_id") organization Organization @relation(fields: [organizationId], references: [id]) diff --git a/src/components/wrappers/dashboard/projects/ButtonDeleteProject/ButtonDeleteProject.tsx b/src/components/wrappers/dashboard/projects/ButtonDeleteProject/ButtonDeleteProject.tsx index 0a82d593..8b7b24c2 100644 --- a/src/components/wrappers/dashboard/projects/ButtonDeleteProject/ButtonDeleteProject.tsx +++ b/src/components/wrappers/dashboard/projects/ButtonDeleteProject/ButtonDeleteProject.tsx @@ -2,29 +2,36 @@ import {Trash2} from "lucide-react"; import {ButtonWithConfirm} from "@/components/wrappers/common/button/button-with-confirm"; +import {useMutation} from "@tanstack/react-query"; +import {deleteProjectAction} from "@/components/wrappers/dashboard/projects/ButtonDeleteProject/delete-project.action"; +import {useRouter} from "next/navigation"; +import {toast} from "sonner"; export type ButtonDeleteProjectProps = { text? : string + projectId: string } export const ButtonDeleteProject = (props: ButtonDeleteProjectProps) => { - - // const mutation = useMutation({ - // mutationFn: () => deleteUserAction(""), - // onSuccess: async () => { - // await signOutAction(); - // }, - // }) + const router = useRouter() + const mutation = useMutation({ + mutationFn: () => deleteProjectAction(props.projectId), + onSuccess: async (result) => { + router.push("/dashboard/projects") + if(result.data.success) { + toast.success(result.data.actionSuccess.message); + } + }, + }) return ( { - // mutation.mutate() - console.log("ok") + mutation.mutate() }} variant={"destructive"} - // isPending={mutation.isPending} + isPending={mutation.isPending} className="gap-2" icon={} /> diff --git a/src/components/wrappers/dashboard/projects/ButtonDeleteProject/delete-project.action.ts b/src/components/wrappers/dashboard/projects/ButtonDeleteProject/delete-project.action.ts new file mode 100644 index 00000000..ed1e5734 --- /dev/null +++ b/src/components/wrappers/dashboard/projects/ButtonDeleteProject/delete-project.action.ts @@ -0,0 +1,47 @@ +"use server" +import {userAction} from "@/safe-actions"; +import {z} from "zod"; +import {v4 as uuidv4} from "uuid"; +import {prisma} from "@/prisma"; +import {ServerActionResult} from "@/types/action-type"; +import {Projects} from "@prisma/client"; + + +export const deleteProjectAction = userAction + .schema(z.string()) + .action(async ({parsedInput, ctx}): Promise> => { + + try { + const uuid = uuidv4() + + const project = await prisma.project.update({ + where: { + id: parsedInput + }, + data:{ + isArchived: true, + slug: uuid + } + }) + + return { + success: true, + value: project, + actionSuccess: { + message: "Projects has been successfully archived.", + messageParams: {projectName: parsedInput.data.name}, + }, + }; + } catch (error) { + return { + success: false, + actionError: { + message: "Failed to archived Projects.", + status: 500, // Optional: Use a meaningful status code + cause: error instanceof Error ? error.message : "Unknown error", + messageParams: {projectName: parsedInput.data.name}, + }, + }; + } + + }); diff --git a/src/components/wrappers/dashboard/projects/ProjectsForm/project-form.action.ts b/src/components/wrappers/dashboard/projects/ProjectsForm/project-form.action.ts index 82af9795..d6c9ccb8 100644 --- a/src/components/wrappers/dashboard/projects/ProjectsForm/project-form.action.ts +++ b/src/components/wrappers/dashboard/projects/ProjectsForm/project-form.action.ts @@ -42,7 +42,7 @@ export const createProjectAction = userAction success: true, value: project, actionSuccess: { - message: "ProjectsForm has been successfully created.", + message: "Projects has been successfully created.", messageParams: {projectName: parsedInput.data.name}, }, }; @@ -50,7 +50,7 @@ export const createProjectAction = userAction return { success: false, actionError: { - message: "Failed to create ProjectsForm.", + message: "Failed to create Projects.", status: 500, // Optional: Use a meaningful status code cause: error instanceof Error ? error.message : "Unknown error", messageParams: {projectName: parsedInput.data.name},