2026-02-14 02:20:19 +00:00

228 lines
8.0 KiB
TypeScript

// routes/tenant/reminders/index.tsx
import { define } from "../../../utils.ts";
import { page } from "fresh";
import {
deleteReminder,
getRemindersByUserId,
markReminderCompleted,
} from "../../../services/reminderService.ts";
import { Reminder } from "../../../db/interfaces.ts";
interface RemindersPageData {
reminders: Reminder[];
error?: string;
}
export const handler = define.handlers({
async GET(ctx) {
const { user_id } = ctx.state.data;
try {
const reminders = await getRemindersByUserId(user_id);
return page<RemindersPageData>({
reminders,
});
} catch (error) {
console.error("Error loading reminders:", error);
return page<RemindersPageData>({
reminders: [],
error: "Failed to load reminders.",
});
}
},
async POST(ctx) {
try {
const formData = await ctx.req.formData();
const action = formData.get("action") as string;
const reminderId = formData.get("reminder_id") as string;
if (!reminderId) {
return new Response("Missing reminder ID", { status: 400 });
}
if (action === "mark_completed") {
await markReminderCompleted(reminderId, true);
} else if (action === "delete") {
await deleteReminder(reminderId);
}
// Redirect back to the list
return new Response("", {
status: 302,
headers: { Location: "/tenant/reminders" },
});
} catch (error) {
console.error("Error processing reminder action:", error);
return new Response("Failed to process action", { status: 500 });
}
},
});
function ReminderStatusPill({ isCompleted }: { isCompleted: boolean }) {
const colorClass = isCompleted
? "bg-green-100 text-green-800"
: "bg-yellow-100 text-yellow-800";
const text = isCompleted ? "Completed" : "Pending";
return (
<span
class={`px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${colorClass}`}
>
{text}
</span>
);
}
export default define.page(function TenantReminders({ data }) {
const { reminders, error } = data as RemindersPageData;
const pendingReminders = reminders.filter((r) => !r.is_completed);
const completedReminders = reminders.filter((r) => r.is_completed);
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 flex justify-between items-center">
<h1 class="text-3xl font-bold text-gray-900">My Reminders</h1>
<a
href="/tenant/reminders/new"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
+ New Reminder
</a>
</div>
</div>
</div>
{/* Main Content */}
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 py-8">
{error && (
<div class="mb-4">
<ReminderStatusPill isCompleted={false} />
</div>
)}
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6 border-b border-gray-200">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Pending Reminders ({pendingReminders.length})
</h3>
</div>
<ul class="divide-y divide-gray-200">
{pendingReminders.length > 0
? (
pendingReminders.map((reminder) => (
<li class="px-6 py-4 flex items-center justify-between">
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">
{reminder.title}
</p>
<p class="text-sm text-gray-500">
Due: {new Date(reminder.due_date).toLocaleDateString()}
</p>
{reminder.description && (
<p class="mt-1 text-xs text-gray-400">
{reminder.description}
</p>
)}
</div>
<div class="flex items-center space-x-4">
<ReminderStatusPill isCompleted={false} />
<form method="POST" class="inline-block">
<input
type="hidden"
name="reminder_id"
value={reminder.id}
/>
<input
type="hidden"
name="action"
value="mark_completed"
/>
<button
type="submit"
class="text-green-600 hover:text-green-900 text-sm font-medium"
>
Mark Complete
</button>
</form>
<form method="POST" class="inline-block">
<input
type="hidden"
name="reminder_id"
value={reminder.id}
/>
<input type="hidden" name="action" value="delete" />
<button
type="submit"
class="text-red-600 hover:text-red-900 text-sm font-medium"
>
Delete
</button>
</form>
</div>
</li>
))
)
: (
<li class="px-6 py-4 text-center text-sm text-gray-500">
You have no pending reminders.
</li>
)}
</ul>
</div>
{/* Completed Reminders */}
<div class="mt-8 bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6 border-b border-gray-200">
<h3 class="text-lg leading-6 font-medium text-gray-900">
Completed Reminders ({completedReminders.length})
</h3>
</div>
<ul class="divide-y divide-gray-200">
{completedReminders.length > 0
? (
completedReminders.map((reminder) => (
<li class="px-6 py-4 flex items-center justify-between bg-gray-50 opacity-70">
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 line-through truncate">
{reminder.title}
</p>
<p class="text-sm text-gray-500">
Due: {new Date(reminder.due_date).toLocaleDateString()}
</p>
</div>
<div class="flex items-center space-x-4">
<ReminderStatusPill isCompleted />
<form method="POST" class="inline-block">
<input
type="hidden"
name="reminder_id"
value={reminder.id}
/>
<input type="hidden" name="action" value="delete" />
<button
type="submit"
class="text-red-600 hover:text-red-900 text-sm font-medium"
>
Delete
</button>
</form>
</div>
</li>
))
)
: (
<li class="px-6 py-4 text-center text-sm text-gray-500">
No completed reminders.
</li>
)}
</ul>
</div>
</div>
</div>
);
});