adding administration panel.

This commit is contained in:
killian-larcher
2024-12-13 12:28:08 +01:00
parent 31e71cd64c
commit 1bc69e4b10
20 changed files with 344 additions and 80 deletions
@@ -4,6 +4,7 @@ import {PropsWithChildren} from "react";
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu";
import {signOutAction} from "@/features/auth/auth.action";
import {redirect} from "next/navigation";
import {CircleUser, LogOut, ShieldHalf} from "lucide-react";
export type LoggedInDropdownProps = PropsWithChildren<{}>
@@ -21,15 +22,29 @@ export const LoggedInDropdown = (props: LoggedInDropdownProps) => {
<DropdownMenuItem onClick={() => {
redirect("/dashboard/profile")
}}>
<span>Account</span>
<div className="flex justify-start items-center gap-2">
<CircleUser size={16}/>
<span>Account</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {
redirect("/dashboard/admin")
}}>
<div className="flex justify-start items-center gap-2">
<ShieldHalf size={16}/>
<span>Administration Panel</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {
signOutAction()
}}>
<span>Sign out</span>
<div className="flex justify-start items-center gap-2">
<LogOut size={16}/>
<span>Log out</span>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
)
}
@@ -3,7 +3,7 @@ import {userAction} from "@/safe-actions";
import {prisma} from "@/prisma";
import {z} from "zod";
import {v4 as uuidv4} from "uuid";
import {EmailFormSchema} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
import {EmailFormSchema} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
export const deleteUserAction = userAction
@@ -1,50 +1,54 @@
"use client"
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/columns-users";
import {User, Settings} from "@prisma/client";
import {backupColumns} from "@/features/backup/columns";
import {SettingsEmailTab} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/SettingsEmailTab";
import {SettingsStorageTab} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/SettingsStorageTab";
import {SettingsEmailTab} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/SettingsEmailTab";
import {SettingsStorageTab} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/SettingsStorageTab";
import {SettingsUsersTab} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/SettingsUsersTab";
export type SettingsTabsProps = {
currentUser: User;
users: User[];
settings: Settings;
}
export const SettingsTabs = (props: SettingsTabsProps) => {
return(
const {currentUser, users} = props;
<Tabs defaultValue="informations" >
<TabsList className="w-full">
<TabsTrigger className="w-full " value="informations">Info</TabsTrigger>
<TabsTrigger className="w-full " value="users">Users</TabsTrigger>
<TabsTrigger className="w-full " value="email">Email</TabsTrigger>
<TabsTrigger className="w-full " value="storage">Storage</TabsTrigger>
</TabsList>
<TabsContent value="informations">
<div className="flex flex-1 flex-col gap-4 py-4">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50"/>
<div className="aspect-video rounded-xl bg-muted/50"/>
<div className="aspect-video rounded-xl bg-muted/50"/>
</div>
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"/>
</div>
</TabsContent>
<TabsContent value="users" >
<SettingsUsersTab users={props.users}/>
</TabsContent>
<TabsContent value="email">
<SettingsEmailTab settings={props.settings}/>
</TabsContent>
<TabsContent value="storage">
<SettingsStorageTab settings={props.settings}/>
</TabsContent>
</Tabs>
return (
// <Tabs defaultValue="informations" >
// <TabsList className="w-full">
// <TabsTrigger className="w-full " value="informations">Info</TabsTrigger>
// <TabsTrigger className="w-full " value="users">Users</TabsTrigger>
// <TabsTrigger className="w-full " value="email">Email</TabsTrigger>
// <TabsTrigger className="w-full " value="storage">Storage</TabsTrigger>
// </TabsList>
// <TabsContent value="informations">
// <div className="flex flex-1 flex-col gap-4 py-4">
// <div className="grid auto-rows-min gap-4 md:grid-cols-3">
// <div className="aspect-video rounded-xl bg-muted/50"/>
// <div className="aspect-video rounded-xl bg-muted/50"/>
// <div className="aspect-video rounded-xl bg-muted/50"/>
// </div>
// <div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"/>
// </div>
// </TabsContent>
// <TabsContent value="users" >
<SettingsUsersTab currentUser={currentUser} users={users}/>
// </TabsContent>
// <TabsContent value="email">
// <SettingsEmailTab settings={props.settings}/>
// </TabsContent>
// <TabsContent value="storage">
// <SettingsStorageTab settings={props.settings}/>
// </TabsContent>
// </Tabs>
)
}
@@ -1,21 +1,30 @@
import {User} from "@prisma/client";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/columns-users";
import {User} from "@prisma/client";
import {UsersDataTable} from "@/components/wrappers/Dashboard/admin/admin-user-table";
export type SettingsUsersTabProps = {
currentUser: User;
users: User[]
}
export const SettingsUsersTab = (props: SettingsUsersTabProps) => {
return (
const {currentUser, users} = props;
return (
<div className="flex flex-col h-full py-4">
<div className="flex gap-4 h-fit justify-between">
<h1>List of Portabase users</h1>
<h1>List of organization's users</h1>
</div>
<div className="mt-5">
<DataTableWithPagination columns={usersColumns} data={props.users}/>
<DataTableWithPagination
columns={usersColumns}
data={users}
DataTable={UsersDataTable}
dataTableProps={{currentUser}}
/>
</div>
</div>
)
@@ -56,13 +56,16 @@ export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
setActiveItem(title);
console.log(title)
}
return(
return (
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild >
<SidebarMenuButton asChild>
<Link
className={cn(buttonVariants({size: "lg", variant: activeItem === item.title ? "secondary" : 'ghost'}), "justify-start p-0")}
className={cn(buttonVariants({
size: "lg",
variant: activeItem === item.title ? "secondary" : 'ghost'
}), "justify-start p-0")}
href={`${BASE_URL}/${item.url}`}
onClick={() => handleItemClick(item.title)}
>
@@ -70,9 +73,47 @@ export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
<SidebarMenuAction className={`peer-data-[active=true]/menu-button:opacity-100 ${activeItem === item.title ? 'active' : ''}`}/>
<SidebarMenuAction
className={`peer-data-[active=true]/menu-button:opacity-100 ${activeItem === item.title ? 'active' : ''}`}/>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
}
// export type SidebarMenuItemCustomProps = {
// title: string
// url: string
// icon: Element
// }
//
//
// export const SidebarMenuItemCustom = (props: SidebarMenuItemCustomProps) => {
//
// const {title, url, icon} = props;
//
// const BASE_URL = "/dashboard";
// const pathname = usePathname();
//
// const [activeItem, setActiveItem] = useState(title);
//
// return (
// <SidebarMenuItem key={title}>
// <SidebarMenuButton asChild>
// <Link
// className={cn(buttonVariants({
// size: "lg",
// variant: activeItem === title ? "secondary" : 'ghost'
// }), "justify-start p-0")}
// href={`${BASE_URL}/${url}`}
// onClick={() => handleItemClick(item.title)}
// >
// <icon/>
// <span>{title}</span>
// </Link>
// </SidebarMenuButton>
// <SidebarMenuAction
// className={`peer-data-[active=true]/menu-button:opacity-100 ${activeItem === title ? 'active' : ''}`}/>
// </SidebarMenuItem>
// )
// }
@@ -13,12 +13,12 @@ import {TooltipProvider} from "@/components/ui/tooltip";
import {
EmailFormSchema,
EmailFormType
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import Link from "next/link";
import {PasswordInput} from "@/components/wrappers/Auth/PaswordInput/password-input";
import {
updateEmailSettingsAction
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.action";
} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/EmailForm/email-form.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
@@ -2,7 +2,7 @@
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {prisma} from "@/prisma";
import {EmailFormSchema} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
import {EmailFormSchema} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
export const updateEmailSettingsAction = userAction
@@ -1,4 +1,4 @@
import {EmailForm} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/EmailForm";
import {EmailForm} from "@/components/wrappers/Dashboard/admin/AdminEmailTab/EmailForm/EmailForm";
import {Settings} from "@prisma/client";
import {Send} from "lucide-react";
import {ButtonWithLoading} from "@/components/wrappers/Button/ButtonWithLoading/ButtonWithLoading";
@@ -2,7 +2,7 @@ import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
import {Info, Send, ShieldCheck} from "lucide-react";
import {Switch} from "@/components/ui/switch";
import {Label} from "@/components/ui/label";
import {StorageS3Form} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/StorageS3Form";
import {StorageS3Form} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/StorageS3Form";
import {useState} from "react";
import {Settings} from "@prisma/client";
import {ButtonWithLoading} from "@/components/wrappers/Button/ButtonWithLoading/ButtonWithLoading";
@@ -13,7 +13,7 @@ import {updateUserAction} from "@/components/wrappers/Dashboard/Profile/UserForm
import {useRouter} from "next/navigation";
import {
updateStorageSettingsAction
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.action";
} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
export type SettingsStorageTabProps = {
@@ -13,12 +13,12 @@ import {TooltipProvider} from "@/components/ui/tooltip";
import {
S3FormSchema,
S3FormType
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.schema";
} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import {useRouter} from "next/navigation";
import {toast} from "sonner";
import {
updateS3SettingsAction
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.action";
} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
export type S3FormProps = {
defaultValues?: S3FormType;
@@ -5,7 +5,7 @@ import {prisma} from "@/prisma";
import {
S3FormSchema,
StorageSwitchSchema
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.schema";
} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
export const updateS3SettingsAction = userAction
@@ -0,0 +1,42 @@
"use client"
import {User, Settings} from "@prisma/client";
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";
export type AdminTabsProps = {
currentUser: User;
users: User[];
settings: Settings;
}
export const AdminTabs = (props: AdminTabsProps) => {
const {currentUser, users, settings} = props;
return (
<Tabs defaultValue="users">
<TabsList className="w-full">
<TabsTrigger className="w-full " value="users">Users</TabsTrigger>
<TabsTrigger className="w-full " value="email">Email</TabsTrigger>
<TabsTrigger className="w-full " value="storage">Storage</TabsTrigger>
</TabsList>
<TabsContent value="users">
<AdminUsersTable currentUser={currentUser} users={users}/>
</TabsContent>
<TabsContent value="email">
<SettingsEmailTab settings={settings}/>
</TabsContent>
<TabsContent value="storage">
<SettingsStorageTab settings={settings}/>
</TabsContent>
</Tabs>
)
}
@@ -0,0 +1,92 @@
import {flexRender, Row, RowData} from "@tanstack/react-table";
import {User} from "@prisma/client";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsUsersTab/columns-users";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {cn} from "@/lib/utils";
export type AdminUsersTableProps = {
currentUser: User;
users: User[]
}
export const AdminUsersTable = (props: AdminUsersTableProps) => {
const {currentUser, users} = props;
return (
<div className="flex flex-col h-full py-4">
<div className="flex gap-4 h-fit justify-between">
<h1>List of Portabase's users</h1>
</div>
<div className="mt-5">
<DataTableWithPagination
columns={usersColumns}
data={users}
DataTable={UsersDataTable}
dataTableProps={{currentUser}}
/>
</div>
</div>
)
}
export type usersDataTableProps = {
currentUser: User;
table: any,
}
export const UsersDataTable = ({currentUser, table}: usersDataTableProps) => {
return (
<div className="rounded-md border w-full ">
<Table className="w-full">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row: Row<User>) => (
<TableRow
className={cn(row.original.id === currentUser.id ? "bg-gray-200 pointer-events-none" : "")}
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))) : (
<TableRow>
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}
@@ -17,7 +17,7 @@ import {useState} from "react";
import {useMutation} from "@tanstack/react-query";
import {
updateStorageSettingsAction
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.action";
} from "@/components/wrappers/Dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {Database} from "@prisma/client";
@@ -5,16 +5,21 @@ import {useState} from "react";
import {
ColumnDef, getCoreRowModel, getPaginationRowModel, getSortedRowModel, SortingState, useReactTable
} from "@tanstack/react-table"
import {DataTable} from "@/components/wrappers/table/data-table";
import {DataTable as BaseDataTable} from "@/components/wrappers/table/data-table";
import {TablePagination} from "@/components/wrappers/table/table-pagination";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
extendedProps?: any
DataTable?: any
dataTableProps?: any
}
export function DataTableWithPagination<TData, TValue>({columns, data, extendedProps}: DataTableProps<TData, TValue>) {
export function DataTableWithPagination<TData, TValue>(props: DataTableProps<TData, TValue>) {
const {columns, data, extendedProps, DataTable = BaseDataTable, dataTableProps} = props;
const [sorting, setSorting] = useState<SortingState>([])
@@ -35,7 +40,7 @@ export function DataTableWithPagination<TData, TValue>({columns, data, extendedP
return (
<div className="flex flex-col justify-between h-full">
<DataTable table={table} />
<DataTable table={table} {...dataTableProps}/>
<TablePagination table={table} pageSizeOptions={[5, 10, 20, 50, 100]}/>
</div>
)
+21 -16
View File
@@ -1,5 +1,6 @@
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {flexRender} from "@tanstack/react-table";
import {flexRender, Row, RowData} from "@tanstack/react-table";
import {cn} from "@/lib/utils";
export type dataTableProps = {
table: any,
@@ -30,21 +31,25 @@ export const DataTable = ({table}: dataTableProps) => {
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
table.getRowModel().rows.map((row: Row<RowData>) => {
console.log("row.original",row.original.id)
return(
<TableRow
className={cn(row.original ?? "bg-gray-200 pointer-events-none")}
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
)
})
) : (
<TableRow>
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">