257 lines
7.2 KiB
Vue
257 lines
7.2 KiB
Vue
<script setup>
|
|
import AppLayout from "@/Layouts/AppLayout.vue";
|
|
import AppCard from "@/Components/app/ui/card/AppCard.vue";
|
|
import CardTitle from "@/Components/ui/card/CardTitle.vue";
|
|
import { Alert, AlertDescription, AlertTitle } from "@/Components/ui/alert";
|
|
import { useForm, router } from "@inertiajs/vue3";
|
|
import { ref } from "vue";
|
|
import { Archive } from "lucide-vue-next";
|
|
import ArchiveRuleCard from "./Partials/ArchiveRuleCard.vue";
|
|
import CreateRuleForm from "./Partials/CreateRuleForm.vue";
|
|
import EditRuleForm from "./Partials/EditRuleForm.vue";
|
|
|
|
const props = defineProps({
|
|
settings: Object,
|
|
archiveEntities: Array,
|
|
actions: Array,
|
|
segments: Array,
|
|
chainPatterns: Array,
|
|
});
|
|
|
|
const newForm = useForm({
|
|
name: "",
|
|
description: "",
|
|
enabled: true,
|
|
strategy: "immediate",
|
|
soft: true,
|
|
reactivate: false,
|
|
focus: "",
|
|
related: [],
|
|
entities: [],
|
|
action_id: null,
|
|
decision_id: null,
|
|
segment_id: null,
|
|
options: { batch_size: 200 },
|
|
});
|
|
|
|
// Editing state & form
|
|
const editingSetting = ref(null);
|
|
const originalEntityMeta = ref({ columns: ["id"] });
|
|
const editForm = useForm({
|
|
name: "",
|
|
description: "",
|
|
enabled: true,
|
|
strategy: "immediate",
|
|
soft: true,
|
|
reactivate: false,
|
|
focus: "",
|
|
related: [],
|
|
entities: [],
|
|
action_id: null,
|
|
decision_id: null,
|
|
segment_id: null,
|
|
options: { batch_size: 200 },
|
|
});
|
|
|
|
function submitCreate() {
|
|
if (!newForm.focus) {
|
|
alert("Select a focus entity.");
|
|
return;
|
|
}
|
|
if (newForm.decision_id && !newForm.action_id) {
|
|
alert("Select an action before choosing a decision.");
|
|
return;
|
|
}
|
|
newForm.entities = [
|
|
{
|
|
table: newForm.focus,
|
|
related: newForm.related,
|
|
columns: ["id"],
|
|
},
|
|
];
|
|
newForm.post(route("settings.archive.store"), {
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
newForm.focus = "";
|
|
newForm.related = [];
|
|
newForm.entities = [];
|
|
newForm.action_id = null;
|
|
newForm.decision_id = null;
|
|
newForm.segment_id = null;
|
|
newForm.reset();
|
|
},
|
|
});
|
|
}
|
|
|
|
function toggleEnabled(setting) {
|
|
router.put(
|
|
route("settings.archive.update", setting.id),
|
|
{
|
|
...setting,
|
|
enabled: !setting.enabled,
|
|
},
|
|
{
|
|
preserveScroll: true,
|
|
}
|
|
);
|
|
}
|
|
|
|
function startEdit(setting) {
|
|
editingSetting.value = setting;
|
|
editForm.name = setting.name || "";
|
|
editForm.description = setting.description || "";
|
|
editForm.enabled = setting.enabled;
|
|
editForm.strategy = setting.strategy || "immediate";
|
|
editForm.soft = setting.soft;
|
|
editForm.reactivate = setting.reactivate ?? false;
|
|
editForm.action_id = setting.action_id ?? null;
|
|
editForm.decision_id = setting.decision_id ?? null;
|
|
editForm.segment_id = setting.segment_id ?? null;
|
|
|
|
const first = Array.isArray(setting.entities) ? setting.entities[0] : null;
|
|
if (first) {
|
|
editForm.focus = first.table || "";
|
|
editForm.related = first.related || [];
|
|
originalEntityMeta.value = {
|
|
columns: first.columns || ["id"],
|
|
};
|
|
} else {
|
|
editForm.focus = "";
|
|
editForm.related = [];
|
|
originalEntityMeta.value = { columns: ["id"] };
|
|
}
|
|
}
|
|
|
|
function cancelEdit() {
|
|
editingSetting.value = null;
|
|
editForm.reset();
|
|
}
|
|
|
|
function submitUpdate() {
|
|
if (!editingSetting.value) return;
|
|
if (!editForm.focus) {
|
|
alert("Select a focus entity.");
|
|
return;
|
|
}
|
|
if (editForm.decision_id && !editForm.action_id) {
|
|
alert("Select an action before choosing a decision.");
|
|
return;
|
|
}
|
|
editForm.entities = [
|
|
{
|
|
table: editForm.focus,
|
|
related: editForm.related,
|
|
columns: originalEntityMeta.value.columns || ["id"],
|
|
},
|
|
];
|
|
editForm.put(route("settings.archive.update", editingSetting.value.id), {
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
cancelEdit();
|
|
},
|
|
});
|
|
}
|
|
|
|
function remove(setting) {
|
|
if (!confirm("Delete archive rule?")) return;
|
|
router.delete(route("settings.archive.destroy", setting.id), {
|
|
preserveScroll: true,
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AppLayout title="Archive Settings">
|
|
<template #header />
|
|
<div class="pt-12">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<Alert variant="default" class="mb-6 border-l-4 border-amber-500">
|
|
<AlertTitle class="text-sm font-medium text-amber-800">
|
|
Archive rule conditions are temporarily inactive
|
|
</AlertTitle>
|
|
<AlertDescription class="text-xs text-amber-800 space-y-2 mt-2">
|
|
<p>
|
|
All enabled rules apply to the focus entity and its selected related tables
|
|
without date/other filters. Stored condition JSON is preserved for future
|
|
reactivation.
|
|
</p>
|
|
<p class="font-medium">The "Run Now" action is currently disabled.</p>
|
|
<div class="mt-3 bg-white/60 rounded p-3 border border-amber-200">
|
|
<p class="font-semibold mb-1 text-amber-900">Chain Path Help</p>
|
|
<p class="mb-1">Supported chained related tables (dot notation):</p>
|
|
<ul class="list-disc ml-4 space-y-0.5">
|
|
<li v-for="cp in chainPatterns" :key="cp">
|
|
<code class="px-1 bg-amber-100 rounded text-xs">{{ cp }}</code>
|
|
</li>
|
|
</ul>
|
|
<p class="mt-1 italic">Only these chains are processed; others are ignored.</p>
|
|
</div>
|
|
</AlertDescription>
|
|
</Alert>
|
|
|
|
<div class="grid gap-6 md:grid-cols-3">
|
|
<div class="md:col-span-2 space-y-4">
|
|
<AppCard
|
|
title=""
|
|
padding="none"
|
|
class="p-0! gap-0"
|
|
header-class="py-3! px-4 gap-0 text-muted-foreground"
|
|
>
|
|
<template #header>
|
|
<div class="flex items-center gap-2">
|
|
<Archive :size="18" />
|
|
<CardTitle class="uppercase">Archive Rules</CardTitle>
|
|
</div>
|
|
</template>
|
|
<div class="p-4 space-y-4">
|
|
<ArchiveRuleCard
|
|
v-for="rule in settings.data"
|
|
:key="rule.id"
|
|
:rule="rule"
|
|
@edit="startEdit"
|
|
@toggle-enabled="toggleEnabled"
|
|
@delete="remove"
|
|
/>
|
|
<div
|
|
v-if="!settings.data.length"
|
|
class="text-sm text-muted-foreground text-center py-8"
|
|
>
|
|
No archive rules defined yet.
|
|
</div>
|
|
</div>
|
|
</AppCard>
|
|
</div>
|
|
|
|
<div>
|
|
<CreateRuleForm
|
|
v-if="!editingSetting"
|
|
:form="newForm"
|
|
:archive-entities="archiveEntities"
|
|
:actions="actions"
|
|
:segments="segments"
|
|
@submit="submitCreate"
|
|
/>
|
|
<EditRuleForm
|
|
v-else
|
|
:form="editForm"
|
|
:setting="editingSetting"
|
|
:archive-entities="archiveEntities"
|
|
:actions="actions"
|
|
:segments="segments"
|
|
@submit="submitUpdate"
|
|
@cancel="cancelEdit"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
pre {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
|
monospace;
|
|
}
|
|
</style>
|