diff --git a/apps/api/src/offboarding-checklist/access-revocation.service.ts b/apps/api/src/offboarding-checklist/access-revocation.service.ts index 41a59a792..8382ab812 100644 --- a/apps/api/src/offboarding-checklist/access-revocation.service.ts +++ b/apps/api/src/offboarding-checklist/access-revocation.service.ts @@ -174,7 +174,11 @@ export class AccessRevocationService { where: { id: revocation.id }, }); - await this.syncAccessRevocationCompletion(organizationId, memberId); + try { + await this.syncAccessRevocationCompletion(organizationId, memberId); + } catch (err) { + this.logger.warn(`Failed to sync access revocation completion for member ${memberId}`, err); + } return { success: true }; } @@ -221,7 +225,11 @@ export class AccessRevocationService { }); } - await this.syncAccessRevocationCompletion(organizationId, memberId, revokedById); + try { + await this.syncAccessRevocationCompletion(organizationId, memberId, revokedById); + } catch (err) { + this.logger.warn(`Failed to sync access revocation completion for member ${memberId}`, err); + } return { confirmed: toCreate.length }; } diff --git a/apps/api/src/offboarding-checklist/offboarding-checklist.service.ts b/apps/api/src/offboarding-checklist/offboarding-checklist.service.ts index 3536cff85..09a437847 100644 --- a/apps/api/src/offboarding-checklist/offboarding-checklist.service.ts +++ b/apps/api/src/offboarding-checklist/offboarding-checklist.service.ts @@ -127,17 +127,44 @@ export class OffboardingChecklistService { completions.map((c) => [c.templateItemId, c]), ); + const completionIds = completions.map((c) => c.id); + + const allAttachments = + completionIds.length > 0 + ? await db.attachment.findMany({ + where: { + organizationId, + entityId: { in: completionIds }, + entityType: AttachmentEntityType.offboarding_checklist, + }, + orderBy: { createdAt: 'asc' }, + }) + : []; + + const attachmentsByCompletion = new Map(); + for (const attachment of allAttachments) { + const existing = attachmentsByCompletion.get(attachment.entityId) ?? []; + existing.push(attachment); + attachmentsByCompletion.set(attachment.entityId, existing); + } + const items = await Promise.all( templateItems.map(async (template) => { const completion = completionMap.get(template.id); - const evidence = completion - ? await this.attachmentsService.getAttachments( - organizationId, - completion.id, - AttachmentEntityType.offboarding_checklist, - ) + const rawAttachments = completion + ? (attachmentsByCompletion.get(completion.id) ?? []) : []; + const evidence = await Promise.all( + rawAttachments.map(async (attachment) => ({ + id: attachment.id, + name: attachment.name, + type: attachment.type, + downloadUrl: await this.attachmentsService.getPresignedDownloadUrl(attachment.url), + createdAt: attachment.createdAt, + })), + ); + return { ...template, templateItemId: template.id, diff --git a/apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx b/apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx deleted file mode 100644 index ed1aa80f1..000000000 --- a/apps/app/src/app/(app)/[orgId]/overview/components/TodosOverview.tsx +++ /dev/null @@ -1,92 +0,0 @@ -'use client'; - -import { useApiSWR } from '@/hooks/use-api-swr'; -import { Badge, Text } from '@trycompai/design-system'; -import { ArrowRight, Checkmark } from '@trycompai/design-system/icons'; -import Link from 'next/link'; -import { useParams } from 'next/navigation'; - -interface PendingMember { - memberId: string; - name: string; - email: string; - image: string | null; - offboardDate: string; - completedItems: number; - totalItems: number; -} - -interface PendingResponse { - members: PendingMember[]; -} - -export function TodosOverview() { - const params = useParams<{ orgId: string }>(); - const organizationId = params.orgId; - const { data, isLoading, error } = useApiSWR( - '/v1/offboarding-checklist/pending', - ); - const members = data?.data?.members ?? []; - - return ( -
-
-
- Todos - {members.length > 0 && ( - {members.length} - )} -
-
- -
- {isLoading ? ( -
- Loading... -
- ) : error ? ( -
- - Failed to load todos - -
- ) : members.length === 0 ? ( -
-
- -
- - All clear — no pending items - -
- ) : ( -
- {members.map((member, index) => ( -
-
-
- - Complete offboarding for {member.name} - - - {member.completedItems}/{member.totalItems} tasks done - -
- - - -
- {index < members.length - 1 && ( -
- )} -
- ))} -
- )} -
-
- ); -} diff --git a/apps/app/src/hooks/use-access-revocations.ts b/apps/app/src/hooks/use-access-revocations.ts index 42aef1342..745441f0b 100644 --- a/apps/app/src/hooks/use-access-revocations.ts +++ b/apps/app/src/hooks/use-access-revocations.ts @@ -2,6 +2,7 @@ import { useApi } from '@/hooks/use-api'; import { useApiSWR } from '@/hooks/use-api-swr'; +import { fileToBase64 } from '@/lib/file-utils'; import { useCallback } from 'react'; interface RevokedBy { @@ -84,15 +85,3 @@ export function useAccessRevocations(memberId: string) { revokeAll, }; } - -function fileToBase64(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - const result = reader.result as string; - resolve(result.split(',')[1]); - }; - reader.onerror = () => reject(new Error('Failed to read file')); - reader.readAsDataURL(file); - }); -} diff --git a/apps/app/src/hooks/use-offboarding-checklist.ts b/apps/app/src/hooks/use-offboarding-checklist.ts index 337c2fa33..78b585fb3 100644 --- a/apps/app/src/hooks/use-offboarding-checklist.ts +++ b/apps/app/src/hooks/use-offboarding-checklist.ts @@ -2,6 +2,7 @@ import { useApi } from '@/hooks/use-api'; import { useApiSWR } from '@/hooks/use-api-swr'; +import { fileToBase64 } from '@/lib/file-utils'; import { useCallback } from 'react'; interface CompletedBy { @@ -129,15 +130,3 @@ export function useOffboardingChecklist(memberId: string) { refreshChecklist: mutate, }; } - -function fileToBase64(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - const result = reader.result as string; - resolve(result.split(',')[1]); - }; - reader.onerror = () => reject(new Error('Failed to read file')); - reader.readAsDataURL(file); - }); -} diff --git a/apps/app/src/lib/file-utils.ts b/apps/app/src/lib/file-utils.ts new file mode 100644 index 000000000..7012e7663 --- /dev/null +++ b/apps/app/src/lib/file-utils.ts @@ -0,0 +1,11 @@ +export function fileToBase64(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + const result = reader.result as string; + resolve(result.split(',')[1]); + }; + reader.onerror = () => reject(new Error('Failed to read file')); + reader.readAsDataURL(file); + }); +}