Merged in ENCOA-82_Permissions (pull request #69)

Updated permissions to have a key to group them
This commit is contained in:
João Ramos
2024-08-14 19:38:30 +00:00
committed by Tiago Ribeiro
4 changed files with 197 additions and 109 deletions

View File

@@ -1,62 +1,105 @@
import {Permission} from "@/interfaces/permissions"; import React from "react";
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; import { Permission } from "@/interfaces/permissions";
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
Row,
} from "@tanstack/react-table";
import Link from "next/link"; import Link from "next/link";
import {convertCamelCaseToReadable} from "@/utils/string"; import { convertCamelCaseToReadable } from "@/utils/string";
interface Props { interface Props {
permissions: Permission[]; permissions: Permission[];
} }
const columnHelper = createColumnHelper<Permission>(); const columnHelper = createColumnHelper<Permission>();
const defaultColumns = [ const defaultColumns = [
columnHelper.accessor("type", { columnHelper.accessor("type", {
header: () => <span>Type</span>, header: () => <span>Type</span>,
cell: ({row, getValue}) => ( cell: ({ row, getValue }) => (
<Link <Link
href={`/permissions/${row.original.id}`} href={`/permissions/${row.original.id}`}
key={row.id} key={row.id}
className="underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer"> className="underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer"
{convertCamelCaseToReadable(getValue() as string)} >
</Link> {convertCamelCaseToReadable(getValue() as string)}
), </Link>
}), ),
}),
]; ];
export default function PermissionList({permissions}: Props) { export default function PermissionList({ permissions }: Props) {
const table = useReactTable({ const table = useReactTable({
data: permissions, data: permissions,
columns: defaultColumns, columns: defaultColumns,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
}); });
return (
<div className="w-full"> const groupedData: { [key: string]: Row<Permission>[] } = table
<div className="w-full flex flex-col gap-2"> .getRowModel()
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full"> .rows.reduce((groups: { [key: string]: Row<Permission>[] }, row) => {
<thead> const parent = row.original.topic;
{table.getHeaderGroups().map((headerGroup) => ( if (!groups[parent]) {
<tr key={headerGroup.id}> groups[parent] = [];
{headerGroup.headers.map((header) => ( }
<th className="py-4 px-4 text-left" key={header.id}> groups[parent].push(row);
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} return groups;
</th> }, {});
))}
</tr> return (
))} <div className="w-full">
</thead> <div className="w-full flex flex-col gap-2">
<tbody className="px-2"> <table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
{table.getRowModel().rows.map((row) => ( <thead>
<tr className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2" key={row.id}> {table.getHeaderGroups().map((headerGroup) => (
{row.getVisibleCells().map((cell) => ( <tr key={headerGroup.id}>
<td className="px-4 py-2 items-center w-fit" key={cell.id}> {headerGroup.headers.map((header) => (
{flexRender(cell.column.columnDef.cell, cell.getContext())} <th className="py-4 px-4 text-left" key={header.id}>
</td> {header.isPlaceholder
))} ? null
</tr> : flexRender(
))} header.column.columnDef.header,
</tbody> header.getContext()
</table> )}
</div> </th>
</div> ))}
); </tr>
))}
</thead>
<tbody className="px-2">
{Object.keys(groupedData).map((parent) => (
<React.Fragment key={parent}>
<tr>
<td className="px-2 py-2 items-center w-fit">
<strong>{parent}</strong>
</td>
</tr>
{groupedData[parent].map((row, i) => (
<tr
className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2"
key={row.id}
>
{row.getVisibleCells().map((cell) => (
<td
className="px-4 py-2 items-center w-fit"
key={cell.id}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
))}
</tr>
))}
</React.Fragment>
))}
</tbody>
</table>
</div>
</div>
);
} }

View File

