diff --git a/src/app/(dashboard)/jobs/[jobId]/components/job-actions-panel.tsx b/src/app/(dashboard)/jobs/[jobId]/components/job-actions-panel.tsx
index b4c54ca..a5744a6 100644
--- a/src/app/(dashboard)/jobs/[jobId]/components/job-actions-panel.tsx
+++ b/src/app/(dashboard)/jobs/[jobId]/components/job-actions-panel.tsx
@@ -9,6 +9,7 @@ import {
Loader2,
PackageCheck,
Play,
+ RotateCcw,
Send,
X,
} from "lucide-react";
@@ -32,6 +33,7 @@ import {
cancelJobAction,
handToClinicAction,
markDeliveredAction,
+ requestRevisionAction,
} from "@/lib/appwrite/job-actions";
import { initialJobActionState } from "@/lib/appwrite/job-types";
import type { Job, TenantKind } from "@/lib/appwrite/schema";
@@ -67,7 +69,10 @@ export function JobActionsPanel({
{/* Clinic finished the prova — approve and send back to lab */}
{isClinic && job.status === "in_progress" && isAtClinic && (
-
+ <>
+
+
+ >
)}
{/* Final delivery — clinic took it from the lab */}
@@ -236,6 +241,68 @@ function ApproveAtClinicButton({ job }: { job: Job }) {
);
}
+function RequestRevisionButton({ job }: { job: Job }) {
+ const router = useRouter();
+ const [state, action, pending] = useActionState(
+ requestRevisionAction,
+ initialJobActionState,
+ );
+ const [open, setOpen] = useState(false);
+
+ useEffect(() => {
+ if (state.ok) {
+ toast.success("Düzeltme talebi gönderildi.");
+ setOpen(false);
+ router.refresh();
+ } else if (state.error) {
+ toast.error(state.error);
+ }
+ }, [state, router]);
+
+ return (
+
+ );
+}
+
function DeliverButton({ jobId }: { jobId: string }) {
const router = useRouter();
const [state, action, pending] = useActionState(markDeliveredAction, initialJobActionState);
diff --git a/src/lib/appwrite/job-actions.ts b/src/lib/appwrite/job-actions.ts
index b013168..2ebffaf 100644
--- a/src/lib/appwrite/job-actions.ts
+++ b/src/lib/appwrite/job-actions.ts
@@ -522,6 +522,95 @@ export async function approveAtClinicAction(
return { ok: true };
}
+/**
+ * Clinic rejects the prova and asks the lab to redo this stage. The job
+ * goes back to the lab without advancing the step, so the same prova
+ * stage will repeat after the lab finishes the rework. A note explaining
+ * what's wrong is required — there's no point bouncing a case back
+ * without telling the lab what to fix.
+ */
+export async function requestRevisionAction(
+ _prev: JobActionState,
+ formData: FormData,
+): Promise {
+ const jobId = String(formData.get("jobId") ?? "").trim();
+ if (!jobId) return { ok: false, error: "İş bulunamadı." };
+ const note = String(formData.get("note") ?? "").trim();
+ if (!note) {
+ return {
+ ok: false,
+ error: "Düzeltme talebi için neyin yanlış olduğunu yazmanız gerek.",
+ };
+ }
+
+ let ctx;
+ try {
+ ctx = await requireTenant();
+ requireRole(ctx, ["owner", "admin", "member"]);
+ requireTenantKind(ctx, ["clinic"]);
+ } catch {
+ return { ok: false, error: "Düzeltme talebini yalnızca klinik açabilir." };
+ }
+
+ const job = await loadJobForTenant(jobId, ctx.tenantId);
+ if (!job || job.clinicTenantId !== ctx.tenantId) {
+ return { ok: false, error: "İş bulunamadı." };
+ }
+ if (job.status !== "in_progress") {
+ return { ok: false, error: "Yalnızca işlemdeki provalar için düzeltme istenebilir." };
+ }
+ if (job.location !== "at_clinic") {
+ return { ok: false, error: "İş şu an klinikte değil." };
+ }
+ if (!job.currentStep) {
+ return { ok: false, error: "Mevcut aşama bilinmiyor." };
+ }
+
+ try {
+ const { tablesDB } = createAdminClient();
+ await tablesDB.updateRow(DATABASE_ID, TABLES.jobs, jobId, {
+ location: "at_lab",
+ // currentStep stays the same — the lab will rework this stage.
+ });
+ await appendJobHistory({
+ job,
+ step: job.currentStep,
+ completedBy: ctx.user.id,
+ note: `[Düzeltme talebi] ${note}`,
+ });
+ void logAudit({
+ tenantId: ctx.tenantId,
+ userId: ctx.user.id,
+ action: "update",
+ entityType: "job",
+ entityId: jobId,
+ changes: {
+ location: "at_lab",
+ revisionRequestedAtStep: job.currentStep,
+ note,
+ },
+ });
+ const stepLabel =
+ job.currentStep === "alt_yapi_prova"
+ ? "alt yapı"
+ : job.currentStep === "ust_yapi_prova"
+ ? "üst yapı"
+ : "cila/bitim";
+ void createNotification({
+ tenantId: job.labTenantId,
+ jobId,
+ message: `Hasta ${job.patientCode} ${stepLabel} provası için düzeltme istendi: ${note.slice(0, 120)}`,
+ });
+ } catch (e) {
+ return { ok: false, error: appwriteError(e, "Düzeltme talebi gönderilemedi.") };
+ }
+
+ revalidatePath(`/jobs/${jobId}`);
+ revalidatePath("/jobs/inbound");
+ revalidatePath("/jobs/outbound");
+ return { ok: true };
+}
+
export async function markDeliveredAction(
_prev: JobActionState,
formData: FormData,