feat(backend): migrate freelancer and portal runtimes
This commit is contained in:
@@ -1,49 +1,26 @@
|
||||
"use server";
|
||||
|
||||
import { createClient } from "@/lib/supabase/server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { cleanText, optionalDate, requiredText } from "@/server/web/form-data";
|
||||
import { requireFreelancerBackend } from "@/server/web/freelancer";
|
||||
|
||||
function cleanText(value: FormDataEntryValue | null) {
|
||||
const text = typeof value === "string" ? value.trim() : "";
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
const ACTIVITY_TYPES = ["note", "call", "meeting", "email"] as const;
|
||||
|
||||
export async function addClientActivity(clientId: string, formData: FormData) {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
data: { user },
|
||||
error: userError,
|
||||
} = await supabase.auth.getUser();
|
||||
const { actor, service } = await requireFreelancerBackend();
|
||||
const rawType = cleanText(formData.get("type"));
|
||||
const type = rawType && ACTIVITY_TYPES.includes(rawType as (typeof ACTIVITY_TYPES)[number])
|
||||
? rawType as (typeof ACTIVITY_TYPES)[number]
|
||||
: "note";
|
||||
|
||||
if (userError || !user) {
|
||||
throw new Error("Kullanıcı bulunamadı.");
|
||||
}
|
||||
|
||||
const title = cleanText(formData.get("title"));
|
||||
if (!title) {
|
||||
throw new Error("Aktivite başlığı zorunludur.");
|
||||
}
|
||||
|
||||
const { error } = await supabase.from("client_activities").insert({
|
||||
user_id: user.id,
|
||||
client_id: clientId,
|
||||
type: formData.get("type") as string || "note",
|
||||
title,
|
||||
service.addClientActivity(actor, {
|
||||
clientId,
|
||||
type,
|
||||
title: requiredText(formData.get("title"), "Aktivite başlığı zorunludur."),
|
||||
content: cleanText(formData.get("content")),
|
||||
activity_date: formData.get("activity_date") as string || new Date().toISOString(),
|
||||
activityDate: optionalDate(formData.get("activity_date")) ?? new Date(),
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Aktivite eklenemedi: ${error.message}`);
|
||||
}
|
||||
|
||||
// Update client's last_contact_date
|
||||
await supabase
|
||||
.from("clients")
|
||||
.update({ last_contact_date: new Date().toISOString() })
|
||||
.eq("id", clientId)
|
||||
.eq("user_id", user.id);
|
||||
|
||||
revalidatePath(`/clients/${clientId}`);
|
||||
revalidatePath(`/clients`);
|
||||
revalidatePath("/clients");
|
||||
}
|
||||
|
||||
@@ -1,34 +1,41 @@
|
||||
import { createClient } from "@/lib/supabase/server";
|
||||
import { ClientDetailClient, type ClientDetailData, type ClientActivity } from "./client-detail-client";
|
||||
import { notFound } from "next/navigation";
|
||||
import { ClientDetailClient, type ClientDetailData, type ClientActivity } from "./client-detail-client";
|
||||
import { DomainError } from "@/server/domain/errors";
|
||||
import { requireFreelancerBackend } from "@/server/web/freelancer";
|
||||
|
||||
export default async function ClientDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const supabase = await createClient();
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
const { actor, service } = await requireFreelancerBackend();
|
||||
|
||||
if (!user) return null;
|
||||
let data: { client: ClientDetailData; activities: ClientActivity[] };
|
||||
try {
|
||||
const row = service.getClient(actor, id);
|
||||
const client: ClientDetailData = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
company_name: row.companyName,
|
||||
email: row.email,
|
||||
phone: row.phone,
|
||||
website: row.website,
|
||||
pipeline_stage: row.pipelineStage,
|
||||
status: row.status,
|
||||
notes: row.notes,
|
||||
client_auth_id: row.authUserId,
|
||||
};
|
||||
const activities: ClientActivity[] = service.listClientActivities(actor, id).map((activity) => ({
|
||||
id: activity.id,
|
||||
type: activity.type,
|
||||
title: activity.title,
|
||||
content: activity.content,
|
||||
activity_date: activity.activityDate.toISOString(),
|
||||
created_at: activity.createdAt.toISOString(),
|
||||
}));
|
||||
|
||||
const { data: clientData, error } = await supabase
|
||||
.from("clients")
|
||||
.select("id, name, company_name, email, phone, website, pipeline_stage, status, notes, client_auth_id")
|
||||
.eq("id", id)
|
||||
.eq("user_id", user.id)
|
||||
.single();
|
||||
|
||||
if (error || !clientData) {
|
||||
notFound();
|
||||
data = { client, activities };
|
||||
} catch (error) {
|
||||
if (error instanceof DomainError && error.code === "NOT_FOUND") notFound();
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { data: activitiesData } = await supabase
|
||||
.from("client_activities")
|
||||
.select("id, type, title, content, activity_date, created_at")
|
||||
.eq("client_id", id)
|
||||
.eq("user_id", user.id)
|
||||
.order("activity_date", { ascending: false });
|
||||
|
||||
const client: ClientDetailData = clientData as ClientDetailData;
|
||||
const activities: ClientActivity[] = (activitiesData || []) as ClientActivity[];
|
||||
|
||||
return <ClientDetailClient client={client} activities={activities} />;
|
||||
return <ClientDetailClient client={data.client} activities={data.activities} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user