// routes/tenant/messages/new/index.tsx import { define } from "../../../../utils.ts"; import { page } from "fresh"; import { getLeasesByTenant } from "../../../../services/leaseService.ts"; import { createMessage, getMessageById, } from "../../../../services/messageService.ts"; import { getTenantInvitesByEmail } from "../../../../services/tenantInviteService.ts"; import { getUserById } from "../../../../services/userService.ts"; import { uploadToLocalBlobStorage } from "../../../../utils/blobStorage.ts"; import { Lease, Message } from "../../../../db/interfaces.ts"; import { FormField } from "../../maintenance/new/components/FormField.tsx"; import { TextAreaField } from "../../maintenance/new/components/TextAreaField.tsx"; import { FileUpload } from "../../maintenance/new/components/FileUpload.tsx"; import { AlertMessage } from "../../maintenance/new/components/AlertMessage.tsx"; interface NewMessagePageData { currentLease?: Lease; landlordId?: string; replyToMessage?: Message | null; initialSubject: string; error?: string; } export const handler = define.handlers({ async GET(ctx) { const { user_id } = ctx.state.data; try { const url = new URL(ctx.req.url); const replyToId = url.searchParams.get("reply_to"); let replyToMessage: Message | null = null; let initialSubject = ""; if (replyToId) { const fetchedMessage = await getMessageById(replyToId); if (fetchedMessage) { replyToMessage = fetchedMessage; const rootId = replyToMessage.parent_message_id || replyToMessage.id; replyToMessage.id = rootId; const originalSubject = replyToMessage.subject || "No Subject"; initialSubject = originalSubject.startsWith("Re:") ? originalSubject : `Re: ${originalSubject}`; } } const leases = await getLeasesByTenant(user_id); const currentLease = leases?.[0]; let landlordId = currentLease?.owner_id ?? null; if (!landlordId && replyToMessage) { const fromReply = replyToMessage.sender_id === user_id ? replyToMessage.recipient_id : replyToMessage.sender_id; landlordId = fromReply ?? null; } if (!landlordId) { const user = await getUserById(user_id); const invites = user?.email ? await getTenantInvitesByEmail(user.email) : []; landlordId = invites?.[0]?.landlord_id ?? null; } if (!landlordId) { return page({ error: "We couldn't find a recipient for your message. If you have a pending invite, use it to message your landlord.", initialSubject: "", }); } return page({ currentLease: currentLease ?? undefined, landlordId, replyToMessage, initialSubject, }); } catch (error) { console.error("Error loading new message form:", error); return page({ error: "Failed to load form", initialSubject: "", }); } }, async POST(ctx) { const { user_id } = ctx.state.data; try { const formData = await ctx.req.formData(); const lease_id = formData.get("lease_id") as string; const recipient_id = formData.get("recipient_id") as string; const subject = formData.get("subject") as string; const content = formData.get("content") as string; const parent_message_id = formData.get("parent_message_id") as string || null; if (!recipient_id || !subject || !content) { return new Response("Missing required fields", { status: 400 }); } const leaseIdForMessage = lease_id?.trim() || null; const attachments: { url: string; name: string; type: string }[] = []; const files = formData.getAll("attachments") as File[]; for (const file of files) { if (file.size > 0) { const blob = new Blob([await file.arrayBuffer()], { type: file.type, }); const uploadResult = await uploadToLocalBlobStorage( file.name, blob, "image", { useTimestamp: true }, ); if (uploadResult.success && uploadResult.url) { attachments.push({ url: uploadResult.url, name: file.name, type: file.type, }); } } } await createMessage( leaseIdForMessage, user_id, recipient_id, "general", // Default message type for tenant-initiated messages subject, content, parent_message_id, attachments.length > 0 ? attachments : null, ); // Redirect to the thread if it was a reply, otherwise to the main list const redirectPath = parent_message_id ? `/tenant/messages/${parent_message_id}` : "/tenant/messages"; return new Response("", { status: 302, headers: { Location: redirectPath }, }); } catch (error) { console.error("Error submitting new message:", error); return page({ error: "Failed to send message. Please try again.", initialSubject: "", }); } }, }); export default define.page(function NewTenantMessage({ data }) { const { currentLease, landlordId, replyToMessage, initialSubject, error, } = data as NewMessagePageData; const isReply = !!replyToMessage; const title = isReply ? "Reply to Message" : "Compose New Message"; const subtitle = isReply ? `Replying to: ${replyToMessage.subject || "No Subject"}` : "Send a new message to your landlord."; if (error && error !== "Failed to load form") { // If there's an error after POST, we should show it // but we don't have the form data to re-render the form, so we show a generic error page return ( ); } if (!landlordId) { return ( ); } return (
{/* Header */}

{title}

{subtitle}

{/* Main Content */}
{/* Hidden Fields */} {currentLease && ( )} {isReply && ( )}
{error && {error}} {/* Submit Button */}
Cancel
); });