Added a "last login" to the users
This commit is contained in:
@@ -1,189 +1,161 @@
|
|||||||
import { Module } from ".";
|
import {Module} from ".";
|
||||||
import { InstructorGender } from "./exam";
|
import {InstructorGender} from "./exam";
|
||||||
import { PermissionType } from "./permissions";
|
import {PermissionType} from "./permissions";
|
||||||
|
|
||||||
export type User =
|
export type User = StudentUser | TeacherUser | CorporateUser | AgentUser | AdminUser | DeveloperUser | MasterCorporateUser;
|
||||||
| StudentUser
|
|
||||||
| TeacherUser
|
|
||||||
| CorporateUser
|
|
||||||
| AgentUser
|
|
||||||
| AdminUser
|
|
||||||
| DeveloperUser
|
|
||||||
| MasterCorporateUser;
|
|
||||||
export type UserStatus = "active" | "disabled" | "paymentDue";
|
export type UserStatus = "active" | "disabled" | "paymentDue";
|
||||||
|
|
||||||
export interface BasicUser {
|
export interface BasicUser {
|
||||||
email: string;
|
email: string;
|
||||||
name: string;
|
name: string;
|
||||||
profilePicture: string;
|
profilePicture: string;
|
||||||
id: string;
|
id: string;
|
||||||
isFirstLogin: boolean;
|
isFirstLogin: boolean;
|
||||||
focus: "academic" | "general";
|
focus: "academic" | "general";
|
||||||
levels: { [key in Module]: number };
|
levels: {[key in Module]: number};
|
||||||
desiredLevels: { [key in Module]: number };
|
desiredLevels: {[key in Module]: number};
|
||||||
type: Type;
|
type: Type;
|
||||||
bio: string;
|
bio: string;
|
||||||
isVerified: boolean;
|
isVerified: boolean;
|
||||||
subscriptionExpirationDate?: null | Date;
|
subscriptionExpirationDate?: null | Date;
|
||||||
registrationDate?: Date;
|
registrationDate?: Date;
|
||||||
status: UserStatus;
|
status: UserStatus;
|
||||||
permissions: PermissionType[],
|
permissions: PermissionType[];
|
||||||
|
lastLogin?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StudentUser extends BasicUser {
|
export interface StudentUser extends BasicUser {
|
||||||
type: "student";
|
type: "student";
|
||||||
preferredGender?: InstructorGender;
|
preferredGender?: InstructorGender;
|
||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
preferredTopics?: string[];
|
preferredTopics?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TeacherUser extends BasicUser {
|
export interface TeacherUser extends BasicUser {
|
||||||
type: "teacher";
|
type: "teacher";
|
||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CorporateUser extends BasicUser {
|
export interface CorporateUser extends BasicUser {
|
||||||
type: "corporate";
|
type: "corporate";
|
||||||
corporateInformation: CorporateInformation;
|
corporateInformation: CorporateInformation;
|
||||||
demographicInformation?: DemographicCorporateInformation;
|
demographicInformation?: DemographicCorporateInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MasterCorporateUser extends BasicUser {
|
export interface MasterCorporateUser extends BasicUser {
|
||||||
type: "mastercorporate";
|
type: "mastercorporate";
|
||||||
corporateInformation: CorporateInformation;
|
corporateInformation: CorporateInformation;
|
||||||
demographicInformation?: DemographicCorporateInformation;
|
demographicInformation?: DemographicCorporateInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentUser extends BasicUser {
|
export interface AgentUser extends BasicUser {
|
||||||
type: "agent";
|
type: "agent";
|
||||||
agentInformation: AgentInformation;
|
agentInformation: AgentInformation;
|
||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminUser extends BasicUser {
|
export interface AdminUser extends BasicUser {
|
||||||
type: "admin";
|
type: "admin";
|
||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeveloperUser extends BasicUser {
|
export interface DeveloperUser extends BasicUser {
|
||||||
type: "developer";
|
type: "developer";
|
||||||
preferredGender?: InstructorGender;
|
preferredGender?: InstructorGender;
|
||||||
demographicInformation?: DemographicInformation;
|
demographicInformation?: DemographicInformation;
|
||||||
preferredTopics?: string[];
|
preferredTopics?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CorporateInformation {
|
export interface CorporateInformation {
|
||||||
companyInformation: CompanyInformation;
|
companyInformation: CompanyInformation;
|
||||||
monthlyDuration: number;
|
monthlyDuration: number;
|
||||||
payment?: {
|
payment?: {
|
||||||
value: number;
|
value: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
commission: number;
|
commission: number;
|
||||||
};
|
};
|
||||||
referralAgent?: string;
|
referralAgent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AgentInformation {
|
export interface AgentInformation {
|
||||||
companyName: string;
|
companyName: string;
|
||||||
commercialRegistration: string;
|
commercialRegistration: string;
|
||||||
companyArabName?: string;
|
companyArabName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompanyInformation {
|
export interface CompanyInformation {
|
||||||
name: string;
|
name: string;
|
||||||
userAmount: number;
|
userAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DemographicInformation {
|
export interface DemographicInformation {
|
||||||
country: string;
|
country: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
gender: Gender;
|
gender: Gender;
|
||||||
employment: EmploymentStatus;
|
employment: EmploymentStatus;
|
||||||
passport_id?: string;
|
passport_id?: string;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DemographicCorporateInformation {
|
export interface DemographicCorporateInformation {
|
||||||
country: string;
|
country: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
gender: Gender;
|
gender: Gender;
|
||||||
position: string;
|
position: string;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Gender = "male" | "female" | "other";
|
export type Gender = "male" | "female" | "other";
|
||||||
export type EmploymentStatus =
|
export type EmploymentStatus = "employed" | "student" | "self-employed" | "unemployed" | "retired" | "other";
|
||||||
| "employed"
|
export const EMPLOYMENT_STATUS: {status: EmploymentStatus; label: string}[] = [
|
||||||
| "student"
|
{status: "student", label: "Student"},
|
||||||
| "self-employed"
|
{status: "employed", label: "Employed"},
|
||||||
| "unemployed"
|
{status: "unemployed", label: "Unemployed"},
|
||||||
| "retired"
|
{status: "self-employed", label: "Self-employed"},
|
||||||
| "other";
|
{status: "retired", label: "Retired"},
|
||||||
export const EMPLOYMENT_STATUS: { status: EmploymentStatus; label: string }[] =
|
{status: "other", label: "Other"},
|
||||||
[
|
];
|
||||||
{ status: "student", label: "Student" },
|
|
||||||
{ status: "employed", label: "Employed" },
|
|
||||||
{ status: "unemployed", label: "Unemployed" },
|
|
||||||
{ status: "self-employed", label: "Self-employed" },
|
|
||||||
{ status: "retired", label: "Retired" },
|
|
||||||
{ status: "other", label: "Other" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface Stat {
|
export interface Stat {
|
||||||
id: string;
|
id: string;
|
||||||
user: string;
|
user: string;
|
||||||
exam: string;
|
exam: string;
|
||||||
exercise: string;
|
exercise: string;
|
||||||
session: string;
|
session: string;
|
||||||
date: number;
|
date: number;
|
||||||
module: Module;
|
module: Module;
|
||||||
solutions: any[];
|
solutions: any[];
|
||||||
type: string;
|
type: string;
|
||||||
timeSpent?: number;
|
timeSpent?: number;
|
||||||
inactivity?: number;
|
inactivity?: number;
|
||||||
assignment?: string;
|
assignment?: string;
|
||||||
score: {
|
score: {
|
||||||
correct: number;
|
correct: number;
|
||||||
total: number;
|
total: number;
|
||||||
missing: number;
|
missing: number;
|
||||||
};
|
};
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
admin: string;
|
admin: string;
|
||||||
name: string;
|
name: string;
|
||||||
participants: string[];
|
participants: string[];
|
||||||
id: string;
|
id: string;
|
||||||
disableEditing?: boolean;
|
disableEditing?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Code {
|
export interface Code {
|
||||||
code: string;
|
code: string;
|
||||||
creator: string;
|
creator: string;
|
||||||
expiryDate: Date;
|
expiryDate: Date;
|
||||||
type: Type;
|
type: Type;
|
||||||
creationDate?: string;
|
creationDate?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
passport_id?: string;
|
passport_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Type =
|
export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent" | "mastercorporate";
|
||||||
| "student"
|
export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent", "mastercorporate"];
|
||||||
| "teacher"
|
|
||||||
| "corporate"
|
|
||||||
| "admin"
|
|
||||||
| "developer"
|
|
||||||
| "agent"
|
|
||||||
| "mastercorporate";
|
|
||||||
export const userTypes: Type[] = [
|
|
||||||
"student",
|
|
||||||
"teacher",
|
|
||||||
"corporate",
|
|
||||||
"admin",
|
|
||||||
"developer",
|
|
||||||
"agent",
|
|
||||||
"mastercorporate",
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -279,10 +279,10 @@ 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})`
|
||||||
: "Not available",
|
: "N/A",
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor("demographicInformation.phone", {
|
columnHelper.accessor("demographicInformation.phone", {
|
||||||
header: (
|
header: (
|
||||||
@@ -291,7 +291,7 @@ export default function UserList({
|
|||||||
<SorterArrow name="phone" />
|
<SorterArrow name="phone" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: (info) => info.getValue() || "Not available",
|
cell: (info) => info.getValue() || "N/A",
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor(
|
columnHelper.accessor(
|
||||||
@@ -301,14 +301,23 @@ export default function UserList({
|
|||||||
id: "employment",
|
id: "employment",
|
||||||
header: (
|
header: (
|
||||||
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "employment"))}>
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "employment"))}>
|
||||||
<span>Employment/Position</span>
|
<span>Employment</span>
|
||||||
<SorterArrow name="employment" />
|
<SorterArrow name="employment" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: (info) => (info.row.original.type === "corporate" ? info.getValue() : capitalize(info.getValue())) || "Not available",
|
cell: (info) => (info.row.original.type === "corporate" ? info.getValue() : capitalize(info.getValue())) || "N/A",
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
columnHelper.accessor("lastLogin", {
|
||||||
|
header: (
|
||||||
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "lastLogin"))}>
|
||||||
|
<span>Last Login</span>
|
||||||
|
<SorterArrow name="lastLogin" />
|
||||||
|
</button>
|
||||||
|
) as any,
|
||||||
|
cell: (info) => (!!info.getValue() ? moment(info.getValue()).format("YYYY-MM-DD HH:mm") : "N/A"),
|
||||||
|
}),
|
||||||
columnHelper.accessor("demographicInformation.gender", {
|
columnHelper.accessor("demographicInformation.gender", {
|
||||||
header: (
|
header: (
|
||||||
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "gender"))}>
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "gender"))}>
|
||||||
@@ -316,7 +325,7 @@ export default function UserList({
|
|||||||
<SorterArrow name="gender" />
|
<SorterArrow name="gender" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
cell: (info) => capitalize(info.getValue()) || "Not available",
|
cell: (info) => capitalize(info.getValue()) || "N/A",
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -379,7 +388,7 @@ export default function UserList({
|
|||||||
columnHelper.accessor("corporateInformation.companyInformation.name", {
|
columnHelper.accessor("corporateInformation.companyInformation.name", {
|
||||||
header: (
|
header: (
|
||||||
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "companyName"))}>
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "companyName"))}>
|
||||||
<span>Company Name</span>
|
<span>Company</span>
|
||||||
<SorterArrow name="companyName" />
|
<SorterArrow name="companyName" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
@@ -388,7 +397,7 @@ export default function UserList({
|
|||||||
columnHelper.accessor("subscriptionExpirationDate", {
|
columnHelper.accessor("subscriptionExpirationDate", {
|
||||||
header: (
|
header: (
|
||||||
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "expiryDate"))}>
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "expiryDate"))}>
|
||||||
<span>Expiry Date</span>
|
<span>Expiration</span>
|
||||||
<SorterArrow name="expiryDate" />
|
<SorterArrow name="expiryDate" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
@@ -401,7 +410,7 @@ export default function UserList({
|
|||||||
columnHelper.accessor("isVerified", {
|
columnHelper.accessor("isVerified", {
|
||||||
header: (
|
header: (
|
||||||
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "verification"))}>
|
<button className="flex gap-2 items-center" onClick={() => setSorter((prev) => selectSorter(prev, "verification"))}>
|
||||||
<span>Verification</span>
|
<span>Verified</span>
|
||||||
<SorterArrow name="verification" />
|
<SorterArrow name="verification" />
|
||||||
</button>
|
</button>
|
||||||
) as any,
|
) as any,
|
||||||
@@ -464,6 +473,15 @@ export default function UserList({
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sorter === "lastLogin" || sorter === reverseString("lastLogin")) {
|
||||||
|
if (!a.lastLogin && b.lastLogin) return sorter === "lastLogin" ? -1 : 1;
|
||||||
|
if (a.lastLogin && !b.lastLogin) return sorter === "lastLogin" ? 1 : -1;
|
||||||
|
if (!a.lastLogin && !b.lastLogin) return 0;
|
||||||
|
if (moment(a.lastLogin).isAfter(b.lastLogin)) return sorter === "lastLogin" ? -1 : 1;
|
||||||
|
if (moment(b.lastLogin).isAfter(a.lastLogin)) return sorter === "lastLogin" ? 1 : -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (sorter === "country" || sorter === reverseString("country")) {
|
if (sorter === "country" || sorter === reverseString("country")) {
|
||||||
if (!a.demographicInformation?.country && b.demographicInformation?.country) return sorter === "country" ? -1 : 1;
|
if (!a.demographicInformation?.country && b.demographicInformation?.country) return sorter === "country" ? -1 : 1;
|
||||||
if (a.demographicInformation?.country && !b.demographicInformation?.country) return sorter === "country" ? 1 : -1;
|
if (a.demographicInformation?.country && !b.demographicInformation?.country) return sorter === "country" ? 1 : -1;
|
||||||
|
|||||||
@@ -107,10 +107,12 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const user = docUser.data() as User;
|
const user = docUser.data() as User;
|
||||||
|
await setDoc(docUser.ref, {lastLogin: new Date().toISOString()}, {merge: true});
|
||||||
|
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
...user,
|
...user,
|
||||||
id: req.session.user.id,
|
id: req.session.user.id,
|
||||||
|
lastLogin: new Date(),
|
||||||
};
|
};
|
||||||
await req.session.save();
|
await req.session.save();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user