From 0a28c2bd41442e48ef7405d56427803d96ed63a7 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 23:55:08 +0100 Subject: [PATCH] Added a "last login" to the users --- src/interfaces/user.ts | 244 ++++++++++++--------------- src/pages/(admin)/Lists/UserList.tsx | 40 +++-- src/pages/api/user.ts | 2 + 3 files changed, 139 insertions(+), 147 deletions(-) diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index ddda1ccb..c704e4b7 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -1,189 +1,161 @@ -import { Module } from "."; -import { InstructorGender } from "./exam"; -import { PermissionType } from "./permissions"; +import {Module} from "."; +import {InstructorGender} from "./exam"; +import {PermissionType} from "./permissions"; -export type User = - | StudentUser - | TeacherUser - | CorporateUser - | AgentUser - | AdminUser - | DeveloperUser - | MasterCorporateUser; +export type User = StudentUser | TeacherUser | CorporateUser | AgentUser | AdminUser | DeveloperUser | MasterCorporateUser; export type UserStatus = "active" | "disabled" | "paymentDue"; export interface BasicUser { - email: string; - name: string; - profilePicture: string; - id: string; - isFirstLogin: boolean; - focus: "academic" | "general"; - levels: { [key in Module]: number }; - desiredLevels: { [key in Module]: number }; - type: Type; - bio: string; - isVerified: boolean; - subscriptionExpirationDate?: null | Date; - registrationDate?: Date; - status: UserStatus; - permissions: PermissionType[], + email: string; + name: string; + profilePicture: string; + id: string; + isFirstLogin: boolean; + focus: "academic" | "general"; + levels: {[key in Module]: number}; + desiredLevels: {[key in Module]: number}; + type: Type; + bio: string; + isVerified: boolean; + subscriptionExpirationDate?: null | Date; + registrationDate?: Date; + status: UserStatus; + permissions: PermissionType[]; + lastLogin?: Date; } export interface StudentUser extends BasicUser { - type: "student"; - preferredGender?: InstructorGender; - demographicInformation?: DemographicInformation; - preferredTopics?: string[]; + type: "student"; + preferredGender?: InstructorGender; + demographicInformation?: DemographicInformation; + preferredTopics?: string[]; } export interface TeacherUser extends BasicUser { - type: "teacher"; - demographicInformation?: DemographicInformation; + type: "teacher"; + demographicInformation?: DemographicInformation; } export interface CorporateUser extends BasicUser { - type: "corporate"; - corporateInformation: CorporateInformation; - demographicInformation?: DemographicCorporateInformation; + type: "corporate"; + corporateInformation: CorporateInformation; + demographicInformation?: DemographicCorporateInformation; } export interface MasterCorporateUser extends BasicUser { - type: "mastercorporate"; - corporateInformation: CorporateInformation; - demographicInformation?: DemographicCorporateInformation; + type: "mastercorporate"; + corporateInformation: CorporateInformation; + demographicInformation?: DemographicCorporateInformation; } export interface AgentUser extends BasicUser { - type: "agent"; - agentInformation: AgentInformation; - demographicInformation?: DemographicInformation; + type: "agent"; + agentInformation: AgentInformation; + demographicInformation?: DemographicInformation; } export interface AdminUser extends BasicUser { - type: "admin"; - demographicInformation?: DemographicInformation; + type: "admin"; + demographicInformation?: DemographicInformation; } export interface DeveloperUser extends BasicUser { - type: "developer"; - preferredGender?: InstructorGender; - demographicInformation?: DemographicInformation; - preferredTopics?: string[]; + type: "developer"; + preferredGender?: InstructorGender; + demographicInformation?: DemographicInformation; + preferredTopics?: string[]; } export interface CorporateInformation { - companyInformation: CompanyInformation; - monthlyDuration: number; - payment?: { - value: number; - currency: string; - commission: number; - }; - referralAgent?: string; + companyInformation: CompanyInformation; + monthlyDuration: number; + payment?: { + value: number; + currency: string; + commission: number; + }; + referralAgent?: string; } export interface AgentInformation { - companyName: string; - commercialRegistration: string; - companyArabName?: string; + companyName: string; + commercialRegistration: string; + companyArabName?: string; } export interface CompanyInformation { - name: string; - userAmount: number; + name: string; + userAmount: number; } export interface DemographicInformation { - country: string; - phone: string; - gender: Gender; - employment: EmploymentStatus; - passport_id?: string; - timezone?: string; + country: string; + phone: string; + gender: Gender; + employment: EmploymentStatus; + passport_id?: string; + timezone?: string; } export interface DemographicCorporateInformation { - country: string; - phone: string; - gender: Gender; - position: string; - timezone?: string; + country: string; + phone: string; + gender: Gender; + position: string; + timezone?: string; } export type Gender = "male" | "female" | "other"; -export type EmploymentStatus = - | "employed" - | "student" - | "self-employed" - | "unemployed" - | "retired" - | "other"; -export const EMPLOYMENT_STATUS: { status: EmploymentStatus; label: string }[] = - [ - { 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 type EmploymentStatus = "employed" | "student" | "self-employed" | "unemployed" | "retired" | "other"; +export const EMPLOYMENT_STATUS: {status: EmploymentStatus; label: string}[] = [ + {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 { - id: string; - user: string; - exam: string; - exercise: string; - session: string; - date: number; - module: Module; - solutions: any[]; - type: string; - timeSpent?: number; - inactivity?: number; - assignment?: string; - score: { - correct: number; - total: number; - missing: number; - }; - isDisabled?: boolean; + id: string; + user: string; + exam: string; + exercise: string; + session: string; + date: number; + module: Module; + solutions: any[]; + type: string; + timeSpent?: number; + inactivity?: number; + assignment?: string; + score: { + correct: number; + total: number; + missing: number; + }; + isDisabled?: boolean; } export interface Group { - admin: string; - name: string; - participants: string[]; - id: string; - disableEditing?: boolean; + admin: string; + name: string; + participants: string[]; + id: string; + disableEditing?: boolean; } export interface Code { - code: string; - creator: string; - expiryDate: Date; - type: Type; - creationDate?: string; - userId?: string; - email?: string; - name?: string; - passport_id?: string; + code: string; + creator: string; + expiryDate: Date; + type: Type; + creationDate?: string; + userId?: string; + email?: string; + name?: string; + passport_id?: string; } -export type Type = - | "student" - | "teacher" - | "corporate" - | "admin" - | "developer" - | "agent" - | "mastercorporate"; -export const userTypes: Type[] = [ - "student", - "teacher", - "corporate", - "admin", - "developer", - "agent", - "mastercorporate", -]; +export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent" | "mastercorporate"; +export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent", "mastercorporate"]; diff --git a/src/pages/(admin)/Lists/UserList.tsx b/src/pages/(admin)/Lists/UserList.tsx index cd939522..fb66d7ae 100644 --- a/src/pages/(admin)/Lists/UserList.tsx +++ b/src/pages/(admin)/Lists/UserList.tsx @@ -279,10 +279,10 @@ export default function UserList({ ) as any, cell: (info) => info.getValue() - ? `${countryCodes.findOne("countryCode" as any, info.getValue()).flag} ${ - countries[info.getValue() as unknown as keyof TCountries].name - } (+${countryCodes.findOne("countryCode" as any, info.getValue()).countryCallingCode})` - : "Not available", + ? `${countryCodes.findOne("countryCode" as any, info.getValue())?.flag} ${ + countries[info.getValue() as unknown as keyof TCountries]?.name + } (+${countryCodes.findOne("countryCode" as any, info.getValue())?.countryCallingCode})` + : "N/A", }), columnHelper.accessor("demographicInformation.phone", { header: ( @@ -291,7 +291,7 @@ export default function UserList({ ) as any, - cell: (info) => info.getValue() || "Not available", + cell: (info) => info.getValue() || "N/A", enableSorting: true, }), columnHelper.accessor( @@ -301,14 +301,23 @@ export default function UserList({ id: "employment", header: ( ) 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, }, ), + columnHelper.accessor("lastLogin", { + header: ( + + ) as any, + cell: (info) => (!!info.getValue() ? moment(info.getValue()).format("YYYY-MM-DD HH:mm") : "N/A"), + }), columnHelper.accessor("demographicInformation.gender", { header: ( ) as any, - cell: (info) => capitalize(info.getValue()) || "Not available", + cell: (info) => capitalize(info.getValue()) || "N/A", enableSorting: true, }), { @@ -379,7 +388,7 @@ export default function UserList({ columnHelper.accessor("corporateInformation.companyInformation.name", { header: ( ) as any, @@ -388,7 +397,7 @@ export default function UserList({ columnHelper.accessor("subscriptionExpirationDate", { header: ( ) as any, @@ -401,7 +410,7 @@ export default function UserList({ columnHelper.accessor("isVerified", { header: ( ) as any, @@ -464,6 +473,15 @@ export default function UserList({ 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 (!a.demographicInformation?.country && b.demographicInformation?.country) return sorter === "country" ? -1 : 1; if (a.demographicInformation?.country && !b.demographicInformation?.country) return sorter === "country" ? 1 : -1; diff --git a/src/pages/api/user.ts b/src/pages/api/user.ts index 6d9c176b..6240aa18 100644 --- a/src/pages/api/user.ts +++ b/src/pages/api/user.ts @@ -107,10 +107,12 @@ async function get(req: NextApiRequest, res: NextApiResponse) { } const user = docUser.data() as User; + await setDoc(docUser.ref, {lastLogin: new Date().toISOString()}, {merge: true}); req.session.user = { ...user, id: req.session.user.id, + lastLogin: new Date(), }; await req.session.save();