diff --git a/db/schema.ts b/db/schema.ts index 0fe7a6d..c2568a7 100755 --- a/db/schema.ts +++ b/db/schema.ts @@ -388,3 +388,11 @@ export const brain_question_progress = pgTable("brain_question_progress", { created_on: bigint("created_on", { mode: "number" }), updated_on: bigint("updated_on", { mode: "number" }), }); + + + + + + + + diff --git a/routes/landlord/leases/index.tsx b/routes/landlord/leases/index.tsx new file mode 100644 index 0000000..ebf9986 --- /dev/null +++ b/routes/landlord/leases/index.tsx @@ -0,0 +1,74 @@ +import { define } from "../../../utils.ts"; +import LeasedHome from "../../../islands/LeasedHome.tsx"; +import { page } from "fresh"; +import { SessionCookie } from "../../auth/callback.ts"; +import WelcomeBar from "../../../islands/WelcomeBar/WelcomeBar.tsx"; +import { getAllProperties } from "../../../services/propertyService.ts"; +import { getLeasesByOwner } from "../../../services/leaseService.ts"; +import { createPublicId } from "../../../helpers.ts"; +import type { Lease, Property } from "../../../db/interfaces.ts"; + +export const handler = define.handlers({ + async GET(ctx) { + const stateData: SessionCookie = ctx.state.data; + if (!stateData?.user_id) { + return new Response("", { + status: 302, + headers: { Location: "/auth/login" }, + }); + } + + const ownerId = stateData.user_id; + let leases: Lease[] = []; + let properties: Property[] = []; + + leases = await getLeasesByOwner(ownerId); + properties = await getAllProperties(); + + const publicId = stateData.user_id + ? await createPublicId(stateData.user_id) + : ""; + + return page({ + stateData, + leases, + properties, + user_name: stateData.user_name, + isEncrypted: stateData.user_encrypted, + publicId, + stateId: ctx.state.stateId, + }); + }, +}); + +export default define.page( + ( + { data }: { + data: { + stateData: SessionCookie; + leases: Lease[]; + properties: Property[]; + user_name: string; + isEncrypted: boolean; + publicId: string; + stateId: string; + }; + }, + ) => ( +
+ +
+ +
+
+ ), +); diff --git a/services/leaseService.ts b/services/leaseService.ts index 2ddd6ed..cf60cbc 100644 --- a/services/leaseService.ts +++ b/services/leaseService.ts @@ -3,7 +3,7 @@ import { and, eq } from "drizzle-orm"; import { db } from "../db/db.ts"; -import { leases } from "../db/schema.ts"; +import { leases, tenant_invites } from "../db/schema.ts"; import { Lease } from "../db/interfaces.ts"; import { getE2ELeaseById, @@ -247,3 +247,66 @@ export async function getLeaseStatsByOwner(owner_id: string) { without_tenant: allLeases.filter((l) => l.tenant_id === null).length, }; } + + + + + + + + + + + + + + +/** + * Link a lease to a tenant invite. If an inviteId is provided, update that + * tenant_invites row to reference this lease. Otherwise attempt to find an + * existing invite by the lease's property and owner and attach the lease_id + * there; if none exists, create a new tenant_invites row linking to the lease. + */ +export async function inviteLease(lease_id: string, inviteId?: string) { + // If an explicit inviteId was provided, update that invite record + if (inviteId) { + const now = Date.now(); + return await db.update(tenant_invites) + .set({ lease_id, updated_on: now }) + .where(eq(tenant_invites.id, inviteId)) + .returning(); + } + + // Otherwise derive the property and landlord from the lease + const lease = await getLeaseById(lease_id); + if (!lease) return null; + + const existing = await db.select().from(tenant_invites).where( + and( + eq(tenant_invites.property_id, lease.property_id), + eq(tenant_invites.landlord_id, lease.owner_id), + ), + ); + + const now = Date.now(); + if (existing.length > 0) { + return await db.update(tenant_invites) + .set({ lease_id, updated_on: now }) + .where(eq(tenant_invites.id, existing[0].id)) + .returning(); + } + + // Create a new invite linking to this lease + return await db.insert(tenant_invites).values({ + id: crypto.randomUUID(), + landlord_id: lease.owner_id, + property_id: lease.property_id, + lease_id, + available_on: null, + invited_emails: JSON.stringify([]), + created_on: now, + updated_on: now, + }).returning(); +} + +