Working on the organization system.

This commit is contained in:
charlesgauthereau
2025-07-16 12:36:11 +02:00
parent 405de7b323
commit 385777535f
61 changed files with 514 additions and 366 deletions
@@ -1,28 +0,0 @@
"use server";
import { RegisterSchema } from "@/components/wrappers/auth/Register/register-form/register-form.schema";
import { action } from "@/safe-actions";
import { signUp } from "@/lib/auth/auth-client";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema/01_user";
import { eq } from "drizzle-orm";
export const registerUserAction = action.schema(RegisterSchema).action(async ({ parsedInput }: { parsedInput: typeof RegisterSchema._type }) => {
const [user] = await db.select().from(drizzleUser).where(eq(drizzleUser.email, parsedInput.email)).limit(1);
if (!user && parsedInput.password === parsedInput.confirmPassword) {
const { data, error } = await signUp.email({
email: parsedInput.email,
password: parsedInput.password,
name: parsedInput.name,
});
if (error) {
throw new Error(error.message);
}
return {
data: data?.user,
};
}
throw new Error("An error occured while creating user");
});
@@ -26,6 +26,7 @@ export const LoginForm = (props: loginFormProps) => {
const form = useZodForm({
schema: LoginSchema,
});
const mutation = useMutation({
mutationFn: async (values: LoginType) => {
const { error } = await signIn.email(values, {
@@ -11,36 +11,37 @@ import { Input } from "@/components/ui/input";
import { Form } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { TooltipProvider, TooltipTrigger, Tooltip, TooltipContent } from "@/components/ui/tooltip";
import { RegisterSchema, RegisterType } from "@/components/wrappers/auth/Register/register-form/register-form.schema";
import { registerUserAction } from "@/components/wrappers/auth/Register/register-form/register-form.action";
import { RegisterSchema, RegisterType } from "@/components/wrappers/auth/register/register-form/register-form.schema";
import { PasswordInput } from "@/components/wrappers/auth/PaswordInput/password-input";
import {signUp} from "@/lib/auth/auth-client";
export type registerFormProps = {
defaultValues?: RegisterType;
};
export const RegisterForm = (props: registerFormProps) => {
const form = useZodForm({
schema: RegisterSchema,
});
const router = useRouter();
const mutation = useMutation({
mutationFn: async (values: RegisterType) => {
console.log(values);
const createUser = await registerUserAction(values);
console.log(createUser);
const data = createUser?.data?.data;
if (createUser?.serverError || !data) {
console.log(createUser?.serverError);
toast.error(createUser?.serverError);
return;
}
toast.success(`Success`);
router.push(`/login`);
router.refresh();
await signUp.email(values, {
onSuccess: () => {
toast.success(`Success`);
router.push(`/login`);
router.refresh();
},
onError: (error) => {
console.log(error);
toast.error(error.error.message);
},
});
},
});
return (
<TooltipProvider>
<Card>
@@ -2,9 +2,9 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { EmailFormSchema } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import { setting as drizzleSetting } from "@/db/schema";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import * as drizzleDb from "@/db";
export const updateEmailSettingsAction = userAction
.schema(
@@ -17,11 +17,11 @@ export const updateEmailSettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({
...data,
})
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -6,7 +6,7 @@ import { sendEmail } from "@/utils/email-helper";
import TestEmailSettings from "../../../../../../emails/TestEmailSettings";
import { render } from "@react-email/render";
import { toast } from "sonner";
import { Setting } from "@/db/schema";
import {Setting} from "@/db/schema/00_setting";
export type SettingsEmailTabProps = {
settings: Setting;
@@ -10,7 +10,7 @@ import { checkConnexionToS3 } from "@/features/upload/public/upload.action";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import { updateStorageSettingsAction } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import { Setting } from "@/db/schema";
import {Setting} from "@/db/schema/00_setting";
export type SettingsStorageTabProps = {
settings: Setting;
@@ -3,9 +3,9 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { db } from "@/db";
import { setting as drizzleSetting } from "@/db/schema";
import { S3FormSchema, StorageSwitchSchema } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateS3SettingsAction = userAction
.schema(
@@ -18,9 +18,9 @@ export const updateS3SettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -39,9 +39,9 @@ export const updateStorageSettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -4,8 +4,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { SettingsEmailTab } from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
import { SettingsStorageTab } from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
import { AdminUsersTable } from "@/components/wrappers/dashboard/admin/admin-user-table";
import { Setting } from "@/db/schema";
import { User } from "@/db/schema/01_user";
import {Setting} from "@/db/schema/00_setting";
export type AdminTabsProps = {
users: User[];
@@ -4,7 +4,7 @@ import { Card, CardContent, CardHeader } from "@/components/ui/card";
import Link from "next/link";
import { formatDateLastContact } from "@/utils/date-formatting";
import { ConnectionCircle } from "@/components/wrappers/common/connection-circle";
import { Agent } from "@/db/schema";
import {Agent} from "@/db/schema/07_agent";
export type agentCardProps = {
data: Agent;
@@ -1,10 +1,10 @@
"use client";
import { generateEdgeKey } from "@/utils/edge_key";
import { getServerUrl } from "@/utils/get-server-url";
import { PasswordInput } from "@/components/wrappers/auth/PaswordInput/password-input";
import { useState } from "react";
import { CopyButton } from "@/components/wrappers/common/button/copy-button";
import { Agent } from "@/db/schema";
import {generateEdgeKey} from "@/utils/edge_key";
import {getServerUrl} from "@/utils/get-server-url";
import {PasswordInput} from "@/components/wrappers/auth/PaswordInput/password-input";
import {useState} from "react";
import {CopyButton} from "@/components/wrappers/common/button/copy-button";
import {Agent} from "@/db/schema/07_agent";
export type AgentCardKeyProps = {
agent: Agent;
@@ -22,7 +22,7 @@ export const AgentCardKey = (props: AgentCardKeyProps) => {
setCode(edge_key);
}}
/>
<CopyButton className="mt-5" value={code} />
<CopyButton className="mt-5" value={code}/>
</>
);
};
@@ -4,12 +4,12 @@ import { AgentSchema } from "@/components/wrappers/dashboard/agent/AgentForm/age
import { z } from "zod";
import { eq, and, ne, count } from "drizzle-orm";
import { db } from "@/db";
import { agent } from "@/db/schema";
import * as drizzleDb from "@/db";
const verifySlugUniqueness = async (slug: string, agentId?: string) => {
const conditions = agentId ? and(eq(agent.slug, slug), ne(agent.id, agentId)) : eq(agent.slug, slug);
const conditions = agentId ? and(eq(drizzleDb.schemas.agent.slug, slug), ne(drizzleDb.schemas.agent.id, agentId)) : eq(drizzleDb.schemas.agent.slug, slug);
const [countResult] = await db.select({ count: count() }).from(agent).where(conditions);
const [countResult] = await db.select({ count: count() }).from(drizzleDb.schemas.agent).where(conditions);
if (countResult.count > 0) {
throw new ActionError("Slug already exists");
@@ -19,7 +19,7 @@ const verifySlugUniqueness = async (slug: string, agentId?: string) => {
export const createAgentAction = userAction.schema(AgentSchema).action(async ({ parsedInput }) => {
await verifySlugUniqueness(parsedInput.slug);
const [createdAgent] = await db.insert(agent).values(parsedInput).returning();
const [createdAgent] = await db.insert(drizzleDb.schemas.agent).values(parsedInput).returning();
return {
data: createdAgent,
@@ -36,7 +36,7 @@ export const updateAgentAction = userAction
.action(async ({ parsedInput }) => {
await verifySlugUniqueness(parsedInput.data.slug, parsedInput.id);
const [updatedAgent] = await db.update(agent).set(parsedInput.data).where(eq(agent.id, parsedInput.id)).returning();
const [updatedAgent] = await db.update(drizzleDb.schemas.agent).set(parsedInput.data).where(eq(drizzleDb.schemas.agent.id, parsedInput.id)).returning();
return {
data: updatedAgent,
@@ -8,7 +8,7 @@ import { PropsWithChildren } from "react";
import { CopyButton } from "@/components/wrappers/common/button/copy-button";
import { getServerUrl } from "@/utils/get-server-url";
import { CodeSnippet } from "@/components/wrappers/code-snippet/CodeSnippet";
import { Agent } from "@/db/schema";
import {Agent} from "@/db/schema/07_agent";
export type agentRegistrationDialogProps = PropsWithChildren<{
agent: Agent;
@@ -3,14 +3,14 @@
import { z } from "zod";
import { userAction } from "@/safe-actions";
import { db } from "@/db";
import { backup } from "@/db/schema";
import { ServerActionResult } from "@/types/action-type";
import { Backup } from "@/db/schema";
import * as drizzleDb from "@/db";
import {Backup} from "@/db/schema/06_database";
export const backupButtonAction = userAction.schema(z.string()).action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
try {
const [createdBackup] = await db
.insert(backup)
.insert(drizzleDb.schemas.backup)
.values({
databaseId: parsedInput,
status: "waiting",
@@ -11,7 +11,7 @@ import { useMutation } from "@tanstack/react-query";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import { updateDatabaseBackupPolicyAction } from "@/components/wrappers/dashboard/database/CronButton/cron.action";
import { Database } from "@/db/schema";
import {Database} from "@/db/schema/06_database";
export type CronButtonProps = {
database: Database;
@@ -6,7 +6,7 @@ import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Database } from "@/db/schema";
import {Database} from "@/db/schema/06_database";
export type CronInputProps = {
database: Database;
@@ -3,8 +3,8 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { db } from "@/db";
import { database } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateDatabaseBackupPolicyAction = userAction
.schema(
@@ -17,11 +17,11 @@ export const updateDatabaseBackupPolicyAction = userAction
const cronPolicy = parsedInput.backupPolicy === "" ? null : parsedInput.backupPolicy;
const [updated] = await db
.update(database)
.update(drizzleDb.schemas.database)
.set({
backupPolicy: cronPolicy,
})
.where(eq(database.id, parsedInput.databaseId))
.where(eq(drizzleDb.schemas.database.id, parsedInput.databaseId))
.returning()
.execute();
@@ -3,9 +3,9 @@
import { z } from "zod";
import { userAction } from "@/safe-actions";
import { db } from "@/db";
import { database } from "@/db/schema";
import { eq } from "drizzle-orm";
import { DatabaseSchema } from "@/components/wrappers/dashboard/database/DatabaseForm/form-database.schema";
import * as drizzleDb from "@/db";
export const updateDatabaseAction = userAction
.schema(
@@ -15,7 +15,7 @@ export const updateDatabaseAction = userAction
})
)
.action(async ({ parsedInput }) => {
const [updated] = await db.update(database).set(parsedInput.data).where(eq(database.id, parsedInput.id)).returning().execute();
const [updated] = await db.update(drizzleDb.schemas.database).set(parsedInput.data).where(eq(drizzleDb.schemas.database.id, parsedInput.id)).returning().execute();
return updated;
});
@@ -7,6 +7,7 @@ import { authClient } from "@/lib/auth/auth-client";
export function OrganizationCombobox() {
const router = useRouter();
const { state } = useSidebar();
const { data: organizations } = authClient.useListOrganizations();
const { data: activeOrganization } = authClient.useActiveOrganization();
@@ -27,10 +28,9 @@ export function OrganizationCombobox() {
await authClient.organization.setActive({
organizationSlug: slug,
});
router.replace(`/dashboard/${slug}/home`);
// router.replace(`/dashboard/${slug}/home`);
router.refresh();
};
const { state } = useSidebar();
return <>{state === "expanded" && <ComboBox sideBar values={values} defaultValue={activeOrganization?.slug} onValueChange={onValueChange} />}</>;
}
@@ -1,12 +1,13 @@
"use server";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { userAction } from "@/safe-actions";
import { eq } from "drizzle-orm";
import { z } from "zod";
import * as drizzleDb from "@/db";
export const updateImageUserAction = userAction.schema(z.string()).action(async ({ parsedInput, ctx }) => {
const [updatedUser] = await db.update(drizzleUser).set({ image: parsedInput }).where(eq(drizzleUser.id, ctx.user.id)).returning();
const [updatedUser] = await db.update(drizzleDb.schemas.user).set({ image: parsedInput }).where(eq(drizzleDb.schemas.user.id, ctx.user.id)).returning();
return {
data: updatedUser,
@@ -3,22 +3,23 @@ import { userAction } from "@/safe-actions";
import { z } from "zod";
import { v4 as uuidv4 } from "uuid";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const deleteUserAction = userAction.schema(z.string()).action(async ({ parsedInput, ctx }) => {
const userId = parsedInput.length > 0 ? parsedInput : ctx.user.id;
const uuid = uuidv4();
const [updatedUser] = await db
.update(drizzleUser)
.update(drizzleDb.schemas.user)
.set({
email: `${uuid}@portabase.com`,
name: `${uuid}`,
//deleted: true,
//todo: add deleted
})
.where(eq(drizzleUser.id, userId))
.where(eq(drizzleDb.schemas.user.id, userId))
.returning();
return {
@@ -3,8 +3,8 @@ import { userAction } from "@/safe-actions";
import { z } from "zod";
import { UserSchema } from "@/components/wrappers/dashboard/profile/UserForm/user-form.schema";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateUserAction = userAction
.schema(
@@ -14,8 +14,7 @@ export const updateUserAction = userAction
})
)
.action(async ({ parsedInput }) => {
const [updatedUser] = await db.update(drizzleUser).set(parsedInput.data).where(eq(drizzleUser.id, parsedInput.id)).returning();
const [updatedUser] = await db.update(drizzleDb.schemas.user).set(parsedInput.data).where(eq(drizzleDb.schemas.user.id, parsedInput.id)).returning();
return {
data: updatedUser,
};
@@ -1,24 +1,24 @@
"use server";
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { v4 as uuidv4 } from "uuid";
import { ServerActionResult } from "@/types/action-type";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { project } from "@/db/schema";
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {v4 as uuidv4} from "uuid";
import {ServerActionResult} from "@/types/action-type";
import {eq} from "drizzle-orm";
import {db} from "@/db";
import * as drizzleDb from "@/db";
export const deleteProjectAction = userAction.schema(z.string()).action(async ({ parsedInput }): Promise<ServerActionResult<typeof project.$inferSelect>> => {
export const deleteProjectAction = userAction.schema(z.string()).action(async ({parsedInput}): Promise<ServerActionResult<typeof drizzleDb.schemas.project.$inferSelect>> => {
try {
const uuid = uuidv4();
const updatedProjects = await db
.update(project)
.update(drizzleDb.schemas.project)
.set({
isArchived: true,
slug: uuid,
})
.where(eq(project.id, parsedInput))
.where(eq(drizzleDb.schemas.project.id, parsedInput))
.returning();
const updatedProject = updatedProjects[0];
@@ -32,7 +32,7 @@ export const deleteProjectAction = userAction.schema(z.string()).action(async ({
value: updatedProject,
actionSuccess: {
message: "Projects has been successfully archived.",
messageParams: { projectId: parsedInput },
messageParams: {projectId: parsedInput},
},
};
} catch (error) {
@@ -42,7 +42,7 @@ export const deleteProjectAction = userAction.schema(z.string()).action(async ({
message: "Failed to archive Projects.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: { projectId: parsedInput },
messageParams: {projectId: parsedInput},
},
};
}
@@ -7,7 +7,7 @@ import { eventUpdate } from "@/types/events";
import { backupColumns } from "@/features/dashboard/backup/columns";
import { restoreColumns } from "@/features/dashboard/restore/columns";
import { DataTable } from "@/components/wrappers/common/table/data-table";
import { Backup, Database, Restoration } from "@/db/schema";
import {Backup, Database, Restoration} from "@/db/schema/06_database";
export type DatabaseTabsProps = {
backups: Backup[];
@@ -5,7 +5,7 @@ import Image from "next/image";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { ConnectionCircle } from "@/components/wrappers/common/connection-circle";
import { formatDateLastContact } from "@/utils/date-formatting";
import { Database } from "@/db/schema";
import {Database} from "@/db/schema/06_database";
export type projectDatabaseCardProps = {
data: Database;
@@ -11,7 +11,8 @@ import { createProjectAction, updateProjectAction } from "@/components/wrappers/
import { useRouter } from "next/navigation";
import { MultiSelect } from "@/components/wrappers/common/multiselect/multi-select";
import { toast } from "sonner";
import { DatabaseWith, Organization } from "@/db/schema";
import {DatabaseWith} from "@/db/schema/06_database";
import {Organization} from "@/db/schema/02_organization";
export type projectFormProps = {
defaultValues?: ProjectType;
@@ -4,9 +4,11 @@ import { userAction } from "@/safe-actions";
import { ProjectSchema } from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm.schema";
import { z } from "zod";
import { ServerActionResult } from "@/types/action-type";
import { Database, database as drizzleDatabase, project as drizzleProject, Project } from "@/db/schema";
import { db } from "@/db";
import { eq, inArray } from "drizzle-orm";
import {Project} from "@/db/schema/05_project";
import * as drizzleDb from "@/db";
import {Database} from "@/db/schema/06_database";
export const createProjectAction = userAction
.schema(
@@ -18,7 +20,7 @@ export const createProjectAction = userAction
.action(async ({ parsedInput }): Promise<ServerActionResult<Project>> => {
try {
const [createdProject] = await db
.insert(drizzleProject)
.insert(drizzleDb.schemas.project)
.values({
name: parsedInput.data.name,
slug: parsedInput.data.slug,
@@ -27,7 +29,7 @@ export const createProjectAction = userAction
.returning();
if (parsedInput.data.databases.length > 0) {
await db.update(drizzleDatabase).set({ projectId: createdProject.id }).where(inArray(drizzleDatabase.id, parsedInput.data.databases));
await db.update(drizzleDb.schemas.database).set({ projectId: createdProject.id }).where(inArray(drizzleDb.schemas.database.id, parsedInput.data.databases));
}
return {
@@ -62,7 +64,7 @@ export const updateProjectAction = userAction
.action(async ({ parsedInput }): Promise<ServerActionResult<Project>> => {
try {
const existing = await db.query.project.findFirst({
where: eq(drizzleProject.id, parsedInput.projectId),
where: eq(drizzleDb.schemas.project.id, parsedInput.projectId),
with: {
databases: true,
},
@@ -79,20 +81,20 @@ export const updateProjectAction = userAction
const databasesToRemove = existingDbIds.filter((id: string) => !newDbIds.includes(id));
if (databasesToAdd.length > 0) {
await db.update(drizzleDatabase).set({ projectId: parsedInput.projectId }).where(inArray(drizzleDatabase.id, databasesToAdd));
await db.update(drizzleDb.schemas.database).set({ projectId: parsedInput.projectId }).where(inArray(drizzleDb.schemas.database.id, databasesToAdd));
}
if (databasesToRemove.length > 0) {
await db.update(drizzleDatabase).set({ projectId: null }).where(inArray(drizzleDatabase.id, databasesToRemove));
await db.update(drizzleDb.schemas.database).set({ projectId: null }).where(inArray(drizzleDb.schemas.database.id, databasesToRemove));
}
const [updatedProject] = await db
.update(drizzleProject)
.update(drizzleDb.schemas.project)
.set({
name: parsedInput.data.name,
slug: parsedInput.data.slug,
})
.where(eq(drizzleProject.id, parsedInput.projectId))
.where(eq(drizzleDb.schemas.project.id, parsedInput.projectId))
.returning();
return {
@@ -8,7 +8,7 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { updateUserOrganizationAction } from "@/components/wrappers/dashboard/settings/SettingsUsersTab/settings-user-tab.action";
import { OrganizationMember } from "@/db/schema/02_organization";
import {OrganizationMember} from "@/db/schema/03_member";
export const usersColumns: ColumnDef<OrganizationMember>[] = [
{
@@ -32,9 +32,8 @@ export async function AppSidebar() {
const organization = await getOrganization({organizationId: member.organizationId});
//todo: à revoir
console.log("organization", organization);
console.log("membrer", member);
console.log("aoaoaoaoaoa", organization);
return (
<Sidebar collapsible="icon">