feat: adds last active timestamp (#17261)

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
This commit is contained in:
Omar López
2024-11-20 00:38:20 +05:30
committed by GitHub
co-authored by Anik Dhabal Babu
parent 6cfe9ff88d
commit 97b920744e
12 changed files with 64 additions and 1 deletions
+1
View File
@@ -67,6 +67,7 @@ describe("generate Csv for Org Users Table @test", () => {
completedOnboarding: true,
teams: [],
attributes: [],
lastActiveAt: "",
};
it("should throw if no headers", () => {
@@ -54,10 +54,11 @@ export async function getServerSession(options: {
return cachedSession;
}
const userFromDb = await prisma.user.findUnique({
const userFromDb = await prisma.user.update({
where: {
email: token.email.toLowerCase(),
},
data: { lastActiveAt: new Date() },
});
if (!userFromDb) {
@@ -37,6 +37,7 @@ import {
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { getFullName } from "@calcom/features/form-builder/utils";
import { UsersRepository } from "@calcom/features/users/users.repository";
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import {
@@ -986,6 +987,11 @@ async function handler(
originalRescheduledBooking,
});
if (booking?.userId) {
const usersRepository = new UsersRepository();
await usersRepository.updateLastActiveAt(booking.userId);
}
evt.uid = booking?.uid ?? null;
evt.oneTimePassword = booking?.oneTimePassword ?? null;
@@ -378,6 +378,11 @@ export default function MemberList(props: Props) {
return filterValue.includes(rows.getValue(id));
},
},
{
id: "lastActiveAt",
header: "Last Active",
cell: ({ row }) => <div>{row.original.lastActiveAt}</div>,
},
{
id: "actions",
cell: ({ row }) => {
@@ -342,6 +342,11 @@ export function UserListTable() {
},
},
...generateAttributeColumns(),
{
id: "lastActiveAt",
header: "Last Active",
cell: ({ row }) => <div>{row.original.lastActiveAt}</div>,
},
{
id: "actions",
enableHiding: false,
@@ -10,6 +10,7 @@ export interface UserTableUser {
accepted: boolean;
disableImpersonation: boolean;
completedOnboarding: boolean;
lastActiveAt: string;
teams: {
id: number;
name: string;
@@ -0,0 +1,5 @@
import type { User } from "@prisma/client";
export interface IUsersRepository {
updateLastActiveAt(userId: number): Promise<User>;
}
@@ -0,0 +1,20 @@
import { captureException } from "@sentry/nextjs";
import db from "@calcom/prisma";
import type { IUsersRepository } from "./users.repository.interface";
export class UsersRepository implements IUsersRepository {
async updateLastActiveAt(userId: number) {
try {
const user = await db.user.update({
where: { id: userId },
data: { lastActiveAt: new Date() },
});
return user;
} catch (err) {
captureException(err);
throw err;
}
}
}
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "lastActiveAt" TIMESTAMP(3);
+1
View File
@@ -277,6 +277,7 @@ model User {
appTheme String?
createdDate DateTime @default(now()) @map(name: "created")
trialEndsAt DateTime?
lastActiveAt DateTime?
eventTypes EventType[] @relation("user_eventtype")
credentials Credential[]
teams Membership[]
@@ -106,6 +106,7 @@ export const listMembersHandler = async ({ ctx, input }: GetOptions) => {
timeZone: true,
disableImpersonation: true,
completedOnboarding: true,
lastActiveAt: true,
teams: {
select: {
team: {
@@ -164,6 +165,13 @@ export const listMembersHandler = async ({ ctx, input }: GetOptions) => {
accepted: membership.accepted,
disableImpersonation: user.disableImpersonation,
completedOnboarding: user.completedOnboarding,
lastActiveAt: membership.user.lastActiveAt
? new Intl.DateTimeFormat(ctx.user.locale, {
timeZone: ctx.user.timeZone,
})
.format(membership.user.lastActiveAt)
.toLowerCase()
: null,
avatarUrl: user.avatarUrl,
teams: user.teams
.filter((team) => team.team.id !== organizationId) // In this context we dont want to return the org team
@@ -25,6 +25,7 @@ const userSelect = Prisma.validator<Prisma.UserSelect>()({
id: true,
bio: true,
disableImpersonation: true,
lastActiveAt: true,
});
export const listMembersHandler = async ({ ctx, input }: ListMembersHandlerOptions) => {
@@ -92,6 +93,13 @@ export const listMembersHandler = async ({ ctx, input }: ListMembersHandlerOptio
disableImpersonation: user.disableImpersonation,
bookerUrl: getBookerBaseUrlSync(profile?.organization?.slug || ""),
teamId: member.teamId,
lastActiveAt: member.user.lastActiveAt
? new Intl.DateTimeFormat(ctx.user.locale, {
timeZone: ctx.user.timeZone,
})
.format(member.user.lastActiveAt)
.toLowerCase()
: null,
};
})
);