Compare commits
31 Commits
migration-
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22209ee1c1 | ||
|
|
0e2f53db0a | ||
|
|
9177a6b2ac | ||
|
|
6d1e8a9788 | ||
|
|
1c61d50a5c | ||
|
|
9f0ba418e5 | ||
|
|
6fd2e64e04 | ||
|
|
2c01e6b460 | ||
|
|
6e0c4d4361 | ||
|
|
745eef981f | ||
|
|
7a33f42bcd | ||
|
|
02564c8426 | ||
|
|
eab6ab03b7 | ||
|
|
6f534662e1 | ||
|
|
fbc7abdabb | ||
|
|
b7349b5df8 | ||
|
|
298901a642 | ||
|
|
88eafafe12 | ||
|
|
31a01a3157 | ||
|
|
a5b3a7e94d | ||
|
|
49e8237e99 | ||
|
|
d5769c2cb9 | ||
|
|
e49a325074 | ||
|
|
e6528392a2 | ||
|
|
620e4dd787 | ||
|
|
e3847baadb | ||
|
|
5b8631ab6a | ||
|
|
f9f29eabb3 | ||
|
|
898edb152f | ||
|
|
bf0d696b2f | ||
|
|
d91b1c14e7 |
@@ -23,6 +23,8 @@ COPY . .
|
|||||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
ENV MONGODB_URI "mongodb+srv://user:JKpFBymv0WLv3STj@encoach.lz18a.mongodb.net/?retryWrites=true&w=majority&appName=EnCoach"
|
||||||
|
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
# If using npm comment out above and use below instead
|
# If using npm comment out above and use below instead
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import moment from "moment";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
mutateUser: KeyedMutator<User>;
|
mutateUser: (user: User) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DemographicInformationInput({user, mutateUser}: Props) {
|
export default function DemographicInformationInput({user, mutateUser}: Props) {
|
||||||
@@ -42,7 +42,7 @@ export default function DemographicInformationInput({user, mutateUser}: Props) {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.patch("/api/users/update", {
|
.patch<{user: User}>("/api/users/update", {
|
||||||
demographicInformation: {
|
demographicInformation: {
|
||||||
country,
|
country,
|
||||||
phone: `+${countryCodes.findOne("countryCode" as any, country!).countryCallingCode}${phone}`,
|
phone: `+${countryCodes.findOne("countryCode" as any, country!).countryCallingCode}${phone}`,
|
||||||
@@ -54,7 +54,7 @@ export default function DemographicInformationInput({user, mutateUser}: Props) {
|
|||||||
},
|
},
|
||||||
agentInformation: user.type === "agent" ? {companyName, commercialRegistration} : undefined,
|
agentInformation: user.type === "agent" ? {companyName, commercialRegistration} : undefined,
|
||||||
})
|
})
|
||||||
.then((response) => mutateUser((response.data as {user: User}).user))
|
.then((response) => mutateUser(response.data.user))
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Something went wrong, please try again later!", {toastId: "user-update-error"});
|
toast.error("Something went wrong, please try again later!", {toastId: "user-update-error"});
|
||||||
})
|
})
|
||||||
@@ -89,7 +89,15 @@ export default function DemographicInformationInput({user, mutateUser}: Props) {
|
|||||||
<label className="font-normal text-base text-mti-gray-dim">Country *</label>
|
<label className="font-normal text-base text-mti-gray-dim">Country *</label>
|
||||||
<CountrySelect value={country} onChange={setCountry} />
|
<CountrySelect value={country} onChange={setCountry} />
|
||||||
</div>
|
</div>
|
||||||
<Input type="tel" name="phone" label="Phone number" onChange={(e) => setPhone(e)} value={phone} placeholder="Enter phone number" required />
|
<Input
|
||||||
|
type="tel"
|
||||||
|
name="phone"
|
||||||
|
label="Phone number"
|
||||||
|
onChange={(e) => setPhone(e)}
|
||||||
|
value={phone}
|
||||||
|
placeholder="Enter phone number"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{user.type === "student" && (
|
{user.type === "student" && (
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -1,51 +1,77 @@
|
|||||||
import {Column, flexRender, getCoreRowModel, getSortedRowModel, useReactTable} from "@tanstack/react-table";
|
import {Column, flexRender, getCoreRowModel, getSortedRowModel, useReactTable} from "@tanstack/react-table";
|
||||||
|
import {useMemo, useState} from "react";
|
||||||
|
import Button from "./Low/Button";
|
||||||
|
|
||||||
|
const SIZE = 25;
|
||||||
|
|
||||||
export default function List<T>({data, columns}: {data: T[]; columns: any[]}) {
|
export default function List<T>({data, columns}: {data: T[]; columns: any[]}) {
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
|
||||||
|
const items = useMemo(() => data.slice(page * SIZE, (page + 1) * SIZE > data.length ? data.length : (page + 1) * SIZE), [data, page]);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data: items,
|
||||||
columns: columns,
|
columns: columns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
<div className="w-full h-full flex flex-col gap-2">
|
||||||
<thead>
|
<div className="w-full flex gap-2 justify-between">
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<Button className="w-full max-w-[200px]" disabled={page === 0} onClick={() => setPage((prev) => prev - 1)}>
|
||||||
<tr key={headerGroup.id}>
|
Previous Page
|
||||||
{headerGroup.headers.map((header) => (
|
</Button>
|
||||||
<th key={header.id} colSpan={header.colSpan}>
|
<div className="flex items-center gap-4 w-fit">
|
||||||
{header.isPlaceholder ? null : (
|
<span className="opacity-80">
|
||||||
<>
|
{page * SIZE + 1} - {(page + 1) * SIZE > data.length ? data.length : (page + 1) * SIZE} / {data.length}
|
||||||
<div
|
</span>
|
||||||
{...{
|
<Button className="w-[200px]" disabled={(page + 1) * SIZE >= data.length} onClick={() => setPage((prev) => prev + 1)}>
|
||||||
className: header.column.getCanSort() ? "cursor-pointer select-none py-4 text-left first:pl-4" : "",
|
Next Page
|
||||||
onClick: header.column.getToggleSortingHandler(),
|
</Button>
|
||||||
}}>
|
</div>
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
</div>
|
||||||
{{
|
|
||||||
asc: " 🔼",
|
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
||||||
desc: " 🔽",
|
<thead>
|
||||||
}[header.column.getIsSorted() as string] ?? null}
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
</div>
|
<tr key={headerGroup.id}>
|
||||||
</>
|
{headerGroup.headers.map((header) => (
|
||||||
)}
|
<th key={header.id} colSpan={header.colSpan}>
|
||||||
</th>
|
{header.isPlaceholder ? null : (
|
||||||
))}
|
<>
|
||||||
</tr>
|
<div
|
||||||
))}
|
{...{
|
||||||
</thead>
|
className: header.column.getCanSort()
|
||||||
<tbody className="px-2">
|
? "cursor-pointer select-none py-4 text-left first:pl-4"
|
||||||
{table.getRowModel().rows.map((row) => (
|
: "",
|
||||||
<tr className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2" key={row.id}>
|
onClick: header.column.getToggleSortingHandler(),
|
||||||
{row.getVisibleCells().map((cell) => (
|
}}>
|
||||||
<td className="px-4 py-2" key={cell.id}>
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{{
|
||||||
</td>
|
asc: " 🔼",
|
||||||
))}
|
desc: " 🔽",
|
||||||
</tr>
|
}[header.column.getIsSorted() as string] ?? null}
|
||||||
))}
|
</div>
|
||||||
</tbody>
|
</>
|
||||||
</table>
|
)}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className="px-2">
|
||||||
|
{table.getRowModel().rows.map((row) => (
|
||||||
|
<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" key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,28 +65,28 @@ export default function Navbar({user, path, navDisabled = false, focusMode = fal
|
|||||||
{
|
{
|
||||||
module: "reading",
|
module: "reading",
|
||||||
icon: () => <BsBook className="h-4 w-4 text-white" />,
|
icon: () => <BsBook className="h-4 w-4 text-white" />,
|
||||||
achieved: user.levels.reading >= user.desiredLevels.reading,
|
achieved: user.levels?.reading || 0 >= user.desiredLevels?.reading || 9,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
module: "listening",
|
module: "listening",
|
||||||
icon: () => <BsHeadphones className="h-4 w-4 text-white" />,
|
icon: () => <BsHeadphones className="h-4 w-4 text-white" />,
|
||||||
achieved: user.levels.listening >= user.desiredLevels.listening,
|
achieved: user.levels?.listening || 0 >= user.desiredLevels?.listening || 9,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
module: "writing",
|
module: "writing",
|
||||||
icon: () => <BsPen className="h-4 w-4 text-white" />,
|
icon: () => <BsPen className="h-4 w-4 text-white" />,
|
||||||
achieved: user.levels.writing >= user.desiredLevels.writing,
|
achieved: user.levels?.writing || 0 >= user.desiredLevels?.writing || 9,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
module: "speaking",
|
module: "speaking",
|
||||||
icon: () => <BsMegaphone className="h-4 w-4 text-white" />,
|
icon: () => <BsMegaphone className="h-4 w-4 text-white" />,
|
||||||
achieved: user.levels.speaking >= user.desiredLevels.speaking,
|
achieved: user.levels?.speaking || 0 >= user.desiredLevels?.speaking || 9,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
module: "level",
|
module: "level",
|
||||||
icon: () => <BsClipboard className="h-4 w-4 text-white" />,
|
icon: () => <BsClipboard className="h-4 w-4 text-white" />,
|
||||||
achieved: user.levels.level >= user.desiredLevels.level,
|
achieved: user.levels?.level || 0 >= user.desiredLevels?.level || 9,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,40 @@ interface Props {
|
|||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const studentHash = {
|
||||||
|
type: "student",
|
||||||
|
size: 25,
|
||||||
|
orderBy: "registrationDate",
|
||||||
|
};
|
||||||
|
|
||||||
|
const teacherHash = {
|
||||||
|
type: "teacher",
|
||||||
|
size: 25,
|
||||||
|
orderBy: "registrationDate",
|
||||||
|
};
|
||||||
|
|
||||||
|
const corporateHash = {
|
||||||
|
type: "corporate",
|
||||||
|
size: 25,
|
||||||
|
orderBy: "registrationDate",
|
||||||
|
};
|
||||||
|
|
||||||
|
const agentsHash = {
|
||||||
|
type: "agent",
|
||||||
|
size: 25,
|
||||||
|
orderBy: "registrationDate",
|
||||||
|
};
|
||||||
|
|
||||||
export default function AdminDashboard({user}: Props) {
|
export default function AdminDashboard({user}: Props) {
|
||||||
const [page, setPage] = useState("");
|
const [page, setPage] = useState("");
|
||||||
const [selectedUser, setSelectedUser] = useState<User>();
|
const [selectedUser, setSelectedUser] = useState<User>();
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
const {users, reload, isLoading} = useUsers();
|
const {users: students, total: totalStudents, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(studentHash);
|
||||||
|
const {users: teachers, total: totalTeachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(teacherHash);
|
||||||
|
const {users: corporates, total: totalCorporate, reload: reloadCorporates, isLoading: isCorporatesLoading} = useUsers(corporateHash);
|
||||||
|
const {users: agents, total: totalAgents, reload: reloadAgents, isLoading: isAgentsLoading} = useUsers(agentsHash);
|
||||||
|
|
||||||
const {groups} = useGroups({});
|
const {groups} = useGroups({});
|
||||||
const {pending, done} = usePaymentStatusUsers();
|
const {pending, done} = usePaymentStatusUsers();
|
||||||
|
|
||||||
@@ -47,9 +75,6 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
setShowModal(!!selectedUser && router.asPath === "/#");
|
setShowModal(!!selectedUser && router.asPath === "/#");
|
||||||
}, [selectedUser, router.asPath]);
|
}, [selectedUser, router.asPath]);
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
useEffect(reload, [page]);
|
|
||||||
|
|
||||||
const inactiveCountryManagerFilter = (x: User) => x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate);
|
const inactiveCountryManagerFilter = (x: User) => x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate);
|
||||||
|
|
||||||
const UserDisplay = (displayUser: User) => (
|
const UserDisplay = (displayUser: User) => (
|
||||||
@@ -279,50 +304,50 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<section className="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 place-items-center items-center justify-between">
|
<section className="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 place-items-center items-center justify-between">
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsPersonFill}
|
Icon={BsPersonFill}
|
||||||
isLoading={isLoading}
|
isLoading={isStudentsLoading}
|
||||||
label="Students"
|
label="Students"
|
||||||
value={users.filter((x) => x.type === "student").length}
|
value={totalStudents}
|
||||||
onClick={() => router.push("/#students")}
|
onClick={() => router.push("/#students")}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsPencilSquare}
|
Icon={BsPencilSquare}
|
||||||
isLoading={isLoading}
|
isLoading={isTeachersLoading}
|
||||||
label="Teachers"
|
label="Teachers"
|
||||||
value={users.filter((x) => x.type === "teacher").length}
|
value={totalTeachers}
|
||||||
onClick={() => router.push("/#teachers")}
|
onClick={() => router.push("/#teachers")}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsBank}
|
Icon={BsBank}
|
||||||
isLoading={isLoading}
|
isLoading={isCorporatesLoading}
|
||||||
label="Corporate"
|
label="Corporate"
|
||||||
value={users.filter((x) => x.type === "corporate").length}
|
value={totalCorporate}
|
||||||
onClick={() => router.push("/#corporate")}
|
onClick={() => router.push("/#corporate")}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsBriefcaseFill}
|
Icon={BsBriefcaseFill}
|
||||||
isLoading={isLoading}
|
isLoading={isAgentsLoading}
|
||||||
label="Country Managers"
|
label="Country Managers"
|
||||||
value={users.filter((x) => x.type === "agent").length}
|
value={totalAgents}
|
||||||
onClick={() => router.push("/#agents")}
|
onClick={() => router.push("/#agents")}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsGlobeCentralSouthAsia}
|
Icon={BsGlobeCentralSouthAsia}
|
||||||
isLoading={isLoading}
|
isLoading={isAgentsLoading}
|
||||||
label="Countries"
|
label="Countries"
|
||||||
value={[...new Set(users.filter((x) => x.demographicInformation).map((x) => x.demographicInformation?.country))].length}
|
value={[...new Set(agents.filter((x) => x.demographicInformation).map((x) => x.demographicInformation?.country))].length}
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#inactiveStudents")}
|
onClick={() => router.push("/#inactiveStudents")}
|
||||||
Icon={BsPersonFill}
|
Icon={BsPersonFill}
|
||||||
isLoading={isLoading}
|
isLoading={isStudentsLoading}
|
||||||
label="Inactive Students"
|
label="Inactive Students"
|
||||||
value={
|
value={
|
||||||
users.filter((x) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)))
|
students.filter((x) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)))
|
||||||
.length
|
.length
|
||||||
}
|
}
|
||||||
color="rose"
|
color="rose"
|
||||||
@@ -330,26 +355,26 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#inactiveCountryManagers")}
|
onClick={() => router.push("/#inactiveCountryManagers")}
|
||||||
Icon={BsBriefcaseFill}
|
Icon={BsBriefcaseFill}
|
||||||
isLoading={isLoading}
|
isLoading={isAgentsLoading}
|
||||||
label="Inactive Country Managers"
|
label="Inactive Country Managers"
|
||||||
value={users.filter(inactiveCountryManagerFilter).length}
|
value={agents.filter(inactiveCountryManagerFilter).length}
|
||||||
color="rose"
|
color="rose"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#inactiveCorporate")}
|
onClick={() => router.push("/#inactiveCorporate")}
|
||||||
Icon={BsBank}
|
Icon={BsBank}
|
||||||
isLoading={isLoading}
|
isLoading={isCorporatesLoading}
|
||||||
label="Inactive Corporate"
|
label="Inactive Corporate"
|
||||||
value={
|
value={
|
||||||
users.filter((x) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)))
|
corporates.filter(
|
||||||
.length
|
(x) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)),
|
||||||
|
).length
|
||||||
}
|
}
|
||||||
color="rose"
|
color="rose"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#paymentdone")}
|
onClick={() => router.push("/#paymentdone")}
|
||||||
Icon={BsCurrencyDollar}
|
Icon={BsCurrencyDollar}
|
||||||
isLoading={isLoading}
|
|
||||||
label="Payment Done"
|
label="Payment Done"
|
||||||
value={done.length}
|
value={done.length}
|
||||||
color="purple"
|
color="purple"
|
||||||
@@ -357,7 +382,6 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#paymentpending")}
|
onClick={() => router.push("/#paymentpending")}
|
||||||
Icon={BsCurrencyDollar}
|
Icon={BsCurrencyDollar}
|
||||||
isLoading={isLoading}
|
|
||||||
label="Pending Payment"
|
label="Pending Payment"
|
||||||
value={pending.length}
|
value={pending.length}
|
||||||
color="rose"
|
color="rose"
|
||||||
@@ -365,14 +389,13 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("https://cms.encoach.com/admin")}
|
onClick={() => router.push("https://cms.encoach.com/admin")}
|
||||||
Icon={BsLayoutSidebar}
|
Icon={BsLayoutSidebar}
|
||||||
isLoading={isLoading}
|
|
||||||
label="Content Management System (CMS)"
|
label="Content Management System (CMS)"
|
||||||
color="green"
|
color="green"
|
||||||
/>
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
onClick={() => router.push("/#corporatestudentslevels")}
|
onClick={() => router.push("/#corporatestudentslevels")}
|
||||||
Icon={BsPersonFill}
|
Icon={BsPersonFill}
|
||||||
isLoading={isLoading}
|
isLoading={isStudentsLoading}
|
||||||
label="Corporate Students Levels"
|
label="Corporate Students Levels"
|
||||||
color="purple"
|
color="purple"
|
||||||
/>
|
/>
|
||||||
@@ -382,8 +405,7 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Latest students</span>
|
<span className="p-4">Latest students</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{students
|
||||||
.filter((x) => x.type === "student")
|
|
||||||
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
@@ -393,8 +415,7 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Latest teachers</span>
|
<span className="p-4">Latest teachers</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{teachers
|
||||||
.filter((x) => x.type === "teacher")
|
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return dateSorter(a, b, "desc", "registrationDate");
|
return dateSorter(a, b, "desc", "registrationDate");
|
||||||
})
|
})
|
||||||
@@ -406,8 +427,7 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Latest corporate</span>
|
<span className="p-4">Latest corporate</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{corporates
|
||||||
.filter((x) => x.type === "corporate")
|
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return dateSorter(a, b, "desc", "registrationDate");
|
return dateSorter(a, b, "desc", "registrationDate");
|
||||||
})
|
})
|
||||||
@@ -419,8 +439,8 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Unpaid Corporate</span>
|
<span className="p-4">Unpaid Corporate</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{corporates
|
||||||
.filter((x) => x.type === "corporate" && x.status === "paymentDue")
|
.filter((x) => x.status === "paymentDue")
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -429,10 +449,9 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Students expiring in 1 month</span>
|
<span className="p-4">Students expiring in 1 month</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{students
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.type === "student" &&
|
|
||||||
x.subscriptionExpirationDate &&
|
x.subscriptionExpirationDate &&
|
||||||
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
||||||
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
||||||
@@ -445,10 +464,9 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Teachers expiring in 1 month</span>
|
<span className="p-4">Teachers expiring in 1 month</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{teachers
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.type === "teacher" &&
|
|
||||||
x.subscriptionExpirationDate &&
|
x.subscriptionExpirationDate &&
|
||||||
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
||||||
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
||||||
@@ -461,10 +479,9 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Country Manager expiring in 1 month</span>
|
<span className="p-4">Country Manager expiring in 1 month</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{agents
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.type === "agent" &&
|
|
||||||
x.subscriptionExpirationDate &&
|
x.subscriptionExpirationDate &&
|
||||||
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
||||||
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
||||||
@@ -477,10 +494,9 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Corporate expiring in 1 month</span>
|
<span className="p-4">Corporate expiring in 1 month</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{corporates
|
||||||
.filter(
|
.filter(
|
||||||
(x) =>
|
(x) =>
|
||||||
x.type === "corporate" &&
|
|
||||||
x.subscriptionExpirationDate &&
|
x.subscriptionExpirationDate &&
|
||||||
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) &&
|
||||||
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
moment().isBefore(moment(x.subscriptionExpirationDate)),
|
||||||
@@ -493,10 +509,8 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Expired Students</span>
|
<span className="p-4">Expired Students</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{students
|
||||||
.filter(
|
.filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)))
|
||||||
(x) => x.type === "student" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)),
|
|
||||||
)
|
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -505,10 +519,8 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Expired Teachers</span>
|
<span className="p-4">Expired Teachers</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{teachers
|
||||||
.filter(
|
.filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)))
|
||||||
(x) => x.type === "teacher" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)),
|
|
||||||
)
|
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -517,10 +529,8 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Expired Country Manager</span>
|
<span className="p-4">Expired Country Manager</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{agents
|
||||||
.filter(
|
.filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)))
|
||||||
(x) => x.type === "agent" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)),
|
|
||||||
)
|
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -529,11 +539,8 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
<span className="p-4">Expired Corporate</span>
|
<span className="p-4">Expired Corporate</span>
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
{users
|
{corporates
|
||||||
.filter(
|
.filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)))
|
||||||
(x) =>
|
|
||||||
x.type === "corporate" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)),
|
|
||||||
)
|
|
||||||
.map((x) => (
|
.map((x) => (
|
||||||
<UserDisplay key={x.id} {...x} />
|
<UserDisplay key={x.id} {...x} />
|
||||||
))}
|
))}
|
||||||
@@ -553,7 +560,10 @@ export default function AdminDashboard({user}: Props) {
|
|||||||
loggedInUser={user}
|
loggedInUser={user}
|
||||||
onClose={(shouldReload) => {
|
onClose={(shouldReload) => {
|
||||||
setSelectedUser(undefined);
|
setSelectedUser(undefined);
|
||||||
if (shouldReload) reload();
|
if (shouldReload && selectedUser!.type === "student") reloadStudents();
|
||||||
|
if (shouldReload && selectedUser!.type === "teacher") reloadTeachers();
|
||||||
|
if (shouldReload && selectedUser!.type === "corporate") reloadCorporates();
|
||||||
|
if (shouldReload && selectedUser!.type === "agent") reloadAgents();
|
||||||
}}
|
}}
|
||||||
onViewStudents={
|
onViewStudents={
|
||||||
selectedUser.type === "corporate" || selectedUser.type === "teacher"
|
selectedUser.type === "corporate" || selectedUser.type === "teacher"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import Checkbox from "@/components/Low/Checkbox";
|
|||||||
import {InstructorGender, Variant} from "@/interfaces/exam";
|
import {InstructorGender, Variant} from "@/interfaces/exam";
|
||||||
import Select from "@/components/Low/Select";
|
import Select from "@/components/Low/Select";
|
||||||
import useExams from "@/hooks/useExams";
|
import useExams from "@/hooks/useExams";
|
||||||
|
import {useListSearch} from "@/hooks/useListSearch";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isCreating: boolean;
|
isCreating: boolean;
|
||||||
@@ -31,7 +32,12 @@ interface Props {
|
|||||||
cancelCreation: () => void;
|
cancelCreation: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SIZE = 12;
|
||||||
|
|
||||||
export default function AssignmentCreator({isCreating, assignment, user, groups, users, cancelCreation}: Props) {
|
export default function AssignmentCreator({isCreating, assignment, user, groups, users, cancelCreation}: Props) {
|
||||||
|
const [studentsPage, setStudentsPage] = useState(0);
|
||||||
|
const [teachersPage, setTeachersPage] = useState(0);
|
||||||
|
|
||||||
const [selectedModules, setSelectedModules] = useState<Module[]>(assignment?.exams.map((e) => e.module) || []);
|
const [selectedModules, setSelectedModules] = useState<Module[]>(assignment?.exams.map((e) => e.module) || []);
|
||||||
const [assignees, setAssignees] = useState<string[]>(assignment?.assignees || []);
|
const [assignees, setAssignees] = useState<string[]>(assignment?.assignees || []);
|
||||||
const [teachers, setTeachers] = useState<string[]>(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]);
|
const [teachers, setTeachers] = useState<string[]>(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]);
|
||||||
@@ -69,6 +75,29 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]);
|
const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]);
|
||||||
const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]);
|
const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]);
|
||||||
|
|
||||||
|
const {rows: filteredStudentsRows, renderSearch: renderStudentSearch, text: studentText} = useListSearch([["name"], ["email"]], userStudents);
|
||||||
|
const {rows: filteredTeachersRows, renderSearch: renderTeacherSearch, text: teacherText} = useListSearch([["name"], ["email"]], userTeachers);
|
||||||
|
|
||||||
|
useEffect(() => setStudentsPage(0), [studentText]);
|
||||||
|
const studentRows = useMemo(
|
||||||
|
() =>
|
||||||
|
filteredStudentsRows.slice(
|
||||||
|
studentsPage * SIZE,
|
||||||
|
(studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE,
|
||||||
|
),
|
||||||
|
[filteredStudentsRows, studentsPage],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => setTeachersPage(0), [teacherText]);
|
||||||
|
const teacherRows = useMemo(
|
||||||
|
() =>
|
||||||
|
filteredTeachersRows.slice(
|
||||||
|
teachersPage * SIZE,
|
||||||
|
(teachersPage + 1) * SIZE > filteredTeachersRows.length ? filteredTeachersRows.length : (teachersPage + 1) * SIZE,
|
||||||
|
),
|
||||||
|
[filteredTeachersRows, teachersPage],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module)));
|
setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module)));
|
||||||
}, [selectedModules]);
|
}, [selectedModules]);
|
||||||
@@ -347,9 +376,9 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section className="w-full flex flex-col gap-3">
|
<section className="w-full flex flex-col gap-4">
|
||||||
<span className="font-semibold">Assignees ({assignees.length} selected)</span>
|
<span className="font-semibold">Assignees ({assignees.length} selected)</span>
|
||||||
<div className="flex gap-4 overflow-x-scroll scrollbar-hide">
|
<div className="grid grid-cols-5 gap-4">
|
||||||
{groups.map((g) => (
|
{groups.map((g) => (
|
||||||
<button
|
<button
|
||||||
key={g.id}
|
key={g.id}
|
||||||
@@ -371,8 +400,11 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{renderStudentSearch()}
|
||||||
|
|
||||||
<div className="flex flex-wrap -md:justify-center gap-4">
|
<div className="flex flex-wrap -md:justify-center gap-4">
|
||||||
{userStudents.map((user) => (
|
{studentRows.map((user) => (
|
||||||
<div
|
<div
|
||||||
onClick={() => toggleAssignee(user)}
|
onClick={() => toggleAssignee(user)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -402,12 +434,32 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex gap-2 justify-between items-center">
|
||||||
|
<div className="flex items-center gap-4 w-fit">
|
||||||
|
<Button className="w-[200px] h-fit" disabled={studentsPage === 0} onClick={() => setStudentsPage((prev) => prev - 1)}>
|
||||||
|
Previous Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 w-fit">
|
||||||
|
<span className="opacity-80">
|
||||||
|
{studentsPage * SIZE + 1} -{" "}
|
||||||
|
{(studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE} /{" "}
|
||||||
|
{filteredStudentsRows.length}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
className="w-[200px]"
|
||||||
|
disabled={(studentsPage + 1) * SIZE >= filteredStudentsRows.length}
|
||||||
|
onClick={() => setStudentsPage((prev) => prev + 1)}>
|
||||||
|
Next Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{user.type !== "teacher" && (
|
{user.type !== "teacher" && (
|
||||||
<section className="w-full flex flex-col gap-3">
|
<section className="w-full flex flex-col gap-3">
|
||||||
<span className="font-semibold">Teachers ({teachers.length} selected)</span>
|
<span className="font-semibold">Teachers ({teachers.length} selected)</span>
|
||||||
<div className="flex gap-4 overflow-x-scroll scrollbar-hide">
|
<div className="grid grid-cols-5 gap-4">
|
||||||
{groups.map((g) => (
|
{groups.map((g) => (
|
||||||
<button
|
<button
|
||||||
key={g.id}
|
key={g.id}
|
||||||
@@ -429,8 +481,11 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{renderTeacherSearch()}
|
||||||
|
|
||||||
<div className="flex flex-wrap -md:justify-center gap-4">
|
<div className="flex flex-wrap -md:justify-center gap-4">
|
||||||
{userTeachers.map((user) => (
|
{teacherRows.map((user) => (
|
||||||
<div
|
<div
|
||||||
onClick={() => toggleTeacher(user)}
|
onClick={() => toggleTeacher(user)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -453,6 +508,29 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full flex gap-2 justify-between items-center">
|
||||||
|
<div className="flex items-center gap-4 w-fit">
|
||||||
|
<Button className="w-[200px] h-fit" disabled={teachersPage === 0} onClick={() => setTeachersPage((prev) => prev - 1)}>
|
||||||
|
Previous Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 w-fit">
|
||||||
|
<span className="opacity-80">
|
||||||
|
{teachersPage * SIZE + 1} -{" "}
|
||||||
|
{(teachersPage + 1) * SIZE > filteredTeachersRows.length
|
||||||
|
? filteredTeachersRows.length
|
||||||
|
: (teachersPage + 1) * SIZE}{" "}
|
||||||
|
/ {filteredTeachersRows.length}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
className="w-[200px]"
|
||||||
|
disabled={(teachersPage + 1) * SIZE >= filteredTeachersRows.length}
|
||||||
|
onClick={() => setTeachersPage((prev) => prev + 1)}>
|
||||||
|
Next Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,400 +1,546 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
||||||
import useUsers, {userHashStudent, userHashTeacher, userHashCorporate} from "@/hooks/useUsers";
|
import useUsers, {
|
||||||
import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user";
|
userHashStudent,
|
||||||
import UserList from "@/pages/(admin)/Lists/UserList";
|
userHashTeacher,
|
||||||
import {dateSorter} from "@/utils";
|
userHashCorporate,
|
||||||
import moment from "moment";
|
} from "@/hooks/useUsers";
|
||||||
import {useEffect, useMemo, useState} from "react";
|
|
||||||
import {
|
import {
|
||||||
BsArrowLeft,
|
CorporateUser,
|
||||||
BsClipboard2Data,
|
Group,
|
||||||
BsClipboard2DataFill,
|
MasterCorporateUser,
|
||||||
BsClock,
|
Stat,
|
||||||
BsGlobeCentralSouthAsia,
|
User,
|
||||||
BsPaperclip,
|
} from "@/interfaces/user";
|
||||||
BsPerson,
|
import UserList from "@/pages/(admin)/Lists/UserList";
|
||||||
BsPersonAdd,
|
import { dateSorter } from "@/utils";
|
||||||
BsPersonFill,
|
import moment from "moment";
|
||||||
BsPersonFillGear,
|
import { useEffect, useMemo, useState } from "react";
|
||||||
BsPersonGear,
|
import {
|
||||||
BsPencilSquare,
|
BsArrowLeft,
|
||||||
BsPersonBadge,
|
BsClipboard2Data,
|
||||||
BsPersonCheck,
|
BsClipboard2DataFill,
|
||||||
BsPeople,
|
BsClock,
|
||||||
BsArrowRepeat,
|
BsGlobeCentralSouthAsia,
|
||||||
BsPlus,
|
BsPaperclip,
|
||||||
BsEnvelopePaper,
|
BsPerson,
|
||||||
|
BsPersonAdd,
|
||||||
|
BsPersonFill,
|
||||||
|
BsPersonFillGear,
|
||||||
|
BsPersonGear,
|
||||||
|
BsPencilSquare,
|
||||||
|
BsPersonBadge,
|
||||||
|
BsPersonCheck,
|
||||||
|
BsPeople,
|
||||||
|
BsArrowRepeat,
|
||||||
|
BsPlus,
|
||||||
|
BsEnvelopePaper,
|
||||||
|
BsDatabase,
|
||||||
} from "react-icons/bs";
|
} from "react-icons/bs";
|
||||||
import UserCard from "@/components/UserCard";
|
import UserCard from "@/components/UserCard";
|
||||||
import useGroups from "@/hooks/useGroups";
|
import useGroups from "@/hooks/useGroups";
|
||||||
import {averageLevelCalculator, calculateAverageLevel, calculateBandScore} from "@/utils/score";
|
import {
|
||||||
import {MODULE_ARRAY} from "@/utils/moduleUtils";
|
averageLevelCalculator,
|
||||||
import {Module} from "@/interfaces";
|
calculateAverageLevel,
|
||||||
import {groupByExam} from "@/utils/stats";
|
calculateBandScore,
|
||||||
|
} from "@/utils/score";
|
||||||
|
import { MODULE_ARRAY } from "@/utils/moduleUtils";
|
||||||
|
import { Module } from "@/interfaces";
|
||||||
|
import { groupByExam } from "@/utils/stats";
|
||||||
import IconCard from "../IconCard";
|
import IconCard from "../IconCard";
|
||||||
import GroupList from "@/pages/(admin)/Lists/GroupList";
|
import GroupList from "@/pages/(admin)/Lists/GroupList";
|
||||||
import useFilterStore from "@/stores/listFilterStore";
|
import useFilterStore from "@/stores/listFilterStore";
|
||||||
import {useRouter} from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useCodes from "@/hooks/useCodes";
|
import useCodes from "@/hooks/useCodes";
|
||||||
import {getUserCorporate} from "@/utils/groups";
|
import { getUserCorporate } from "@/utils/groups";
|
||||||
import useAssignments from "@/hooks/useAssignments";
|
import useAssignments from "@/hooks/useAssignments";
|
||||||
import {Assignment} from "@/interfaces/results";
|
import { Assignment } from "@/interfaces/results";
|
||||||
import AssignmentView from "../AssignmentView";
|
import AssignmentView from "../AssignmentView";
|
||||||
import AssignmentCreator from "../AssignmentCreator";
|
import AssignmentCreator from "../AssignmentCreator";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import AssignmentCard from "../AssignmentCard";
|
import AssignmentCard from "../AssignmentCard";
|
||||||
import {createColumnHelper} from "@tanstack/react-table";
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
import Checkbox from "@/components/Low/Checkbox";
|
import Checkbox from "@/components/Low/Checkbox";
|
||||||
import List from "@/components/List";
|
import List from "@/components/List";
|
||||||
import {getUserCompanyName} from "@/resources/user";
|
import { getUserCompanyName } from "@/resources/user";
|
||||||
import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments";
|
import {
|
||||||
|
futureAssignmentFilter,
|
||||||
|
pastAssignmentFilter,
|
||||||
|
archivedAssignmentFilter,
|
||||||
|
activeAssignmentFilter,
|
||||||
|
} from "@/utils/assignments";
|
||||||
import useUserBalance from "@/hooks/useUserBalance";
|
import useUserBalance from "@/hooks/useUserBalance";
|
||||||
import AssignmentsPage from "../views/AssignmentsPage";
|
import AssignmentsPage from "../views/AssignmentsPage";
|
||||||
import StudentPerformancePage from "./StudentPerformancePage";
|
import StudentPerformancePage from "./StudentPerformancePage";
|
||||||
import MasterStatistical from "../MasterCorporate/MasterStatistical";
|
import MasterStatistical from "../MasterCorporate/MasterStatistical";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: CorporateUser;
|
user: CorporateUser;
|
||||||
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const studentHash = {
|
const studentHash = {
|
||||||
type: "student",
|
type: "student",
|
||||||
orderBy: "registrationDate",
|
orderBy: "registrationDate",
|
||||||
size: 25,
|
size: 25,
|
||||||
};
|
};
|
||||||
|
|
||||||
const teacherHash = {
|
const teacherHash = {
|
||||||
type: "teacher",
|
type: "teacher",
|
||||||
orderBy: "registrationDate",
|
orderBy: "registrationDate",
|
||||||
size: 25,
|
size: 25,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CorporateDashboard({user, linkedCorporate}: Props) {
|
export default function CorporateDashboard({ user, linkedCorporate }: Props) {
|
||||||
const [selectedUser, setSelectedUser] = useState<User>();
|
const [selectedUser, setSelectedUser] = useState<User>();
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
const {data: stats} = useFilterRecordsByUser<Stat[]>();
|
const { data: stats } = useFilterRecordsByUser<Stat[]>();
|
||||||
const {groups} = useGroups({admin: user.id});
|
const { groups } = useGroups({ admin: user.id });
|
||||||
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
|
const {
|
||||||
const {balance} = useUserBalance();
|
assignments,
|
||||||
|
isLoading: isAssignmentsLoading,
|
||||||
|
reload: reloadAssignments,
|
||||||
|
} = useAssignments({ corporate: user.id });
|
||||||
|
const { balance } = useUserBalance();
|
||||||
|
|
||||||
const {users: students, total: totalStudents, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(studentHash);
|
const {
|
||||||
const {users: teachers, total: totalTeachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(teacherHash);
|
users: students,
|
||||||
|
total: totalStudents,
|
||||||
|
reload: reloadStudents,
|
||||||
|
isLoading: isStudentsLoading,
|
||||||
|
} = useUsers(studentHash);
|
||||||
|
const {
|
||||||
|
users: teachers,
|
||||||
|
total: totalTeachers,
|
||||||
|
reload: reloadTeachers,
|
||||||
|
isLoading: isTeachersLoading,
|
||||||
|
} = useUsers(teacherHash);
|
||||||
|
|
||||||
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]);
|
const assignmentsGroups = useMemo(
|
||||||
|
() =>
|
||||||
|
groups.filter(
|
||||||
|
(x) => x.admin === user.id || x.participants.includes(user.id)
|
||||||
|
),
|
||||||
|
[groups, user.id]
|
||||||
|
);
|
||||||
|
|
||||||
const assignmentsUsers = useMemo(
|
const assignmentsUsers = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[...teachers, ...students].filter((x) =>
|
[...teachers, ...students].filter((x) =>
|
||||||
!!selectedUser
|
!!selectedUser
|
||||||
? groups
|
? groups
|
||||||
.filter((g) => g.admin === selectedUser.id)
|
.filter((g) => g.admin === selectedUser.id)
|
||||||
.flatMap((g) => g.participants)
|
.flatMap((g) => g.participants)
|
||||||
.includes(x.id) || false
|
.includes(x.id) || false
|
||||||
: groups.flatMap((g) => g.participants).includes(x.id),
|
: groups.flatMap((g) => g.participants).includes(x.id)
|
||||||
),
|
),
|
||||||
[groups, teachers, students, selectedUser],
|
[groups, teachers, students, selectedUser]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowModal(!!selectedUser && router.asPath === "/#");
|
setShowModal(!!selectedUser && router.asPath === "/#");
|
||||||
}, [selectedUser, router.asPath]);
|
}, [selectedUser, router.asPath]);
|
||||||
|
|
||||||
const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
|
const getStatsByStudent = (user: User) =>
|
||||||
|
stats.filter((s) => s.user === user.id);
|
||||||
|
|
||||||
const UserDisplay = (displayUser: User) => (
|
const UserDisplay = (displayUser: User) => (
|
||||||
<div
|
<div
|
||||||
onClick={() => setSelectedUser(displayUser)}
|
onClick={() => setSelectedUser(displayUser)}
|
||||||
className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300">
|
className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300"
|
||||||
<img src={displayUser.profilePicture} alt={displayUser.name} className="rounded-full w-10 h-10" />
|
>
|
||||||
<div className="flex flex-col gap-1 items-start">
|
<img
|
||||||
<span>{displayUser.name}</span>
|
src={displayUser.profilePicture}
|
||||||
<span className="text-sm opacity-75">{displayUser.email}</span>
|
alt={displayUser.name}
|
||||||
</div>
|
className="rounded-full w-10 h-10"
|
||||||
</div>
|
/>
|
||||||
);
|
<div className="flex flex-col gap-1 items-start">
|
||||||
|
<span>{displayUser.name}</span>
|
||||||
|
<span className="text-sm opacity-75">{displayUser.email}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
const GroupsList = () => {
|
// this workaround will allow us toreuse the master statistical due to master corporate restraints
|
||||||
const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id);
|
// while still being able to use the corporate user
|
||||||
|
const groupedByNameCorporateIds = useMemo(
|
||||||
|
() => ({
|
||||||
|
[user.corporateInformation?.companyInformation?.name || user.name]: [
|
||||||
|
user.id,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
const teachersAndStudents = useMemo(
|
||||||
|
() => [...students, ...teachers],
|
||||||
|
[students, teachers]
|
||||||
|
);
|
||||||
|
const MasterStatisticalPage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div
|
||||||
|
onClick={() => router.push("/")}
|
||||||
|
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
|
||||||
|
>
|
||||||
|
<BsArrowLeft className="text-xl" />
|
||||||
|
<span>Back</span>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-semibold">Master Statistical</h2>
|
||||||
|
</div>
|
||||||
|
<MasterStatistical
|
||||||
|
users={teachersAndStudents}
|
||||||
|
corporateUsers={groupedByNameCorporateIds}
|
||||||
|
displaySelection={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const GroupsList = () => {
|
||||||
<>
|
const filter = (x: Group) =>
|
||||||
<div className="flex flex-col gap-4">
|
x.admin === user.id || x.participants.includes(user.id);
|
||||||
<div
|
|
||||||
onClick={() => router.push("/")}
|
|
||||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
|
||||||
<BsArrowLeft className="text-xl" />
|
|
||||||
<span>Back</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="text-2xl font-semibold">Groups ({groups.filter(filter).length})</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<GroupList user={user} />
|
return (
|
||||||
</>
|
<>
|
||||||
);
|
<div className="flex flex-col gap-4">
|
||||||
};
|
<div
|
||||||
|
onClick={() => router.push("/")}
|
||||||
|
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
|
||||||
|
>
|
||||||
|
<BsArrowLeft className="text-xl" />
|
||||||
|
<span>Back</span>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-semibold">
|
||||||
|
Groups ({groups.filter(filter).length})
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
const averageLevelCalculator = (studentStats: Stat[]) => {
|
<GroupList user={user} />
|
||||||
const formattedStats = studentStats
|
</>
|
||||||
.map((s) => ({
|
);
|
||||||
focus: students.find((u) => u.id === s.user)?.focus,
|
};
|
||||||
score: s.score,
|
|
||||||
module: s.module,
|
|
||||||
}))
|
|
||||||
.filter((f) => !!f.focus);
|
|
||||||
const bandScores = formattedStats.map((s) => ({
|
|
||||||
module: s.module,
|
|
||||||
level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const levels: {[key in Module]: number} = {
|
const averageLevelCalculator = (studentStats: Stat[]) => {
|
||||||
reading: 0,
|
const formattedStats = studentStats
|
||||||
listening: 0,
|
.map((s) => ({
|
||||||
writing: 0,
|
focus: students.find((u) => u.id === s.user)?.focus,
|
||||||
speaking: 0,
|
score: s.score,
|
||||||
level: 0,
|
module: s.module,
|
||||||
};
|
}))
|
||||||
bandScores.forEach((b) => (levels[b.module] += b.level));
|
.filter((f) => !!f.focus);
|
||||||
|
const bandScores = formattedStats.map((s) => ({
|
||||||
|
module: s.module,
|
||||||
|
level: calculateBandScore(
|
||||||
|
s.score.correct,
|
||||||
|
s.score.total,
|
||||||
|
s.module,
|
||||||
|
s.focus!
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
return calculateAverageLevel(levels);
|
const levels: { [key in Module]: number } = {
|
||||||
};
|
reading: 0,
|
||||||
|
listening: 0,
|
||||||
|
writing: 0,
|
||||||
|
speaking: 0,
|
||||||
|
level: 0,
|
||||||
|
};
|
||||||
|
bandScores.forEach((b) => (levels[b.module] += b.level));
|
||||||
|
|
||||||
if (router.asPath === "/#students")
|
return calculateAverageLevel(levels);
|
||||||
return (
|
};
|
||||||
<UserList
|
|
||||||
user={user}
|
|
||||||
type="student"
|
|
||||||
renderHeader={(total) => (
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div
|
|
||||||
onClick={() => router.push("/")}
|
|
||||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
|
||||||
<BsArrowLeft className="text-xl" />
|
|
||||||
<span>Back</span>
|
|
||||||
</div>
|
|
||||||
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (router.asPath === "/#teachers")
|
if (router.asPath === "/#students")
|
||||||
return (
|
return (
|
||||||
<UserList
|
<UserList
|
||||||
user={user}
|
user={user}
|
||||||
type="teacher"
|
type="student"
|
||||||
renderHeader={(total) => (
|
renderHeader={(total) => (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div
|
<div
|
||||||
onClick={() => router.push("/")}
|
onClick={() => router.push("/")}
|
||||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
|
||||||
<BsArrowLeft className="text-xl" />
|
>
|
||||||
<span>Back</span>
|
<BsArrowLeft className="text-xl" />
|
||||||
</div>
|
<span>Back</span>
|
||||||
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
</div>
|
||||||
</div>
|
<h2 className="text-2xl font-semibold">Students ({total})</h2>
|
||||||
)}
|
</div>
|
||||||
/>
|
)}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
if (router.asPath === "/#groups") return <GroupsList />;
|
if (router.asPath === "/#teachers")
|
||||||
if (router.asPath === "/#studentsPerformance") return <StudentPerformancePage user={user} />;
|
return (
|
||||||
|
<UserList
|
||||||
|
user={user}
|
||||||
|
type="teacher"
|
||||||
|
renderHeader={(total) => (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div
|
||||||
|
onClick={() => router.push("/")}
|
||||||
|
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
|
||||||
|
>
|
||||||
|
<BsArrowLeft className="text-xl" />
|
||||||
|
<span>Back</span>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-semibold">Teachers ({total})</h2>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
if (router.asPath === "/#assignments")
|
if (router.asPath === "/#groups") return <GroupsList />;
|
||||||
return (
|
if (router.asPath === "/#studentsPerformance")
|
||||||
<AssignmentsPage
|
return <StudentPerformancePage user={user} />;
|
||||||
assignments={assignments}
|
|
||||||
user={user}
|
|
||||||
groups={assignmentsGroups}
|
|
||||||
reloadAssignments={reloadAssignments}
|
|
||||||
isLoading={isAssignmentsLoading}
|
|
||||||
onBack={() => router.push("/")}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
if (router.asPath === "/#assignments")
|
||||||
<>
|
return (
|
||||||
<Modal isOpen={showModal} onClose={() => setSelectedUser(undefined)}>
|
<AssignmentsPage
|
||||||
<>
|
assignments={assignments}
|
||||||
{selectedUser && (
|
user={user}
|
||||||
<div className="w-full flex flex-col gap-8">
|
groups={assignmentsGroups}
|
||||||
<UserCard
|
reloadAssignments={reloadAssignments}
|
||||||
loggedInUser={user}
|
isLoading={isAssignmentsLoading}
|
||||||
onClose={(shouldReload) => {
|
onBack={() => router.push("/")}
|
||||||
setSelectedUser(undefined);
|
/>
|
||||||
if (shouldReload && selectedUser!.type === "student") reloadStudents();
|
);
|
||||||
if (shouldReload && selectedUser!.type === "teacher") reloadTeachers();
|
|
||||||
}}
|
|
||||||
onViewStudents={
|
|
||||||
selectedUser.type === "corporate" || selectedUser.type === "teacher"
|
|
||||||
? () => {
|
|
||||||
appendUserFilters({
|
|
||||||
id: "view-students",
|
|
||||||
filter: (x: User) => x.type === "student",
|
|
||||||
});
|
|
||||||
appendUserFilters({
|
|
||||||
id: "belongs-to-admin",
|
|
||||||
filter: (x: User) =>
|
|
||||||
groups
|
|
||||||
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
|
|
||||||
.flatMap((g) => g.participants)
|
|
||||||
.includes(x.id),
|
|
||||||
});
|
|
||||||
|
|
||||||
router.push("/list/users");
|
if (router.asPath === "/#statistical") return <MasterStatisticalPage />;
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onViewTeachers={
|
|
||||||
selectedUser.type === "corporate" || selectedUser.type === "student"
|
|
||||||
? () => {
|
|
||||||
appendUserFilters({
|
|
||||||
id: "view-teachers",
|
|
||||||
filter: (x: User) => x.type === "teacher",
|
|
||||||
});
|
|
||||||
appendUserFilters({
|
|
||||||
id: "belongs-to-admin",
|
|
||||||
filter: (x: User) =>
|
|
||||||
groups
|
|
||||||
.filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
|
|
||||||
.flatMap((g) => g.participants)
|
|
||||||
.includes(x.id),
|
|
||||||
});
|
|
||||||
|
|
||||||
router.push("/list/users");
|
return (
|
||||||
}
|
<>
|
||||||
: undefined
|
<Modal isOpen={showModal} onClose={() => setSelectedUser(undefined)}>
|
||||||
}
|
<>
|
||||||
user={selectedUser}
|
{selectedUser && (
|
||||||
/>
|
<div className="w-full flex flex-col gap-8">
|
||||||
</div>
|
<UserCard
|
||||||
)}
|
loggedInUser={user}
|
||||||
</>
|
onClose={(shouldReload) => {
|
||||||
</Modal>
|
setSelectedUser(undefined);
|
||||||
|
if (shouldReload && selectedUser!.type === "student")
|
||||||
|
reloadStudents();
|
||||||
|
if (shouldReload && selectedUser!.type === "teacher")
|
||||||
|
reloadTeachers();
|
||||||
|
}}
|
||||||
|
onViewStudents={
|
||||||
|
selectedUser.type === "corporate" ||
|
||||||
|
selectedUser.type === "teacher"
|
||||||
|
? () => {
|
||||||
|
appendUserFilters({
|
||||||
|
id: "view-students",
|
||||||
|
filter: (x: User) => x.type === "student",
|
||||||
|
});
|
||||||
|
appendUserFilters({
|
||||||
|
id: "belongs-to-admin",
|
||||||
|
filter: (x: User) =>
|
||||||
|
groups
|
||||||
|
.filter(
|
||||||
|
(g) =>
|
||||||
|
g.admin === selectedUser.id ||
|
||||||
|
g.participants.includes(selectedUser.id)
|
||||||
|
)
|
||||||
|
.flatMap((g) => g.participants)
|
||||||
|
.includes(x.id),
|
||||||
|
});
|
||||||
|
|
||||||
<>
|
router.push("/list/users");
|
||||||
{!!linkedCorporate && (
|
}
|
||||||
<div className="absolute top-4 right-4 bg-neutral-200 px-2 rounded-lg py-1">
|
: undefined
|
||||||
Linked to: <b>{linkedCorporate?.corporateInformation?.companyInformation.name || linkedCorporate.name}</b>
|
}
|
||||||
</div>
|
onViewTeachers={
|
||||||
)}
|
selectedUser.type === "corporate" ||
|
||||||
<section className="grid grid-cols-5 -md:grid-cols-2 gap-4 text-center">
|
selectedUser.type === "student"
|
||||||
<IconCard
|
? () => {
|
||||||
onClick={() => router.push("/#students")}
|
appendUserFilters({
|
||||||
isLoading={isStudentsLoading}
|
id: "view-teachers",
|
||||||
Icon={BsPersonFill}
|
filter: (x: User) => x.type === "teacher",
|
||||||
label="Students"
|
});
|
||||||
value={totalStudents}
|
appendUserFilters({
|
||||||
color="purple"
|
id: "belongs-to-admin",
|
||||||
/>
|
filter: (x: User) =>
|
||||||
<IconCard
|
groups
|
||||||
onClick={() => router.push("/#teachers")}
|
.filter(
|
||||||
isLoading={isTeachersLoading}
|
(g) =>
|
||||||
Icon={BsPencilSquare}
|
g.admin === selectedUser.id ||
|
||||||
label="Teachers"
|
g.participants.includes(selectedUser.id)
|
||||||
value={totalTeachers}
|
)
|
||||||
color="purple"
|
.flatMap((g) => g.participants)
|
||||||
/>
|
.includes(x.id),
|
||||||
<IconCard
|
});
|
||||||
Icon={BsClipboard2Data}
|
|
||||||
label="Exams Performed"
|
|
||||||
value={stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user)).length}
|
|
||||||
color="purple"
|
|
||||||
/>
|
|
||||||
<IconCard
|
|
||||||
Icon={BsPaperclip}
|
|
||||||
isLoading={isStudentsLoading}
|
|
||||||
label="Average Level"
|
|
||||||
value={averageLevelCalculator(stats.filter((s) => groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
|
|
||||||
color="purple"
|
|
||||||
/>
|
|
||||||
<IconCard onClick={() => router.push("/#groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" />
|
|
||||||
<IconCard
|
|
||||||
Icon={BsPersonCheck}
|
|
||||||
label="User Balance"
|
|
||||||
value={`${balance}/${user.corporateInformation?.companyInformation?.userAmount || 0}`}
|
|
||||||
color="purple"
|
|
||||||
/>
|
|
||||||
<IconCard
|
|
||||||
Icon={BsClock}
|
|
||||||
label="Expiration Date"
|
|
||||||
value={user.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).format("DD/MM/yyyy") : "Unlimited"}
|
|
||||||
color="rose"
|
|
||||||
/>
|
|
||||||
<IconCard
|
|
||||||
Icon={BsPersonFillGear}
|
|
||||||
isLoading={isStudentsLoading}
|
|
||||||
label="Student Performance"
|
|
||||||
value={totalStudents}
|
|
||||||
color="purple"
|
|
||||||
onClick={() => router.push("/#studentsPerformance")}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
disabled={isAssignmentsLoading}
|
|
||||||
onClick={() => router.push("/#assignments")}
|
|
||||||
className="bg-white col-span-2 rounded-xl shadow p-4 flex flex-col gap-4 items-center w-96 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300">
|
|
||||||
<BsEnvelopePaper className="text-6xl text-mti-purple-light" />
|
|
||||||
<span className="flex flex-col gap-1 items-center text-xl">
|
|
||||||
<span className="text-lg">Assignments</span>
|
|
||||||
<span className="font-semibold text-mti-purple-light">
|
|
||||||
{isAssignmentsLoading ? "Loading..." : assignments.filter((a) => !a.archived).length}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full justify-between">
|
router.push("/list/users");
|
||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
}
|
||||||
<span className="p-4">Latest students</span>
|
: undefined
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
}
|
||||||
{students
|
user={selectedUser}
|
||||||
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
/>
|
||||||
.map((x) => (
|
</div>
|
||||||
<UserDisplay key={x.id} {...x} />
|
)}
|
||||||
))}
|
</>
|
||||||
</div>
|
</Modal>
|
||||||
</div>
|
|
||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<>
|
||||||
<span className="p-4">Latest teachers</span>
|
{!!linkedCorporate && (
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
<div className="absolute top-4 right-4 bg-neutral-200 px-2 rounded-lg py-1">
|
||||||
{teachers
|
Linked to:{" "}
|
||||||
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
<b>
|
||||||
.map((x) => (
|
{linkedCorporate?.corporateInformation?.companyInformation.name ||
|
||||||
<UserDisplay key={x.id} {...x} />
|
linkedCorporate.name}
|
||||||
))}
|
</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
<section className="grid grid-cols-5 -md:grid-cols-2 gap-4 text-center">
|
||||||
<span className="p-4">Highest level students</span>
|
<IconCard
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
onClick={() => router.push("/#students")}
|
||||||
{students
|
isLoading={isStudentsLoading}
|
||||||
.sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
|
Icon={BsPersonFill}
|
||||||
.map((x) => (
|
label="Students"
|
||||||
<UserDisplay key={x.id} {...x} />
|
value={totalStudents}
|
||||||
))}
|
color="purple"
|
||||||
</div>
|
/>
|
||||||
</div>
|
<IconCard
|
||||||
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
onClick={() => router.push("/#teachers")}
|
||||||
<span className="p-4">Highest exam count students</span>
|
isLoading={isTeachersLoading}
|
||||||
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
Icon={BsPencilSquare}
|
||||||
{students
|
label="Teachers"
|
||||||
.sort(
|
value={totalTeachers}
|
||||||
(a, b) =>
|
color="purple"
|
||||||
Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length,
|
/>
|
||||||
)
|
<IconCard
|
||||||
.map((x) => (
|
Icon={BsClipboard2Data}
|
||||||
<UserDisplay key={x.id} {...x} />
|
label="Exams Performed"
|
||||||
))}
|
value={
|
||||||
</div>
|
stats.filter((s) =>
|
||||||
</div>
|
groups.flatMap((g) => g.participants).includes(s.user)
|
||||||
</section>
|
).length
|
||||||
</>
|
}
|
||||||
</>
|
color="purple"
|
||||||
);
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPaperclip}
|
||||||
|
isLoading={isStudentsLoading}
|
||||||
|
label="Average Level"
|
||||||
|
value={averageLevelCalculator(
|
||||||
|
stats.filter((s) =>
|
||||||
|
groups.flatMap((g) => g.participants).includes(s.user)
|
||||||
|
)
|
||||||
|
).toFixed(1)}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
onClick={() => router.push("/#groups")}
|
||||||
|
Icon={BsPeople}
|
||||||
|
label="Groups"
|
||||||
|
value={groups.length}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPersonCheck}
|
||||||
|
label="User Balance"
|
||||||
|
value={`${balance}/${
|
||||||
|
user.corporateInformation?.companyInformation?.userAmount || 0
|
||||||
|
}`}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsClock}
|
||||||
|
label="Expiration Date"
|
||||||
|
value={
|
||||||
|
user.subscriptionExpirationDate
|
||||||
|
? moment(user.subscriptionExpirationDate).format("DD/MM/yyyy")
|
||||||
|
: "Unlimited"
|
||||||
|
}
|
||||||
|
color="rose"
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsPersonFillGear}
|
||||||
|
isLoading={isStudentsLoading}
|
||||||
|
label="Student Performance"
|
||||||
|
value={totalStudents}
|
||||||
|
color="purple"
|
||||||
|
onClick={() => router.push("/#studentsPerformance")}
|
||||||
|
/>
|
||||||
|
<IconCard
|
||||||
|
Icon={BsDatabase}
|
||||||
|
label="Master Statistical"
|
||||||
|
color="purple"
|
||||||
|
onClick={() => router.push("/#statistical")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
disabled={isAssignmentsLoading}
|
||||||
|
onClick={() => router.push("/#assignments")}
|
||||||
|
className="bg-white col-span-2 rounded-xl shadow p-4 flex flex-col gap-4 items-center w-96 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300"
|
||||||
|
>
|
||||||
|
<BsEnvelopePaper className="text-6xl text-mti-purple-light" />
|
||||||
|
<span className="flex flex-col gap-1 items-center text-xl">
|
||||||
|
<span className="text-lg">Assignments</span>
|
||||||
|
<span className="font-semibold text-mti-purple-light">
|
||||||
|
{isAssignmentsLoading
|
||||||
|
? "Loading..."
|
||||||
|
: assignments.filter((a) => !a.archived).length}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="grid grid-cols-1 md:grid-cols-2 gap-4 w-full justify-between">
|
||||||
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
|
<span className="p-4">Latest students</span>
|
||||||
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
|
{students
|
||||||
|
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
||||||
|
.map((x) => (
|
||||||
|
<UserDisplay key={x.id} {...x} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
|
<span className="p-4">Latest teachers</span>
|
||||||
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
|
{teachers
|
||||||
|
.sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
|
||||||
|
.map((x) => (
|
||||||
|
<UserDisplay key={x.id} {...x} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
|
<span className="p-4">Highest level students</span>
|
||||||
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
|
{students
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
calculateAverageLevel(b.levels) -
|
||||||
|
calculateAverageLevel(a.levels)
|
||||||
|
)
|
||||||
|
.map((x) => (
|
||||||
|
<UserDisplay key={x.id} {...x} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white shadow flex flex-col rounded-xl w-full">
|
||||||
|
<span className="p-4">Highest exam count students</span>
|
||||||
|
<div className="flex flex-col items-start h-96 overflow-scroll scrollbar-hide">
|
||||||
|
{students
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
Object.keys(groupByExam(getStatsByStudent(b))).length -
|
||||||
|
Object.keys(groupByExam(getStatsByStudent(a))).length
|
||||||
|
)
|
||||||
|
.map((x) => (
|
||||||
|
<UserDisplay key={x.id} {...x} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, {useEffect, useMemo, useState} from "react";
|
||||||
import {CorporateUser, User} from "@/interfaces/user";
|
import {CorporateUser, User} from "@/interfaces/user";
|
||||||
import {BsFileExcel, BsBank, BsPersonFill} from "react-icons/bs";
|
import {BsFileExcel, BsBank, BsPersonFill} from "react-icons/bs";
|
||||||
import IconCard from "../IconCard";
|
import IconCard from "../IconCard";
|
||||||
@@ -14,6 +14,7 @@ import {useListSearch} from "@/hooks/useListSearch";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {toast} from "react-toastify";
|
import {toast} from "react-toastify";
|
||||||
import Button from "@/components/Low/Button";
|
import Button from "@/components/Low/Button";
|
||||||
|
import {getUserName} from "@/utils/users";
|
||||||
|
|
||||||
interface GroupedCorporateUsers {
|
interface GroupedCorporateUsers {
|
||||||
// list of user Ids
|
// list of user Ids
|
||||||
@@ -42,10 +43,13 @@ interface UserCount {
|
|||||||
maxUserCount: number;
|
maxUserCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchFilters = [["email"], ["user"], ["userId"]];
|
const searchFilters = [["email"], ["user"], ["userId"], ["exams"], ["assignment"]];
|
||||||
|
|
||||||
|
const SIZE = 16;
|
||||||
|
|
||||||
const MasterStatistical = (props: Props) => {
|
const MasterStatistical = (props: Props) => {
|
||||||
const {users, corporateUsers, displaySelection = true} = props;
|
const {users, corporateUsers, displaySelection = true} = props;
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
|
||||||
// const corporateRelevantUsers = React.useMemo(
|
// const corporateRelevantUsers = React.useMemo(
|
||||||
// () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[],
|
// () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[],
|
||||||
@@ -59,7 +63,6 @@ const MasterStatistical = (props: Props) => {
|
|||||||
const [endDate, setEndDate] = React.useState<Date | null>(moment().endOf("year").toDate());
|
const [endDate, setEndDate] = React.useState<Date | null>(moment().endOf("year").toDate());
|
||||||
|
|
||||||
const {assignments} = useAssignmentsCorporates({
|
const {assignments} = useAssignmentsCorporates({
|
||||||
// corporates: [...corporates, "tYU0HTiJdjMsS8SB7XJsUdMMP892"],
|
|
||||||
corporates: selectedCorporates,
|
corporates: selectedCorporates,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
@@ -73,12 +76,13 @@ const MasterStatistical = (props: Props) => {
|
|||||||
const userResults = a.assignees.map((assignee) => {
|
const userResults = a.assignees.map((assignee) => {
|
||||||
const userStats = a.results.find((r) => r.user === assignee)?.stats || [];
|
const userStats = a.results.find((r) => r.user === assignee)?.stats || [];
|
||||||
const userData = users.find((u) => u.id === assignee);
|
const userData = users.find((u) => u.id === assignee);
|
||||||
const corporate = users.find((u) => u.id === a.assigner)?.name || "";
|
const corporate = getUserName(users.find((u) => u.id === a.assigner));
|
||||||
const commonData = {
|
const commonData = {
|
||||||
user: userData?.name || "",
|
user: userData?.name || "N/A",
|
||||||
email: userData?.email || "",
|
email: userData?.email || "N/A",
|
||||||
userId: assignee,
|
userId: assignee,
|
||||||
corporateId: a.corporateId,
|
corporateId: a.corporateId,
|
||||||
|
exams: a.exams.map((x) => x.id).join(", "),
|
||||||
corporate,
|
corporate,
|
||||||
assignment: a.name,
|
assignment: a.name,
|
||||||
};
|
};
|
||||||
@@ -87,7 +91,7 @@ const MasterStatistical = (props: Props) => {
|
|||||||
...commonData,
|
...commonData,
|
||||||
correct: 0,
|
correct: 0,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
// date: moment(),
|
date: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +108,8 @@ const MasterStatistical = (props: Props) => {
|
|||||||
[assignments, users],
|
[assignments, users],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => console.log(assignments), [assignments]);
|
||||||
|
|
||||||
const getCorporateScores = (corporateId: string): UserCount => {
|
const getCorporateScores = (corporateId: string): UserCount => {
|
||||||
const corporateAssignmentsUsers = assignments.filter((a) => a.corporateId === corporateId).reduce((acc, a) => acc + a.assignees.length, 0);
|
const corporateAssignmentsUsers = assignments.filter((a) => a.corporateId === corporateId).reduce((acc, a) => acc + a.assignees.length, 0);
|
||||||
|
|
||||||
@@ -166,13 +172,6 @@ const MasterStatistical = (props: Props) => {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
columnHelper.accessor("corporate", {
|
|
||||||
header: "Corporate",
|
|
||||||
id: "corporate",
|
|
||||||
cell: (info) => {
|
|
||||||
return <span>{info.getValue()}</span>;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
columnHelper.accessor("assignment", {
|
columnHelper.accessor("assignment", {
|
||||||
header: "Assignment",
|
header: "Assignment",
|
||||||
id: "assignment",
|
id: "assignment",
|
||||||
@@ -192,7 +191,7 @@ const MasterStatistical = (props: Props) => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("correct", {
|
columnHelper.accessor("correct", {
|
||||||
header: "Correct",
|
header: "Score",
|
||||||
id: "correct",
|
id: "correct",
|
||||||
cell: (info) => {
|
cell: (info) => {
|
||||||
return <span>{info.getValue()}</span>;
|
return <span>{info.getValue()}</span>;
|
||||||
@@ -204,7 +203,7 @@ const MasterStatistical = (props: Props) => {
|
|||||||
cell: (info) => {
|
cell: (info) => {
|
||||||
const date = info.getValue();
|
const date = info.getValue();
|
||||||
if (date) {
|
if (date) {
|
||||||
return <span>{date.format("DD/MM/YYYY")}</span>;
|
return <span>{!!date ? date.format("DD/MM/YYYY") : "N/A"}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span>{""}</span>;
|
return <span>{""}</span>;
|
||||||
@@ -214,8 +213,14 @@ const MasterStatistical = (props: Props) => {
|
|||||||
|
|
||||||
const {rows: filteredRows, renderSearch, text: searchText} = useListSearch(searchFilters, tableResults);
|
const {rows: filteredRows, renderSearch, text: searchText} = useListSearch(searchFilters, tableResults);
|
||||||
|
|
||||||
|
useEffect(() => setPage(0), [searchText]);
|
||||||
|
const rows = useMemo(
|
||||||
|
() => filteredRows.slice(page * SIZE, (page + 1) * SIZE > filteredRows.length ? filteredRows.length : (page + 1) * SIZE),
|
||||||
|
[filteredRows, page],
|
||||||
|
);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: filteredRows,
|
data: rows,
|
||||||
columns: defaultColumns,
|
columns: defaultColumns,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
});
|
});
|
||||||
@@ -344,7 +349,22 @@ const MasterStatistical = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="w-full h-full flex flex-col gap-4">
|
||||||
|
<div className="w-full flex gap-2 justify-between">
|
||||||
|
<Button className="w-full max-w-[200px]" disabled={page === 0} onClick={() => setPage((prev) => prev - 1)}>
|
||||||
|
Previous Page
|
||||||
|
</Button>
|
||||||
|
<div className="flex items-center gap-4 w-fit">
|
||||||
|
<span className="opacity-80">
|
||||||
|
{page * SIZE + 1} - {(page + 1) * SIZE > filteredRows.length ? filteredRows.length : (page + 1) * SIZE} /{" "}
|
||||||
|
{filteredRows.length}
|
||||||
|
</span>
|
||||||
|
<Button className="w-[200px]" disabled={(page + 1) * SIZE >= rows.length} onClick={() => setPage((prev) => prev + 1)}>
|
||||||
|
Next Page
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table className="rounded-xl h-full bg-mti-purple-ultralight/40 w-full">
|
<table className="rounded-xl h-full bg-mti-purple-ultralight/40 w-full">
|
||||||
<thead>
|
<thead>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
|||||||
@@ -7,23 +7,14 @@ import {BsArrowLeft} from "react-icons/bs";
|
|||||||
import MasterStatistical from "./MasterStatistical";
|
import MasterStatistical from "./MasterStatistical";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
groupedByNameCorporates: Record<string, CorporateUser[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MasterStatisticalPage = () => {
|
const MasterStatisticalPage = ({ groupedByNameCorporates }: Props) => {
|
||||||
const {users} = useUsers();
|
const {users} = useUsers();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const groupedByNameCorporates = useMemo(
|
|
||||||
() =>
|
|
||||||
groupBy(
|
|
||||||
users.filter((x) => x.type === "corporate"),
|
|
||||||
(x: CorporateUser) => x.corporateInformation?.companyInformation?.name || "N/A",
|
|
||||||
),
|
|
||||||
[users],
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupedByNameCorporateIds = useMemo(
|
const groupedByNameCorporateIds = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Object.keys(groupedByNameCorporates).reduce((accm, x) => {
|
Object.keys(groupedByNameCorporates).reduce((accm, x) => {
|
||||||
|
|||||||
@@ -83,18 +83,6 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
|
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
|
||||||
|
|
||||||
const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]);
|
const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]);
|
||||||
const assignmentsUsers = useMemo(
|
|
||||||
() =>
|
|
||||||
[...students, ...teachers].filter((x) =>
|
|
||||||
!!selectedUser
|
|
||||||
? groups
|
|
||||||
.filter((g) => g.admin === selectedUser.id)
|
|
||||||
.flatMap((g) => g.participants)
|
|
||||||
.includes(x.id) || false
|
|
||||||
: groups.flatMap((g) => g.participants).includes(x.id),
|
|
||||||
),
|
|
||||||
[groups, selectedUser, teachers, students],
|
|
||||||
);
|
|
||||||
|
|
||||||
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -129,6 +117,19 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const {users} = useUsers();
|
||||||
|
|
||||||
|
const groupedByNameCorporates = useMemo(
|
||||||
|
() =>
|
||||||
|
groupBy(
|
||||||
|
users.filter((x) => x.type === "corporate"),
|
||||||
|
(x: CorporateUser) => x.corporateInformation?.companyInformation?.name || "N/A",
|
||||||
|
) as Record<string, CorporateUser[]>,
|
||||||
|
[users],
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupedByNameCorporatesKeys = Object.keys(groupedByNameCorporates);
|
||||||
|
|
||||||
const GroupsList = () => {
|
const GroupsList = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -148,7 +149,7 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (router.asPath === "/#studentsPerformance") return <StudentPerformancePage user={user} />;
|
if (router.asPath === "/#studentsPerformance") return <StudentPerformancePage user={user} />;
|
||||||
if (router.asPath === "/#statistical") return <MasterStatisticalPage />;
|
if (router.asPath === "/#statistical") return <MasterStatisticalPage groupedByNameCorporates={groupedByNameCorporates} />;
|
||||||
if (router.asPath === "/#groups") return <GroupsList />;
|
if (router.asPath === "/#groups") return <GroupsList />;
|
||||||
|
|
||||||
if (router.asPath === "/#students")
|
if (router.asPath === "/#students")
|
||||||
@@ -360,12 +361,18 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
color="purple"
|
color="purple"
|
||||||
onClick={() => router.push("/#corporate")}
|
onClick={() => router.push("/#corporate")}
|
||||||
/>
|
/>
|
||||||
<IconCard Icon={BsBank} label="Corporate" value={totalCorporate} isLoading={isCorporatesLoading} color="purple" />
|
<IconCard
|
||||||
|
Icon={BsBank}
|
||||||
|
label="Corporate"
|
||||||
|
value={groupedByNameCorporatesKeys.length}
|
||||||
|
isLoading={isCorporatesLoading}
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
<IconCard
|
<IconCard
|
||||||
Icon={BsPersonFillGear}
|
Icon={BsPersonFillGear}
|
||||||
isLoading={isStudentsLoading}
|
isLoading={isStudentsLoading}
|
||||||
label="Student Performance"
|
label="Student Performance"
|
||||||
value={students.length}
|
value={totalStudents}
|
||||||
color="purple"
|
color="purple"
|
||||||
onClick={() => router.push("/#studentsPerformance")}
|
onClick={() => router.push("/#studentsPerformance")}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {calculateAverageLevel} from "@/utils/score";
|
|||||||
import {sortByModuleName} from "@/utils/moduleUtils";
|
import {sortByModuleName} from "@/utils/moduleUtils";
|
||||||
import {capitalize} from "lodash";
|
import {capitalize} from "lodash";
|
||||||
import ProfileSummary from "@/components/ProfileSummary";
|
import ProfileSummary from "@/components/ProfileSummary";
|
||||||
import {Variant} from "@/interfaces/exam";
|
import {ShuffleMap, Shuffles, Variant} from "@/interfaces/exam";
|
||||||
import useSessions, {Session} from "@/hooks/useSessions";
|
import useSessions, {Session} from "@/hooks/useSessions";
|
||||||
import SessionCard from "@/components/Medium/SessionCard";
|
import SessionCard from "@/components/Medium/SessionCard";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
@@ -41,6 +41,7 @@ export default function Selection({user, page, onStart, disableSelection = false
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadSession = async (session: Session) => {
|
const loadSession = async (session: Session) => {
|
||||||
|
state.setShuffles(session.userSolutions.map((x) => ({exerciseID: x.exercise, shuffles: x.shuffleMaps ? x.shuffleMaps : []})));
|
||||||
state.setSelectedModules(session.selectedModules);
|
state.setSelectedModules(session.selectedModules);
|
||||||
state.setExam(session.exam);
|
state.setExam(session.exam);
|
||||||
state.setExams(session.exams);
|
state.setExams(session.exams);
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import {Exam} from "@/interfaces/exam";
|
import {Exam} from "@/interfaces/exam";
|
||||||
import {ExamState} from "@/stores/examStore";
|
import {ExamState} from "@/stores/examStore";
|
||||||
import Axios from "axios";
|
import axios from "axios";
|
||||||
import {setupCache} from "axios-cache-interceptor";
|
import {setupCache} from "axios-cache-interceptor";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
const instance = Axios.create();
|
|
||||||
const axios = setupCache(instance);
|
|
||||||
|
|
||||||
export type Session = ExamState & {user: string; id: string; date: string};
|
export type Session = ExamState & {user: string; id: string; date: string};
|
||||||
|
|
||||||
export default function useSessions(user?: string) {
|
export default function useSessions(user?: string) {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ export default function useUser({redirectTo = "", redirectIfFound = false} = {})
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
// If redirectIfFound is also set, redirect if the user was found
|
// If redirectIfFound is also set, redirect if the user was found
|
||||||
(redirectIfFound && user && user.isVerified) ||
|
(redirectIfFound && user) ||
|
||||||
// If redirectTo is set, redirect if the user was not found.
|
// If redirectTo is set, redirect if the user was not found.
|
||||||
(redirectTo && !redirectIfFound && (!user || (user && !user.isVerified)))
|
(redirectTo && !redirectIfFound && !user)
|
||||||
) {
|
) {
|
||||||
Router.push(redirectTo);
|
Router.push(redirectTo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const userHashStudent = {type: "student"} as {type: Type};
|
|||||||
export const userHashTeacher = {type: "teacher"} as {type: Type};
|
export const userHashTeacher = {type: "teacher"} as {type: Type};
|
||||||
export const userHashCorporate = {type: "corporate"} as {type: Type};
|
export const userHashCorporate = {type: "corporate"} as {type: Type};
|
||||||
|
|
||||||
export default function useUsers(props?: {type?: string; page?: number; size?: number; orderBy?: string; direction?: "asc" | "desc"}) {
|
export default function useUsers(props?: {type?: string; page?: number; size?: number; orderBy?: string; direction?: "asc" | "desc", searchTerm?: string | undefined}) {
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -35,7 +35,7 @@ export default function useUsers(props?: {type?: string; page?: number; size?: n
|
|||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
useEffect(getData, [props?.page, props?.size, props?.type, props?.orderBy, props?.direction]);
|
useEffect(getData, [props?.page, props?.size, props?.type, props?.orderBy, props?.direction, props?.searchTerm]);
|
||||||
|
|
||||||
return {users, total, isLoading, isError, reload: getData};
|
return {users, total, isLoading, isError, reload: getData};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ if (!process.env.MONGODB_URI) {
|
|||||||
throw new Error('Invalid/Missing environment variable: "MONGODB_URI"');
|
throw new Error('Invalid/Missing environment variable: "MONGODB_URI"');
|
||||||
}
|
}
|
||||||
|
|
||||||
const uri = process.env.MONGODB_URI;
|
const uri = process.env.MONGODB_URI || "";
|
||||||
const options = {};
|
const options = {};
|
||||||
|
|
||||||
let client: MongoClient;
|
let client: MongoClient;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const USER_TYPE_PERMISSIONS: {
|
|||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
perm: "createCodeAdmin",
|
perm: "createCodeAdmin",
|
||||||
list: ["student", "teacher", "agent", "corporate", "admin", "mastercorporate"],
|
list: ["student", "teacher", "agent", "corporate", "mastercorporate"],
|
||||||
},
|
},
|
||||||
developer: {
|
developer: {
|
||||||
perm: undefined,
|
perm: undefined,
|
||||||
@@ -161,7 +161,7 @@ export default function BatchCreateUser({user, users, permissions, onFinish}: Pr
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.post("/api/batch_users", { users: newUsers.map(user => ({...user, type, expiryDate})) });
|
await axios.post("/api/batch_users", {users: newUsers.map((user) => ({...user, type, expiryDate}))});
|
||||||
toast.success(`Successfully added ${newUsers.length} user(s)!`);
|
toast.success(`Successfully added ${newUsers.length} user(s)!`);
|
||||||
onFinish();
|
onFinish();
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1,36 +1,41 @@
|
|||||||
import Button from "@/components/Low/Button";
|
import Button from "@/components/Low/Button";
|
||||||
import {PERMISSIONS} from "@/constants/userPermissions";
|
import { PERMISSIONS } from "@/constants/userPermissions";
|
||||||
import useGroups from "@/hooks/useGroups";
|
import useGroups from "@/hooks/useGroups";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
import {Type, User, userTypes, CorporateUser, Group} from "@/interfaces/user";
|
import { Type, User, userTypes, CorporateUser, Group } from "@/interfaces/user";
|
||||||
import {Popover, Transition} from "@headlessui/react";
|
import { Popover, Transition } from "@headlessui/react";
|
||||||
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
|
import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {capitalize, reverse} from "lodash";
|
import { capitalize, reverse } from "lodash";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {Fragment, useEffect, useState, useMemo} from "react";
|
import { Fragment, useEffect, useState, useMemo } from "react";
|
||||||
import {BsArrowDown, BsArrowDownUp, BsArrowUp, BsCheck, BsCheckCircle, BsEye, BsFillExclamationOctagonFill, BsPerson, BsTrash} from "react-icons/bs";
|
import { BsArrowDown, BsArrowDownUp, BsArrowUp, BsCheck, BsCheckCircle, BsEye, BsFillExclamationOctagonFill, BsPerson, BsTrash } from "react-icons/bs";
|
||||||
import {toast} from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import {countries, TCountries} from "countries-list";
|
import { countries, TCountries } from "countries-list";
|
||||||
import countryCodes from "country-codes-list";
|
import countryCodes from "country-codes-list";
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
import UserCard from "@/components/UserCard";
|
import UserCard from "@/components/UserCard";
|
||||||
import {getUserCompanyName, isAgentUser, USER_TYPE_LABELS} from "@/resources/user";
|
import { getUserCompanyName, isAgentUser, USER_TYPE_LABELS } from "@/resources/user";
|
||||||
import useFilterStore from "@/stores/listFilterStore";
|
import useFilterStore from "@/stores/listFilterStore";
|
||||||
import {useRouter} from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import {isCorporateUser} from "@/resources/user";
|
import { isCorporateUser } from "@/resources/user";
|
||||||
import {useListSearch} from "@/hooks/useListSearch";
|
import { useListSearch } from "@/hooks/useListSearch";
|
||||||
import {getUserCorporate} from "@/utils/groups";
|
import { getUserCorporate } from "@/utils/groups";
|
||||||
import {asyncSorter} from "@/utils";
|
import { asyncSorter } from "@/utils";
|
||||||
import {exportListToExcel, UserListRow} from "@/utils/users";
|
import { exportListToExcel, UserListRow } from "@/utils/users";
|
||||||
import {checkAccess} from "@/utils/permissions";
|
import { checkAccess } from "@/utils/permissions";
|
||||||
import {PermissionType} from "@/interfaces/permissions";
|
import { PermissionType } from "@/interfaces/permissions";
|
||||||
import usePermissions from "@/hooks/usePermissions";
|
import usePermissions from "@/hooks/usePermissions";
|
||||||
import useUserBalance from "@/hooks/useUserBalance";
|
import useUserBalance from "@/hooks/useUserBalance";
|
||||||
|
import Input from "@/components/Low/Input";
|
||||||
const columnHelper = createColumnHelper<User>();
|
const columnHelper = createColumnHelper<User>();
|
||||||
const searchFields = [["name"], ["email"], ["corporateInformation", "companyInformation", "name"]];
|
const searchFields = [["name"], ["email"], ["corporateInformation", "companyInformation", "name"]];
|
||||||
|
|
||||||
|
const corporatesHash = {
|
||||||
|
type: "corporate",
|
||||||
|
};
|
||||||
|
|
||||||
const CompanyNameCell = ({users, user, groups}: {user: User; users: User[]; groups: Group[]}) => {
|
const CompanyNameCell = ({users, user, groups}: {user: User; users: User[]; groups: Group[]}) => {
|
||||||
const [companyName, setCompanyName] = useState("");
|
const [companyName, setCompanyName] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -59,17 +64,14 @@ export default function UserList({
|
|||||||
const [displayUsers, setDisplayUsers] = useState<User[]>([]);
|
const [displayUsers, setDisplayUsers] = useState<User[]>([]);
|
||||||
const [selectedUser, setSelectedUser] = useState<User>();
|
const [selectedUser, setSelectedUser] = useState<User>();
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
|
const [searchTerm, setSearchTerm] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
const userHash = useMemo(
|
const { users, total, isLoading, reload } = useUsers({type: type, size: 16, page: page, searchTerm: searchTerm});
|
||||||
() => ({
|
|
||||||
type,
|
const {users: corporates} = useUsers(corporatesHash);
|
||||||
size: 16,
|
|
||||||
page,
|
const totalUsers = useMemo(() => [...users, ...corporates], [users, corporates]);
|
||||||
}),
|
|
||||||
[type, page],
|
|
||||||
);
|
|
||||||
|
|
||||||
const {users, total, isLoading, reload} = useUsers(userHash);
|
|
||||||
const {permissions} = usePermissions(user?.id || "");
|
const {permissions} = usePermissions(user?.id || "");
|
||||||
const {balance} = useUserBalance();
|
const {balance} = useUserBalance();
|
||||||
const {groups} = useGroups({
|
const {groups} = useGroups({
|
||||||
@@ -106,20 +108,20 @@ export default function UserList({
|
|||||||
if (!confirm(`Are you sure you want to delete ${user.name}'s account?`)) return;
|
if (!confirm(`Are you sure you want to delete ${user.name}'s account?`)) return;
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.delete<{ok: boolean}>(`/api/user?id=${user.id}`)
|
.delete<{ ok: boolean }>(`/api/user?id=${user.id}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("User deleted successfully!");
|
toast.success("User deleted successfully!");
|
||||||
reload();
|
reload();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Something went wrong!", {toastId: "delete-error"});
|
toast.error("Something went wrong!", { toastId: "delete-error" });
|
||||||
})
|
})
|
||||||
.finally(reload);
|
.finally(reload);
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyAccount = (user: User) => {
|
const verifyAccount = (user: User) => {
|
||||||
axios
|
axios
|
||||||
.post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {
|
.post<{ user?: User; ok?: boolean }>(`/api/users/update?id=${user.id}`, {
|
||||||
...user,
|
...user,
|
||||||
isVerified: true,
|
isVerified: true,
|
||||||
})
|
})
|
||||||
@@ -128,22 +130,21 @@ export default function UserList({
|
|||||||
reload();
|
reload();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Something went wrong!", {toastId: "update-error"});
|
toast.error("Something went wrong!", { toastId: "update-error" });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDisableAccount = (user: User) => {
|
const toggleDisableAccount = (user: User) => {
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
`Are you sure you want to ${user.status === "disabled" ? "enable" : "disable"} ${
|
`Are you sure you want to ${user.status === "disabled" ? "enable" : "disable"} ${user.name
|
||||||
user.name
|
|
||||||
}'s account? This change is usually related to their payment state.`,
|
}'s account? This change is usually related to their payment state.`,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {
|
.post<{ user?: User; ok?: boolean }>(`/api/users/update?id=${user.id}`, {
|
||||||
...user,
|
...user,
|
||||||
status: user.status === "disabled" ? "active" : "disabled",
|
status: user.status === "disabled" ? "active" : "disabled",
|
||||||
})
|
})
|
||||||
@@ -152,18 +153,18 @@ export default function UserList({
|
|||||||
reload();
|
reload();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Something went wrong!", {toastId: "update-error"});
|
toast.error("Something went wrong!", { toastId: "update-error" });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const SorterArrow = ({name}: {name: string}) => {
|
const SorterArrow = ({ name }: { name: string }) => {
|
||||||
if (sorter === name) return <BsArrowUp />;
|
if (sorter === name) return <BsArrowUp />;
|
||||||
if (sorter === reverseString(name)) return <BsArrowDown />;
|
if (sorter === reverseString(name)) return <BsArrowDown />;
|
||||||
|
|
||||||
return <BsArrowDownUp />;
|
return <BsArrowDownUp />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionColumn = ({row}: {row: {original: User}}) => {
|
const actionColumn = ({ row }: { row: { original: User } }) => {
|
||||||
const updateUserPermission = PERMISSIONS.updateUser[row.original.type] as {
|
const updateUserPermission = PERMISSIONS.updateUser[row.original.type] as {
|
||||||
list: Type[];
|
list: Type[];
|
||||||
perm: PermissionType;
|
perm: PermissionType;
|
||||||
@@ -208,11 +209,11 @@ export default function UserList({
|
|||||||
<SorterArrow name="name" />
|
<SorterArrow name="name" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: ({row, getValue}) => (
|
cell: ({ row, getValue }) => (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) &&
|
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) &&
|
||||||
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
||||||
)}
|
)}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) ? setSelectedUser(row.original) : null
|
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) ? setSelectedUser(row.original) : null
|
||||||
@@ -230,9 +231,8 @@ export default function UserList({
|
|||||||
) as any,
|
) as any,
|
||||||
cell: (info) =>
|
cell: (info) =>
|
||||||
info.getValue()
|
info.getValue()
|
||||||
? `${countryCodes.findOne("countryCode" as any, info.getValue())?.flag} ${
|
? `${countryCodes.findOne("countryCode" as any, info.getValue())?.flag} ${countries[info.getValue() as unknown as keyof TCountries]?.name
|
||||||
countries[info.getValue() as unknown as keyof TCountries]?.name
|
} (+${countryCodes.findOne("countryCode" as any, info.getValue())?.countryCallingCode})`
|
||||||
} (+${countryCodes.findOne("countryCode" as any, info.getValue())?.countryCallingCode})`
|
|
||||||
: "N/A",
|
: "N/A",
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("demographicInformation.phone", {
|
columnHelper.accessor("demographicInformation.phone", {
|
||||||
@@ -298,11 +298,11 @@ export default function UserList({
|
|||||||
<SorterArrow name="name" />
|
<SorterArrow name="name" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: ({row, getValue}) => (
|
cell: ({ row, getValue }) => (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) &&
|
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) &&
|
||||||
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
||||||
)}
|
)}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) ? setSelectedUser(row.original) : null
|
checkAccess(user, ["admin", "corporate", "developer", "mastercorporate"]) ? setSelectedUser(row.original) : null
|
||||||
@@ -318,11 +318,11 @@ export default function UserList({
|
|||||||
<SorterArrow name="email" />
|
<SorterArrow name="email" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: ({row, getValue}) => (
|
cell: ({ row, getValue }) => (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type) &&
|
PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type) &&
|
||||||
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
"underline text-mti-purple-light hover:text-mti-purple-dark transition ease-in-out duration-300 cursor-pointer",
|
||||||
)}
|
)}
|
||||||
onClick={() => (PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type) ? setSelectedUser(row.original) : null)}>
|
onClick={() => (PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type) ? setSelectedUser(row.original) : null)}>
|
||||||
{getValue()}
|
{getValue()}
|
||||||
@@ -354,7 +354,7 @@ export default function UserList({
|
|||||||
<SorterArrow name="companyName" />
|
<SorterArrow name="companyName" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: (info) => <CompanyNameCell user={info.row.original} users={users} groups={groups} />,
|
cell: (info) => <CompanyNameCell user={info.row.original} users={totalUsers} groups={groups} />,
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("subscriptionExpirationDate", {
|
columnHelper.accessor("subscriptionExpirationDate", {
|
||||||
header: (
|
header: (
|
||||||
@@ -505,19 +505,17 @@ export default function UserList({
|
|||||||
return a.id.localeCompare(b.id);
|
return a.id.localeCompare(b.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {rows: filteredRows, renderSearch} = useListSearch<User>(searchFields, displayUsers);
|
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data: filteredRows,
|
data: displayUsers,
|
||||||
columns: (!showDemographicInformation ? defaultColumns : demographicColumns) as any,
|
columns: (!showDemographicInformation ? defaultColumns : demographicColumns) as any,
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const downloadExcel = () => {
|
const downloadExcel = () => {
|
||||||
const csv = exportListToExcel(filteredRows, users, groups);
|
const csv = exportListToExcel(displayUsers, users, groups);
|
||||||
|
|
||||||
const element = document.createElement("a");
|
const element = document.createElement("a");
|
||||||
const file = new Blob([csv], {type: "text/csv"});
|
const file = new Blob([csv], { type: "text/csv" });
|
||||||
element.href = URL.createObjectURL(file);
|
element.href = URL.createObjectURL(file);
|
||||||
element.download = "users.csv";
|
element.download = "users.csv";
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
@@ -551,53 +549,53 @@ export default function UserList({
|
|||||||
onViewStudents={
|
onViewStudents={
|
||||||
(selectedUser.type === "corporate" || selectedUser.type === "teacher") && studentsFromAdmin.length > 0
|
(selectedUser.type === "corporate" || selectedUser.type === "teacher") && studentsFromAdmin.length > 0
|
||||||
? () => {
|
? () => {
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "view-students",
|
id: "view-students",
|
||||||
filter: viewStudentFilter,
|
filter: viewStudentFilter,
|
||||||
});
|
});
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "belongs-to-admin",
|
id: "belongs-to-admin",
|
||||||
filter: belongsToAdminFilter,
|
filter: belongsToAdminFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
router.push("/list/users");
|
router.push("/list/users");
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onViewTeachers={
|
onViewTeachers={
|
||||||
(selectedUser.type === "corporate" || selectedUser.type === "student") && teachersFromAdmin.length > 0
|
(selectedUser.type === "corporate" || selectedUser.type === "student") && teachersFromAdmin.length > 0
|
||||||
? () => {
|
? () => {
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "view-teachers",
|
id: "view-teachers",
|
||||||
filter: viewTeacherFilter,
|
filter: viewTeacherFilter,
|
||||||
});
|
});
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "belongs-to-admin",
|
id: "belongs-to-admin",
|
||||||
filter: belongsToAdminFilter,
|
filter: belongsToAdminFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
router.push("/list/users");
|
router.push("/list/users");
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onViewCorporate={
|
onViewCorporate={
|
||||||
selectedUser.type === "teacher" || selectedUser.type === "student"
|
selectedUser.type === "teacher" || selectedUser.type === "student"
|
||||||
? () => {
|
? () => {
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "view-corporate",
|
id: "view-corporate",
|
||||||
filter: (x: User) => x.type === "corporate",
|
filter: (x: User) => x.type === "corporate",
|
||||||
});
|
});
|
||||||
appendUserFilters({
|
appendUserFilters({
|
||||||
id: "belongs-to-admin",
|
id: "belongs-to-admin",
|
||||||
filter: (x: User) =>
|
filter: (x: User) =>
|
||||||
groups
|
groups
|
||||||
.filter((g) => g.participants.includes(selectedUser.id))
|
.filter((g) => g.participants.includes(selectedUser.id))
|
||||||
.flatMap((g) => [g.admin, ...g.participants])
|
.flatMap((g) => [g.admin, ...g.participants])
|
||||||
.includes(x.id),
|
.includes(x.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
router.push("/list/users");
|
router.push("/list/users");
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onClose={(shouldReload) => {
|
onClose={(shouldReload) => {
|
||||||
@@ -619,7 +617,7 @@ export default function UserList({
|
|||||||
</Modal>
|
</Modal>
|
||||||
<div className="w-full flex flex-col gap-2">
|
<div className="w-full flex flex-col gap-2">
|
||||||
<div className="w-full flex gap-2 items-end">
|
<div className="w-full flex gap-2 items-end">
|
||||||
{renderSearch()}
|
<Input label="Search" type="text" name="search" onChange={setSearchTerm} placeholder="Enter search text" value={searchTerm} />
|
||||||
<Button className="w-full max-w-[200px] mb-1" variant="outline" onClick={downloadExcel}>
|
<Button className="w-full max-w-[200px] mb-1" variant="outline" onClick={downloadExcel}>
|
||||||
Download List
|
Download List
|
||||||
</Button>
|
</Button>
|
||||||
@@ -634,12 +632,12 @@ export default function UserList({
|
|||||||
</Button>
|
</Button>
|
||||||
<div className="flex items-center gap-4 w-fit">
|
<div className="flex items-center gap-4 w-fit">
|
||||||
<span className="opacity-80">
|
<span className="opacity-80">
|
||||||
{page * 16 + 1} - {(page + 1) * 16 > total ? total : (page + 1) * 16} / {total}
|
{page * userHash.size + 1} - {(page + 1) * userHash.size > total ? total : (page + 1) * userHash.size} / {total}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
className="w-[200px]"
|
className="w-[200px]"
|
||||||
disabled={page * 16 >= total}
|
disabled={(page + 1) * userHash.size >= total}
|
||||||
onClick={() => setPage((prev) => prev + 1)}>
|
onClick={() => setPage((prev) => prev + 1)}>
|
||||||
Next Page
|
Next Page
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -141,7 +141,11 @@ export default function UserCreator({user, users, permissions, onFinish}: Props)
|
|||||||
setType("student");
|
setType("student");
|
||||||
setPosition(undefined);
|
setPosition(undefined);
|
||||||
})
|
})
|
||||||
.catch(() => toast.error("Something went wrong! Please try again later!"))
|
.catch((error) => {
|
||||||
|
const data = error?.response?.data;
|
||||||
|
if (!!data?.message) return toast.error(data.message);
|
||||||
|
toast.error("Something went wrong! Please try again later!");
|
||||||
|
})
|
||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,109 +1,91 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type {NextApiRequest, NextApiResponse} from "next";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
import { withIronSessionApiRoute } from "iron-session/next";
|
import {withIronSessionApiRoute} from "iron-session/next";
|
||||||
import { sessionOptions } from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import { Group } from "@/interfaces/user";
|
import {Group} from "@/interfaces/user";
|
||||||
import { updateExpiryDateOnGroup } from "@/utils/groups.be";
|
import {updateExpiryDateOnGroup} from "@/utils/groups.be";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
|
|
||||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (req.method === "GET") return await get(req, res);
|
if (req.method === "GET") return await get(req, res);
|
||||||
if (req.method === "DELETE") return await del(req, res);
|
if (req.method === "DELETE") return await del(req, res);
|
||||||
if (req.method === "PATCH") return await patch(req, res);
|
if (req.method === "PATCH") return await patch(req, res);
|
||||||
|
|
||||||
res.status(404).json(undefined);
|
res.status(404).json(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function get(req: NextApiRequest, res: NextApiResponse) {
|
async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ ok: false });
|
res.status(401).json({ok: false});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = req.query as { id: string };
|
const {id} = req.query as {id: string};
|
||||||
|
|
||||||
const snapshot = await db.collection("groups").findOne({ id: id});
|
const snapshot = await db.collection("groups").findOne({id: id});
|
||||||
|
|
||||||
if (snapshot) {
|
if (snapshot) {
|
||||||
res.status(200).json({ ...snapshot });
|
res.status(200).json({...snapshot});
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json(undefined);
|
res.status(404).json(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function del(req: NextApiRequest, res: NextApiResponse) {
|
async function del(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ ok: false });
|
res.status(401).json({ok: false});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = req.query as { id: string };
|
const {id} = req.query as {id: string};
|
||||||
const group = await db.collection("groups").findOne<Group>({id: id});
|
const group = await db.collection("groups").findOne<Group>({id: id});
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
if (
|
if (user.type === "admin" || user.type === "developer" || user.id === group.admin) {
|
||||||
user.type === "admin" ||
|
await db.collection("groups").deleteOne({id: id});
|
||||||
user.type === "developer" ||
|
|
||||||
user.id === group.admin
|
|
||||||
) {
|
|
||||||
await db.collection("groups").deleteOne({ id: id });
|
|
||||||
|
|
||||||
res.status(200).json({ ok: true });
|
res.status(200).json({ok: true});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(403).json({ ok: false });
|
res.status(403).json({ok: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function patch(req: NextApiRequest, res: NextApiResponse) {
|
async function patch(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ ok: false });
|
res.status(401).json({ok: false});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = req.query as { id: string };
|
const {id} = req.query as {id: string};
|
||||||
|
|
||||||
const group = await db.collection("groups").findOne<Group>({id: id});
|
const group = await db.collection("groups").findOne<Group>({id: id});
|
||||||
if (!group) {
|
if (!group) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
if (
|
if (user.type === "admin" || user.type === "developer" || user.id === group.admin) {
|
||||||
user.type === "admin" ||
|
if ("participants" in req.body) {
|
||||||
user.type === "developer" ||
|
const newParticipants = (req.body.participants as string[]).filter((x) => !group.participants.includes(x));
|
||||||
user.id === group.admin
|
await Promise.all(newParticipants.map(async (p) => await updateExpiryDateOnGroup(p, group.admin)));
|
||||||
) {
|
}
|
||||||
if ("participants" in req.body) {
|
|
||||||
const newParticipants = (req.body.participants as string[]).filter(
|
|
||||||
(x) => !group.participants.includes(x),
|
|
||||||
);
|
|
||||||
await Promise.all(
|
|
||||||
newParticipants.map(
|
|
||||||
async (p) => await updateExpiryDateOnGroup(p, group.admin),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.collection("grading").updateOne(
|
await db.collection("groups").updateOne({id: req.session.user.id}, {$set: {id, ...req.body}}, {upsert: true});
|
||||||
{ id: req.session.user.id },
|
|
||||||
{ $set: {id: id, ...req.body} },
|
|
||||||
{ upsert: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).json({ ok: true });
|
res.status(200).json({ok: true});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(403).json({ ok: false });
|
res.status(403).json({ok: false});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
return res.status(200).json({ok: true});
|
return res.status(200).json({ok: true});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
if (error.code.includes("email-already-in-use")) return res.status(403).json({error, message: "E-mail is already in the platform."});
|
||||||
|
|
||||||
console.log(`Failing - ${email}`);
|
console.log(`Failing - ${email}`);
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return res.status(401).json({error});
|
return res.status(401).json({error});
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const {user} = req.query as {user?: string};
|
const {user} = req.query as {user?: string};
|
||||||
|
|
||||||
const q = user ? {user: user} : {};
|
const q = user ? {user: user} : {};
|
||||||
const sessions = await db.collection("sessions").find<Session>(q).toArray();
|
const sessions = await db.collection("sessions").find<Session>(q).limit(10).toArray();
|
||||||
|
|
||||||
res.status(200).json(
|
res.status(200).json(
|
||||||
sessions.filter((x) => {
|
sessions.filter((x) => {
|
||||||
@@ -41,12 +41,8 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const session = req.body;
|
const session = req.body;
|
||||||
|
|
||||||
await db.collection("sessions").updateOne(
|
await db.collection("sessions").updateOne({id: session.id}, {$set: session}, {upsert: true});
|
||||||
{ id: session.id},
|
|
||||||
{ $set: session },
|
|
||||||
{ upsert: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(200).json({ok: true});
|
res.status(200).json({ok: true});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
page,
|
page,
|
||||||
orderBy,
|
orderBy,
|
||||||
direction = "desc",
|
direction = "desc",
|
||||||
} = req.query as {size?: string; type?: Type; page?: string; orderBy?: string; direction?: "asc" | "desc"};
|
searchTerm
|
||||||
|
} = req.query as {size?: string; type?: Type; page?: string; orderBy?: string; direction?: "asc" | "desc"; searchTerm?: string | undefined};
|
||||||
|
|
||||||
const {users, total} = await getLinkedUsers(
|
const {users, total} = await getLinkedUsers(
|
||||||
req.session.user?.id,
|
req.session.user?.id,
|
||||||
@@ -29,6 +30,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
size !== undefined ? parseInt(size) : undefined,
|
size !== undefined ? parseInt(size) : undefined,
|
||||||
orderBy,
|
orderBy,
|
||||||
direction,
|
direction,
|
||||||
|
searchTerm
|
||||||
);
|
);
|
||||||
|
|
||||||
res.status(200).json({users, total});
|
res.status(200).json({users, total});
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type {NextApiRequest, NextApiResponse} from "next";
|
||||||
import { app, storage } from "@/firebase";
|
import {app, storage} from "@/firebase";
|
||||||
import { withIronSessionApiRoute } from "iron-session/next";
|
import {withIronSessionApiRoute} from "iron-session/next";
|
||||||
import { sessionOptions } from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import { Group, User } from "@/interfaces/user";
|
import {Group, User} from "@/interfaces/user";
|
||||||
import { getDownloadURL, getStorage, ref, uploadBytes } from "firebase/storage";
|
import {getDownloadURL, getStorage, ref, uploadBytes} from "firebase/storage";
|
||||||
import { getAuth, signInWithEmailAndPassword, updateEmail, updatePassword } from "firebase/auth";
|
import {getAuth, signInWithEmailAndPassword, updateEmail, updatePassword} from "firebase/auth";
|
||||||
import { errorMessages } from "@/constants/errors";
|
import {errorMessages} from "@/constants/errors";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { Payment } from "@/interfaces/paypal";
|
import {Payment} from "@/interfaces/paypal";
|
||||||
import { toFixedNumber } from "@/utils/number";
|
import {toFixedNumber} from "@/utils/number";
|
||||||
import { propagateExpiryDateChanges, propagateStatusChange } from "@/utils/propagate.user.changes";
|
import {propagateExpiryDateChanges, propagateStatusChange} from "@/utils/propagate.user.changes";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
@@ -41,7 +41,7 @@ const managePaymentRecords = async (user: User, userId: string | undefined): Pro
|
|||||||
date: new Date().toISOString(),
|
date: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const corporatePayments = await db.collection("payments").find({ corporate: userId }).toArray();
|
const corporatePayments = await db.collection("payments").find({corporate: userId}).toArray();
|
||||||
if (corporatePayments.length === 0) {
|
if (corporatePayments.length === 0) {
|
||||||
await addPaymentRecord(data);
|
await addPaymentRecord(data);
|
||||||
return true;
|
return true;
|
||||||
@@ -71,20 +71,17 @@ const managePaymentRecords = async (user: User, userId: string | undefined): Pro
|
|||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
res.status(401).json({ ok: false });
|
res.status(401).json({ok: false});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryId = req.query.id as string;
|
const queryId = req.query.id as string;
|
||||||
|
|
||||||
let user = await db.collection("users").findOne<User>({ id: queryId ? (queryId as string) : req.session.user.id });
|
let user = await db.collection("users").findOne<User>({id: queryId ? (queryId as string) : req.session.user.id});
|
||||||
const updatedUser = req.body as User & { password?: string; newPassword?: string };
|
const updatedUser = req.body as User & {password?: string; newPassword?: string};
|
||||||
|
|
||||||
if (!!queryId) {
|
if (!!queryId) {
|
||||||
await db.collection("users").updateOne(
|
await db.collection("users").updateOne({id: queryId}, {$set: updatedUser});
|
||||||
{ id: queryId },
|
|
||||||
{ $set: updatedUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
await managePaymentRecords(updatedUser, updatedUser.id);
|
await managePaymentRecords(updatedUser, updatedUser.id);
|
||||||
if (updatedUser.status || updatedUser.type === "corporate") {
|
if (updatedUser.status || updatedUser.type === "corporate") {
|
||||||
@@ -93,7 +90,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
propagateExpiryDateChanges(queryId, user?.subscriptionExpirationDate, updatedUser.subscriptionExpirationDate || null);
|
propagateExpiryDateChanges(queryId, user?.subscriptionExpirationDate, updatedUser.subscriptionExpirationDate || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ ok: true });
|
res.status(200).json({ok: true});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,17 +110,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password);
|
const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password);
|
||||||
await updatePassword(credential.user, updatedUser.newPassword);
|
await updatePassword(credential.user, updatedUser.newPassword);
|
||||||
} catch {
|
} catch {
|
||||||
res.status(400).json({ error: "E001", message: errorMessages.E001 });
|
res.status(400).json({error: "E001", message: errorMessages.E001});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedUser.email !== req.session.user.email && updatedUser.password) {
|
if (updatedUser.email !== req.session.user.email && updatedUser.password) {
|
||||||
try {
|
try {
|
||||||
const usersWithSameEmail = await db.collection("users").find({ email: updatedUser.email.toLowerCase() }).toArray();
|
const usersWithSameEmail = await db.collection("users").find({email: updatedUser.email.toLowerCase()}).toArray();
|
||||||
|
|
||||||
if (usersWithSameEmail.length > 0) {
|
if (usersWithSameEmail.length > 0) {
|
||||||
res.status(400).json({ error: "E003", message: errorMessages.E003 });
|
res.status(400).json({error: "E003", message: errorMessages.E003});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,22 +128,24 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
await updateEmail(credential.user, updatedUser.email);
|
await updateEmail(credential.user, updatedUser.email);
|
||||||
|
|
||||||
if (req.session.user.type === "student") {
|
if (req.session.user.type === "student") {
|
||||||
const corporateAdmins = (await db.collection("users").find<User>({ type: "corporate" }).toArray()).map((x) => x.id);
|
const corporateAdmins = (await db.collection("users").find<User>({type: "corporate"}).toArray()).map((x) => x.id);
|
||||||
|
|
||||||
const groups = await db.collection("groups").find<Group>({
|
const groups = await db
|
||||||
participants: req.session.user!.id,
|
.collection("groups")
|
||||||
admin: { $in: corporateAdmins }
|
.find<Group>({
|
||||||
}).toArray();
|
participants: req.session.user!.id,
|
||||||
|
admin: {$in: corporateAdmins},
|
||||||
|
})
|
||||||
|
.toArray();
|
||||||
|
|
||||||
groups.forEach(async (group) => {
|
groups.forEach(async (group) => {
|
||||||
await db.collection("groups").updateOne(
|
await db
|
||||||
{ id: group.id },
|
.collection("groups")
|
||||||
{ $set: { participants: group.participants.filter((x) => x !== req.session.user!.id) } }
|
.updateOne({id: group.id}, {$set: {participants: group.participants.filter((x) => x !== req.session.user!.id)}});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
res.status(400).json({ error: "E002", message: errorMessages.E002 });
|
res.status(400).json({error: "E002", message: errorMessages.E002});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,22 +158,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
delete updatedUser.password;
|
delete updatedUser.password;
|
||||||
delete updatedUser.newPassword;
|
delete updatedUser.newPassword;
|
||||||
|
|
||||||
await db.collection("users").updateOne(
|
await db.collection("users").updateOne({id: queryId}, {$set: updatedUser});
|
||||||
{ id: queryId },
|
|
||||||
{ $set: updatedUser }
|
|
||||||
);
|
|
||||||
|
|
||||||
user = await db.collection("users").findOne<User>({ id: req.session.user.id });
|
|
||||||
|
|
||||||
if (!queryId) {
|
if (!queryId) {
|
||||||
req.session.user = user ? user : null;
|
req.session.user = updatedUser ? {...updatedUser, id: req.session.user.id} : null;
|
||||||
await req.session.save();
|
await req.session.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user) {
|
if ({...updatedUser, id: req.session.user!.id}) {
|
||||||
await managePaymentRecords(user, queryId);
|
await managePaymentRecords({...updatedUser, id: req.session.user!.id}, queryId);
|
||||||
}
|
}
|
||||||
res.status(200).json({ user });
|
|
||||||
|
res.status(200).json({user: {...updatedUser, id: req.session.user!.id}});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {User} from "@/interfaces/user";
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {User} from "@/interfaces/user";
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {User} from "@/interfaces/user";
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import {getUsers} from "@/utils/users.be";
|
|||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
|||||||
envVariables[x] = process.env[x]!;
|
envVariables[x] = process.env[x]!;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
@@ -68,22 +68,18 @@ interface Props {
|
|||||||
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
linkedCorporate?: CorporateUser | MasterCorporateUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Home({linkedCorporate}: Props) {
|
export default function Home({user: propsUser, linkedCorporate}: Props) {
|
||||||
|
const [user, setUser] = useState(propsUser);
|
||||||
const [showDiagnostics, setShowDiagnostics] = useState(false);
|
const [showDiagnostics, setShowDiagnostics] = useState(false);
|
||||||
const [showDemographicInput, setShowDemographicInput] = useState(false);
|
const [showDemographicInput, setShowDemographicInput] = useState(false);
|
||||||
const [selectedScreen, setSelectedScreen] = useState<Type>("admin");
|
const [selectedScreen, setSelectedScreen] = useState<Type>("admin");
|
||||||
|
|
||||||
const {user, mutateUser} = useUser({redirectTo: "/login"});
|
const {mutateUser} = useUser({redirectTo: "/login"});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
setShowDemographicInput(
|
// setShowDemographicInput(!user.demographicInformation || !user.demographicInformation.country || !user.demographicInformation.phone);
|
||||||
!user.demographicInformation ||
|
|
||||||
!user.demographicInformation.country ||
|
|
||||||
!user.demographicInformation.gender ||
|
|
||||||
!user.demographicInformation.phone,
|
|
||||||
);
|
|
||||||
setShowDiagnostics(user.isFirstLogin && user.type === "student");
|
setShowDiagnostics(user.isFirstLogin && user.type === "student");
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
@@ -135,7 +131,13 @@ export default function Home({linkedCorporate}: Props) {
|
|||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<Layout user={user} navDisabled>
|
<Layout user={user} navDisabled>
|
||||||
<DemographicInformationInput mutateUser={mutateUser} user={user} />
|
<DemographicInformationInput
|
||||||
|
mutateUser={(user) => {
|
||||||
|
setUser(user);
|
||||||
|
mutateUser(user);
|
||||||
|
}}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
envVariables[x] = process.env[x]!;
|
envVariables[x] = process.env[x]!;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
envVariables[x] = process.env[x]!;
|
envVariables[x] = process.env[x]!;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user && user.isVerified) {
|
if (user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/",
|
destination: "/",
|
||||||
@@ -56,7 +56,7 @@ export default function Login() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && user.isVerified) router.push("/");
|
if (user) router.push("/");
|
||||||
}, [router, user]);
|
}, [router, user]);
|
||||||
|
|
||||||
const forgotPassword = () => {
|
const forgotPassword = () => {
|
||||||
@@ -173,7 +173,7 @@ export default function Login() {
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />}
|
{/* {user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />} */}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
envVariables[x] = process.env[x]!;
|
envVariables[x] = process.env[x]!;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const getServerSideProps = withIronSessionSsr(async (context) => {
|
|||||||
const {req, params} = context;
|
const {req, params} = context;
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import PermissionList from "@/components/PermissionList";
|
|||||||
export const getServerSideProps = withIronSessionSsr(async ({req}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import {getUsers} from "@/utils/users.be";
|
|||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import useGradingSystem from "@/hooks/useGrading";
|
|||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export default function Register({code: queryCode}: {code: string}) {
|
|||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />}
|
{/* {user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />} */}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import useUsers from "@/hooks/useUsers";
|
|||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const COLORS = ["#1EB3FF", "#FF790A", "#3D9F11", "#EF5DA8", "#414288"];
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const columnHelper = createColumnHelper<TicketWithCorporate>();
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import {sortByModule} from "@/utils/moduleUtils";
|
|||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { withIronSessionSsr } from "iron-session/next";
|
import {withIronSessionSsr} from "iron-session/next";
|
||||||
import { sessionOptions } from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import { User } from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import { ToastContainer } from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
import Layout from "@/components/High/Layout";
|
import Layout from "@/components/High/Layout";
|
||||||
import { shouldRedirectHome } from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import { useEffect, useState } from "react";
|
import {useEffect, useState} from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { FaPlus } from "react-icons/fa";
|
import {FaPlus} from "react-icons/fa";
|
||||||
import useRecordStore from "@/stores/recordStore";
|
import useRecordStore from "@/stores/recordStore";
|
||||||
import router from "next/router";
|
import router from "next/router";
|
||||||
import useTrainingContentStore from "@/stores/trainingContentStore";
|
import useTrainingContentStore from "@/stores/trainingContentStore";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { ITrainingContent } from "@/training/TrainingInterfaces";
|
import {ITrainingContent} from "@/training/TrainingInterfaces";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { uuidv4 } from "@firebase/util";
|
import {uuidv4} from "@firebase/util";
|
||||||
import TrainingScore from "@/training/TrainingScore";
|
import TrainingScore from "@/training/TrainingScore";
|
||||||
import ModuleBadge from "@/components/ModuleBadge";
|
import ModuleBadge from "@/components/ModuleBadge";
|
||||||
import RecordFilter from "@/components/Medium/RecordFilter";
|
import RecordFilter from "@/components/Medium/RecordFilter";
|
||||||
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
|
|
||||||
if (!user || !user.isVerified) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: "/login",
|
destination: "/login",
|
||||||
@@ -43,22 +43,23 @@ export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { user: req.session.user },
|
props: {user: req.session.user},
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
const Training: React.FC<{ user: User }> = ({ user }) => {
|
const Training: React.FC<{user: User}> = ({user}) => {
|
||||||
const [recordUserId, setRecordTraining] = useRecordStore((state) => [
|
const [recordUserId, setRecordTraining] = useRecordStore((state) => [state.selectedUser, state.setTraining]);
|
||||||
state.selectedUser,
|
|
||||||
state.setTraining,
|
|
||||||
]);
|
|
||||||
const [filter, setFilter] = useState<"months" | "weeks" | "days" | "assignments">();
|
const [filter, setFilter] = useState<"months" | "weeks" | "days" | "assignments">();
|
||||||
|
|
||||||
const [stats, setTrainingStats] = useTrainingContentStore((state) => [state.stats, state.setStats]);
|
const [stats, setTrainingStats] = useTrainingContentStore((state) => [state.stats, state.setStats]);
|
||||||
const [isNewContentLoading, setIsNewContentLoading] = useState(stats.length != 0);
|
const [isNewContentLoading, setIsNewContentLoading] = useState(stats.length != 0);
|
||||||
const [groupedByTrainingContent, setGroupedByTrainingContent] = useState<{ [key: string]: ITrainingContent }>();
|
const [groupedByTrainingContent, setGroupedByTrainingContent] = useState<{[key: string]: ITrainingContent}>();
|
||||||
|
|
||||||
const { data: trainingContent, isLoading: areRecordsLoading } = useFilterRecordsByUser<ITrainingContent[]>(recordUserId || user?.id, undefined, "training");
|
const {data: trainingContent, isLoading: areRecordsLoading} = useFilterRecordsByUser<ITrainingContent[]>(
|
||||||
|
recordUserId || user?.id,
|
||||||
|
undefined,
|
||||||
|
"training",
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleRouteChange = (url: string) => {
|
const handleRouteChange = (url: string) => {
|
||||||
@@ -74,7 +75,7 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const postStats = async () => {
|
const postStats = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post<{ id: string }>(`/api/training`, { userID: user.id, stats: stats });
|
const response = await axios.post<{id: string}>(`/api/training`, {userID: user.id, stats: stats});
|
||||||
return response.data.id;
|
return response.data.id;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsNewContentLoading(false);
|
setIsNewContentLoading(false);
|
||||||
@@ -97,12 +98,12 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
|
|||||||
router.push("/record");
|
router.push("/record");
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterTrainingContentByDate = (trainingContent: { [key: string]: ITrainingContent }) => {
|
const filterTrainingContentByDate = (trainingContent: {[key: string]: ITrainingContent}) => {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
const filterDate = moment()
|
const filterDate = moment()
|
||||||
.subtract({ [filter as string]: 1 })
|
.subtract({[filter as string]: 1})
|
||||||
.format("x");
|
.format("x");
|
||||||
const filteredTrainingContent: { [key: string]: ITrainingContent } = {};
|
const filteredTrainingContent: {[key: string]: ITrainingContent} = {};
|
||||||
|
|
||||||
Object.keys(trainingContent).forEach((timestamp) => {
|
Object.keys(trainingContent).forEach((timestamp) => {
|
||||||
if (timestamp >= filterDate) filteredTrainingContent[timestamp] = trainingContent[timestamp];
|
if (timestamp >= filterDate) filteredTrainingContent[timestamp] = trainingContent[timestamp];
|
||||||
@@ -117,10 +118,10 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
|
|||||||
const grouped = trainingContent.reduce((acc, content) => {
|
const grouped = trainingContent.reduce((acc, content) => {
|
||||||
acc[content.created_at] = content;
|
acc[content.created_at] = content;
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as { [key: number]: ITrainingContent });
|
}, {} as {[key: number]: ITrainingContent});
|
||||||
|
|
||||||
setGroupedByTrainingContent(grouped);
|
setGroupedByTrainingContent(grouped);
|
||||||
}else {
|
} else {
|
||||||
setGroupedByTrainingContent(undefined);
|
setGroupedByTrainingContent(undefined);
|
||||||
}
|
}
|
||||||
}, [trainingContent]);
|
}, [trainingContent]);
|
||||||
@@ -138,7 +139,7 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
|
|||||||
|
|
||||||
const trainingContentContainer = (timestamp: string) => {
|
const trainingContentContainer = (timestamp: string) => {
|
||||||
if (!groupedByTrainingContent) return <></>;
|
if (!groupedByTrainingContent) return <></>;
|
||||||
|
|
||||||
const trainingContent: ITrainingContent = groupedByTrainingContent[timestamp];
|
const trainingContent: ITrainingContent = groupedByTrainingContent[timestamp];
|
||||||
const uniqueModules = [...new Set(trainingContent.exams.map((exam) => exam.module))];
|
const uniqueModules = [...new Set(trainingContent.exams.map((exam) => exam.module))];
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ const Training: React.FC<{ user: User }> = ({ user }) => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<RecordFilter user={user} filterState={{ filter: filter, setFilter: setFilter }} assignments={false} >
|
<RecordFilter user={user} filterState={{filter: filter, setFilter: setFilter}} assignments={false}>
|
||||||
{user.type === "student" && (
|
{user.type === "student" && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
import { Assignment } from "@/interfaces/results";
|
import {Assignment} from "@/interfaces/results";
|
||||||
import { getAllAssignersByCorporate } from "@/utils/groups.be";
|
import {getAllAssignersByCorporate} from "@/utils/groups.be";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
|
|
||||||
export const getAssignmentsByAssigner = async (id: string, startDate?: Date, endDate?: Date) => {
|
export const getAssignmentsByAssigner = async (id: string, startDate?: Date, endDate?: Date) => {
|
||||||
let query: any = { assigner: id };
|
let query: any = {assigner: id};
|
||||||
|
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
query.startDate = { $gte: startDate.toISOString() };
|
query.startDate = {$gte: startDate.toISOString()};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endDate) {
|
if (endDate) {
|
||||||
query.endDate = { $lte: endDate.toISOString() };
|
query.endDate = {$lte: endDate.toISOString()};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db.collection("assignments").find<Assignment>(query).toArray();
|
return await db.collection("assignments").find<Assignment>(query).toArray();
|
||||||
@@ -26,7 +26,14 @@ export const getAssignmentsByAssignerBetweenDates = async (id: string, startDate
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getAssignmentsByAssigners = async (ids: string[], startDate?: Date, endDate?: Date) => {
|
export const getAssignmentsByAssigners = async (ids: string[], startDate?: Date, endDate?: Date) => {
|
||||||
return (await Promise.all(ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate)))).flat();
|
return await db
|
||||||
|
.collection("assignments")
|
||||||
|
.find<Assignment>({
|
||||||
|
assigner: {$in: ids},
|
||||||
|
...(!!startDate ? {startDate: {$gte: startDate.toISOString()}} : {}),
|
||||||
|
...(!!endDate ? {endDate: {$lte: endDate.toISOString()}} : {}),
|
||||||
|
})
|
||||||
|
.toArray();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssignmentsForCorporates = async (idsList: string[], startDate?: Date, endDate?: Date) => {
|
export const getAssignmentsForCorporates = async (idsList: string[], startDate?: Date, endDate?: Date) => {
|
||||||
@@ -57,4 +64,4 @@ export const getAssignmentsForCorporates = async (idsList: string[], startDate?:
|
|||||||
);
|
);
|
||||||
|
|
||||||
return assignments.flat();
|
return assignments.flat();
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {DeveloperUser, Stat, StudentUser, User} from "@/interfaces/user";
|
|||||||
import {Module} from "@/interfaces";
|
import {Module} from "@/interfaces";
|
||||||
import {getCorporateUser} from "@/resources/user";
|
import {getCorporateUser} from "@/resources/user";
|
||||||
import {getUserCorporate} from "./groups.be";
|
import {getUserCorporate} from "./groups.be";
|
||||||
import { Db, ObjectId } from "mongodb";
|
import {Db, ObjectId} from "mongodb";
|
||||||
|
|
||||||
export const getExams = async (
|
export const getExams = async (
|
||||||
db: Db,
|
db: Db,
|
||||||
@@ -18,18 +18,18 @@ export const getExams = async (
|
|||||||
variant?: Variant,
|
variant?: Variant,
|
||||||
instructorGender?: InstructorGender,
|
instructorGender?: InstructorGender,
|
||||||
): Promise<Exam[]> => {
|
): Promise<Exam[]> => {
|
||||||
|
const allExams = await db
|
||||||
|
.collection(module)
|
||||||
|
.find<Exam>({
|
||||||
|
isDiagnostic: false,
|
||||||
|
})
|
||||||
|
.toArray();
|
||||||
|
|
||||||
const allExams = await db.collection(module).find<Exam>({
|
const shuffledPublicExams = shuffle(
|
||||||
isDiagnostic: false
|
allExams.map((doc) => ({
|
||||||
}).toArray();
|
...doc,
|
||||||
|
module,
|
||||||
const shuffledPublicExams = (
|
})) as Exam[],
|
||||||
shuffle(
|
|
||||||
allExams.map((doc) => ({
|
|
||||||
...doc,
|
|
||||||
module,
|
|
||||||
})) as Exam[],
|
|
||||||
)
|
|
||||||
).filter((x) => !x.private);
|
).filter((x) => !x.private);
|
||||||
|
|
||||||
let exams: Exam[] = await filterByOwners(shuffledPublicExams, userId);
|
let exams: Exam[] = await filterByOwners(shuffledPublicExams, userId);
|
||||||
@@ -39,9 +39,12 @@ export const getExams = async (
|
|||||||
exams = await filterByPreference(db, exams, module, userId);
|
exams = await filterByPreference(db, exams, module, userId);
|
||||||
|
|
||||||
if (avoidRepeated === "true") {
|
if (avoidRepeated === "true") {
|
||||||
const stats = await db.collection("stats").find<Stat>({
|
const stats = await db
|
||||||
user: userId
|
.collection("stats")
|
||||||
}).toArray();
|
.find<Stat>({
|
||||||
|
user: userId,
|
||||||
|
})
|
||||||
|
.toArray();
|
||||||
|
|
||||||
const filteredExams = exams.filter((x) => !stats.map((s) => s.exam).includes(x.id));
|
const filteredExams = exams.filter((x) => !stats.map((s) => s.exam).includes(x.id));
|
||||||
|
|
||||||
@@ -78,7 +81,7 @@ const filterByOwners = async (exams: Exam[], userID?: string) => {
|
|||||||
|
|
||||||
const filterByDifficulty = async (db: Db, exams: Exam[], module: Module, userID?: string) => {
|
const filterByDifficulty = async (db: Db, exams: Exam[], module: Module, userID?: string) => {
|
||||||
if (!userID) return exams;
|
if (!userID) return exams;
|
||||||
const user = await db.collection("users").findOne<User>({ _id: new ObjectId(userID) });
|
const user = await db.collection("users").findOne<User>({id: userID});
|
||||||
if (!user) return exams;
|
if (!user) return exams;
|
||||||
|
|
||||||
const difficulty = user.levels[module] <= 3 ? "easy" : user.levels[module] <= 6 ? "medium" : "hard";
|
const difficulty = user.levels[module] <= 3 ? "easy" : user.levels[module] <= 6 ? "medium" : "hard";
|
||||||
@@ -92,7 +95,7 @@ const filterByPreference = async (db: Db, exams: Exam[], module: Module, userID?
|
|||||||
|
|
||||||
if (!userID) return exams;
|
if (!userID) return exams;
|
||||||
|
|
||||||
const user = await db.collection("users").findOne<StudentUser | DeveloperUser>({ _id: new ObjectId(userID) });
|
const user = await db.collection("users").findOne<StudentUser | DeveloperUser>({id: userID});
|
||||||
if (!user) return exams;
|
if (!user) return exams;
|
||||||
|
|
||||||
if (!["developer", "student"].includes(user.type)) return exams;
|
if (!["developer", "student"].includes(user.type)) return exams;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import {app} from "@/firebase";
|
import {app} from "@/firebase";
|
||||||
|
import {Assignment} from "@/interfaces/results";
|
||||||
import {CorporateUser, Group, MasterCorporateUser, StudentUser, TeacherUser, User} from "@/interfaces/user";
|
import {CorporateUser, Group, MasterCorporateUser, StudentUser, TeacherUser, User} from "@/interfaces/user";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {getUser} from "./users.be";
|
import {getLinkedUsers, getUser} from "./users.be";
|
||||||
import {getSpecificUsers} from "./users.be";
|
import {getSpecificUsers} from "./users.be";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
@@ -71,17 +72,10 @@ export const getUsersGroups = async (ids: string[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getAllAssignersByCorporate = async (corporateID: string): Promise<string[]> => {
|
export const getAllAssignersByCorporate = async (corporateID: string): Promise<string[]> => {
|
||||||
const groups = await getUserGroups(corporateID);
|
const linkedTeachers = await getLinkedUsers(corporateID, "mastercorporate", "teacher");
|
||||||
const groupUsers = (await Promise.all(groups.map(async (g) => await Promise.all(g.participants.map(getUser)))))
|
const linkedCorporates = await getLinkedUsers(corporateID, "mastercorporate", "corporate");
|
||||||
.flat()
|
|
||||||
.filter((x) => !!x) as User[];
|
|
||||||
const teacherPromises = await Promise.all(
|
|
||||||
groupUsers.map(async (u) =>
|
|
||||||
u.type === "teacher" ? u.id : u.type === "corporate" ? [...(await getAllAssignersByCorporate(u.id)), u.id] : undefined,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return teacherPromises.filter((x) => !!x).flat() as string[];
|
return [...linkedTeachers.users.map((x) => x.id), ...linkedCorporates.users.map((x) => x.id)];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGroupsForUser = async (admin?: string, participant?: string) => {
|
export const getGroupsForUser = async (admin?: string, participant?: string) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {CorporateUser, Group, Type, User} from "@/interfaces/user";
|
import { CorporateUser, Group, Type, User } from "@/interfaces/user";
|
||||||
import {getGroupsForUser, getParticipantGroups, getUserGroups, getUsersGroups} from "./groups.be";
|
import { getGroupsForUser, getParticipantGroups, getUserGroups, getUsersGroups } from "./groups.be";
|
||||||
import {last, uniq, uniqBy} from "lodash";
|
import { last, uniq, uniqBy } from "lodash";
|
||||||
import {getUserCodes} from "./codes.be";
|
import { getUserCodes } from "./codes.be";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export async function getUsers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getUser(id: string): Promise<User | undefined> {
|
export async function getUser(id: string): Promise<User | undefined> {
|
||||||
const user = await db.collection("users").findOne<User>({id});
|
const user = await db.collection("users").findOne<User>({ id });
|
||||||
return !!user ? user : undefined;
|
return !!user ? user : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export async function getSpecificUsers(ids: string[]) {
|
|||||||
|
|
||||||
return await db
|
return await db
|
||||||
.collection("users")
|
.collection("users")
|
||||||
.find<User>({id: {$in: ids}})
|
.find<User>({ id: { $in: ids } })
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,21 +33,32 @@ export async function getLinkedUsers(
|
|||||||
size?: number,
|
size?: number,
|
||||||
sort?: string,
|
sort?: string,
|
||||||
direction?: "asc" | "desc",
|
direction?: "asc" | "desc",
|
||||||
|
searchTerm?: string | undefined,
|
||||||
) {
|
) {
|
||||||
const filters = {
|
const filters: any = {};
|
||||||
...(!!type ? {type} : {}),
|
|
||||||
};
|
if (type) {
|
||||||
|
filters.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchTerm) {
|
||||||
|
filters.$or = [
|
||||||
|
{ name: { $regex: searchTerm, $options: 'i' } },
|
||||||
|
{ email: { $regex: searchTerm, $options: 'i' } },
|
||||||
|
{ company: { $regex: searchTerm, $options: 'i' } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
if (!userID || userType === "admin" || userType === "developer") {
|
if (!userID || userType === "admin" || userType === "developer") {
|
||||||
const users = await db
|
const users = await db
|
||||||
.collection("users")
|
.collection("users")
|
||||||
.find<User>(filters)
|
.find<User>(filters)
|
||||||
.sort(sort ? {[sort]: direction === "desc" ? -1 : 1} : {})
|
.sort(sort ? { [sort]: direction === "desc" ? -1 : 1 } : {})
|
||||||
.skip(page && size ? page * size : 0)
|
.skip(page && size ? page * size : 0)
|
||||||
.limit(size || 0)
|
.limit(size || 0)
|
||||||
.toArray();
|
.toArray();
|
||||||
const total = await db.collection("users").countDocuments(filters);
|
const total = await db.collection("users").countDocuments(filters);
|
||||||
return {users, total};
|
return { users, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminGroups = await getUserGroups(userID);
|
const adminGroups = await getUserGroups(userID);
|
||||||
@@ -56,22 +67,22 @@ export async function getLinkedUsers(
|
|||||||
|
|
||||||
const participants = uniq([
|
const participants = uniq([
|
||||||
...adminGroups.flatMap((x) => x.participants),
|
...adminGroups.flatMap((x) => x.participants),
|
||||||
...groups.flat().flatMap((x) => x.participants),
|
...(userType === "mastercorporate" ? groups.flat().flatMap((x) => x.participants) : []),
|
||||||
...(userType === "teacher" ? belongingGroups.flatMap((x) => x.participants) : []),
|
...(userType === "teacher" ? belongingGroups.flatMap((x) => x.participants) : []),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ⨯ [FirebaseError: Invalid Query. A non-empty array is required for 'in' filters.] {
|
// ⨯ [FirebaseError: Invalid Query. A non-empty array is required for 'in' filters.] {
|
||||||
if (participants.length === 0) return {users: [], total: 0};
|
if (participants.length === 0) return { users: [], total: 0 };
|
||||||
|
|
||||||
const users = await db
|
const users = await db
|
||||||
.collection("users")
|
.collection("users")
|
||||||
.find<User>({...filters, id: {$in: participants}})
|
.find<User>({ ...filters, id: { $in: participants } })
|
||||||
.skip(page && size ? page * size : 0)
|
.skip(page && size ? page * size : 0)
|
||||||
.limit(size || 0)
|
.limit(size || 0)
|
||||||
.toArray();
|
.toArray();
|
||||||
const total = await db.collection("users").countDocuments({...filters, id: {$in: participants}});
|
const total = await db.collection("users").countDocuments({ ...filters, id: { $in: participants } });
|
||||||
|
|
||||||
return {users, total};
|
return { users, total };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserBalance(user: User) {
|
export async function getUserBalance(user: User) {
|
||||||
|
|||||||
483
yarn.lock
483
yarn.lock
@@ -191,7 +191,7 @@
|
|||||||
"@emotion/utils" "0.11.3"
|
"@emotion/utils" "0.11.3"
|
||||||
"@emotion/weak-memoize" "0.2.5"
|
"@emotion/weak-memoize" "0.2.5"
|
||||||
|
|
||||||
"@emotion/cache@^11.13.0":
|
"@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0":
|
||||||
version "11.13.1"
|
version "11.13.1"
|
||||||
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz"
|
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz"
|
||||||
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
|
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
|
||||||
@@ -202,27 +202,16 @@
|
|||||||
"@emotion/weak-memoize" "^0.4.0"
|
"@emotion/weak-memoize" "^0.4.0"
|
||||||
stylis "4.2.0"
|
stylis "4.2.0"
|
||||||
|
|
||||||
"@emotion/cache@^11.4.0":
|
|
||||||
version "11.13.1"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz"
|
|
||||||
integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==
|
|
||||||
dependencies:
|
|
||||||
"@emotion/memoize" "^0.9.0"
|
|
||||||
"@emotion/sheet" "^1.4.0"
|
|
||||||
"@emotion/utils" "^1.4.0"
|
|
||||||
"@emotion/weak-memoize" "^0.4.0"
|
|
||||||
stylis "4.2.0"
|
|
||||||
|
|
||||||
"@emotion/hash@^0.9.2":
|
|
||||||
version "0.9.2"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz"
|
|
||||||
integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==
|
|
||||||
|
|
||||||
"@emotion/hash@0.8.0":
|
"@emotion/hash@0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz"
|
||||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||||
|
|
||||||
|
"@emotion/hash@^0.9.2":
|
||||||
|
version "0.9.2"
|
||||||
|
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz"
|
||||||
|
integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==
|
||||||
|
|
||||||
"@emotion/is-prop-valid@^0.8.2":
|
"@emotion/is-prop-valid@^0.8.2":
|
||||||
version "0.8.8"
|
version "0.8.8"
|
||||||
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
|
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
|
||||||
@@ -230,16 +219,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/memoize" "0.7.4"
|
"@emotion/memoize" "0.7.4"
|
||||||
|
|
||||||
"@emotion/memoize@^0.9.0":
|
|
||||||
version "0.9.0"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz"
|
|
||||||
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
|
|
||||||
|
|
||||||
"@emotion/memoize@0.7.4":
|
"@emotion/memoize@0.7.4":
|
||||||
version "0.7.4"
|
version "0.7.4"
|
||||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
|
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
|
||||||
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||||
|
|
||||||
|
"@emotion/memoize@^0.9.0":
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz"
|
||||||
|
integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==
|
||||||
|
|
||||||
"@emotion/react@^11.8.1":
|
"@emotion/react@^11.8.1":
|
||||||
version "11.13.0"
|
version "11.13.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.13.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.13.0.tgz"
|
||||||
@@ -265,7 +254,7 @@
|
|||||||
"@emotion/utils" "0.11.3"
|
"@emotion/utils" "0.11.3"
|
||||||
csstype "^2.5.7"
|
csstype "^2.5.7"
|
||||||
|
|
||||||
"@emotion/serialize@^1.2.0":
|
"@emotion/serialize@^1.2.0", "@emotion/serialize@^1.3.0":
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz"
|
||||||
integrity sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==
|
integrity sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==
|
||||||
@@ -276,67 +265,56 @@
|
|||||||
"@emotion/utils" "^1.4.0"
|
"@emotion/utils" "^1.4.0"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
"@emotion/serialize@^1.3.0":
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz"
|
|
||||||
integrity sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==
|
|
||||||
dependencies:
|
|
||||||
"@emotion/hash" "^0.9.2"
|
|
||||||
"@emotion/memoize" "^0.9.0"
|
|
||||||
"@emotion/unitless" "^0.9.0"
|
|
||||||
"@emotion/utils" "^1.4.0"
|
|
||||||
csstype "^3.0.2"
|
|
||||||
|
|
||||||
"@emotion/sheet@^1.4.0":
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz"
|
|
||||||
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
|
|
||||||
|
|
||||||
"@emotion/sheet@0.9.4":
|
"@emotion/sheet@0.9.4":
|
||||||
version "0.9.4"
|
version "0.9.4"
|
||||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz"
|
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz"
|
||||||
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
|
integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
|
||||||
|
|
||||||
|
"@emotion/sheet@^1.4.0":
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz"
|
||||||
|
integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==
|
||||||
|
|
||||||
"@emotion/stylis@0.8.5":
|
"@emotion/stylis@0.8.5":
|
||||||
version "0.8.5"
|
version "0.8.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
|
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
|
||||||
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
|
||||||
|
|
||||||
"@emotion/unitless@^0.9.0":
|
|
||||||
version "0.9.0"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz"
|
|
||||||
integrity sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==
|
|
||||||
|
|
||||||
"@emotion/unitless@0.7.5":
|
"@emotion/unitless@0.7.5":
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
||||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||||
|
|
||||||
|
"@emotion/unitless@^0.9.0":
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz"
|
||||||
|
integrity sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==
|
||||||
|
|
||||||
"@emotion/use-insertion-effect-with-fallbacks@^1.1.0":
|
"@emotion/use-insertion-effect-with-fallbacks@^1.1.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz"
|
||||||
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
|
integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==
|
||||||
|
|
||||||
"@emotion/utils@^1.4.0":
|
|
||||||
version "1.4.0"
|
|
||||||
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz"
|
|
||||||
integrity sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==
|
|
||||||
|
|
||||||
"@emotion/utils@0.11.3":
|
"@emotion/utils@0.11.3":
|
||||||
version "0.11.3"
|
version "0.11.3"
|
||||||
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz"
|
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz"
|
||||||
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
|
integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
|
||||||
|
|
||||||
"@emotion/weak-memoize@^0.4.0":
|
"@emotion/utils@^1.4.0":
|
||||||
version "0.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz"
|
||||||
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
|
integrity sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==
|
||||||
|
|
||||||
"@emotion/weak-memoize@0.2.5":
|
"@emotion/weak-memoize@0.2.5":
|
||||||
version "0.2.5"
|
version "0.2.5"
|
||||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz"
|
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz"
|
||||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||||
|
|
||||||
|
"@emotion/weak-memoize@^0.4.0":
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz"
|
||||||
|
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.4.1":
|
"@eslint/eslintrc@^1.4.1":
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz"
|
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz"
|
||||||
@@ -511,7 +489,7 @@
|
|||||||
"@firebase/util" "1.9.3"
|
"@firebase/util" "1.9.3"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/database-compat@^0.3.4", "@firebase/database-compat@0.3.4":
|
"@firebase/database-compat@0.3.4", "@firebase/database-compat@^0.3.4":
|
||||||
version "0.3.4"
|
version "0.3.4"
|
||||||
resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz"
|
resolved "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-0.3.4.tgz"
|
||||||
integrity sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==
|
integrity sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==
|
||||||
@@ -523,7 +501,7 @@
|
|||||||
"@firebase/util" "1.9.3"
|
"@firebase/util" "1.9.3"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/database-types@^0.10.4", "@firebase/database-types@0.10.4":
|
"@firebase/database-types@0.10.4", "@firebase/database-types@^0.10.4":
|
||||||
version "0.10.4"
|
version "0.10.4"
|
||||||
resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz"
|
resolved "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.10.4.tgz"
|
||||||
integrity sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==
|
integrity sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==
|
||||||
@@ -744,13 +722,6 @@
|
|||||||
node-fetch "2.6.7"
|
node-fetch "2.6.7"
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/util@^1.9.7":
|
|
||||||
version "1.9.7"
|
|
||||||
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz"
|
|
||||||
integrity sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==
|
|
||||||
dependencies:
|
|
||||||
tslib "^2.1.0"
|
|
||||||
|
|
||||||
"@firebase/util@1.9.3":
|
"@firebase/util@1.9.3":
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz"
|
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz"
|
||||||
@@ -758,6 +729,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
|
"@firebase/util@^1.9.7":
|
||||||
|
version "1.9.7"
|
||||||
|
resolved "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz"
|
||||||
|
integrity sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.1.0"
|
||||||
|
|
||||||
"@firebase/webchannel-wrapper@0.9.0":
|
"@firebase/webchannel-wrapper@0.9.0":
|
||||||
version "0.9.0"
|
version "0.9.0"
|
||||||
resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.9.0.tgz"
|
resolved "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.9.0.tgz"
|
||||||
@@ -1020,6 +998,46 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "7.1.7"
|
glob "7.1.7"
|
||||||
|
|
||||||
|
"@next/swc-darwin-arm64@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz#d0a160cf78c18731c51cc0bff131c706b3e9bb05"
|
||||||
|
integrity sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==
|
||||||
|
|
||||||
|
"@next/swc-darwin-x64@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz#eb832a992407f6e6352eed05a073379f1ce0589c"
|
||||||
|
integrity sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==
|
||||||
|
|
||||||
|
"@next/swc-linux-arm64-gnu@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz#098fdab57a4664969bc905f5801ef5a89582c689"
|
||||||
|
integrity sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==
|
||||||
|
|
||||||
|
"@next/swc-linux-arm64-musl@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz#243a1cc1087fb75481726dd289c7b219fa01f2b5"
|
||||||
|
integrity sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==
|
||||||
|
|
||||||
|
"@next/swc-linux-x64-gnu@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz#b8a2e436387ee4a52aa9719b718992e0330c4953"
|
||||||
|
integrity sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==
|
||||||
|
|
||||||
|
"@next/swc-linux-x64-musl@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz#cb8a9adad5fb8df86112cfbd363aab5c6d32757b"
|
||||||
|
integrity sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==
|
||||||
|
|
||||||
|
"@next/swc-win32-arm64-msvc@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz#81f996c1c38ea0900d4e7719cc8814be8a835da0"
|
||||||
|
integrity sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==
|
||||||
|
|
||||||
|
"@next/swc-win32-ia32-msvc@14.2.5":
|
||||||
|
version "14.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz#f61c74ce823e10b2bc150e648fc192a7056422e0"
|
||||||
|
integrity sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@14.2.5":
|
"@next/swc-win32-x64-msvc@14.2.5":
|
||||||
version "14.2.5"
|
version "14.2.5"
|
||||||
resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz"
|
resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz"
|
||||||
@@ -1033,7 +1051,7 @@
|
|||||||
"@nodelib/fs.stat" "2.0.5"
|
"@nodelib/fs.stat" "2.0.5"
|
||||||
run-parallel "^1.1.9"
|
run-parallel "^1.1.9"
|
||||||
|
|
||||||
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
|
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||||
@@ -1591,6 +1609,14 @@
|
|||||||
resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz"
|
resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz"
|
||||||
integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
|
integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
|
||||||
|
|
||||||
|
"@swc/helpers@0.5.5":
|
||||||
|
version "0.5.5"
|
||||||
|
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz"
|
||||||
|
integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==
|
||||||
|
dependencies:
|
||||||
|
"@swc/counter" "^0.1.3"
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@swc/helpers@^0.4.2":
|
"@swc/helpers@^0.4.2":
|
||||||
version "0.4.14"
|
version "0.4.14"
|
||||||
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz"
|
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz"
|
||||||
@@ -1605,14 +1631,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@swc/helpers@0.5.5":
|
|
||||||
version "0.5.5"
|
|
||||||
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz"
|
|
||||||
integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==
|
|
||||||
dependencies:
|
|
||||||
"@swc/counter" "^0.1.3"
|
|
||||||
tslib "^2.4.0"
|
|
||||||
|
|
||||||
"@tanstack/react-table@^8.10.1":
|
"@tanstack/react-table@^8.10.1":
|
||||||
version "8.19.3"
|
version "8.19.3"
|
||||||
resolved "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz"
|
resolved "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.19.3.tgz"
|
||||||
@@ -1826,7 +1844,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz"
|
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz"
|
||||||
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
|
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@18.13.0":
|
"@types/node@*", "@types/node@18.13.0", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.1.0":
|
||||||
version "18.13.0"
|
version "18.13.0"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz"
|
||||||
integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==
|
integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==
|
||||||
@@ -2652,7 +2670,7 @@ classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
|
|||||||
resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz"
|
resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz"
|
||||||
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
||||||
|
|
||||||
client-only@^0.0.1, client-only@0.0.1:
|
client-only@0.0.1, client-only@^0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
||||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||||
@@ -2675,20 +2693,16 @@ cliui@^7.0.2:
|
|||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
wrap-ansi "^7.0.0"
|
wrap-ansi "^7.0.0"
|
||||||
|
|
||||||
cliui@^8.0.1:
|
|
||||||
version "8.0.1"
|
|
||||||
resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz"
|
|
||||||
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
|
|
||||||
dependencies:
|
|
||||||
string-width "^4.2.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
wrap-ansi "^7.0.0"
|
|
||||||
|
|
||||||
clone@^2.1.2:
|
clone@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz"
|
||||||
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
|
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
|
||||||
|
|
||||||
|
clsx@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz"
|
||||||
|
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
|
||||||
|
|
||||||
clsx@^1.1.1:
|
clsx@^1.1.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
||||||
@@ -2699,11 +2713,6 @@ clsx@^2.0.0, clsx@^2.1.1:
|
|||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
|
||||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||||
|
|
||||||
clsx@2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz"
|
|
||||||
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
|
|
||||||
|
|
||||||
color-convert@^1.9.0:
|
color-convert@^1.9.0:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
|
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
|
||||||
@@ -2718,16 +2727,16 @@ color-convert@^2.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-name "~1.1.4"
|
color-name "~1.1.4"
|
||||||
|
|
||||||
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
|
|
||||||
version "1.1.4"
|
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
|
||||||
|
|
||||||
color-name@1.1.3:
|
color-name@1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||||
|
|
||||||
|
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||||
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
color-string@^1.9.1:
|
color-string@^1.9.1:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz"
|
resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz"
|
||||||
@@ -2953,6 +2962,13 @@ dayjs@^1.8.34:
|
|||||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz"
|
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz"
|
||||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||||
|
|
||||||
|
debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||||
|
version "4.3.4"
|
||||||
|
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
|
||||||
|
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debug@^3.2.7:
|
debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||||
@@ -2960,13 +2976,6 @@ debug@^3.2.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4:
|
|
||||||
version "4.3.4"
|
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
|
|
||||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
|
||||||
dependencies:
|
|
||||||
ms "2.1.2"
|
|
||||||
|
|
||||||
decamelize@^1.2.0:
|
decamelize@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
|
||||||
@@ -3141,7 +3150,7 @@ eastasianwidth@^0.2.0:
|
|||||||
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
|
||||||
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
||||||
|
|
||||||
ecdsa-sig-formatter@^1.0.11, ecdsa-sig-formatter@1.0.11:
|
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
|
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
|
||||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||||
@@ -3894,6 +3903,11 @@ fs.realpath@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
|
||||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||||
|
|
||||||
|
fsevents@~2.3.2:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
fstream@^1.0.12:
|
fstream@^1.0.12:
|
||||||
version "1.0.12"
|
version "1.0.12"
|
||||||
resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz"
|
resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz"
|
||||||
@@ -3996,7 +4010,7 @@ get-tsconfig@^4.2.0:
|
|||||||
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz"
|
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz"
|
||||||
integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==
|
integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==
|
||||||
|
|
||||||
glob-parent@^5.1.2:
|
glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||||
@@ -4010,12 +4024,29 @@ glob-parent@^6.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.3"
|
is-glob "^4.0.3"
|
||||||
|
|
||||||
glob-parent@~5.1.2:
|
glob@7.1.6:
|
||||||
version "5.1.2"
|
version "7.1.6"
|
||||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
|
||||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
|
glob@7.1.7, glob@^7.1.3, glob@^7.1.4:
|
||||||
|
version "7.1.7"
|
||||||
|
resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz"
|
||||||
|
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^10.4.2:
|
glob@^10.4.2:
|
||||||
version "10.4.5"
|
version "10.4.5"
|
||||||
@@ -4029,18 +4060,6 @@ glob@^10.4.2:
|
|||||||
package-json-from-dist "^1.0.0"
|
package-json-from-dist "^1.0.0"
|
||||||
path-scurry "^1.11.1"
|
path-scurry "^1.11.1"
|
||||||
|
|
||||||
glob@^7.1.3, glob@^7.1.4, glob@7.1.7:
|
|
||||||
version "7.1.7"
|
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz"
|
|
||||||
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
|
||||||
dependencies:
|
|
||||||
fs.realpath "^1.0.0"
|
|
||||||
inflight "^1.0.4"
|
|
||||||
inherits "2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
once "^1.3.0"
|
|
||||||
path-is-absolute "^1.0.0"
|
|
||||||
|
|
||||||
glob@^7.2.3:
|
glob@^7.2.3:
|
||||||
version "7.2.3"
|
version "7.2.3"
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
||||||
@@ -4064,18 +4083,6 @@ glob@^8.0.0:
|
|||||||
minimatch "^5.0.1"
|
minimatch "^5.0.1"
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
|
|
||||||
glob@7.1.6:
|
|
||||||
version "7.1.6"
|
|
||||||
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
|
|
||||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
|
||||||
dependencies:
|
|
||||||
fs.realpath "^1.0.0"
|
|
||||||
inflight "^1.0.4"
|
|
||||||
inherits "2"
|
|
||||||
minimatch "^3.0.4"
|
|
||||||
once "^1.3.0"
|
|
||||||
path-is-absolute "^1.0.0"
|
|
||||||
|
|
||||||
globals@^11.1.0:
|
globals@^11.1.0:
|
||||||
version "11.12.0"
|
version "11.12.0"
|
||||||
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
|
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
|
||||||
@@ -4374,7 +4381,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3, inherits@2:
|
inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -5062,18 +5069,18 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
lru-cache@^10.2.0:
|
lru-cache@6.0.0, lru-cache@^6.0.0:
|
||||||
version "10.4.3"
|
|
||||||
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz"
|
|
||||||
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
|
||||||
|
|
||||||
lru-cache@^6.0.0, lru-cache@6.0.0:
|
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
|
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
|
||||||
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
lru-cache@^10.2.0:
|
||||||
|
version "10.4.3"
|
||||||
|
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz"
|
||||||
|
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
||||||
|
|
||||||
lru-memoizer@^2.2.0:
|
lru-memoizer@^2.2.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz"
|
resolved "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz"
|
||||||
@@ -5149,7 +5156,7 @@ micromatch@^4.0.4, micromatch@^4.0.5:
|
|||||||
braces "^3.0.2"
|
braces "^3.0.2"
|
||||||
picomatch "^2.3.1"
|
picomatch "^2.3.1"
|
||||||
|
|
||||||
"mime-db@>= 1.43.0 < 2", mime-db@1.52.0:
|
mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
|
||||||
version "1.52.0"
|
version "1.52.0"
|
||||||
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
|
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
|
||||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
@@ -5173,14 +5180,7 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^5.0.1:
|
minimatch@^5.0.1, minimatch@^5.1.0:
|
||||||
version "5.1.6"
|
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
|
|
||||||
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
|
||||||
dependencies:
|
|
||||||
brace-expansion "^2.0.1"
|
|
||||||
|
|
||||||
minimatch@^5.1.0:
|
|
||||||
version "5.1.6"
|
version "5.1.6"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz"
|
||||||
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
||||||
@@ -5224,11 +5224,6 @@ minizlib@^2.1.1:
|
|||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
mkdirp@^1.0.3, mkdirp@^1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
|
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
|
||||||
|
|
||||||
"mkdirp@>=0.5 0":
|
"mkdirp@>=0.5 0":
|
||||||
version "0.5.6"
|
version "0.5.6"
|
||||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz"
|
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz"
|
||||||
@@ -5236,6 +5231,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
|
mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
|
||||||
|
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||||
|
|
||||||
moment-timezone@^0.5.44:
|
moment-timezone@^0.5.44:
|
||||||
version "0.5.45"
|
version "0.5.45"
|
||||||
resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz"
|
resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz"
|
||||||
@@ -5265,7 +5265,7 @@ mongodb@^6.8.1:
|
|||||||
bson "^6.7.0"
|
bson "^6.7.0"
|
||||||
mongodb-connection-string-url "^3.0.0"
|
mongodb-connection-string-url "^3.0.0"
|
||||||
|
|
||||||
ms@^2.1.1, ms@2.1.2:
|
ms@2.1.2, ms@^2.1.1:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
@@ -5327,21 +5327,14 @@ node-addon-api@^5.0.0:
|
|||||||
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz"
|
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz"
|
||||||
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
||||||
|
|
||||||
node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@2.6.7:
|
node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
|
||||||
version "2.6.7"
|
version "2.6.7"
|
||||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
|
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
|
||||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-fetch@^2.6.12:
|
node-fetch@^2.6.12, node-fetch@^2.6.9:
|
||||||
version "2.7.0"
|
|
||||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
|
||||||
dependencies:
|
|
||||||
whatwg-url "^5.0.0"
|
|
||||||
|
|
||||||
node-fetch@^2.6.9:
|
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||||
@@ -5702,7 +5695,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^
|
|||||||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
||||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||||
|
|
||||||
postcss@^8, postcss@^8.0.9, postcss@^8.4.21, postcss@8.4.31:
|
postcss@8.4.31, postcss@^8, postcss@^8.0.9, postcss@^8.4.21:
|
||||||
version "8.4.31"
|
version "8.4.31"
|
||||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz"
|
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz"
|
||||||
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
|
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
|
||||||
@@ -5744,15 +5737,6 @@ promise-polyfill@^8.3.0:
|
|||||||
resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz"
|
resolved "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz"
|
||||||
integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==
|
integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==
|
||||||
|
|
||||||
prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
|
||||||
version "15.8.1"
|
|
||||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
|
||||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
|
||||||
dependencies:
|
|
||||||
loose-envify "^1.4.0"
|
|
||||||
object-assign "^4.1.1"
|
|
||||||
react-is "^16.13.1"
|
|
||||||
|
|
||||||
prop-types@15.7.2:
|
prop-types@15.7.2:
|
||||||
version "15.7.2"
|
version "15.7.2"
|
||||||
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
|
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
|
||||||
@@ -5762,6 +5746,15 @@ prop-types@15.7.2:
|
|||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.8.1"
|
react-is "^16.8.1"
|
||||||
|
|
||||||
|
prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||||
|
version "15.8.1"
|
||||||
|
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
|
||||||
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
react-is "^16.13.1"
|
||||||
|
|
||||||
proto3-json-serializer@^1.0.0:
|
proto3-json-serializer@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz"
|
||||||
@@ -5785,6 +5778,24 @@ protobufjs-cli@1.1.1:
|
|||||||
tmp "^0.2.1"
|
tmp "^0.2.1"
|
||||||
uglify-js "^3.7.7"
|
uglify-js "^3.7.7"
|
||||||
|
|
||||||
|
protobufjs@7.2.4:
|
||||||
|
version "7.2.4"
|
||||||
|
resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz"
|
||||||
|
integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==
|
||||||
|
dependencies:
|
||||||
|
"@protobufjs/aspromise" "^1.1.2"
|
||||||
|
"@protobufjs/base64" "^1.1.2"
|
||||||
|
"@protobufjs/codegen" "^2.0.4"
|
||||||
|
"@protobufjs/eventemitter" "^1.1.0"
|
||||||
|
"@protobufjs/fetch" "^1.1.0"
|
||||||
|
"@protobufjs/float" "^1.0.2"
|
||||||
|
"@protobufjs/inquire" "^1.1.0"
|
||||||
|
"@protobufjs/path" "^1.1.2"
|
||||||
|
"@protobufjs/pool" "^1.1.0"
|
||||||
|
"@protobufjs/utf8" "^1.1.0"
|
||||||
|
"@types/node" ">=13.7.0"
|
||||||
|
long "^5.0.0"
|
||||||
|
|
||||||
protobufjs@^6.11.3:
|
protobufjs@^6.11.3:
|
||||||
version "6.11.3"
|
version "6.11.3"
|
||||||
resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz"
|
resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz"
|
||||||
@@ -5840,24 +5851,6 @@ protobufjs@^7.2.5:
|
|||||||
"@types/node" ">=13.7.0"
|
"@types/node" ">=13.7.0"
|
||||||
long "^5.0.0"
|
long "^5.0.0"
|
||||||
|
|
||||||
protobufjs@7.2.4:
|
|
||||||
version "7.2.4"
|
|
||||||
resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz"
|
|
||||||
integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==
|
|
||||||
dependencies:
|
|
||||||
"@protobufjs/aspromise" "^1.1.2"
|
|
||||||
"@protobufjs/base64" "^1.1.2"
|
|
||||||
"@protobufjs/codegen" "^2.0.4"
|
|
||||||
"@protobufjs/eventemitter" "^1.1.0"
|
|
||||||
"@protobufjs/fetch" "^1.1.0"
|
|
||||||
"@protobufjs/float" "^1.0.2"
|
|
||||||
"@protobufjs/inquire" "^1.1.0"
|
|
||||||
"@protobufjs/path" "^1.1.2"
|
|
||||||
"@protobufjs/pool" "^1.1.0"
|
|
||||||
"@protobufjs/utf8" "^1.1.0"
|
|
||||||
"@types/node" ">=13.7.0"
|
|
||||||
long "^5.0.0"
|
|
||||||
|
|
||||||
proxy-from-env@^1.1.0:
|
proxy-from-env@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
||||||
@@ -6159,33 +6152,7 @@ read-excel-file@^5.7.1:
|
|||||||
fflate "^0.7.3"
|
fflate "^0.7.3"
|
||||||
unzipper "^0.12.2"
|
unzipper "^0.12.2"
|
||||||
|
|
||||||
readable-stream@^2.0.0:
|
readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.3.6:
|
||||||
version "2.3.8"
|
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
|
|
||||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
|
||||||
dependencies:
|
|
||||||
core-util-is "~1.0.0"
|
|
||||||
inherits "~2.0.3"
|
|
||||||
isarray "~1.0.0"
|
|
||||||
process-nextick-args "~2.0.0"
|
|
||||||
safe-buffer "~5.1.1"
|
|
||||||
string_decoder "~1.1.1"
|
|
||||||
util-deprecate "~1.0.1"
|
|
||||||
|
|
||||||
readable-stream@^2.0.2:
|
|
||||||
version "2.3.8"
|
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
|
|
||||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
|
||||||
dependencies:
|
|
||||||
core-util-is "~1.0.0"
|
|
||||||
inherits "~2.0.3"
|
|
||||||
isarray "~1.0.0"
|
|
||||||
process-nextick-args "~2.0.0"
|
|
||||||
safe-buffer "~5.1.1"
|
|
||||||
string_decoder "~1.1.1"
|
|
||||||
util-deprecate "~1.0.1"
|
|
||||||
|
|
||||||
readable-stream@^2.0.5:
|
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
|
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
|
||||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||||
@@ -6207,19 +6174,6 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
|||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
readable-stream@~2.3.6:
|
|
||||||
version "2.3.8"
|
|
||||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
|
|
||||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
|
||||||
dependencies:
|
|
||||||
core-util-is "~1.0.0"
|
|
||||||
inherits "~2.0.3"
|
|
||||||
isarray "~1.0.0"
|
|
||||||
process-nextick-args "~2.0.0"
|
|
||||||
safe-buffer "~5.1.1"
|
|
||||||
string_decoder "~1.1.1"
|
|
||||||
util-deprecate "~1.0.1"
|
|
||||||
|
|
||||||
readdir-glob@^1.1.2:
|
readdir-glob@^1.1.2:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz"
|
resolved "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz"
|
||||||
@@ -6326,13 +6280,6 @@ reusify@^1.0.4:
|
|||||||
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
|
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
rimraf@^3.0.2:
|
|
||||||
version "3.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
|
|
||||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
|
||||||
dependencies:
|
|
||||||
glob "^7.1.3"
|
|
||||||
|
|
||||||
rimraf@2:
|
rimraf@2:
|
||||||
version "2.7.1"
|
version "2.7.1"
|
||||||
resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz"
|
resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz"
|
||||||
@@ -6340,6 +6287,13 @@ rimraf@2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
|
rimraf@^3.0.2:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
|
||||||
|
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.1.3"
|
||||||
|
|
||||||
run-parallel@^1.1.9:
|
run-parallel@^1.1.9:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz"
|
||||||
@@ -6347,7 +6301,7 @@ run-parallel@^1.1.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask "^1.2.2"
|
queue-microtask "^1.2.2"
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0:
|
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@@ -6554,20 +6508,6 @@ streamsearch@^1.1.0:
|
|||||||
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
|
||||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||||
|
|
||||||
string_decoder@^1.1.1:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
|
||||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
|
||||||
dependencies:
|
|
||||||
safe-buffer "~5.2.0"
|
|
||||||
|
|
||||||
string_decoder@~1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
|
||||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
|
||||||
dependencies:
|
|
||||||
safe-buffer "~5.1.0"
|
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0":
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||||
@@ -6627,6 +6567,20 @@ string.prototype.trimstart@^1.0.6:
|
|||||||
define-properties "^1.1.4"
|
define-properties "^1.1.4"
|
||||||
es-abstract "^1.20.4"
|
es-abstract "^1.20.4"
|
||||||
|
|
||||||
|
string_decoder@^1.1.1:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
|
||||||
|
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
|
string_decoder@~1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
|
||||||
|
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||||
@@ -7081,7 +7035,7 @@ use-sidecar@^1.1.2:
|
|||||||
detect-node-es "^1.1.0"
|
detect-node-es "^1.1.0"
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-sync-external-store@^1.2.0, use-sync-external-store@1.2.0:
|
use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
@@ -7091,12 +7045,7 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||||
|
|
||||||
uuid@^8.0.0:
|
uuid@^8.0.0, uuid@^8.3.0:
|
||||||
version "8.3.2"
|
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
|
||||||
|
|
||||||
uuid@^8.3.0:
|
|
||||||
version "8.3.2"
|
version "8.3.2"
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
@@ -7331,11 +7280,6 @@ yargs-parser@^20.2.2:
|
|||||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
|
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
|
||||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||||
|
|
||||||
yargs-parser@^21.1.1:
|
|
||||||
version "21.1.1"
|
|
||||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"
|
|
||||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
|
||||||
|
|
||||||
yargs@^15.3.1:
|
yargs@^15.3.1:
|
||||||
version "15.4.1"
|
version "15.4.1"
|
||||||
resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz"
|
resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz"
|
||||||
@@ -7366,19 +7310,6 @@ yargs@^16.2.0:
|
|||||||
y18n "^5.0.5"
|
y18n "^5.0.5"
|
||||||
yargs-parser "^20.2.2"
|
yargs-parser "^20.2.2"
|
||||||
|
|
||||||
yargs@^17.7.2:
|
|
||||||
version "17.7.2"
|
|
||||||
resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz"
|
|
||||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
|
||||||
dependencies:
|
|
||||||
cliui "^8.0.1"
|
|
||||||
escalade "^3.1.1"
|
|
||||||
get-caller-file "^2.0.5"
|
|
||||||
require-directory "^2.1.1"
|
|
||||||
string-width "^4.2.3"
|
|
||||||
y18n "^5.0.5"
|
|
||||||
yargs-parser "^21.1.1"
|
|
||||||
|
|
||||||
yocto-queue@^0.1.0:
|
yocto-queue@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
|
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user