4ef0482732
- Server actions: property/customer/search/presentation/activity/investor CRUD - Matching engine: matchPropertyToSearches + syncMatchesForSearch on search save - UI: form sheets + table clients for all modules - Public /sunum/[token] page (no auth) with property card grid + expiry check - All pages force-dynamic for auth guard compatibility
144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
"use server";
|
||
|
||
import { ID, Permission, Role } from "node-appwrite";
|
||
import { revalidatePath } from "next/cache";
|
||
|
||
import { propertySchema } from "@/lib/validation/properties";
|
||
import { DATABASE_ID, TABLES, type Property } from "./schema";
|
||
import { createAdminClient } from "./server";
|
||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||
import { matchPropertyToSearches } from "./matching";
|
||
|
||
type ActionState = { ok: boolean; error?: string; fieldErrors?: Record<string, string[]> };
|
||
|
||
export async function createPropertyAction(
|
||
_prev: ActionState,
|
||
formData: FormData,
|
||
): Promise<ActionState> {
|
||
const ctx = await requireTenant();
|
||
const raw = Object.fromEntries(formData.entries());
|
||
const parsed = propertySchema.safeParse(raw);
|
||
if (!parsed.success) {
|
||
return { ok: false, fieldErrors: parsed.error.flatten().fieldErrors };
|
||
}
|
||
|
||
const { tablesDB } = createAdminClient();
|
||
const id = ID.unique();
|
||
const data = parsed.data;
|
||
|
||
try {
|
||
const row = await tablesDB.createRow(
|
||
DATABASE_ID,
|
||
TABLES.properties,
|
||
id,
|
||
{
|
||
tenantId: ctx.tenantId,
|
||
title: data.title,
|
||
description: data.description,
|
||
propertyType: data.propertyType,
|
||
listingType: data.listingType,
|
||
status: data.status ?? "aktif",
|
||
price: data.price,
|
||
currency: data.currency ?? "TRY",
|
||
roomCount: data.roomCount,
|
||
grossM2: data.grossM2,
|
||
netM2: data.netM2,
|
||
floor: data.floor,
|
||
totalFloors: data.totalFloors,
|
||
buildingAge: data.buildingAge,
|
||
city: data.city,
|
||
district: data.district,
|
||
neighborhood: data.neighborhood,
|
||
address: data.address,
|
||
mapLat: data.mapLat,
|
||
mapLng: data.mapLng,
|
||
featuresJson: data.featuresJson,
|
||
imageIds: data.imageIds,
|
||
createdBy: ctx.user.id,
|
||
assigneeId: data.assigneeId,
|
||
},
|
||
[
|
||
Permission.read(Role.team(ctx.tenantId)),
|
||
Permission.update(Role.team(ctx.tenantId)),
|
||
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
||
Permission.delete(Role.team(ctx.tenantId, "admin")),
|
||
],
|
||
);
|
||
|
||
await matchPropertyToSearches(row as unknown as Property, ctx.tenantId, ctx.user.id).catch(
|
||
() => {},
|
||
);
|
||
} catch {
|
||
return { ok: false, error: "İlan oluşturulamadı." };
|
||
}
|
||
|
||
revalidatePath("/properties");
|
||
return { ok: true };
|
||
}
|
||
|
||
export async function updatePropertyAction(
|
||
id: string,
|
||
_prev: ActionState,
|
||
formData: FormData,
|
||
): Promise<ActionState> {
|
||
const ctx = await requireTenant();
|
||
const raw = Object.fromEntries(formData.entries());
|
||
const parsed = propertySchema.safeParse(raw);
|
||
if (!parsed.success) {
|
||
return { ok: false, fieldErrors: parsed.error.flatten().fieldErrors };
|
||
}
|
||
|
||
const { tablesDB } = createAdminClient();
|
||
const data = parsed.data;
|
||
|
||
try {
|
||
const row = await tablesDB.updateRow(DATABASE_ID, TABLES.properties, id, {
|
||
title: data.title,
|
||
description: data.description,
|
||
propertyType: data.propertyType,
|
||
listingType: data.listingType,
|
||
status: data.status,
|
||
price: data.price,
|
||
currency: data.currency,
|
||
roomCount: data.roomCount,
|
||
grossM2: data.grossM2,
|
||
netM2: data.netM2,
|
||
floor: data.floor,
|
||
totalFloors: data.totalFloors,
|
||
buildingAge: data.buildingAge,
|
||
city: data.city,
|
||
district: data.district,
|
||
neighborhood: data.neighborhood,
|
||
address: data.address,
|
||
mapLat: data.mapLat,
|
||
mapLng: data.mapLng,
|
||
featuresJson: data.featuresJson,
|
||
imageIds: data.imageIds,
|
||
assigneeId: data.assigneeId,
|
||
});
|
||
|
||
await matchPropertyToSearches(row as unknown as Property, ctx.tenantId, ctx.user.id).catch(
|
||
() => {},
|
||
);
|
||
} catch {
|
||
return { ok: false, error: "İlan güncellenemedi." };
|
||
}
|
||
|
||
revalidatePath("/properties");
|
||
return { ok: true };
|
||
}
|
||
|
||
export async function deletePropertyAction(id: string): Promise<ActionState> {
|
||
await requireTenant();
|
||
const { tablesDB } = createAdminClient();
|
||
|
||
try {
|
||
await tablesDB.deleteRow(DATABASE_ID, TABLES.properties, id);
|
||
} catch {
|
||
return { ok: false, error: "İlan silinemedi." };
|
||
}
|
||
|
||
revalidatePath("/properties");
|
||
return { ok: true };
|
||
}
|