Apply patch [skip ci]
This commit is contained in:
parent
27bac30445
commit
726358ce8f
23
AGENTS.md
23
AGENTS.md
@ -1,13 +1,17 @@
|
|||||||
# CyberHybridHub - Deno Fresh Web App Setup Guide
|
# CyberHybridHub - Deno Fresh Web App Setup Guide
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
CyberHybridHub is a modern web application built with Deno Fresh framework, utilizing Drizzle ORM for database management, TailwindCSS for styling, and designed for Datona sandbox environments.
|
|
||||||
|
CyberHybridHub is a modern web application built with Deno Fresh framework,
|
||||||
|
utilizing Drizzle ORM for database management, TailwindCSS for styling, and
|
||||||
|
designed for Datona sandbox environments.
|
||||||
|
|
||||||
<dependencies_and_installation>
|
<dependencies_and_installation>
|
||||||
|
|
||||||
**Package Manager**: Use Deno CLI exclusively (no npm/yarn required)
|
**Package Manager**: Use Deno CLI exclusively (no npm/yarn required)
|
||||||
|
|
||||||
**Core Dependencies**:
|
**Core Dependencies**:
|
||||||
|
|
||||||
- **Deno**: Latest runtime (https://deno.land/)
|
- **Deno**: Latest runtime (https://deno.land/)
|
||||||
- **Fresh Framework**: `jsr:@fresh/core@^2.2.0`
|
- **Fresh Framework**: `jsr:@fresh/core@^2.2.0`
|
||||||
- **Database**: Deno KV (built-in key-value storage)
|
- **Database**: Deno KV (built-in key-value storage)
|
||||||
@ -16,18 +20,20 @@ CyberHybridHub is a modern web application built with Deno Fresh framework, util
|
|||||||
- **Authentication**: Local authentication system
|
- **Authentication**: Local authentication system
|
||||||
|
|
||||||
**Database Setup**:
|
**Database Setup**:
|
||||||
|
|
||||||
- Deno KV is used for Datona sandbox compatibility
|
- Deno KV is used for Datona sandbox compatibility
|
||||||
- Database initialized automatically on startup
|
- Database initialized automatically on startup
|
||||||
- No external database server required
|
- No external database server required
|
||||||
- Built-in Deno KV storage handles all data persistence
|
- Built-in Deno KV storage handles all data persistence
|
||||||
|
|
||||||
**External Services**:
|
**External Services**:
|
||||||
|
|
||||||
- **Local File Storage**: Used for blob storage in sandbox
|
- **Local File Storage**: Used for blob storage in sandbox
|
||||||
- **In-Memory Cache**: Built-in Deno KV for caching
|
- **In-Memory Cache**: Built-in Deno KV for caching
|
||||||
- No external service dependencies required for sandbox
|
- No external service dependencies required for sandbox
|
||||||
|
|
||||||
**Environment Variables**:
|
**Environment Variables**: Set the following environment variables before
|
||||||
Set the following environment variables before running the application:
|
running the application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Server Configuration
|
# Server Configuration
|
||||||
@ -46,6 +52,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Startup Sequence**:
|
**Startup Sequence**:
|
||||||
|
|
||||||
1. Install Deno:
|
1. Install Deno:
|
||||||
```bash
|
```bash
|
||||||
curl -LO https://github.com/denoland/deno/releases/latest/download/deno-x86_64-unknown-linux-gnu.zip
|
curl -LO https://github.com/denoland/deno/releases/latest/download/deno-x86_64-unknown-linux-gnu.zip
|
||||||
@ -58,6 +65,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
4. Start development server: `deno task dev` (Deno KV initializes automatically)
|
4. Start development server: `deno task dev` (Deno KV initializes automatically)
|
||||||
|
|
||||||
**Available Deno Tasks**:
|
**Available Deno Tasks**:
|
||||||
|
|
||||||
- `deno task dev` - Start development server with Vite
|
- `deno task dev` - Start development server with Vite
|
||||||
- `deno task build` - Build for production
|
- `deno task build` - Build for production
|
||||||
- `deno task start` - Start production server
|
- `deno task start` - Start production server
|
||||||
@ -65,12 +73,14 @@ LOCAL_DEV_MODE="true"
|
|||||||
- `deno task e2e` - Run Playwright end-to-end tests
|
- `deno task e2e` - Run Playwright end-to-end tests
|
||||||
|
|
||||||
**Development Workflow**:
|
**Development Workflow**:
|
||||||
|
|
||||||
1. Make code changes
|
1. Make code changes
|
||||||
2. Run `deno task check` to validate code quality
|
2. Run `deno task check` to validate code quality
|
||||||
3. Test with `deno task test` and `deno task e2e`
|
3. Test with `deno task test` and `deno task e2e`
|
||||||
4. Deno KV handles data persistence automatically
|
4. Deno KV handles data persistence automatically
|
||||||
|
|
||||||
**Datona Sandbox Deployment**:
|
**Datona Sandbox Deployment**:
|
||||||
|
|
||||||
1. Set `SANDBOX_MODE="true"` in environment
|
1. Set `SANDBOX_MODE="true"` in environment
|
||||||
2. Ensure all external API calls are disabled
|
2. Ensure all external API calls are disabled
|
||||||
3. Use local file storage instead of cloud services
|
3. Use local file storage instead of cloud services
|
||||||
@ -82,6 +92,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
### Directory Structure
|
### Directory Structure
|
||||||
|
|
||||||
- `routes/` - Fresh framework route handlers and pages
|
- `routes/` - Fresh framework route handlers and pages
|
||||||
- `islands/` - Interactive Preact components
|
- `islands/` - Interactive Preact components
|
||||||
- `components/` - Reusable UI components
|
- `components/` - Reusable UI components
|
||||||
@ -93,6 +104,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
- `drizzle/` - Database migration files
|
- `drizzle/` - Database migration files
|
||||||
|
|
||||||
### Key Technologies
|
### Key Technologies
|
||||||
|
|
||||||
- **Fresh**: Full-stack web framework with edge-first architecture
|
- **Fresh**: Full-stack web framework with edge-first architecture
|
||||||
- **Deno KV**: Built-in key-value storage for data persistence
|
- **Deno KV**: Built-in key-value storage for data persistence
|
||||||
- **TailwindCSS**: Utility-first CSS framework
|
- **TailwindCSS**: Utility-first CSS framework
|
||||||
@ -100,6 +112,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
- **Local Authentication**: JWT-based authentication system
|
- **Local Authentication**: JWT-based authentication system
|
||||||
|
|
||||||
### Authentication Flow
|
### Authentication Flow
|
||||||
|
|
||||||
1. User registers or logs in via local authentication
|
1. User registers or logs in via local authentication
|
||||||
2. Credentials validated against Deno KV storage
|
2. Credentials validated against Deno KV storage
|
||||||
3. JWT token generated for session management
|
3. JWT token generated for session management
|
||||||
@ -109,6 +122,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
## Development Guidelines
|
## Development Guidelines
|
||||||
|
|
||||||
### Code Style
|
### Code Style
|
||||||
|
|
||||||
- Use TypeScript for all new code
|
- Use TypeScript for all new code
|
||||||
- Follow Fresh framework conventions
|
- Follow Fresh framework conventions
|
||||||
- Utilize TailwindCSS for styling
|
- Utilize TailwindCSS for styling
|
||||||
@ -116,6 +130,7 @@ LOCAL_DEV_MODE="true"
|
|||||||
- Write tests for new features and functions
|
- Write tests for new features and functions
|
||||||
|
|
||||||
### Database Operations
|
### Database Operations
|
||||||
|
|
||||||
- All data operations use Deno KV key-value storage
|
- All data operations use Deno KV key-value storage
|
||||||
- No schema migrations required
|
- No schema migrations required
|
||||||
- Automatic data persistence and backup
|
- Automatic data persistence and backup
|
||||||
@ -123,10 +138,10 @@ LOCAL_DEV_MODE="true"
|
|||||||
- Simple API for CRUD operations
|
- Simple API for CRUD operations
|
||||||
|
|
||||||
### Security Considerations
|
### Security Considerations
|
||||||
|
|
||||||
- Never commit secrets or API keys
|
- Never commit secrets or API keys
|
||||||
- Use environment variables for sensitive configuration
|
- Use environment variables for sensitive configuration
|
||||||
- Implement proper input validation and sanitization
|
- Implement proper input validation and sanitization
|
||||||
- Follow OWASP security guidelines
|
- Follow OWASP security guidelines
|
||||||
- Use local authentication to avoid external dependencies
|
- Use local authentication to avoid external dependencies
|
||||||
- Regular security audits and dependency updates
|
- Regular security audits and dependency updates
|
||||||
|
|
||||||
|
|||||||
30
TODO.md
30
TODO.md
@ -3,39 +3,50 @@
|
|||||||
## Code Formatting Issues (deno task check)
|
## Code Formatting Issues (deno task check)
|
||||||
|
|
||||||
### Critical Issues
|
### Critical Issues
|
||||||
|
|
||||||
- **5 files not formatted properly** - Need to run `deno fmt` to fix formatting
|
- **5 files not formatted properly** - Need to run `deno fmt` to fix formatting
|
||||||
- Files affected:
|
- Files affected:
|
||||||
- `routes/tenant/messages/index.tsx` - Import order and formatting issues
|
- `routes/tenant/messages/index.tsx` - Import order and formatting issues
|
||||||
- `routes/tenant/settings/index.tsx` - Function definition formatting and indentation
|
- `routes/tenant/settings/index.tsx` - Function definition formatting and
|
||||||
|
indentation
|
||||||
- `services/messageService.ts` - Import order issues
|
- `services/messageService.ts` - Import order issues
|
||||||
- Additional files with formatting problems (truncated output)
|
- Additional files with formatting problems (truncated output)
|
||||||
|
|
||||||
### Specific Formatting Problems
|
### Specific Formatting Problems
|
||||||
|
|
||||||
1. **Import Statement Order** - Multiple files have incorrect import ordering
|
1. **Import Statement Order** - Multiple files have incorrect import ordering
|
||||||
2. **Function Definition Formatting** - Functions not properly formatted according to Deno standards
|
2. **Function Definition Formatting** - Functions not properly formatted
|
||||||
|
according to Deno standards
|
||||||
3. **Indentation Issues** - Inconsistent indentation in JSX components
|
3. **Indentation Issues** - Inconsistent indentation in JSX components
|
||||||
4. **Line Break Issues** - Missing or extra line breaks in various files
|
4. **Line Break Issues** - Missing or extra line breaks in various files
|
||||||
|
|
||||||
## E2E Test Failures (deno task e2e)
|
## E2E Test Failures (deno task e2e)
|
||||||
|
|
||||||
### Failed Tests
|
### Failed Tests
|
||||||
|
|
||||||
1. **reminders.spec.ts:4** - "landlord can send reminder and tenant receives it"
|
1. **reminders.spec.ts:4** - "landlord can send reminder and tenant receives it"
|
||||||
- **Error**: Element with role "heading" and name matching /messages/i not found
|
- **Error**: Element with role "heading" and name matching /messages/i not
|
||||||
|
found
|
||||||
- **Expected**: Messages heading should be visible
|
- **Expected**: Messages heading should be visible
|
||||||
- **Status**: Timeout after 5000ms
|
- **Status**: Timeout after 5000ms
|
||||||
|
|
||||||
2. **tenant-dashboard.spec.ts:50** - "tenant messages list shows landlord communication"
|
2. **tenant-dashboard.spec.ts:50** - "tenant messages list shows landlord
|
||||||
- **Error**: Element with role "heading" and name matching /messages/i not found
|
communication"
|
||||||
|
- **Error**: Element with role "heading" and name matching /messages/i not
|
||||||
|
found
|
||||||
- **Expected**: Messages heading should be visible
|
- **Expected**: Messages heading should be visible
|
||||||
- **Status**: Timeout after 5000ms
|
- **Status**: Timeout after 5000ms
|
||||||
|
|
||||||
### Test Infrastructure Issues
|
### Test Infrastructure Issues
|
||||||
- **Static File Serving Error**: Server crashes when trying to serve static files
|
|
||||||
|
- **Static File Serving Error**: Server crashes when trying to serve static
|
||||||
|
files
|
||||||
- Error: "Failed to read directory: No such file or directory (os error 2)"
|
- Error: "Failed to read directory: No such file or directory (os error 2)"
|
||||||
- Path: `/Users/nanderson/nathanAndrinoid/cyberhybridhub.com/static`
|
- Path: `/Users/nanderson/nathanAndrinoid/cyberhybridhub.com/static`
|
||||||
- Impact: Tests failing due to missing static assets
|
- Impact: Tests failing due to missing static assets
|
||||||
|
|
||||||
### Root Cause Analysis
|
### Root Cause Analysis
|
||||||
|
|
||||||
- Both failed tests are looking for a "Messages" heading that doesn't exist
|
- Both failed tests are looking for a "Messages" heading that doesn't exist
|
||||||
- This suggests either:
|
- This suggests either:
|
||||||
- The messages page/component is not rendering properly
|
- The messages page/component is not rendering properly
|
||||||
@ -45,20 +56,25 @@
|
|||||||
## Priority Actions
|
## Priority Actions
|
||||||
|
|
||||||
### High Priority
|
### High Priority
|
||||||
1. **Fix static file serving** - Create missing `/static` directory or fix configuration
|
|
||||||
|
1. **Fix static file serving** - Create missing `/static` directory or fix
|
||||||
|
configuration
|
||||||
2. **Investigate messages page** - Check why messages heading is not rendering
|
2. **Investigate messages page** - Check why messages heading is not rendering
|
||||||
3. **Run code formatting** - Execute `deno fmt` to fix all formatting issues
|
3. **Run code formatting** - Execute `deno fmt` to fix all formatting issues
|
||||||
|
|
||||||
### Medium Priority
|
### Medium Priority
|
||||||
|
|
||||||
1. **Review test expectations** - Verify if test assertions match actual UI
|
1. **Review test expectations** - Verify if test assertions match actual UI
|
||||||
2. **Check navigation flow** - Ensure users can properly navigate to messages
|
2. **Check navigation flow** - Ensure users can properly navigate to messages
|
||||||
3. **Fix import statements** - Correct import order in affected files
|
3. **Fix import statements** - Correct import order in affected files
|
||||||
|
|
||||||
### Low Priority
|
### Low Priority
|
||||||
|
|
||||||
1. **Code style consistency** - Ensure all files follow consistent formatting
|
1. **Code style consistency** - Ensure all files follow consistent formatting
|
||||||
2. **Add error handling** - Improve error handling for missing static files
|
2. **Add error handling** - Improve error handling for missing static files
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Create `/static` directory if missing
|
1. Create `/static` directory if missing
|
||||||
2. Run `deno fmt` to fix formatting
|
2. Run `deno fmt` to fix formatting
|
||||||
3. Debug messages page rendering issue
|
3. Debug messages page rendering issue
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import { define } from "../../../utils.ts";
|
|||||||
import { page } from "fresh";
|
import { page } from "fresh";
|
||||||
import { SessionCookie } from "../../auth/callback.ts";
|
import { SessionCookie } from "../../auth/callback.ts";
|
||||||
import {
|
import {
|
||||||
|
DashboardMessage,
|
||||||
deleteMessagesForUser,
|
deleteMessagesForUser,
|
||||||
getMessagesForUser,
|
getMessagesForUser,
|
||||||
markMessagesAsRead,
|
markMessagesAsRead,
|
||||||
DashboardMessage,
|
|
||||||
MessagePage,
|
MessagePage,
|
||||||
} from "../../../services/messageService.ts";
|
} from "../../../services/messageService.ts";
|
||||||
import LandlordMessageSelection from "../../../islands/LandlordMessageSelection.tsx";
|
import LandlordMessageSelection from "../../../islands/LandlordMessageSelection.tsx";
|
||||||
|
|||||||
@ -49,7 +49,9 @@ export const handler = define.handlers({
|
|||||||
return page<PropertyPageData>({
|
return page<PropertyPageData>({
|
||||||
lease: currentLease,
|
lease: currentLease,
|
||||||
property,
|
property,
|
||||||
propertyPhotos: propertyPhotos.filter((m: PropertyMedia) => m.media_type === "photo"),
|
propertyPhotos: propertyPhotos.filter((m: PropertyMedia) =>
|
||||||
|
m.media_type === "photo"
|
||||||
|
),
|
||||||
leaseDocuments,
|
leaseDocuments,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -148,159 +150,167 @@ function DocumentList({ documents }: { documents: LeaseDocument[] }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default define.page(function TenantPropertyPage({ data }: { data: PropertyPageData }) {
|
export default define.page(
|
||||||
const { lease, property, propertyPhotos, leaseDocuments, error } =
|
function TenantPropertyPage({ data }: { data: PropertyPageData }) {
|
||||||
data as PropertyPageData;
|
const { lease, property, propertyPhotos, leaseDocuments, error } =
|
||||||
|
data as PropertyPageData;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div class="min-h-screen bg-gray-50 p-8">
|
<div class="min-h-screen bg-gray-50 p-8">
|
||||||
<AlertMessage type="error">{error}</AlertMessage>
|
<AlertMessage type="error">{error}</AlertMessage>
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<a
|
<a
|
||||||
href="/tenant/dashboard"
|
href="/tenant/dashboard"
|
||||||
class="text-blue-600 hover:text-blue-500 font-medium"
|
class="text-blue-600 hover:text-blue-500 font-medium"
|
||||||
>
|
>
|
||||||
Go to Dashboard
|
Go to Dashboard
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!property) {
|
|
||||||
return (
|
|
||||||
<div class="min-h-screen bg-gray-50 p-8">
|
|
||||||
<AlertMessage type="error">
|
|
||||||
Property details could not be loaded.
|
|
||||||
</AlertMessage>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const addressLine = formatPropertyAddress(property);
|
|
||||||
const amenities = parseList(property.amenities);
|
|
||||||
const rules = parseList(lease?.late_fee_rules ?? "");
|
|
||||||
const photoItems = (propertyPhotos ?? []).filter(
|
|
||||||
(photo): photo is PropertyMedia & { file_url: string } =>
|
|
||||||
Boolean(photo.file_url),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="min-h-screen bg-gray-50">
|
|
||||||
{/* Header */}
|
|
||||||
<div class="bg-white shadow">
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
||||||
<div class="py-6">
|
|
||||||
<h1 class="text-3xl font-bold text-gray-900">
|
|
||||||
Your Current Residence
|
|
||||||
</h1>
|
|
||||||
<p class="mt-1 text-sm text-gray-500">
|
|
||||||
{addressLine || "Address not available"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
{/* Main Content */}
|
if (!property) {
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 py-8 space-y-8">
|
return (
|
||||||
{/* Property Photos */}
|
<div class="min-h-screen bg-gray-50 p-8">
|
||||||
{photoItems.length > 0 && (
|
<AlertMessage type="error">
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg p-6">
|
Property details could not be loaded.
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4 border-b pb-2">
|
</AlertMessage>
|
||||||
Property Photos
|
</div>
|
||||||
</h3>
|
);
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
}
|
||||||
{photoItems.map((photo) => (
|
|
||||||
<img
|
const addressLine = formatPropertyAddress(property);
|
||||||
src={photo.file_url}
|
const amenities = parseList(property.amenities);
|
||||||
alt={photo.file_name || "Property Photo"}
|
const rules = parseList(lease?.late_fee_rules ?? "");
|
||||||
class="w-full h-48 object-cover rounded-lg shadow-md"
|
const photoItems = (propertyPhotos ?? []).filter(
|
||||||
/>
|
(photo): photo is PropertyMedia & { file_url: string } =>
|
||||||
))}
|
Boolean(photo.file_url),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="min-h-screen bg-gray-50">
|
||||||
|
{/* Header */}
|
||||||
|
<div class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="py-6">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-900">
|
||||||
|
Your Current Residence
|
||||||
|
</h1>
|
||||||
|
<p class="mt-1 text-sm text-gray-500">
|
||||||
|
{addressLine || "Address not available"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Property Details */}
|
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
|
||||||
<div class="px-4 py-5 sm:px-6">
|
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
|
||||||
Property Details
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="border-t border-gray-200">
|
|
||||||
<dl>
|
|
||||||
<DetailItem
|
|
||||||
label="Address"
|
|
||||||
value={addressLine || "Address not available"}
|
|
||||||
/>
|
|
||||||
<DetailItem
|
|
||||||
label="Unit Number"
|
|
||||||
value={property.address_2}
|
|
||||||
/>
|
|
||||||
<DetailItem label="Bedrooms" value={property.bedrooms} />
|
|
||||||
<DetailItem label="Bathrooms" value={property.bathrooms} />
|
|
||||||
<DetailItem
|
|
||||||
label="Square Footage"
|
|
||||||
value={property.square_footage}
|
|
||||||
/>
|
|
||||||
<DetailItem label="Year Built" value={property.year_built} />
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Amenities */}
|
{/* Main Content */}
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 py-8 space-y-8">
|
||||||
<div class="px-4 py-5 sm:px-6">
|
{/* Property Photos */}
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
{photoItems.length > 0 && (
|
||||||
Amenities
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg p-6">
|
||||||
</h3>
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4 border-b pb-2">
|
||||||
</div>
|
Property Photos
|
||||||
<div class="border-t border-gray-200 p-6">
|
</h3>
|
||||||
{amenities.length > 0
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||||
? (
|
{photoItems.map((photo) => (
|
||||||
<ul class="list-disc list-inside space-y-1 text-sm text-gray-700">
|
<img
|
||||||
{amenities.map((amenity: string) => <li key={amenity}>{amenity}</li>)}
|
src={photo.file_url}
|
||||||
</ul>
|
alt={photo.file_name || "Property Photo"}
|
||||||
)
|
class="w-full h-48 object-cover rounded-lg shadow-md"
|
||||||
: <p class="text-sm text-gray-500">No amenities listed.</p>}
|
/>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Rules */}
|
{/* Property Details */}
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
<div class="px-4 py-5 sm:px-6">
|
<div class="px-4 py-5 sm:px-6">
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
Community Rules
|
Property Details
|
||||||
</h3>
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200">
|
||||||
|
<dl>
|
||||||
|
<DetailItem
|
||||||
|
label="Address"
|
||||||
|
value={addressLine || "Address not available"}
|
||||||
|
/>
|
||||||
|
<DetailItem
|
||||||
|
label="Unit Number"
|
||||||
|
value={property.address_2}
|
||||||
|
/>
|
||||||
|
<DetailItem label="Bedrooms" value={property.bedrooms} />
|
||||||
|
<DetailItem label="Bathrooms" value={property.bathrooms} />
|
||||||
|
<DetailItem
|
||||||
|
label="Square Footage"
|
||||||
|
value={property.square_footage}
|
||||||
|
/>
|
||||||
|
<DetailItem label="Year Built" value={property.year_built} />
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-gray-200 p-6">
|
|
||||||
{rules.length > 0
|
|
||||||
? (
|
|
||||||
<ul class="list-disc list-inside space-y-1 text-sm text-gray-700">
|
|
||||||
{rules.map((rule: string) => <li key={rule}>{rule}</li>)}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
: <p class="text-sm text-gray-500">No specific rules listed.</p>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Lease Documents */}
|
{/* Amenities */}
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
<div class="px-4 py-5 sm:px-6">
|
<div class="px-4 py-5 sm:px-6">
|
||||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
Lease Documents
|
Amenities
|
||||||
</h3>
|
</h3>
|
||||||
<p class="mt-1 max-w-2xl text-sm text-gray-500">
|
</div>
|
||||||
Your current lease agreement and related documents.
|
<div class="border-t border-gray-200 p-6">
|
||||||
</p>
|
{amenities.length > 0
|
||||||
|
? (
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm text-gray-700">
|
||||||
|
{amenities.map((amenity: string) => (
|
||||||
|
<li key={amenity}>{amenity}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
: <p class="text-sm text-gray-500">No amenities listed.</p>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-t border-gray-200">
|
|
||||||
<DocumentList documents={leaseDocuments || []} />
|
{/* Rules */}
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:px-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
|
Community Rules
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200 p-6">
|
||||||
|
{rules.length > 0
|
||||||
|
? (
|
||||||
|
<ul class="list-disc list-inside space-y-1 text-sm text-gray-700">
|
||||||
|
{rules.map((rule: string) => <li key={rule}>{rule}</li>)}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
No specific rules listed.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lease Documents */}
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:px-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
|
Lease Documents
|
||||||
|
</h3>
|
||||||
|
<p class="mt-1 max-w-2xl text-sm text-gray-500">
|
||||||
|
Your current lease agreement and related documents.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200">
|
||||||
|
<DocumentList documents={leaseDocuments || []} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
},
|
||||||
});
|
);
|
||||||
|
|||||||
@ -56,46 +56,48 @@ export const handler = define.handlers({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default define.page(function TenantSettingsPage({ data }: { data: SettingsPageData }) {
|
export default define.page(
|
||||||
const { user, error } = data as SettingsPageData;
|
function TenantSettingsPage({ data }: { data: SettingsPageData }) {
|
||||||
|
const { user, error } = data as SettingsPageData;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div class="min-h-screen bg-gray-50 p-8">
|
<div class="min-h-screen bg-gray-50 p-8">
|
||||||
<div class="max-w-4xl mx-auto">
|
<div class="max-w-4xl mx-auto">
|
||||||
<h1 class="text-3xl font-bold text-red-600 mb-4">Error</h1>
|
<h1 class="text-3xl font-bold text-red-600 mb-4">Error</h1>
|
||||||
<p class="text-gray-700">{error}</p>
|
<p class="text-gray-700">{error}</p>
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<a
|
<a
|
||||||
href="/tenant/dashboard"
|
href="/tenant/dashboard"
|
||||||
class="text-blue-600 hover:text-blue-500 font-medium"
|
class="text-blue-600 hover:text-blue-500 font-medium"
|
||||||
>
|
>
|
||||||
Go to Dashboard
|
Go to Dashboard
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="min-h-screen bg-gray-50">
|
||||||
|
{/* Header */}
|
||||||
|
<div class="bg-white shadow">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="py-6">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-900">Tenant Settings</h1>
|
||||||
|
<p class="mt-1 text-sm text-gray-500">
|
||||||
|
Manage your notification and payment preferences.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8 py-8">
|
||||||
|
<TenantSettings user={user} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
return (
|
|
||||||
<div class="min-h-screen bg-gray-50">
|
|
||||||
{/* Header */}
|
|
||||||
<div class="bg-white shadow">
|
|
||||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
||||||
<div class="py-6">
|
|
||||||
<h1 class="text-3xl font-bold text-gray-900">Tenant Settings</h1>
|
|
||||||
<p class="mt-1 text-sm text-gray-500">
|
|
||||||
Manage your notification and payment preferences.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8 py-8">
|
|
||||||
<TenantSettings user={user} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import {
|
|||||||
addE2EMessage,
|
addE2EMessage,
|
||||||
deleteE2EMessage,
|
deleteE2EMessage,
|
||||||
deleteE2EMessagesForUser,
|
deleteE2EMessagesForUser,
|
||||||
getE2EMessageById,
|
|
||||||
getE2EMessageThread,
|
|
||||||
getE2ELeaseById,
|
getE2ELeaseById,
|
||||||
|
getE2EMessageById,
|
||||||
getE2EMessagesByLease,
|
getE2EMessagesByLease,
|
||||||
getE2EMessagesByUser,
|
getE2EMessagesByUser,
|
||||||
|
getE2EMessageThread,
|
||||||
getE2EPropertyById,
|
getE2EPropertyById,
|
||||||
getE2EUserById,
|
getE2EUserById,
|
||||||
isE2EEnabled,
|
isE2EEnabled,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user