mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Some bug and visuals improvements.
This commit is contained in:
@@ -1,21 +1,15 @@
|
|||||||
import { PageParams } from "@/types/next";
|
import { PageParams } from "@/types/next";
|
||||||
import * as drizzleDb from "@/db";
|
|
||||||
|
|
||||||
import { Page, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
|
import { Page, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
|
||||||
import { AdminTabs } from "@/components/wrappers/dashboard/admin/admin-tabs";
|
import { AdminTabs } from "@/components/wrappers/dashboard/admin/admin-tabs";
|
||||||
import { currentUser } from "@/lib/auth/current-user";
|
|
||||||
import { db } from "@/db";
|
import { db } from "@/db";
|
||||||
import {eq, isNotNull, isNull} from "drizzle-orm";
|
import {isNull} from "drizzle-orm";
|
||||||
|
|
||||||
|
|
||||||
export default async function RoutePage(props: PageParams<{}>) {
|
export default async function RoutePage(props: PageParams<{}>) {
|
||||||
const user = await currentUser();
|
|
||||||
|
|
||||||
const users = await db.query.user.findMany({
|
const users = await db.query.user.findMany({
|
||||||
where: (fields) => isNull(fields.deletedAt)
|
where: (fields) => isNull(fields.deletedAt)
|
||||||
});
|
});
|
||||||
// const users = await db.query.user.findMany();
|
|
||||||
console.log("users",users);
|
|
||||||
|
|
||||||
const settings = await db.query.setting.findFirst({
|
const settings = await db.query.setting.findFirst({
|
||||||
where: (fields, { eq }) => eq(fields.name, "system"),
|
where: (fields, { eq }) => eq(fields.name, "system"),
|
||||||
@@ -26,7 +20,7 @@ export default async function RoutePage(props: PageParams<{}>) {
|
|||||||
<PageHeader>
|
<PageHeader>
|
||||||
<PageTitle>Administration Panel</PageTitle>
|
<PageTitle>Administration Panel</PageTitle>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<PageContent className="mt-10">
|
<PageContent>
|
||||||
<AdminTabs settings={settings!} users={users} />
|
<AdminTabs settings={settings!} users={users} />
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default async function RoutePage(props: PageParams<{}>) {
|
|||||||
</PageActions>
|
</PageActions>
|
||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<PageContent className="mt-10">
|
<PageContent>
|
||||||
{agents.length > 0 ? (
|
{agents.length > 0 ? (
|
||||||
<CardsWithPagination data={agents} cardItem={AgentCard} cardsPerPage={4} numberOfColumns={1} />
|
<CardsWithPagination data={agents} cardItem={AgentCard} cardsPerPage={4} numberOfColumns={1} />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,28 +1,20 @@
|
|||||||
"use client";
|
|
||||||
import { authClient } from "@/lib/auth/auth-client";
|
|
||||||
import { PageParams } from "@/types/next";
|
import { PageParams } from "@/types/next";
|
||||||
import {Page, PageHeader, PageTitle} from "@/features/layout/page";
|
import {Page, PageHeader, PageTitle} from "@/features/layout/page";
|
||||||
|
|
||||||
export default function RoutePage(props: PageParams<{}>) {
|
export default async function RoutePage(props: PageParams<{}>) {
|
||||||
const { data: activeOrganization } = authClient.useActiveOrganization();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<PageTitle>Dashboard</PageTitle>
|
<PageTitle>Dashboard</PageTitle>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="flex flex-1 flex-col gap-4">
|
<div className="flex flex-1 flex-col gap-4">
|
||||||
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
|
<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 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>
|
||||||
{/*<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />*/}
|
|
||||||
{/*{JSON.stringify(activeOrganization)}*/}
|
|
||||||
{/*{activeOrganization?.name}*/}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ export default async function RoutePage(props: PageParams<{
|
|||||||
numberOfColumns={1}
|
numberOfColumns={1}
|
||||||
extendedProps={proj}
|
extendedProps={proj}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : (
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-muted-foreground py-20">
|
||||||
|
<p className="text-lg font-medium">No databases found</p>
|
||||||
|
<p className="text-sm mt-2">You haven’t added any databases to this project yet.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default async function RoutePage(props: PageParams<{ }>) {
|
|||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<PageContent className="mt-10">
|
<PageContent>
|
||||||
{projects.length > 0 ? (
|
{projects.length > 0 ? (
|
||||||
<CardsWithPagination organizationSlug={organization.slug} data={projects} cardItem={ProjectCard}
|
<CardsWithPagination organizationSlug={organization.slug} data={projects} cardItem={ProjectCard}
|
||||||
cardsPerPage={4} numberOfColumns={1}/>
|
cardsPerPage={4} numberOfColumns={1}/>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import {
|
|||||||
DeleteOrganizationButton
|
DeleteOrganizationButton
|
||||||
} from "@/components/wrappers/dashboard/organization/DeleteOrganization/DeleteOrganizationButton";
|
} from "@/components/wrappers/dashboard/organization/DeleteOrganization/DeleteOrganizationButton";
|
||||||
import {EditButtonSettings} from "@/components/wrappers/dashboard/settings/EditButtonSettings/EditButtonSettings";
|
import {EditButtonSettings} from "@/components/wrappers/dashboard/settings/EditButtonSettings/EditButtonSettings";
|
||||||
|
import {
|
||||||
|
SettingsOrganizationMembersTable
|
||||||
|
} from "@/components/wrappers/dashboard/settings/settings-organization-members-table";
|
||||||
|
|
||||||
|
|
||||||
export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
||||||
@@ -17,11 +20,14 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log(organization);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<PageTitle className="flex items-center">
|
<PageTitle className="flex items-center">
|
||||||
Settings
|
Organization settings
|
||||||
{organization.slug != "default" ?
|
{organization.slug != "default" ?
|
||||||
<EditButtonSettings/>
|
<EditButtonSettings/>
|
||||||
: null}
|
: null}
|
||||||
@@ -32,11 +38,11 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
|||||||
: null}
|
: null}
|
||||||
</PageActions>
|
</PageActions>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<PageDescription>
|
{/*<PageDescription>*/}
|
||||||
Manage your organization settings.
|
{/* Manage your organization settings.*/}
|
||||||
</PageDescription>
|
{/*</PageDescription>*/}
|
||||||
<PageContent>
|
<PageContent>
|
||||||
{/* TODO add the list of organisation members (add, remove, edit) */}
|
<SettingsOrganizationMembersTable organization={organization} />
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@@ -1,5 +1,158 @@
|
|||||||
"use client";
|
// "use client";
|
||||||
|
//
|
||||||
|
// import {
|
||||||
|
// ColumnDef,
|
||||||
|
// flexRender,
|
||||||
|
// getCoreRowModel,
|
||||||
|
// useReactTable,
|
||||||
|
// getPaginationRowModel,
|
||||||
|
// getSortedRowModel,
|
||||||
|
// SortingState,
|
||||||
|
// ColumnFiltersState,
|
||||||
|
// getFilteredRowModel,
|
||||||
|
// } from "@tanstack/react-table";
|
||||||
|
//
|
||||||
|
// import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
|
// import { Input } from "@/components/ui/input";
|
||||||
|
//
|
||||||
|
// import { useState } from "react";
|
||||||
|
// import { TablePagination } from "./table-pagination";
|
||||||
|
// import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
//
|
||||||
|
// interface DataTableProps<TData, TValue> {
|
||||||
|
// columns: ColumnDef<TData, TValue>[];
|
||||||
|
// data: TData[];
|
||||||
|
// enableFilter?: boolean;
|
||||||
|
// enableSelect?: boolean;
|
||||||
|
// enablePagination?: boolean;
|
||||||
|
// paginationOptions?: {
|
||||||
|
// pageSize: number[];
|
||||||
|
// pageVisible: number;
|
||||||
|
// className?: string;
|
||||||
|
// };
|
||||||
|
// filterOptions?: {
|
||||||
|
// title?: string;
|
||||||
|
// key: string;
|
||||||
|
// };
|
||||||
|
// emptyText?: string;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function DataTable<TData, TValue>({
|
||||||
|
// columns,
|
||||||
|
// data,
|
||||||
|
// enableFilter = false,
|
||||||
|
// enablePagination = true,
|
||||||
|
// enableSelect = true,
|
||||||
|
// paginationOptions = { pageSize: [10, 20, 30, 40, 50, 100], pageVisible: 3 },
|
||||||
|
// filterOptions = { key: "id", title: "Filter by ID" },
|
||||||
|
// emptyText = "No data.",
|
||||||
|
// }: DataTableProps<TData, TValue>) {
|
||||||
|
// const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
// const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
|
// const [rowSelection, setRowSelection] = useState({});
|
||||||
|
//
|
||||||
|
// if (enableSelect && data.length > 0) {
|
||||||
|
// const selectColumnExists = columns.some((column) => column.id === "select");
|
||||||
|
//
|
||||||
|
// if (!selectColumnExists) {
|
||||||
|
// columns.unshift({
|
||||||
|
// id: "select",
|
||||||
|
// header: ({ table }) => (
|
||||||
|
// <Checkbox
|
||||||
|
// checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
|
||||||
|
// onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
// aria-label="Select all"
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
|
// cell: ({ row }) => <Checkbox checked={row.getIsSelected()} onCheckedChange={(value) => row.toggleSelected(!!value)} aria-label="Select row" />,
|
||||||
|
// enableSorting: false,
|
||||||
|
// enableHiding: false,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const table = useReactTable({
|
||||||
|
// data,
|
||||||
|
// columns,
|
||||||
|
// getCoreRowModel: getCoreRowModel(),
|
||||||
|
// getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
// onSortingChange: setSorting,
|
||||||
|
// getSortedRowModel: getSortedRowModel(),
|
||||||
|
// onColumnFiltersChange: setColumnFilters,
|
||||||
|
// getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
// onRowSelectionChange: setRowSelection,
|
||||||
|
// state: {
|
||||||
|
// sorting,
|
||||||
|
// columnFilters,
|
||||||
|
// rowSelection,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// <div className="h-full">
|
||||||
|
// {enableFilter && (
|
||||||
|
// <div className="flex items-center py-4">
|
||||||
|
// <Input
|
||||||
|
// placeholder={`${filterOptions.title ?? `Filter by ${filterOptions.key}`}`}
|
||||||
|
// value={(table.getColumn(filterOptions.key)?.getFilterValue() as string) ?? ""}
|
||||||
|
// onChange={(event) => table.getColumn(filterOptions.key)?.setFilterValue(event.target.value)}
|
||||||
|
// className="max-w-sm"
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// <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) => (
|
||||||
|
// <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>
|
||||||
|
// ))
|
||||||
|
// ) : (
|
||||||
|
// <TableRow>
|
||||||
|
// <TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
// {emptyText}
|
||||||
|
// </TableCell>
|
||||||
|
// </TableRow>
|
||||||
|
// )}
|
||||||
|
// </TableBody>
|
||||||
|
// </Table>
|
||||||
|
// </div>
|
||||||
|
// <div className="flex items-center justify-end space-x-2 py-4 mt-6">
|
||||||
|
// {enableSelect && (
|
||||||
|
// <div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
// {table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s) selected.
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// {enablePagination && (
|
||||||
|
// <TablePagination
|
||||||
|
// table={table}
|
||||||
|
// maxVisiblePages={paginationOptions?.pageVisible}
|
||||||
|
// pageSizeOptions={paginationOptions.pageSize}
|
||||||
|
// className={paginationOptions.className}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
"use client"
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
flexRender,
|
flexRender,
|
||||||
@@ -12,12 +165,14 @@ import {
|
|||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||||
import { Input } from "@/components/ui/input";
|
import {Input} from "@/components/ui/input";
|
||||||
|
|
||||||
import { useState } from "react";
|
import {useState} from "react";
|
||||||
import { TablePagination } from "./table-pagination";
|
import {TablePagination} from "./table-pagination";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import {Checkbox} from "@/components/ui/checkbox";
|
||||||
|
import {Button} from "@/components/ui/button";
|
||||||
|
import {useRouter} from "next/navigation";
|
||||||
|
|
||||||
interface DataTableProps<TData, TValue> {
|
interface DataTableProps<TData, TValue> {
|
||||||
columns: ColumnDef<TData, TValue>[];
|
columns: ColumnDef<TData, TValue>[];
|
||||||
@@ -34,37 +189,47 @@ interface DataTableProps<TData, TValue> {
|
|||||||
title?: string;
|
title?: string;
|
||||||
key: string;
|
key: string;
|
||||||
};
|
};
|
||||||
emptyText?: string;
|
emptyButton?: {
|
||||||
|
text: string;
|
||||||
|
variant: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
highlightRow?: (row: TData) => boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataTable<TData, TValue>({
|
export function DataTable<TData, TValue>({
|
||||||
columns,
|
columns,
|
||||||
data,
|
data,
|
||||||
enableFilter = false,
|
enableFilter = false,
|
||||||
enablePagination = true,
|
enablePagination = true,
|
||||||
enableSelect = true,
|
enableSelect = true,
|
||||||
paginationOptions = { pageSize: [10, 20, 30, 40, 50, 100], pageVisible: 3 },
|
paginationOptions = {pageSize: [10, 20, 30, 40, 50, 100], pageVisible: 3},
|
||||||
filterOptions = { key: "id", title: "Filter by ID" },
|
filterOptions = {key: "id", title: "Filter by ID"},
|
||||||
emptyText = "No data.",
|
emptyButton,
|
||||||
}: DataTableProps<TData, TValue>) {
|
highlightRow
|
||||||
|
}: DataTableProps<TData, TValue>) {
|
||||||
const [sorting, setSorting] = useState<SortingState>([]);
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||||
const [rowSelection, setRowSelection] = useState({});
|
const [rowSelection, setRowSelection] = useState({});
|
||||||
|
|
||||||
|
|
||||||
if (enableSelect && data.length > 0) {
|
if (enableSelect && data.length > 0) {
|
||||||
const selectColumnExists = columns.some((column) => column.id === "select");
|
const selectColumnExists = columns.some((column) => column.id === "select");
|
||||||
|
|
||||||
if (!selectColumnExists) {
|
if (!selectColumnExists) {
|
||||||
columns.unshift({
|
columns.unshift({
|
||||||
id: "select",
|
id: "select",
|
||||||
header: ({ table }) => (
|
header: ({table}) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
|
checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
|
||||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
aria-label="Select all"
|
aria-label="Select all"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
cell: ({ row }) => <Checkbox checked={row.getIsSelected()} onCheckedChange={(value) => row.toggleSelected(!!value)} aria-label="Select row" />,
|
cell: ({row}) => <Checkbox checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"/>,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
});
|
});
|
||||||
@@ -87,9 +252,11 @@ export function DataTable<TData, TValue>({
|
|||||||
rowSelection,
|
rowSelection,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
className="flex flex-col h-full"
|
||||||
|
>
|
||||||
{enableFilter && (
|
{enableFilter && (
|
||||||
<div className="flex items-center py-4">
|
<div className="flex items-center py-4">
|
||||||
<Input
|
<Input
|
||||||
@@ -100,55 +267,86 @@ export function DataTable<TData, TValue>({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="rounded-md border w-full">
|
<div className="flex flex-col justify-between h-full">
|
||||||
<Table className="w-full">
|
<div className="rounded-md border w-full">
|
||||||
<TableHeader>
|
<Table className="w-full">
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<TableHeader>
|
||||||
<TableRow key={headerGroup.id}>
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
{headerGroup.headers.map((header) => {
|
<TableRow key={headerGroup.id}>
|
||||||
return (
|
{headerGroup.headers.map((header) => {
|
||||||
<TableHead key={header.id}>
|
return (
|
||||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
<TableHead key={header.id}>
|
||||||
</TableHead>
|
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
);
|
</TableHead>
|
||||||
})}
|
);
|
||||||
</TableRow>
|
})}
|
||||||
))}
|
|
||||||
</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>
|
</TableRow>
|
||||||
))
|
))}
|
||||||
) : (
|
</TableHeader>
|
||||||
<TableRow>
|
<TableBody>
|
||||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
{table.getRowModel().rows?.length ? (
|
||||||
{emptyText}
|
table.getRowModel().rows.map((row) => (
|
||||||
</TableCell>
|
<TableRow key={row.id}
|
||||||
</TableRow>
|
className={highlightRow && highlightRow(row.original) ? "bg-gray-100 pointer-events-none" : ""}
|
||||||
)}
|
data-state={row.getIsSelected() && "selected"}>
|
||||||
</TableBody>
|
{row.getVisibleCells().map((cell) => (
|
||||||
</Table>
|
<TableCell
|
||||||
</div>
|
key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>
|
||||||
<div className="flex items-center justify-end space-x-2 py-4 mt-6">
|
))}
|
||||||
{enableSelect && (
|
</TableRow>
|
||||||
<div className="flex-1 text-sm text-muted-foreground">
|
))
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s) selected.
|
) : (
|
||||||
</div>
|
<TableRow>
|
||||||
)}
|
<TableCell colSpan={columns.length} className="p-0">
|
||||||
{enablePagination && (
|
{emptyButton ? (
|
||||||
<TablePagination
|
<Button
|
||||||
table={table}
|
variant={emptyButton.variant}
|
||||||
maxVisiblePages={paginationOptions?.pageVisible}
|
className="btn btn-primary cursor-pointer w-full rounded-tr-none rounded-tl-none py-12 text-lg font-bold"
|
||||||
pageSizeOptions={paginationOptions.pageSize}
|
onClick={() => {
|
||||||
className={paginationOptions.className}
|
router.push(emptyButton.path)
|
||||||
/>
|
}}
|
||||||
)}
|
>
|
||||||
|
<span>{emptyButton.text}</span>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-center py-12 text-lg font-bold">
|
||||||
|
<span className="text-lg font-bold">No data available</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-end space-x-2 py-4 mt-6">
|
||||||
|
{enableSelect && table.getFilteredRowModel().rows.length >= 1 && (
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of {table.getFilteredRowModel().rows.length} row(s)
|
||||||
|
selected.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{enablePagination && table.getFilteredRowModel().rows.length >= 1 && (
|
||||||
|
<TablePagination
|
||||||
|
table={table}
|
||||||
|
maxVisiblePages={paginationOptions?.pageVisible}
|
||||||
|
pageSizeOptions={(() => {
|
||||||
|
const rowCount = table.getFilteredRowModel().rows.length;
|
||||||
|
const allSizes = paginationOptions.pageSize.sort((a, b) => a - b);
|
||||||
|
const validSizes = allSizes.filter((size) => size <= rowCount);
|
||||||
|
const nextSize = allSizes.find((size) => size > rowCount);
|
||||||
|
if (nextSize) validSizes.push(nextSize);
|
||||||
|
return validSizes;
|
||||||
|
})()}
|
||||||
|
className={paginationOptions.className}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,26 +5,40 @@ import { SettingsEmailTab } from "@/components/wrappers/dashboard/admin/AdminEma
|
|||||||
import { SettingsStorageTab } from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
|
import { SettingsStorageTab } from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
|
||||||
import { AdminUsersTable } from "@/components/wrappers/dashboard/admin/admin-user-table";
|
import { AdminUsersTable } from "@/components/wrappers/dashboard/admin/admin-user-table";
|
||||||
import { User } from "@/db/schema/01_user";
|
import { User } from "@/db/schema/01_user";
|
||||||
import {Setting} from "@/db/schema/00_setting";
|
import { Setting } from "@/db/schema/00_setting";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
export type AdminTabsProps = {
|
export type AdminTabsProps = {
|
||||||
users: User[];
|
users: User[];
|
||||||
settings: Setting;
|
settings: Setting;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdminTabs = (props: AdminTabsProps) => {
|
export const AdminTabs = ({ users, settings }: AdminTabsProps) => {
|
||||||
const { users, settings } = props;
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const [tab, setTab] = useState<string>(() => searchParams.get("tab") ?? "users");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const newTab = searchParams.get("tab") ?? "users";
|
||||||
|
setTab(newTab);
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
|
const handleChangeTab = (value: string) => {
|
||||||
|
router.push(`?tab=${value}`);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="users">
|
<Tabs className="h-full" value={tab} onValueChange={handleChangeTab}>
|
||||||
<TabsList className="w-full">
|
<TabsList className="w-full">
|
||||||
<TabsTrigger className="w-full " value="users">
|
<TabsTrigger className="w-full" value="users">
|
||||||
Users
|
Users
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger className="w-full " value="email">
|
<TabsTrigger className="w-full" value="email">
|
||||||
Email
|
Email
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger className="w-full " value="storage">
|
<TabsTrigger className="w-full" value="storage">
|
||||||
Storage
|
Storage
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const AdminUsersTable = (props: AdminUsersTableProps) => {
|
|||||||
<div className="flex gap-4 h-fit justify-between">
|
<div className="flex gap-4 h-fit justify-between">
|
||||||
<h1>List of Portabase's users</h1>
|
<h1>List of Portabase's users</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5">
|
<div className="mt-5 h-full">
|
||||||
<DataTable columns={usersColumnsAdmin} data={users} />
|
<DataTable columns={usersColumnsAdmin} data={users} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
|
|||||||
header: "Role",
|
header: "Role",
|
||||||
cell: ({row}) => {
|
cell: ({row}) => {
|
||||||
const [role, setRole] = useState<string>(row.getValue("role"));
|
const [role, setRole] = useState<string>(row.getValue("role"));
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const isCurrentUser = session?.user.email === row.original.email;
|
||||||
|
|
||||||
const updateMutation = useMutation({
|
const updateMutation = useMutation({
|
||||||
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
|
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
|
||||||
@@ -37,7 +39,11 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge className="cursor-pointer" onClick={() => handleUpdateRole()} variant="outline">
|
<Badge
|
||||||
|
className={isCurrentUser ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
|
||||||
|
onClick={isCurrentUser ? undefined : () => handleUpdateRole()}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
{role}
|
{role}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
@@ -73,23 +79,18 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{!session || session?.user.email === row.original.email ? null :
|
|
||||||
<ButtonWithLoading
|
<ButtonWithLoading
|
||||||
|
disabled={!session || session?.user.email === row.original.email}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
text=""
|
text=""
|
||||||
icon={<Trash2 color="red" size={15}/>}
|
icon={<Trash2 color="red" size={15}/>}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await mutation.mutateAsync();
|
await mutation.mutateAsync();
|
||||||
}}
|
}}
|
||||||
size="icon"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function AppSidebar() {
|
|||||||
<SidebarMenuCustomMain/>
|
<SidebarMenuCustomMain/>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
|
|
||||||
<SidebarMenu>
|
<SidebarMenu className="mb-2">
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<LoggedInButton />
|
<LoggedInButton />
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const SideBarFooterCredit = (props: SideBarFooterCreditProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{state === "expanded" && (
|
{state === "expanded" && (
|
||||||
<div className="text-center">
|
<div className="text-center mb-2">
|
||||||
<h1 className="text-[10px]">Portabase Community Edition v{env.NEXT_PUBLIC_PROJECT_VERSION}</h1>
|
<h1 className="text-[10px]">Portabase Community Edition v{env.NEXT_PUBLIC_PROJECT_VERSION}</h1>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,10 +13,15 @@ export const ProjectCard = (props: projectCardProps) => {
|
|||||||
const { data: project, organizationSlug } = props;
|
const { data: project, organizationSlug } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/dashboard/projects/${project.id}`} className="block transition-all duration-200 hover:scale-[1.01] hover:shadow-md">
|
<Link
|
||||||
|
href={`/dashboard/projects/${project.id}`}
|
||||||
|
className="block transition-all duration-200 hover:scale-[1.01] hover:shadow-md"
|
||||||
|
>
|
||||||
<Card className="flex flex-row justify-between">
|
<Card className="flex flex-row justify-between">
|
||||||
<div className="">
|
<div className="flex-1 text-left">
|
||||||
<CardHeader className="text-2xl font-bold">{project.name}</CardHeader>
|
<CardHeader className="text-2xl font-bold">
|
||||||
|
{project.name}
|
||||||
|
</CardHeader>
|
||||||
<CardContent>{project.databases.length} databases</CardContent>
|
<CardContent>{project.databases.length} databases</CardContent>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export const DatabaseCard = (props: databaseCardProps) => {
|
|||||||
|
|
||||||
<Card className="flex flex-row justify-between">
|
<Card className="flex flex-row justify-between">
|
||||||
<div className="flex items-center space-x-4 px-4">
|
<div className="flex items-center space-x-4 px-4">
|
||||||
<Image src="/PostgreSQL.png" alt="Database type Icon" width={60} height={60} className="object-cover ml-4" />
|
<Image src="/images/postgresql.png" alt="Database type Icon" width={60} height={60} className="object-cover ml-4" />
|
||||||
|
|
||||||
<div className="justify-between">
|
<div className="justify-between">
|
||||||
<div className="font-medium">Name: {database.name}</div>
|
<div className="font-medium">Name: {database.name}</div>
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import {User, Settings, UserOrganization} from "@prisma/client";
|
|
||||||
import {SettingsUsersTab} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/SettingsUsersTab";
|
|
||||||
|
|
||||||
|
|
||||||
export type SettingsTabsProps = {
|
|
||||||
currentUser: User;
|
|
||||||
users: UserOrganization[];
|
|
||||||
settings: Settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SettingsTabs = (props: SettingsTabsProps) => {
|
|
||||||
|
|
||||||
const {currentUser, users} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsUsersTab currentUser={currentUser} users={users}/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users-settings";
|
|
||||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
|
||||||
import {flexRender, Row} from "@tanstack/react-table";
|
|
||||||
import {cn} from "@/lib/utils";
|
|
||||||
|
|
||||||
|
|
||||||
export type SettingsUsersTabProps = {
|
|
||||||
// currentUser: User;
|
|
||||||
// users: UserOrganization[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SettingsUsersTab = (props: SettingsUsersTabProps) => {
|
|
||||||
|
|
||||||
// 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 organization'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<UserOrganization>) => {*/}
|
|
||||||
{/* return(*/}
|
|
||||||
{/* <TableRow*/}
|
|
||||||
{/* className={cn((row.original.userId) === currentUser.id ? "opacity-40 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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import { useMutation } from "@tanstack/react-query";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
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/03_member";
|
|
||||||
|
|
||||||
export const usersColumns: ColumnDef<OrganizationMember>[] = [
|
|
||||||
{
|
|
||||||
accessorKey: "role",
|
|
||||||
header: "Role",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const [role, setRole] = useState<string>(row.getValue("role"));
|
|
||||||
|
|
||||||
const updateMutation = useMutation({
|
|
||||||
mutationFn: () => updateUserOrganizationAction({ id: row.original.id, role: role }),
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success(`User updated successfully.`);
|
|
||||||
// router.refresh()
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
toast.error(`An error occurred while updating user information.`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleUpdateRole = async () => {
|
|
||||||
const nextRole = role === "admin" ? "member" : role === "member" ? "admin" : "admin";
|
|
||||||
setRole(nextRole);
|
|
||||||
await updateMutation.mutateAsync();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Badge className="cursor-pointer" onClick={() => handleUpdateRole()} variant="outline">
|
|
||||||
{role}
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "user.name",
|
|
||||||
header: "Name",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "user.email",
|
|
||||||
header: "Email",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "updatedAt",
|
|
||||||
header: "Updated At",
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
"use server"
|
|
||||||
import {userAction} from "@/safe-actions";
|
|
||||||
import {z} from "zod";
|
|
||||||
import {prisma} from "@/prisma";
|
|
||||||
|
|
||||||
|
|
||||||
export const updateUserOrganizationAction = userAction
|
|
||||||
.schema(
|
|
||||||
z.object({
|
|
||||||
id: z.string(),
|
|
||||||
role: z.string(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.action(async ({parsedInput, ctx}) => {
|
|
||||||
const updatedUserOrganization = await prisma.userOrganization.update({
|
|
||||||
where: {
|
|
||||||
id: parsedInput.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
role: parsedInput.role,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
data: updatedUserOrganization,
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import {MemberWithUser} from "@/db/schema/02_organization";
|
||||||
|
import {useState} from "react";
|
||||||
|
import {useSession} from "@/lib/auth/auth-client";
|
||||||
|
import {useMutation} from "@tanstack/react-query";
|
||||||
|
import {updateUserAction} from "@/components/wrappers/dashboard/profile/UserForm/user-form.action";
|
||||||
|
import {toast} from "sonner";
|
||||||
|
import {Badge} from "@/components/ui/badge";
|
||||||
|
|
||||||
|
export const organizationMemberColumns: ColumnDef<MemberWithUser>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "role",
|
||||||
|
header: "Role",
|
||||||
|
cell: ({row}) => {
|
||||||
|
const [role, setRole] = useState<string>(row.getValue("role"));
|
||||||
|
const { data: session } = useSession();
|
||||||
|
const isCurrentUser = session?.user.email === row.original.user.email;
|
||||||
|
|
||||||
|
// const updateMutation = useMutation({
|
||||||
|
// mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
|
||||||
|
// onSuccess: () => {
|
||||||
|
// toast.success(`User updated successfully.`);
|
||||||
|
// },
|
||||||
|
// onError: () => {
|
||||||
|
// toast.error(`An error occurred while updating user information.`);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const handleUpdateRole = async () => {
|
||||||
|
// const nextRole = role === "admin" ? "pending" : role === "pending" ? "user" : "admin";
|
||||||
|
// setRole(nextRole);
|
||||||
|
// await updateMutation.mutateAsync();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
className={isCurrentUser ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
|
||||||
|
onClick={isCurrentUser ? undefined : () => handleUpdateRole()}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
{role}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "user.name",
|
||||||
|
header: "Name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "user.email",
|
||||||
|
header: "Email",
|
||||||
|
}
|
||||||
|
|
||||||
|
];
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import {DataTable} from "@/components/wrappers/common/table/data-table";
|
||||||
|
import {usersColumnsAdmin} from "@/components/wrappers/dashboard/admin/columns-users";
|
||||||
|
import {MemberWithUser, Organization, OrganizationWithMembers} from "@/db/schema/02_organization";
|
||||||
|
import {OrganizationInvitation} from "@/db/schema/04_invitation";
|
||||||
|
import {organizationMemberColumns} from "@/components/wrappers/dashboard/settings/columns-organization-members";
|
||||||
|
|
||||||
|
|
||||||
|
interface SettingsOrganizationMembersTableProps {
|
||||||
|
organization: OrganizationWithMembers
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsOrganizationMembersTable = ({organization}: SettingsOrganizationMembersTableProps) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full py-4">
|
||||||
|
<div className="flex gap-4 h-fit justify-between">
|
||||||
|
<h1>List of Organization members</h1>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 h-full">
|
||||||
|
<DataTable columns={organizationMemberColumns} data={organization.members as MemberWithUser[]}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user