Merge branch 'main' into open-swe/c8b0eca2-cb28-4a8f-ba09-09a7b62df99e
This commit is contained in:
commit
14012a6943
6
TODO.md
6
TODO.md
@ -3,6 +3,7 @@
|
||||
## High Priority
|
||||
|
||||
### Implement Lease Creation for Landlord Dashboard
|
||||
|
||||
- **Add "Create New Lease" button** to the landlord dashboard
|
||||
- **Lease creation form** with basic price information:
|
||||
- Rent amount
|
||||
@ -18,6 +19,7 @@
|
||||
- Update tenant field when invitation is accepted
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Fix TypeScript import errors** in receipt API routes
|
||||
- **Add missing `getTransactionByStripeId` function** to paymentService.ts
|
||||
- **Fix type annotations** for API route handlers
|
||||
@ -26,6 +28,7 @@
|
||||
## Technical Implementation Details
|
||||
|
||||
### Lease Creation Flow
|
||||
|
||||
1. **Landlord Dashboard Enhancement**
|
||||
- Add "Create New Lease" button component
|
||||
- Create lease creation modal/page
|
||||
@ -42,11 +45,13 @@
|
||||
- PUT `/api/leases/:id/accept` - Tenant accepts invitation
|
||||
|
||||
### Payment Service Fixes
|
||||
|
||||
- Implement `getTransactionByStripeId(stripeId: string)` function
|
||||
- Fix import paths in API routes
|
||||
- Add proper TypeScript types for Fresh handlers
|
||||
|
||||
## Development Tasks
|
||||
|
||||
- [ ] Create lease creation UI components
|
||||
- [ ] Implement lease creation API endpoint
|
||||
- [ ] Add tenant invitation system
|
||||
@ -56,6 +61,7 @@
|
||||
- [ ] Update landlord dashboard with new lease button
|
||||
|
||||
## Testing
|
||||
|
||||
- [ ] Unit tests for lease creation
|
||||
- [ ] Integration tests for tenant invitation flow
|
||||
- [ ] E2E tests for complete lease lifecycle
|
||||
|
||||
@ -2,7 +2,7 @@ import { define } from "../../../utils.ts";
|
||||
import { getTransactionByStripeId } from "../../../services/paymentService.ts";
|
||||
|
||||
export const handler = define.handlers({
|
||||
async GET(ctx: any) {
|
||||
async GET(ctx) {
|
||||
const stripeId = ctx.params.id;
|
||||
if (!stripeId) {
|
||||
return new Response("Not Found", { status: 404 });
|
||||
|
||||
@ -28,7 +28,8 @@ export const handler = define.handlers({
|
||||
}
|
||||
}
|
||||
|
||||
const lease_id = (data.lease_id as string) || (data["lease_id"] as string);
|
||||
const lease_id = (data.lease_id as string) ||
|
||||
(data["lease_id"] as string);
|
||||
if (!lease_id) {
|
||||
return new Response(JSON.stringify({ error: "lease_id is required" }), {
|
||||
status: 400,
|
||||
@ -46,10 +47,13 @@ export const handler = define.handlers({
|
||||
|
||||
const property_id = lease.property_id as string | undefined;
|
||||
if (!property_id) {
|
||||
return new Response(JSON.stringify({ error: "lease has no property_id" }), {
|
||||
return new Response(
|
||||
JSON.stringify({ error: "lease has no property_id" }),
|
||||
{
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const invite = await createTenantInvite(user_id as string, property_id);
|
||||
|
||||
@ -91,7 +91,7 @@ export const handler = define.handlers({
|
||||
|
||||
export default define.page(
|
||||
function LandlordDashboard({ data }: LandlordDashboardProps) {
|
||||
const { stateData, dashboardData, error } = data;
|
||||
const { stateData: _stateData, dashboardData, error } = data;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@ -452,10 +452,16 @@ export default define.page(
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<button class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded">
|
||||
<button
|
||||
type="button"
|
||||
class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded"
|
||||
>
|
||||
Approve
|
||||
</button>
|
||||
<button class="text-xs bg-red-100 text-red-800 px-2 py-1 rounded">
|
||||
<button
|
||||
type="button"
|
||||
class="text-xs bg-red-100 text-red-800 px-2 py-1 rounded"
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -282,4 +282,3 @@ export default define.page(function LandlordPayments({ data }) {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -171,4 +171,3 @@ export default define.page(function LandlordPortfolio({ data }) {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -536,4 +536,3 @@ export default define.page(function LandlordPropertyCleanup({ data }) {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@ -391,7 +391,7 @@ export async function cancelAutoPayment(
|
||||
export async function getTransactionByStripeId(stripeId: string) {
|
||||
if (isE2EEnabled()) {
|
||||
const transactions = getE2EPaymentTransactionsByOwner(""); // Get all transactions and filter
|
||||
return transactions.find(tx => tx.stripe_payment_id === stripeId) || null;
|
||||
return transactions.find((tx) => tx.stripe_payment_id === stripeId) || null;
|
||||
}
|
||||
const result = await db.select()
|
||||
.from(payment_transactions)
|
||||
|
||||
@ -75,5 +75,3 @@ test("landlord can update maintenance status", async ({ page }: { page: Page })
|
||||
await expect(page).toHaveURL(/\/landlord\/maintenance/);
|
||||
await expect(statusSelect).toHaveValue("in_progress");
|
||||
});
|
||||
|
||||
|
||||
|
||||
41
tests/e2e/lease-creation.spec.ts
Normal file
41
tests/e2e/lease-creation.spec.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { expect, Page, test } from "@playwright/test";
|
||||
import { applyDeterministicPage, e2eHeaders, useE2EUser } from "./helpers.ts";
|
||||
|
||||
test("lease acceptance workflow (E2E fixtures)", async ({ page }: { page: Page }) => {
|
||||
await applyDeterministicPage(page);
|
||||
|
||||
// Landlord side: navigate to leased homes
|
||||
await useE2EUser(page, "landlord");
|
||||
await page.goto("/landlord/dashboard");
|
||||
const link = page.getByRole("link", { name: /create new lease/i });
|
||||
if (await link.count()) {
|
||||
await link.click();
|
||||
} else {
|
||||
await page.goto("/leased-home");
|
||||
}
|
||||
await expect(page.getByRole("heading", { name: /leased homes/i }))
|
||||
.toBeVisible();
|
||||
|
||||
// Create an invite for an existing e2e lease (server-side fixture)
|
||||
const inviteResp = await page.request.post("/api/update/lease_invite", {
|
||||
headers: { "Content-Type": "application/json", ...e2eHeaders("landlord") },
|
||||
data: { lease_id: "e2e-lease-1" },
|
||||
});
|
||||
expect(inviteResp.ok()).toBeTruthy();
|
||||
|
||||
// Tenant accepts the lease
|
||||
await useE2EUser(page, "tenant");
|
||||
const acceptResp = await page.request.post("/api/update/lease_accept", {
|
||||
headers: { "Content-Type": "application/json", ...e2eHeaders("tenant") },
|
||||
data: { lease_id: "e2e-lease-1" },
|
||||
});
|
||||
expect(acceptResp.ok()).toBeTruthy();
|
||||
|
||||
const body = await acceptResp.json();
|
||||
expect(body?.success).toBeTruthy();
|
||||
// The response may include the updated lease; ensure status is active if present
|
||||
const lease = body?.lease;
|
||||
if (lease) {
|
||||
expect(lease.lease_status === "active").toBeTruthy();
|
||||
}
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user