diff --git a/src/app/(dashboard)/activities/page.tsx b/src/app/(dashboard)/activities/page.tsx index 410b356..144ad44 100644 --- a/src/app/(dashboard)/activities/page.tsx +++ b/src/app/(dashboard)/activities/page.tsx @@ -11,9 +11,9 @@ import { ActivitiesClient } from "@/components/activities/activities-client"; export default async function ActivitiesPage() { const ctx = await requireTenant(); - const { tablesDB } = createAdminClient(); + const { tablesDB, teams, users } = createAdminClient(); - const [customers, properties, activitiesResult] = await Promise.all([ + const [customers, properties, activitiesResult, membershipsResult] = await Promise.all([ listCustomers(ctx.tenantId), listProperties(ctx.tenantId), tablesDB.listRows({ @@ -25,10 +25,22 @@ export default async function ActivitiesPage() { Query.limit(300), ], }), + teams.listMemberships(ctx.tenantId), ]); const activities = JSON.parse(JSON.stringify(activitiesResult.rows)) as Activity[]; + const members = ( + await Promise.all( + membershipsResult.memberships + .filter((m) => m.userId && m.confirm) + .map(async (m) => { + const u = await users.get(m.userId).catch(() => null); + return u ? { id: m.userId, name: u.name } : null; + }), + ) + ).filter((m): m is { id: string; name: string } => m !== null); + return (
); diff --git a/src/components/activities/activities-client.tsx b/src/components/activities/activities-client.tsx index 527640d..c32ce05 100644 --- a/src/components/activities/activities-client.tsx +++ b/src/components/activities/activities-client.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; -import { DotsThree, Plus, PencilSimple, Trash, CheckCircle, List, CalendarDots } from '@/lib/icons'; +import { DotsThree, Plus, PencilSimple, Trash, CheckCircle, List, CalendarDots, Users } from '@/lib/icons'; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; @@ -19,19 +19,23 @@ import { } from "@/lib/appwrite/activity-actions"; import { ActivityFormSheet } from "./activity-form-sheet"; import { ActivityCalendar } from "./activity-calendar"; +import { ActivityTeamView } from "./activity-team-view"; import { SendSummaryDialog } from "./send-summary-dialog"; import { DeleteConfirmDialog } from "@/components/ui/delete-confirm-dialog"; import type { Activity, Customer, Property } from "@/lib/appwrite/schema"; import { ACTIVITY_TYPE_LABELS } from "@/lib/appwrite/schema"; import type { TenantRole } from "@/lib/appwrite/tenant-guard"; +import type { TeamMember } from "./activity-form-sheet"; -type ViewMode = "list" | "calendar"; +type ViewMode = "list" | "calendar" | "team"; interface ActivitiesClientProps { initialActivities: Activity[]; customers: Customer[]; properties: Property[]; role: TenantRole; + members: TeamMember[]; + currentUserId: string; } export function ActivitiesClient({ @@ -39,6 +43,8 @@ export function ActivitiesClient({ customers, properties, role, + members, + currentUserId, }: ActivitiesClientProps) { const router = useRouter(); const [activities, setActivities] = useState(initialActivities); @@ -126,6 +132,20 @@ export function ActivitiesClient({ Takvim + {(role === "owner" || role === "admin") && ( + + )} + + {/* Content */} +
+

+ {a.title} +

+
+ + {ACTIVITY_TYPE_LABELS[a.type] ?? a.type} + + {a.dueDate && ( + + {new Date(a.dueDate).toLocaleDateString("tr-TR", { day: "numeric", month: "short" })} + {isOverdue && " · Gecikmiş"} + + )} + {a.description && ( + + {a.description} + + )} +
+
+ + {/* Actions */} + + + + + + {!isCompleted && ( + onComplete(a)}> + + Tamamla + + )} + onEdit(a)}> + + Düzenle + + onDelete(a)} + className="text-destructive focus:text-destructive" + > + + Sil + + + + + ); +} diff --git a/src/lib/appwrite/activity-actions.ts b/src/lib/appwrite/activity-actions.ts index 6995c8a..fb27703 100644 --- a/src/lib/appwrite/activity-actions.ts +++ b/src/lib/appwrite/activity-actions.ts @@ -37,6 +37,7 @@ export async function createActivityAction( customerId: data.customerId, propertyId: data.propertyId, dueDate: data.dueDate, + assigneeId: data.assigneeId || ctx.user.id, createdBy: ctx.user.id, }, [ @@ -77,6 +78,7 @@ export async function updateActivityAction( customerId: data.customerId, propertyId: data.propertyId, dueDate: data.dueDate, + assigneeId: data.assigneeId || null, }); } catch { return { ok: false, error: "Aktivite güncellenemedi." }; diff --git a/src/lib/appwrite/activity-email-actions.ts b/src/lib/appwrite/activity-email-actions.ts index c250951..0b5e7ee 100644 --- a/src/lib/appwrite/activity-email-actions.ts +++ b/src/lib/appwrite/activity-email-actions.ts @@ -53,7 +53,9 @@ export async function sendDailySummaryAction( }); if (target === "me") { - const mine = todayActivities.filter((a) => a.createdBy === ctx.user.id); + const mine = todayActivities.filter( + (a) => (a.assigneeId ?? a.createdBy) === ctx.user.id, + ); if (mine.length === 0) { return { ok: false, error: "Bugün için planlanmış aktiviteniz bulunmuyor." }; } @@ -75,7 +77,9 @@ export async function sendDailySummaryAction( for (const m of membershipsResult.memberships) { if (!m.userId || !m.confirm) continue; - const memberActivities = todayActivities.filter((a) => a.createdBy === m.userId); + const memberActivities = todayActivities.filter( + (a) => (a.assigneeId ?? a.createdBy) === m.userId, + ); if (memberActivities.length === 0) continue; const member = await users.get(m.userId).catch(() => null); diff --git a/src/lib/appwrite/schema.ts b/src/lib/appwrite/schema.ts index f740fd1..3ab8a4d 100644 --- a/src/lib/appwrite/schema.ts +++ b/src/lib/appwrite/schema.ts @@ -192,6 +192,7 @@ export interface Activity extends Row { dueDate?: string; completedAt?: string; createdBy: string; + assigneeId?: string; } export interface TenantSettings extends Row { diff --git a/src/lib/validation/activities.ts b/src/lib/validation/activities.ts index 3c91208..0cd7dc8 100644 --- a/src/lib/validation/activities.ts +++ b/src/lib/validation/activities.ts @@ -7,6 +7,7 @@ export const activitySchema = z.object({ customerId: z.string().max(36).optional(), propertyId: z.string().max(36).optional(), dueDate: z.string().optional(), + assigneeId: z.string().max(36).optional(), }); export type ActivityFormValues = z.infer;