ENCOA-273

This commit is contained in:
Tiago Ribeiro
2024-12-11 14:09:10 +00:00
parent d074ec390c
commit eabfcd026b
7 changed files with 144 additions and 97 deletions

View File

@@ -3,6 +3,7 @@ import { RolePermission } from "@/resources/entityPermissions";
export interface Entity {
id: string;
label: string;
licenses: number
}
export interface Role {

View File

@@ -8,6 +8,7 @@ import {Entity} from "@/interfaces/entity";
import { doesEntityAllow } from "@/utils/permissions";
import { getUser } from "@/utils/users.be";
import { requestUser } from "@/utils/api";
import { isAdmin } from "@/utils/users";
const db = client.db(process.env.MONGODB_DB);
@@ -51,11 +52,19 @@ async function patch(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query as { id: string };
if (!user.entities.map((x) => x.id).includes(id)) {
if (!user.entities.map((x) => x.id).includes(id) && !isAdmin(user)) {
return res.status(403).json({ ok: false });
}
if (req.body.label) {
const entity = await db.collection<Entity>("entities").updateOne({ id }, { $set: { label: req.body.label } });
return res.status(200).json({ ok: entity.acknowledged });
}
if (req.body.licenses) {
const entity = await db.collection<Entity>("entities").updateOne({ id }, { $set: { licenses: req.body.licenses } });
return res.status(200).json({ ok: entity.acknowledged });
}
return res.status(200).json({ ok: true });
}

View File

@@ -37,6 +37,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const entity: Entity = {
id: v4(),
label: req.body.label,
licenses: req.body.licenses
};
const members = req.body.members as string[] | undefined || []

View File

@@ -9,8 +9,5 @@ type Data = {
};
export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
// await db.collection("users").updateMany({}, {$set: {entities: []}});
await db.collection("invites").deleteMany({});
res.status(200).json({ name: "John Doe" });
}

View File

@@ -32,6 +32,7 @@ import {
BsClockFill,
BsEnvelopeFill,
BsFillPersonVcardFill,
BsHash,
BsPerson,
BsPlus,
BsSquare,
@@ -165,6 +166,27 @@ export default function Home({user, entity, users, linkedUsers}: Props) {
.finally(() => setIsLoading(false));
};
const editLicenses = () => {
if (!isAdmin(user)) return;
const licenses = prompt("Update the number of licenses:", (entity.licenses || 0).toString());
if (!licenses) return;
if (!parseInt(licenses) || parseInt(licenses) <= 0) return toast.error("Write a valid number of licenses!")
setIsLoading(true);
axios
.patch(`/api/entities/${entity.id}`, { licenses })
.then(() => {
toast.success("The entity has been updated successfully!");
router.replace(router.asPath);
})
.catch((e) => {
console.error(e);
toast.error("Something went wrong!");
})
.finally(() => setIsLoading(false));
};
const deleteGroup = () => {
if (!canDeleteEntity) return;
if (!confirm("Are you sure you want to delete this entity?")) return;
@@ -274,7 +296,7 @@ export default function Home({user, entity, users, linkedUsers}: Props) {
className="text-mti-purple hover:text-mti-purple-dark transition ease-in-out duration-300 text-xl">
<BsChevronLeft />
</Link>
<h2 className="font-bold text-2xl">{entity.label}</h2>
<h2 className="font-bold text-2xl">{entity.label} {isAdmin(user) && `- ${entity.licenses || 0} licenses`}</h2>
</div>
</div>
<div className="flex items-center gap-2">
@@ -285,6 +307,15 @@ export default function Home({user, entity, users, linkedUsers}: Props) {
<BsTag />
<span className="text-xs">Rename Entity</span>
</button>
{isAdmin(user) && (
<button
onClick={editLicenses}
disabled={isLoading || !isAdmin(user)}
className="flex items-center gap-1 px-2 py-2 border rounded-full hover:bg-neutral-100 disabled:hover:bg-transparent disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition ease-in-out duration-300">
<BsHash />
<span className="text-xs">Edit Licenses</span>
</button>
)}
<button
onClick={() => router.push(`/entities/${entity.id}/roles`)}
disabled={isLoading || !canViewRoles}

View File

@@ -51,6 +51,7 @@ export default function Home({user, users}: Props) {
const [isLoading, setIsLoading] = useState(false);
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
const [label, setLabel] = useState("");
const [licenses, setLicenses] = useState(0);
const { rows, renderSearch } = useListSearch<User>([["name"], ["corporateInformation", "companyInformation", "name"]], users);
const { items, renderMinimal } = usePagination<User>(rows, 16);
@@ -64,7 +65,7 @@ export default function Home({user, users}: Props) {
setIsLoading(true);
axios
.post<Entity>(`/api/entities`, {label, members: selectedUsers})
.post<Entity>(`/api/entities`, { label, licenses, members: selectedUsers })
.then((result) => {
toast.success("Your entity has been created successfully!");
router.replace(`/entities/${result.data.id}`);
@@ -104,7 +105,7 @@ export default function Home({user, users}: Props) {
<div className="flex items-center gap-4">
<button
onClick={createGroup}
disabled={!label.trim() || isLoading}
disabled={!label.trim() || licenses <= 0 || isLoading}
className="flex items-center gap-1 px-2 py-2 border rounded-full border-mti-green bg-mti-green-light text-white hover:bg-mti-green-dark disabled:hover:bg-mti-green-light disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition ease-in-out duration-300">
<BsCheck />
<span className="text-xs">Create Entity</span>
@@ -112,10 +113,17 @@ export default function Home({user, users}: Props) {
</div>
</div>
<Divider />
<div className="w-full grid grid-cols-2 gap-4">
<div className="flex flex-col gap-4 w-full">
<span className="font-semibold text-xl">Entity Label:</span>
<Input name="name" onChange={setLabel} type="text" placeholder="Entity A" />
</div>
<div className="flex flex-col gap-4 w-full">
<span className="font-semibold text-xl">Licenses:</span>
<Input name="licenses" min={0} onChange={(v) => setLicenses(parseInt(v))} type="number" placeholder="12" />
</div>
</div>
<Divider />
<div className="flex items-center justify-between mb-4">
<span className="font-semibold text-xl">Members ({selectedUsers.length} selected):</span>

View File

@@ -6,7 +6,7 @@ import { ToastContainer } from "react-toastify";
import Layout from "@/components/High/Layout";
import { GroupWithUsers, User } from "@/interfaces/user";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { getUserName } from "@/utils/users";
import { getUserName, isAdmin } from "@/utils/users";
import { convertToUsers, getGroupsForUser } from "@/utils/groups.be";
import { countEntityUsers, getEntityUsers, getSpecificUsers, getUsers } from "@/utils/users.be";
import { checkAccess, findAllowedEntities, getTypesOfUser } from "@/utils/permissions";
@@ -64,7 +64,7 @@ export default function Home({ user, entities }: Props) {
</span>
<span className="flex items-center gap-1">
<span className="bg-mti-purple text-white font-semibold px-2">Members</span>
<span className="bg-mti-purple-light/50 px-2">{count}</span>
<span className="bg-mti-purple-light/50 px-2">{count}{isAdmin(user) && ` / ${entity.licenses || 0}`}</span>
</span>
<span>
{users.map(getUserName).join(", ")}{' '}