mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Working on role in workspace.
This commit is contained in:
@@ -32,10 +32,13 @@ export const registerUserAction = action
|
||||
where: {slug: "default"},
|
||||
});
|
||||
|
||||
const organizationRole = users.length > 0 ? "member" : "admin"
|
||||
|
||||
await prisma.userOrganization.create({
|
||||
data: {
|
||||
userId: newUser.id,
|
||||
organizationId: defaultOrganization.id
|
||||
organizationId: defaultOrganization.id,
|
||||
role:organizationRole
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {flexRender, Row, RowData} from "@tanstack/react-table";
|
||||
|
||||
import {User} from "@prisma/client";
|
||||
import {User, UserOrganization} from "@prisma/client";
|
||||
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {cn} from "@/lib/utils";
|
||||
@@ -23,7 +23,7 @@ export const AdminUsersTable = (props: AdminUsersTableProps) => {
|
||||
<DataTableWithPagination
|
||||
columns={usersColumnsAdmin}
|
||||
data={users}
|
||||
DataTable={UsersDataTable}
|
||||
DataTable={UsersDataTableAdmin}
|
||||
dataTableProps={{currentUser}}
|
||||
/>
|
||||
</div>
|
||||
@@ -37,7 +37,7 @@ export type usersDataTableProps = {
|
||||
table: any,
|
||||
}
|
||||
|
||||
export const UsersDataTable = ({currentUser, table}: usersDataTableProps) => {
|
||||
export const UsersDataTableAdmin = ({currentUser, table}: usersDataTableProps) => {
|
||||
|
||||
return (
|
||||
<div className="rounded-md border w-full ">
|
||||
@@ -62,22 +62,26 @@ export const UsersDataTable = ({currentUser, table}: usersDataTableProps) => {
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row: Row<User>) => (
|
||||
<TableRow
|
||||
className={cn(row.original.id === 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>
|
||||
))) : (
|
||||
table.getRowModel().rows.map((row: Row<User>) => {
|
||||
return(
|
||||
<TableRow
|
||||
className={cn((row.original.id) === 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.
|
||||
|
||||
@@ -14,7 +14,7 @@ export const LoggedInButton = async () => {
|
||||
|
||||
|
||||
return (
|
||||
<LoggedInDropdown>
|
||||
<LoggedInDropdown user={user}>
|
||||
<SidebarMenuButton>
|
||||
<Avatar className="size-6">
|
||||
<AvatarFallback>{user.name?.[0]}</AvatarFallback>
|
||||
|
||||
@@ -5,8 +5,11 @@ import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger
|
||||
import {signOutAction} from "@/features/auth/auth.action";
|
||||
import {redirect} from "next/navigation";
|
||||
import {CircleUser, LogOut, ShieldHalf} from "lucide-react";
|
||||
import {User} from "@prisma/client";
|
||||
|
||||
export type LoggedInDropdownProps = PropsWithChildren<{}>
|
||||
export type LoggedInDropdownProps = PropsWithChildren<{
|
||||
user: User
|
||||
}>
|
||||
|
||||
export const LoggedInDropdown = (props: LoggedInDropdownProps) => {
|
||||
|
||||
@@ -27,14 +30,16 @@ export const LoggedInDropdown = (props: LoggedInDropdownProps) => {
|
||||
<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>
|
||||
{props.user.role == "admin" ?
|
||||
<DropdownMenuItem onClick={() => {
|
||||
redirect("/dashboard/admin")
|
||||
}}>
|
||||
<div className="flex justify-start items-center gap-2">
|
||||
<ShieldHalf size={16}/>
|
||||
<span>Administration Panel</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
: null}
|
||||
<DropdownMenuItem onClick={() => {
|
||||
signOutAction()
|
||||
}}>
|
||||
|
||||
@@ -44,7 +44,8 @@ export const createOrganizationAction = userAction
|
||||
await prisma.userOrganization.create({
|
||||
data: {
|
||||
userId: ctx.user.id,
|
||||
organizationId: organization.id
|
||||
organizationId: organization.id,
|
||||
role: "admin"
|
||||
},
|
||||
});
|
||||
|
||||
@@ -113,6 +114,7 @@ export const updateOrganizationAction = userAction
|
||||
data: usersToAdd.map((userId) => ({
|
||||
userId: userId,
|
||||
organizationId: organization.id,
|
||||
role: "member"
|
||||
})),
|
||||
skipDuplicates: true, // Optional: to avoid duplicate insertion errors
|
||||
});
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ export const ButtonDeleteProject = (props: ButtonDeleteProjectProps) => {
|
||||
const mutation = useMutation({
|
||||
mutationFn: () => deleteProjectAction(props.projectId),
|
||||
onSuccess: async (result) => {
|
||||
router.push("/dashboard/projects")
|
||||
router.push("/dashboard")
|
||||
if(result.data.success) {
|
||||
toast.success(result.data.actionSuccess.message);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
|
||||
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
|
||||
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users-settings";
|
||||
import {User, Settings} from "@prisma/client";
|
||||
import {backupColumns} from "@/features/backup/columns";
|
||||
import {SettingsEmailTab} from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
|
||||
import {SettingsStorageTab} from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
|
||||
import {User, Settings, UserOrganization} from "@prisma/client";
|
||||
import {SettingsUsersTab} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/SettingsUsersTab";
|
||||
|
||||
|
||||
export type SettingsTabsProps = {
|
||||
currentUser: User;
|
||||
users: User[];
|
||||
users: UserOrganization[];
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {User} from "@prisma/client";
|
||||
import {User, UserOrganization} from "@prisma/client";
|
||||
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
|
||||
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users-settings";
|
||||
import {UsersDataTable} from "@/components/wrappers/dashboard/admin/admin-user-table";
|
||||
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: User[]
|
||||
users: UserOrganization[]
|
||||
}
|
||||
|
||||
export const SettingsUsersTab = (props: SettingsUsersTabProps) => {
|
||||
@@ -28,4 +30,68 @@ export const SettingsUsersTab = (props: SettingsUsersTabProps) => {
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
+18
-18
@@ -2,7 +2,7 @@
|
||||
|
||||
import {ColumnDef} from "@tanstack/react-table"
|
||||
import {Badge} from "@/components/ui/badge";
|
||||
import {User} from "@prisma/client";
|
||||
import {User, UserOrganization} from "@prisma/client";
|
||||
import {updateUserAction} from "@/components/wrappers/dashboard/profile/UserForm/user-form.action";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import {toast} from "sonner";
|
||||
@@ -12,7 +12,7 @@ import {Trash2} from "lucide-react";
|
||||
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
|
||||
export const usersColumns: ColumnDef<User>[] = [
|
||||
export const usersColumns: ColumnDef<UserOrganization>[] = [
|
||||
{
|
||||
accessorKey: "role",
|
||||
header: "Role",
|
||||
@@ -20,24 +20,24 @@ export const usersColumns: ColumnDef<User>[] = [
|
||||
const router = useRouter();
|
||||
const [role, setRole] = useState<string>(row.getValue("role"))
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
|
||||
onSuccess: () => {
|
||||
toast.success(`User updated successfully.`);
|
||||
router.refresh()
|
||||
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(`An error occurred while updating user information.`);
|
||||
},
|
||||
});
|
||||
// const updateMutation = useMutation({
|
||||
// mutationFn: () => updateUserAction({id: row.original.id, data: {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" ? "pending"
|
||||
: role === "pending" ? "user"
|
||||
const nextRole = role === "admin" ? "member"
|
||||
: role === "member" ? "user"
|
||||
: "admin";
|
||||
setRole(nextRole);
|
||||
await updateMutation.mutateAsync()
|
||||
// await updateMutation.mutateAsync()
|
||||
};
|
||||
|
||||
return <Badge
|
||||
@@ -47,11 +47,11 @@ export const usersColumns: ColumnDef<User>[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
accessorKey: "user.name",
|
||||
header: "Name"
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
accessorKey: "user.email",
|
||||
header: "Email"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,9 +7,11 @@ import {cn} from "@/lib/utils";
|
||||
import {buttonVariants} from "@/components/ui/button";
|
||||
import {ChartArea, Layers, Settings, ShieldHalf} from "lucide-react";
|
||||
import {usePathname} from "next/navigation";
|
||||
import {UserOrganization} from "@prisma/client";
|
||||
|
||||
export type SidebarMenuCustomProps = {
|
||||
currentOrganizationSlug: string
|
||||
currentOrganizationSlug: string,
|
||||
currentOrganizationUser: UserOrganization,
|
||||
}
|
||||
|
||||
export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
|
||||
@@ -27,13 +29,17 @@ export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
|
||||
title: "Statistics",
|
||||
url: "statistics",
|
||||
icon: ChartArea,
|
||||
},
|
||||
{
|
||||
}
|
||||
]
|
||||
|
||||
if (props.currentOrganizationUser.role === "admin") {
|
||||
items.push({
|
||||
title: "Settings",
|
||||
url: "settings",
|
||||
icon: Settings,
|
||||
},
|
||||
]
|
||||
},)
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const currentUrl = pathname;
|
||||
|
||||
@@ -37,6 +37,15 @@ export async function AppSidebar() {
|
||||
})
|
||||
|
||||
|
||||
const currentOrganizationUser = await prisma.userOrganization.findFirst({
|
||||
where:{
|
||||
userId: user.id,
|
||||
organization:{
|
||||
slug: currentOrganizationSlug != "" ? currentOrganizationSlug : "default",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return (
|
||||
<Sidebar collapsible="icon">
|
||||
@@ -57,7 +66,7 @@ export async function AppSidebar() {
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Application</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenuCustom currentOrganizationSlug={currentOrganizationSlug}/>
|
||||
<SidebarMenuCustom currentOrganizationUser={currentOrganizationUser} currentOrganizationSlug={currentOrganizationSlug}/>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
{user.role == "admin" ?
|
||||
|
||||
Reference in New Issue
Block a user