From a5886eb01eb21cafaac6651c8f7e712d8c4add70 Mon Sep 17 00:00:00 2001 From: Nathan Anderson Date: Thu, 29 Jan 2026 11:27:15 -0600 Subject: [PATCH] better --- db/interfaces.ts | 1 + modules/KvCacheStore.ts | 6 ++-- routes/landlord/dashboard/index.tsx | 2 +- routes/tenant/applications/[id]/index.tsx | 4 +-- routes/tenant/applications/index.tsx | 2 +- routes/tenant/messages/[id]/index.tsx | 2 +- services/e2eFixtures.ts | 37 +++++++++++++++++++++-- services/landlordService.ts | 4 +-- services/leaseService.ts | 2 +- services/tenantApplicationService.ts | 12 ++++++-- utils/tokenEvaluator.ts | 2 +- 11 files changed, 58 insertions(+), 16 deletions(-) diff --git a/db/interfaces.ts b/db/interfaces.ts index f2baba1..cb9f6b9 100644 --- a/db/interfaces.ts +++ b/db/interfaces.ts @@ -215,6 +215,7 @@ export interface TenantInvite { id: string; landlord_id?: string | null; property_id?: string | null; + lease_id?: string | null; available_on?: number | null; invited_emails?: string | null; created_on?: number | null; diff --git a/modules/KvCacheStore.ts b/modules/KvCacheStore.ts index 9c841a8..a91a15b 100644 --- a/modules/KvCacheStore.ts +++ b/modules/KvCacheStore.ts @@ -836,7 +836,7 @@ export class KvCacheStore { case "roles": { const roles = await sqlGetAllRoles(); - const role = roles.find((r: { id: string; description: string }) => r.id === id); + const role = roles.find((r: { id: string; description: string | null }) => r.id === id); if (!role) return null; return { id: role.id, @@ -847,8 +847,8 @@ export class KvCacheStore { case "user_roles": { const roles = await sqlGetUserRoles(id as string); if (!roles || roles.length === 0) return null; - return roles.map((r: { roleId: string }) => r.roleId).filter((r: { roleId: string }) => - r.roleId !== "" + return roles.map((r: { roleId: string | null }) => r.roleId).filter((roleId: string | null) => + roleId !== "" ) as T; } diff --git a/routes/landlord/dashboard/index.tsx b/routes/landlord/dashboard/index.tsx index db45753..ee95313 100644 --- a/routes/landlord/dashboard/index.tsx +++ b/routes/landlord/dashboard/index.tsx @@ -81,7 +81,7 @@ export const handler = define.handlers({ stateData, dashboardData: { ...dashboardData, - pendingLeaseApprovals, + pendingApprovals, }, }); } catch (error) { diff --git a/routes/tenant/applications/[id]/index.tsx b/routes/tenant/applications/[id]/index.tsx index 6081c59..3cdb19d 100644 --- a/routes/tenant/applications/[id]/index.tsx +++ b/routes/tenant/applications/[id]/index.tsx @@ -25,7 +25,7 @@ export const handler = define.handlers({ } try { - const application = await getApplicationById(applicationId); + const application = await getApplicationById(applicationId) as TenantApplication | null; if (!application || application.applicant_id !== user_id) { return page({ @@ -48,7 +48,7 @@ export const handler = define.handlers({ if (application.document_urls_json) { try { - documentUrls = JSON.parse(application.document_urls_json as string); + documentUrls = JSON.parse(application.document_urls_json as unknown as string); } catch (e) { console.error("Failed to parse document URLs:", e); } diff --git a/routes/tenant/applications/index.tsx b/routes/tenant/applications/index.tsx index 3b79273..41a598a 100644 --- a/routes/tenant/applications/index.tsx +++ b/routes/tenant/applications/index.tsx @@ -22,7 +22,7 @@ export const handler = define.handlers({ } try { - const applications = await getApplicationsByApplicant(user_id); + const applications = await getApplicationsByApplicant(user_id) as TenantApplication[]; return page({ applications: applications || [], diff --git a/routes/tenant/messages/[id]/index.tsx b/routes/tenant/messages/[id]/index.tsx index 8232e01..b3b95a1 100644 --- a/routes/tenant/messages/[id]/index.tsx +++ b/routes/tenant/messages/[id]/index.tsx @@ -72,7 +72,7 @@ function MessageBubble( const attachments = Array.isArray(message.attachments_json) ? message.attachments_json : message.attachments_json - ? JSON.parse(message.attachments_json as string) + ? JSON.parse(message.attachments_json as unknown as string) : []; const content = message.content_encrypted || message.content || ""; diff --git a/services/e2eFixtures.ts b/services/e2eFixtures.ts index 17f3c6e..a987ec5 100644 --- a/services/e2eFixtures.ts +++ b/services/e2eFixtures.ts @@ -220,7 +220,7 @@ export function addE2EMessage( content, content_encrypted: content, parent_message_id: parentMessageId, - attachments_json: attachmentsJson, + attachments_json: attachmentsJson ? JSON.parse(attachmentsJson) as Record : null, is_read: false, created_on: BASE_TIME, }; @@ -927,6 +927,7 @@ export function getE2ETenantApplicationsByLandlord(landlordId: string) { application_notes: "Strong rental history.", status: app.application_status || "pending", created_at: app.submitted_on, + document_urls_json: app.document_urls_json ? JSON.stringify(app.document_urls_json) : null, }; }); } @@ -949,7 +950,7 @@ export function createE2ETenantApplication( applicant_id: applicantId, application_status: "pending", application_data_encrypted: applicationData, - document_urls_json: documentUrlsJson, + document_urls_json: documentUrlsJson ? JSON.parse(documentUrlsJson) as Record : null, submitted_on: BASE_TIME, reviewed_on: null, }; @@ -1221,3 +1222,35 @@ export function getE2ELandlordDashboardData(userId: string) { monthlyRevenue, }; } + +export function getE2EPendingApprovals() { + return e2eLeases.filter((lease) => + lease.approval_status === "pending" || lease.approval_status === "requested" + ); +} + +export function addE2ELeaseWithApproval( + propertyId: string, + tenantId: string, + ownerId: string, + approvalStatus: string = "pending", +) { + const newLease: Lease = { + id: crypto.randomUUID(), + property_id: propertyId, + property_address: getE2EPropertyById(propertyId)?.address_1 || "", + tenant_id: tenantId, + owner_id: ownerId, + start_date: BASE_TIME + 1000 * 60 * 60 * 24 * 7, + end_date: BASE_TIME + 1000 * 60 * 60 * 24 * 377, + rent: 2000, + security_deposit: 2000, + lease_status: "draft", + approval_status: approvalStatus, + approval_requested_at: approvalStatus === "pending" ? BASE_TIME : null, + created_on: BASE_TIME, + updated_on: BASE_TIME, + }; + e2eLeases = [newLease, ...e2eLeases]; + return newLease; +} diff --git a/services/landlordService.ts b/services/landlordService.ts index 845fa32..e7ec555 100644 --- a/services/landlordService.ts +++ b/services/landlordService.ts @@ -216,7 +216,7 @@ export async function getPendingLeaseApprovals( landlord_id: string, ) { if (isE2EEnabled()) { - return getE2EPendingApprovals(landlord_id); + return getE2EPendingApprovals(); } const pendingLeases = await db.select() @@ -311,7 +311,7 @@ export async function getLandlordDashboardData( // In a real implementation, you'd use an IN clause // For now, we'll get all applications and filter applicationsResult = await db.select() - .from(tenant_applications); + .from(tenant_applications) as TenantApplication[]; applicationsResult = applicationsResult.filter((app) => app.property_id && property_ids.includes(app.property_id) diff --git a/services/leaseService.ts b/services/leaseService.ts index f81b614..bf5a782 100644 --- a/services/leaseService.ts +++ b/services/leaseService.ts @@ -434,7 +434,7 @@ export async function rejectLease( // Get pending lease approvals for a landlord export async function getPendingApprovals(landlord_id: string) { if (isE2EEnabled()) { - return getE2EPendingApprovals(landlord_id); + return getE2EPendingApprovals(); } return await db.select() diff --git a/services/tenantApplicationService.ts b/services/tenantApplicationService.ts index cd99922..5873f8a 100644 --- a/services/tenantApplicationService.ts +++ b/services/tenantApplicationService.ts @@ -18,7 +18,7 @@ export interface TenantApplication { applicant_id?: string | null; application_status?: string | null; // 'pending', 'approved', 'rejected' application_data_encrypted?: string | null; // Encrypted application details - document_urls_json?: string | null; + document_urls_json?: Record | null; submitted_on?: number | null; reviewed_on?: number | null; } @@ -49,7 +49,14 @@ export interface ApplicationData { } // Extended interface for dashboard tenant applications with UI properties -export interface DashboardTenantApplication extends TenantApplication { +export interface DashboardTenantApplication { + id: string; + property_id?: string | null; + applicant_id?: string | null; + application_status?: string | null; + application_data_encrypted?: string | null; + submitted_on?: number | null; + reviewed_on?: number | null; applicant_name?: string; applicant_email?: string; property_address?: string; @@ -57,6 +64,7 @@ export interface DashboardTenantApplication extends TenantApplication { application_notes?: string; status?: string; // UI-friendly status (maps to application_status) created_at?: number | null; // UI-friendly timestamp (maps to submitted_on) + document_urls_json?: string | null; // UI-friendly string representation } // Create new tenant application diff --git a/utils/tokenEvaluator.ts b/utils/tokenEvaluator.ts index f04e700..c082872 100644 --- a/utils/tokenEvaluator.ts +++ b/utils/tokenEvaluator.ts @@ -37,7 +37,7 @@ export async function isCreateTokenValid( tokenId: string, stateId: string, ): Promise { - const token = await sqlGetWebToken(tokenId); + const token = await sqlGetWebToken(tokenId) as WebToken | null; const now = Date.now(); if (