feat: leads module — kanban board, aktiviteler, takvim entegrasyonu, müşteriye dönüştür
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { ID, Permission, Role } from "node-appwrite";
|
||||
|
||||
import { DATABASE_ID, TABLES, type Lead, type LeadActivityType } from "./schema";
|
||||
import { createAdminClient } from "./server";
|
||||
import { requireTenant } from "./tenant-guard";
|
||||
|
||||
export type ActivityActionState = { ok: boolean; error?: string };
|
||||
|
||||
export async function addLeadActivityAction(
|
||||
_prev: ActivityActionState,
|
||||
formData: FormData,
|
||||
): Promise<ActivityActionState> {
|
||||
const leadId = String(formData.get("leadId") ?? "");
|
||||
const type = String(formData.get("type") ?? "note") as LeadActivityType;
|
||||
const content = String(formData.get("content") ?? "").trim();
|
||||
const occurredAt = String(formData.get("occurredAt") ?? "") || new Date().toISOString();
|
||||
|
||||
if (!leadId || !content) return { ok: false, error: "Zorunlu alanlar eksik." };
|
||||
|
||||
let ctx;
|
||||
try {
|
||||
ctx = await requireTenant();
|
||||
} catch {
|
||||
return { ok: false, error: "Yetkiniz yok." };
|
||||
}
|
||||
|
||||
try {
|
||||
const { tablesDB } = createAdminClient();
|
||||
const lead = (await tablesDB.getRow(DATABASE_ID, TABLES.leads, leadId)) as unknown as Lead;
|
||||
if (lead.tenantId !== ctx.tenantId) return { ok: false, error: "Erişim engellendi." };
|
||||
|
||||
await tablesDB.createRow(
|
||||
DATABASE_ID,
|
||||
TABLES.leadActivities,
|
||||
ID.unique(),
|
||||
{
|
||||
tenantId: ctx.tenantId,
|
||||
createdBy: ctx.user.id,
|
||||
leadId,
|
||||
type,
|
||||
content,
|
||||
occurredAt,
|
||||
},
|
||||
[
|
||||
Permission.read(Role.team(ctx.tenantId)),
|
||||
Permission.update(Role.team(ctx.tenantId)),
|
||||
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
||||
],
|
||||
);
|
||||
|
||||
// Update lastContactAt on the lead
|
||||
await tablesDB.updateRow(DATABASE_ID, TABLES.leads, leadId, {
|
||||
lastContactAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
revalidatePath("/leads");
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
return { ok: false, error: e instanceof Error ? e.message : "Hata oluştu." };
|
||||
}
|
||||
}
|
||||
|
||||
export async function scheduleFollowUpAction(
|
||||
_prev: ActivityActionState,
|
||||
formData: FormData,
|
||||
): Promise<ActivityActionState> {
|
||||
const leadId = String(formData.get("leadId") ?? "");
|
||||
const followUpAt = String(formData.get("followUpAt") ?? "");
|
||||
const note = String(formData.get("note") ?? "").trim();
|
||||
|
||||
if (!leadId || !followUpAt) return { ok: false, error: "Tarih seçin." };
|
||||
|
||||
let ctx;
|
||||
try {
|
||||
ctx = await requireTenant();
|
||||
} catch {
|
||||
return { ok: false, error: "Yetkiniz yok." };
|
||||
}
|
||||
|
||||
try {
|
||||
const { tablesDB } = createAdminClient();
|
||||
const lead = (await tablesDB.getRow(DATABASE_ID, TABLES.leads, leadId)) as unknown as Lead;
|
||||
if (lead.tenantId !== ctx.tenantId) return { ok: false, error: "Erişim engellendi." };
|
||||
|
||||
const followUpDate = new Date(followUpAt);
|
||||
const endDate = new Date(followUpDate.getTime() + 60 * 60 * 1000); // +1h
|
||||
|
||||
const permissions = [
|
||||
Permission.read(Role.team(ctx.tenantId)),
|
||||
Permission.update(Role.team(ctx.tenantId)),
|
||||
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
||||
];
|
||||
|
||||
// Create calendar event
|
||||
const event = await tablesDB.createRow(
|
||||
DATABASE_ID,
|
||||
TABLES.calendarEvents,
|
||||
ID.unique(),
|
||||
{
|
||||
tenantId: ctx.tenantId,
|
||||
createdBy: ctx.user.id,
|
||||
title: `Takip: ${lead.contactName || lead.name}`,
|
||||
description: note || `Lead takip görüşmesi — ${lead.name}`,
|
||||
start: followUpDate.toISOString(),
|
||||
end: endDate.toISOString(),
|
||||
allDay: false,
|
||||
leadId,
|
||||
color: "#f97316", // orange — lead events
|
||||
},
|
||||
permissions,
|
||||
);
|
||||
|
||||
// Update lead nextFollowUpAt + calendarEventId
|
||||
await tablesDB.updateRow(DATABASE_ID, TABLES.leads, leadId, {
|
||||
nextFollowUpAt: followUpDate.toISOString(),
|
||||
calendarEventId: event.$id,
|
||||
});
|
||||
|
||||
// Log activity
|
||||
await tablesDB.createRow(
|
||||
DATABASE_ID,
|
||||
TABLES.leadActivities,
|
||||
ID.unique(),
|
||||
{
|
||||
tenantId: ctx.tenantId,
|
||||
createdBy: ctx.user.id,
|
||||
leadId,
|
||||
type: "meeting",
|
||||
content: `Takip planlandı: ${followUpDate.toLocaleString("tr-TR")}${note ? ` — ${note}` : ""}`,
|
||||
calendarEventId: event.$id,
|
||||
occurredAt: new Date().toISOString(),
|
||||
},
|
||||
permissions,
|
||||
);
|
||||
|
||||
revalidatePath("/leads");
|
||||
revalidatePath("/calendar");
|
||||
return { ok: true };
|
||||
} catch (e) {
|
||||
return { ok: false, error: e instanceof Error ? e.message : "Hata oluştu." };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user