@@ -1,58 +1,89 @@
export const markets = ["au", "br", "de"] as const; export interface PermissionTopic {
topic: string;
list: string[];
}
export const permissions = [ export const permissions = [
// generate codes are basicly invites {
"createCodeStudent", topic: "Manage Corporate",
"createCodeTeacher", list: [
"createCodeCorporate", "viewCorporate",
"createCodeCountryManager", "editCorporate",
"createCodeAdmin", "deleteCorporate",
// exams "createCodeCorporate",
"createReadingExam", ],
"createListeningExam", },
"createWritingExam", {
"createSpeakingExam", topic: "Manage Admin",
"createLevelExam", list: ["viewAdmin", "editAdmin", "deleteAdmin", "createCodeAdmin"],
// view pages },
"viewExams", {
"viewExercises", topic: "Manage Student",
"viewRecords", list: ["viewStudent", "editStudent", "deleteStudent", "createCodeStudent"],
"viewStats", },
"viewTickets", {
"viewPaymentRecords", topic: "Manage Teacher",
// view data list: ["viewTeacher", "editTeacher", "deleteTeacher", "createCodeTeacher"],
"viewStudent", },
"viewTeacher", {
"viewCorporate", topic: "Manage Country Manager",
"viewCountryManager", list: [
"viewAdmin", "viewCountryManager",
"viewGroup", "editCountryManager",
"viewCodes", "deleteCountryManager",
// edit data "createCodeCountryManager",
"editStudent", ],
"editTeacher", },
"editCorporate", {
"editCountryManager", topic: "Manage Exams",
"editAdmin", list: [
"editGroup", "createReadingExam",
// delete data "createListeningExam",
"deleteStudent", "createWritingExam",
"deleteTeacher", "createSpeakingExam",
"deleteCorporate", "createLevelExam",
"deleteCountryManager", ],
"deleteAdmin", },
"deleteGroup", {
"deleteCodes", topic: "View Pages",
// create options list: [
"createGroup", "viewExams",
"createCodes", "viewExercises",
"all", "viewRecords",
"viewStats",
"viewTickets",
"viewPaymentRecords",
],
},
{
topic: "Manage Group",
list: ["viewGroup", "editGroup", "deleteGroup", "createGroup"],
},
{
topic: "Manage Codes",
list: ["viewCodes", "deleteCodes", "createCodes"],
},
{
topic: "Others",
list: ["all"],
},
] as const; ] as const;
export type PermissionType = (typeof permissions)[keyof typeof permissions]; const permissionsList = [
...new Set(
permissions.reduce(
(accm: string[], permission) => [...accm, ...permission.list],
[]
)
),
];
export type PermissionType =
(typeof permissionsList)[keyof typeof permissionsList];
export interface Permission { export interface Permission {
id: string; id: string;
type: PermissionType; type: PermissionType;
users: string[]; topic: string;
users: string[];
} }

View File

@@ -55,9 +55,7 @@ interface Props {
} }
export default function Page(props: Props) { export default function Page(props: Props) {
const { permissions, user } = props; const { permissions, user } = props;
return ( return (
<> <>
<Head> <Head>

View File

@@ -16,12 +16,13 @@ import {
Permission, Permission,
PermissionType, PermissionType,
permissions, permissions,
PermissionTopic,
} from "@/interfaces/permissions"; } from "@/interfaces/permissions";
import {v4} from "uuid"; import { v4 } from "uuid";
const db = getFirestore(app); const db = getFirestore(app);
async function createPermission(type: string) { async function createPermission(topic: string, type: string) {
const permData = doc(db, "permissions", v4()); const permData = doc(db, "permissions", v4());
const permDoc = await getDoc(permData); const permDoc = await getDoc(permData);
if (permDoc.exists()) { if (permDoc.exists()) {
@@ -30,9 +31,14 @@ async function createPermission(type: string) {
await setDoc(permData, { await setDoc(permData, {
type, type,
topic,
users: [], users: [],
}); });
} }
interface PermissionsHelperList {
topic: string;
type: string;
}
export function getPermissions(userId: string | undefined, docs: Permission[]) { export function getPermissions(userId: string | undefined, docs: Permission[]) {
if (!userId) { if (!userId) {
return []; return [];
@@ -52,9 +58,19 @@ export function getPermissions(userId: string | undefined, docs: Permission[]) {
} }
export async function bootstrap() { export async function bootstrap() {
await permissions.forEach(async (type) => { await permissions
await createPermission(type); .reduce((accm: PermissionsHelperList[], permissionData) => {
}); return [
...accm,
...permissionData.list.map((type) => ({
topic: permissionData.topic,
type,
})),
];
}, [])
.forEach(async ({ topic, type }) => {
await createPermission(topic, type);
});
} }
export async function getPermissionDoc(id: string) { export async function getPermissionDoc(id: string) {