first working release
This commit is contained in:
58
src/actions/cableActions.ts
Normal file
58
src/actions/cableActions.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
// Discriminated union return type allows the client to branch on success/failure
|
||||
export type CreateCableResult =
|
||||
| { success: true; cableId: number }
|
||||
| { success: false; error: string };
|
||||
|
||||
/**
|
||||
* Creates a new Cable and returns either the new cable's ID (on success)
|
||||
* or an error message (on validation/DB failure).
|
||||
* The client component handles navigation to the detail page on success.
|
||||
*/
|
||||
export async function createCable(
|
||||
formData: FormData
|
||||
): Promise<CreateCableResult> {
|
||||
const identifier = (formData.get("identifier") as string)?.trim();
|
||||
const description = (formData.get("description") as string)?.trim();
|
||||
const notes = (formData.get("notes") as string)?.trim();
|
||||
const startLocationId = Number(formData.get("startLocationId"));
|
||||
const endLocationId = Number(formData.get("endLocationId"));
|
||||
|
||||
if (!identifier) {
|
||||
return { success: false, error: "Bezeichnung ist erforderlich." };
|
||||
}
|
||||
if (!startLocationId || !endLocationId) {
|
||||
return { success: false, error: "Start- und End-Ort müssen ausgewählt werden." };
|
||||
}
|
||||
|
||||
try {
|
||||
const cable = await prisma.cable.create({
|
||||
data: {
|
||||
identifier,
|
||||
description: description || null,
|
||||
notes: notes || null,
|
||||
startLocationId,
|
||||
endLocationId,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath("/cables");
|
||||
return { success: true, cableId: cable.id };
|
||||
} catch (e) {
|
||||
console.error("createCable failed:", e);
|
||||
return { success: false, error: "Fehler beim Speichern des Kabels." };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a Cable and all its Cores (via Prisma's onDelete: Cascade).
|
||||
* Navigation back to /cables is handled by the client component.
|
||||
*/
|
||||
export async function deleteCable(id: number): Promise<void> {
|
||||
await prisma.cable.delete({ where: { id } });
|
||||
revalidatePath("/cables");
|
||||
}
|
||||
52
src/actions/coreActions.ts
Normal file
52
src/actions/coreActions.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
export type FormState = {
|
||||
error?: string;
|
||||
success?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a Core/Wire to an existing Cable. Designed for useActionState.
|
||||
*/
|
||||
export async function createCore(
|
||||
prevState: FormState,
|
||||
formData: FormData
|
||||
): Promise<FormState> {
|
||||
const cableId = Number(formData.get("cableId"));
|
||||
const color = (formData.get("color") as string)?.trim();
|
||||
const notes = (formData.get("notes") as string)?.trim();
|
||||
|
||||
if (!color) {
|
||||
return { error: "Farbe ist erforderlich." };
|
||||
}
|
||||
if (!cableId || isNaN(cableId)) {
|
||||
return { error: "Ungültige Kabel-ID." };
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.core.create({
|
||||
data: {
|
||||
cableId,
|
||||
color,
|
||||
notes: notes || null,
|
||||
},
|
||||
});
|
||||
|
||||
revalidatePath(`/cables/${cableId}`);
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
console.error("createCore failed:", e);
|
||||
return { error: "Fehler beim Speichern der Ader." };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single Core by ID.
|
||||
*/
|
||||
export async function deleteCore(id: number, cableId: number): Promise<void> {
|
||||
await prisma.core.delete({ where: { id } });
|
||||
revalidatePath(`/cables/${cableId}`);
|
||||
}
|
||||
52
src/actions/locationActions.ts
Normal file
52
src/actions/locationActions.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
"use server";
|
||||
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { revalidatePath } from "next/cache";
|
||||
|
||||
// Shared form state shape used with useActionState
|
||||
export type FormState = {
|
||||
error?: string;
|
||||
success?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new Location. Designed for use with React's useActionState hook.
|
||||
*/
|
||||
export async function createLocation(
|
||||
prevState: FormState,
|
||||
formData: FormData
|
||||
): Promise<FormState> {
|
||||
const name = (formData.get("name") as string)?.trim();
|
||||
const description = (formData.get("description") as string)?.trim();
|
||||
|
||||
if (!name) {
|
||||
return { error: "Name ist erforderlich." };
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.location.create({
|
||||
data: {
|
||||
name,
|
||||
description: description || null,
|
||||
},
|
||||
});
|
||||
|
||||
// Invalidate both pages that show location data
|
||||
revalidatePath("/locations");
|
||||
revalidatePath("/cables");
|
||||
return { success: true };
|
||||
} catch (e) {
|
||||
console.error("createLocation failed:", e);
|
||||
return { error: "Fehler beim Anlegen des Ortes. Bitte erneut versuchen." };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a Location by ID. The UI prevents calling this if the location
|
||||
* is still referenced by cables (enforced via disabled state on the button).
|
||||
*/
|
||||
export async function deleteLocation(id: number): Promise<void> {
|
||||
await prisma.location.delete({ where: { id } });
|
||||
revalidatePath("/locations");
|
||||
revalidatePath("/cables");
|
||||
}
|
||||
Reference in New Issue
Block a user