feat(recurring): düzenli işlem kuralları, planlı kayıt üretimi, onay/atla/ertele ve seri düzenleme
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
import { tr } from "@/i18n/tr";
|
||||
import { fail, ok, type ActionResult } from "@/lib/action-result";
|
||||
import { getHouseholdActionContext } from "@/lib/dal";
|
||||
import { confirmOccurrenceSchema, recurringRuleFormSchema } from "@/shared/schemas/recurring";
|
||||
import {
|
||||
confirmOccurrence,
|
||||
createRule,
|
||||
deleteRule,
|
||||
getRuleForm,
|
||||
postponeOccurrence,
|
||||
skipOccurrence,
|
||||
updateRule,
|
||||
type RuleFormValues,
|
||||
} from "./service";
|
||||
|
||||
const idSchema = z.string().regex(/^[a-f0-9]{24}$/i);
|
||||
const optionalId = idSchema.optional();
|
||||
const scopeSchema = z.enum(["all", "following"]);
|
||||
const ymd = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
||||
|
||||
function fieldErrors(error: z.ZodError) {
|
||||
const out: Record<string, string> = {};
|
||||
for (const issue of error.issues) {
|
||||
const key = issue.path.join(".");
|
||||
if (key && !out[key]) out[key] = issue.message;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function revalidate() {
|
||||
revalidatePath("/", "layout");
|
||||
}
|
||||
|
||||
export async function createRuleAction(input: unknown): Promise<ActionResult<{ id: string }>> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const parsed = recurringRuleFormSchema.safeParse(input);
|
||||
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
|
||||
const result = await createRule(ctx.user.id, ctx.household.id, parsed.data);
|
||||
if (result.ok) revalidate();
|
||||
return result.ok ? ok({ id: result.data.id }) : result;
|
||||
}
|
||||
|
||||
export async function updateRuleAction(
|
||||
ruleId: unknown,
|
||||
input: unknown,
|
||||
scope: unknown,
|
||||
fromTransactionId?: unknown,
|
||||
): Promise<ActionResult<{ id: string }>> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(ruleId);
|
||||
const s = scopeSchema.safeParse(scope);
|
||||
const from = optionalId.safeParse(fromTransactionId ?? undefined);
|
||||
if (!id.success || !s.success || !from.success) return fail(tr.common.notFound);
|
||||
const parsed = recurringRuleFormSchema.safeParse(input);
|
||||
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
|
||||
const result = await updateRule(
|
||||
ctx.user.id,
|
||||
ctx.household.id,
|
||||
id.data,
|
||||
parsed.data,
|
||||
s.data,
|
||||
from.data,
|
||||
);
|
||||
if (result.ok) revalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function deleteRuleAction(
|
||||
ruleId: unknown,
|
||||
scope: unknown,
|
||||
fromTransactionId?: unknown,
|
||||
): Promise<ActionResult<{ removed: number }>> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(ruleId);
|
||||
const s = scopeSchema.safeParse(scope);
|
||||
const from = optionalId.safeParse(fromTransactionId ?? undefined);
|
||||
if (!id.success || !s.success || !from.success) return fail(tr.common.notFound);
|
||||
const result = await deleteRule(ctx.user.id, ctx.household.id, id.data, s.data, from.data);
|
||||
if (result.ok) revalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getRuleFormAction(ruleId: unknown): Promise<ActionResult<RuleFormValues>> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(ruleId);
|
||||
if (!id.success) return fail(tr.common.notFound);
|
||||
const values = await getRuleForm(ctx.user.id, ctx.household.id, id.data);
|
||||
return values ? ok(values) : fail(tr.common.notFound);
|
||||
}
|
||||
|
||||
export async function confirmOccurrenceAction(
|
||||
transactionId: unknown,
|
||||
input: unknown,
|
||||
): Promise<ActionResult> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(transactionId);
|
||||
if (!id.success) return fail(tr.common.notFound);
|
||||
const parsed = confirmOccurrenceSchema.safeParse(input);
|
||||
if (!parsed.success) return fail(tr.adminUsers.errors.checkFields, fieldErrors(parsed.error));
|
||||
const result = await confirmOccurrence(ctx.user.id, ctx.household.id, id.data, parsed.data);
|
||||
if (result.ok) revalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function skipOccurrenceAction(transactionId: unknown): Promise<ActionResult> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(transactionId);
|
||||
if (!id.success) return fail(tr.common.notFound);
|
||||
const result = await skipOccurrence(ctx.user.id, ctx.household.id, id.data);
|
||||
if (result.ok) revalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function postponeOccurrenceAction(
|
||||
transactionId: unknown,
|
||||
date: unknown,
|
||||
): Promise<ActionResult> {
|
||||
const ctx = await getHouseholdActionContext("member");
|
||||
if (!ctx) return fail(tr.common.unauthorized);
|
||||
const id = idSchema.safeParse(transactionId);
|
||||
const d = ymd.safeParse(date);
|
||||
if (!id.success) return fail(tr.common.notFound);
|
||||
if (!d.success) return fail(tr.validation.date, { date: tr.validation.date });
|
||||
const result = await postponeOccurrence(ctx.user.id, ctx.household.id, id.data, d.data);
|
||||
if (result.ok) revalidate();
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user