Merge pull request #44 from nathanAndrinoid/open-swe/11a3ae30-7b0d-4b7e-937c-38d32a878cac
[WIP]: Update TODO.md and add Create New Lease button to landlord dashboard
This commit is contained in:
commit
3c27be316b
5
TODO.md
5
TODO.md
@ -5,10 +5,6 @@ development plans for the application.
|
|||||||
|
|
||||||
## High Priority
|
## High Priority
|
||||||
|
|
||||||
- **Implement Payment Processing**: The current rent payment system in
|
|
||||||
`routes/tenant/rent/pay/index.tsx` is a placeholder. It needs to be fully
|
|
||||||
implemented with a payment provider like Stripe to handle real transactions
|
|
||||||
securely.
|
|
||||||
|
|
||||||
## Backend & API
|
## Backend & API
|
||||||
|
|
||||||
@ -27,6 +23,7 @@ development plans for the application.
|
|||||||
- Tenant invitation and management (`islands/LandlordInviteTenant.tsx`)
|
- Tenant invitation and management (`islands/LandlordInviteTenant.tsx`)
|
||||||
- Messaging with tenants (`islands/LandlordMessageThreads.tsx`)
|
- Messaging with tenants (`islands/LandlordMessageThreads.tsx`)
|
||||||
- Financial tracking and reporting
|
- Financial tracking and reporting
|
||||||
|
|
||||||
- **Complete Tenant Dashboard**: Finalize all features under the
|
- **Complete Tenant Dashboard**: Finalize all features under the
|
||||||
`routes/tenant/` directory. This includes:
|
`routes/tenant/` directory. This includes:
|
||||||
- Viewing lease documents
|
- Viewing lease documents
|
||||||
|
|||||||
@ -29,7 +29,6 @@ export default function AddLeaseDialog(
|
|||||||
.toISOString().split("T")[0],
|
.toISOString().split("T")[0],
|
||||||
rent: propertyCost !== undefined ? String(propertyCost) : "",
|
rent: propertyCost !== undefined ? String(propertyCost) : "",
|
||||||
security_deposit: "",
|
security_deposit: "",
|
||||||
lease_status: "",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -102,14 +101,6 @@ export default function AddLeaseDialog(
|
|||||||
onInput={(e) =>
|
onInput={(e) =>
|
||||||
setForm({ ...form, security_deposit: e.currentTarget.value })}
|
setForm({ ...form, security_deposit: e.currentTarget.value })}
|
||||||
/>
|
/>
|
||||||
<input
|
|
||||||
name="lease_status"
|
|
||||||
placeholder="Lease Status"
|
|
||||||
class="border p-2 rounded w-full"
|
|
||||||
value={form.lease_status}
|
|
||||||
onInput={(e) =>
|
|
||||||
setForm({ ...form, lease_status: e.currentTarget.value })}
|
|
||||||
/>
|
|
||||||
<div class="flex justify-end gap-2 mt-4">
|
<div class="flex justify-end gap-2 mt-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// islands/AsyncYesNoPop.ts
|
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import AddLeaseDialog from "./AddLeaseDialog.tsx";
|
import AddLeaseDialog from "./AddLeaseDialog.tsx";
|
||||||
import { Lease, Property } from "../../db/interfaces.ts";
|
import { Lease, Property } from "../../db/interfaces.ts";
|
||||||
@ -6,11 +5,13 @@ import { Lease, Property } from "../../db/interfaces.ts";
|
|||||||
export function PopAddLeaseDialog(leases: Lease[], properties: Property[]) {
|
export function PopAddLeaseDialog(leases: Lease[], properties: Property[]) {
|
||||||
const [popup, setPopup] = useState<
|
const [popup, setPopup] = useState<
|
||||||
{
|
{
|
||||||
propertyId: string;
|
propertyId: string | null;
|
||||||
popupKey: number;
|
popupKey: number;
|
||||||
resolve: (value: boolean) => void;
|
resolve: (value: boolean) => void;
|
||||||
} | null
|
} | null
|
||||||
>(null);
|
>(null);
|
||||||
|
const [selectedPropertyId, setSelectedPropertyId] = useState("");
|
||||||
|
const [showPropertySelection, setShowPropertySelection] = useState(false);
|
||||||
|
|
||||||
function getPropertyInfo(propertyId: string) {
|
function getPropertyInfo(propertyId: string) {
|
||||||
const property = properties.find((p) => p.id === propertyId);
|
const property = properties.find((p) => p.id === propertyId);
|
||||||
@ -25,16 +26,21 @@ export function PopAddLeaseDialog(leases: Lease[], properties: Property[]) {
|
|||||||
|
|
||||||
// Show the popup and return a Promise for the answer
|
// Show the popup and return a Promise for the answer
|
||||||
const popAddLease = (
|
const popAddLease = (
|
||||||
propertyId: string,
|
propertyId?: string,
|
||||||
popupKey: number,
|
popupKey?: number,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setPopup({ propertyId, popupKey, resolve });
|
if (propertyId) {
|
||||||
|
setPopup({ propertyId, popupKey: popupKey || 0, resolve });
|
||||||
|
} else {
|
||||||
|
setShowPropertySelection(true);
|
||||||
|
setPopup({ propertyId: null, popupKey: popupKey || 0, resolve });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render the popup if needed
|
// Render the popup if needed
|
||||||
const popupAddLeaseDialog = popup
|
const popupAddLeaseDialog = popup && popup.propertyId
|
||||||
? (() => {
|
? (() => {
|
||||||
const { title, address, price } = getPropertyInfo(popup.propertyId);
|
const { title, address, price } = getPropertyInfo(popup.propertyId);
|
||||||
return (
|
return (
|
||||||
@ -48,11 +54,63 @@ export function PopAddLeaseDialog(leases: Lease[], properties: Property[]) {
|
|||||||
onClose={() => {
|
onClose={() => {
|
||||||
popup.resolve(false);
|
popup.resolve(false);
|
||||||
setPopup(null);
|
setPopup(null);
|
||||||
|
setShowPropertySelection(false);
|
||||||
|
setSelectedPropertyId("");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})()
|
})()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return [popAddLease, popupAddLeaseDialog] as const;
|
// Render property selection dialog if no propertyId provided
|
||||||
|
const propertySelectionDialog = showPropertySelection
|
||||||
|
? (() => {
|
||||||
|
return (
|
||||||
|
<div class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-30 z-50">
|
||||||
|
<div class="bg-white p-6 rounded shadow-lg w-full max-w-md space-y-4">
|
||||||
|
<h3 class="text-xl font-bold mb-2">Select Property</h3>
|
||||||
|
<select
|
||||||
|
class="border p-2 rounded w-full"
|
||||||
|
value={selectedPropertyId}
|
||||||
|
onInput={(e) => setSelectedPropertyId(e.currentTarget.value)}
|
||||||
|
>
|
||||||
|
<option value="">-- Select a property --</option>
|
||||||
|
{properties.map((p) => (
|
||||||
|
<option key={p.id} value={p.id}>
|
||||||
|
{p.title} - {p.address_1}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div class="flex justify-end gap-2 mt-4">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 rounded border"
|
||||||
|
onClick={() => {
|
||||||
|
if (popup) popup.resolve(false);
|
||||||
|
setPopup(null);
|
||||||
|
setShowPropertySelection(false);
|
||||||
|
setSelectedPropertyId("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 rounded bg-blue-600 text-white"
|
||||||
|
disabled={!selectedPropertyId}
|
||||||
|
onClick={() => {
|
||||||
|
setPopup({ ...popup!, propertyId: selectedPropertyId });
|
||||||
|
setShowPropertySelection(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return [popAddLease, popupAddLeaseDialog || propertySelectionDialog] as const;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
// routes/api/create/lease.ts
|
// routes/api/create/lease.ts
|
||||||
import { define } from "../../../utils.ts";
|
import { define } from "../../../utils.ts";
|
||||||
import { createLease, getProperty } from "../../../services/propertyService.ts";
|
import { createLease } from "../../../services/leaseService.ts";
|
||||||
|
import { getProperty } from "../../../services/propertyService.ts";
|
||||||
import { SessionCookie } from "../../auth/callback.ts";
|
import { SessionCookie } from "../../auth/callback.ts";
|
||||||
|
|
||||||
export const handler = define.handlers({
|
export const handler = define.handlers({
|
||||||
async POST(ctx) {
|
async POST(ctx) {
|
||||||
try {
|
try {
|
||||||
const stateData: SessionCookie = ctx.state.data;
|
const _stateData: SessionCookie = ctx.state.data;
|
||||||
const form = await ctx.req.formData();
|
const form = await ctx.req.formData();
|
||||||
const sender = form.get("sender");
|
const sender = form.get("sender");
|
||||||
const tenant_id = stateData.user_id;
|
const tenant_id = form.get("tenant_id")?.toString();
|
||||||
const propertyId = form.get("property_id")?.toString() || "";
|
const propertyId = form.get("property_id")?.toString() || "";
|
||||||
const title = form.get("title")?.toString() || "";
|
const title = form.get("title")?.toString() || "";
|
||||||
const rent = Number(form.get("rent"));
|
const rent = Number(form.get("rent"));
|
||||||
@ -19,18 +20,20 @@ export const handler = define.handlers({
|
|||||||
const end_date = form.get("end_date")
|
const end_date = form.get("end_date")
|
||||||
? Date.parse(form.get("end_date")?.toString() + "T00:00:00")
|
? Date.parse(form.get("end_date")?.toString() + "T00:00:00")
|
||||||
: null;
|
: null;
|
||||||
|
const security_deposit = Number(form.get("security_deposit")) || 0;
|
||||||
const property = await getProperty(propertyId);
|
const property = await getProperty(propertyId);
|
||||||
|
|
||||||
if (property.length == 1 && start_date && end_date) {
|
if (property.length == 1 && start_date && end_date) {
|
||||||
const owner_id = property[0].owner_id as string;
|
const owner_id = property[0].owner_id as string;
|
||||||
await createLease(
|
await createLease(
|
||||||
propertyId,
|
|
||||||
tenant_id,
|
|
||||||
owner_id,
|
|
||||||
title,
|
title,
|
||||||
rent,
|
propertyId,
|
||||||
|
owner_id,
|
||||||
start_date,
|
start_date,
|
||||||
end_date,
|
end_date,
|
||||||
|
rent,
|
||||||
|
security_deposit,
|
||||||
|
tenant_id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export async function createLease(
|
|||||||
end_date,
|
end_date,
|
||||||
rent,
|
rent,
|
||||||
security_deposit,
|
security_deposit,
|
||||||
lease_status: "draft",
|
lease_status: "new",
|
||||||
lease_document_url: null,
|
lease_document_url: null,
|
||||||
payment_schedule_id: payment_schedule_id || null,
|
payment_schedule_id: payment_schedule_id || null,
|
||||||
late_fee_rules: null,
|
late_fee_rules: null,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user