Teren-app/resources/js/Pages/Settings/Workflow/Partials/ActionTable.vue
2026-01-02 12:32:20 +01:00

407 lines
11 KiB
Vue

<script setup>
// flowbite-vue table imports removed; using DataTableClient
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/Components/ui/dialog";
import {
AlertDialog,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogFooter,
} from "@/Components/ui/alert-dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/Components/ui/dropdown-menu";
import { computed, onMounted, ref } from "vue";
import { router, useForm } from "@inertiajs/vue3";
import InputLabel from "@/Components/InputLabel.vue";
import { Input } from "@/Components/ui/input";
import AppCombobox from "@/Components/app/ui/AppCombobox.vue";
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
import { Button } from "@/Components/ui/button";
import DataTableClient from "@/Components/DataTable/DataTableClient.vue";
import InlineColorPicker from "@/Components/InlineColorPicker.vue";
import AppPopover from "@/Components/app/ui/AppPopover.vue";
import { FilterIcon, MoreHorizontal, Pencil, Trash } from "lucide-vue-next";
const props = defineProps({
actions: Array,
decisions: Array,
segments: Array,
});
const drawerEdit = ref(false);
const drawerCreate = ref(false);
const showDelete = ref(false);
const toDelete = ref(null);
const search = ref("");
const selectedSegment = ref(null);
const selectOptions = computed(() =>
props.decisions.map((d) => ({
label: d.name,
value: d.id,
}))
);
const segmentOptions = computed(() =>
props.segments.map((d) => ({
label: d.name,
value: d.id,
}))
);
// DataTable state
const sort = ref({ key: null, direction: null });
const page = ref(1);
const pageSize = ref(25);
const columns = [
{ key: "id", label: "#", sortable: true, class: "w-16" },
{ key: "name", label: "Ime", sortable: true },
{ key: "color_tag", label: "Barva", sortable: false },
{ key: "segment", label: "Segment", sortable: false },
{ key: "decisions", label: "Odločitve", sortable: false, class: "w-32" },
];
const form = useForm({
id: 0,
name: "",
color_tag: "",
segment_id: null,
decisions: [],
});
const createForm = useForm({
name: "",
color_tag: "",
segment_id: null,
decisions: [],
});
const openEditDrawer = (item) => {
form.decisions = [];
form.id = item.id;
form.name = item.name;
form.color_tag = item.color_tag;
form.segment_id = item.segment ? item.segment.id : null;
drawerEdit.value = true;
// AppMultiSelect expects array of values
form.decisions = item.decisions.map((d) => d.id);
};
const closeEditDrawer = () => {
drawerEdit.value = false;
form.reset();
};
const openCreateDrawer = () => {
createForm.reset();
drawerCreate.value = true;
};
const closeCreateDrawer = () => {
drawerCreate.value = false;
createForm.reset();
};
const filtered = computed(() => {
const term = search.value?.toLowerCase() ?? "";
return (props.actions || []).filter((a) => {
const matchesSearch =
!term ||
a.name?.toLowerCase().includes(term) ||
a.color_tag?.toLowerCase().includes(term);
const matchesSegment =
!selectedSegment.value || a.segment?.id === selectedSegment.value;
return matchesSearch && matchesSegment;
});
});
const update = () => {
// Transform decisions from array of IDs to array of objects
const decisionsPayload = form.decisions
.map((id) => {
const decision = props.decisions.find((d) => d.id === Number(id) || d.id === id);
if (!decision) {
console.warn("Decision not found for id:", id);
return null;
}
return { id: decision.id, name: decision.name };
})
.filter(Boolean); // Remove null entries
form
.transform((data) => ({
...data,
decisions: decisionsPayload,
}))
.put(route("settings.actions.update", { id: form.id }), {
onSuccess: () => {
closeEditDrawer();
},
});
};
const store = () => {
// Transform decisions from array of IDs to array of objects
const decisionsPayload = createForm.decisions
.map((id) => {
const decision = props.decisions.find((d) => d.id === Number(id) || d.id === id);
if (!decision) {
console.warn("Decision not found for id:", id);
return null;
}
return { id: decision.id, name: decision.name };
})
.filter(Boolean); // Remove null entries
createForm
.transform((data) => ({
...data,
decisions: decisionsPayload,
}))
.post(route("settings.actions.store"), {
onSuccess: () => {
closeCreateDrawer();
},
});
};
const confirmDelete = (action) => {
toDelete.value = action;
showDelete.value = true;
};
const cancelDelete = () => {
toDelete.value = null;
showDelete.value = false;
};
const destroyAction = () => {
if (!toDelete.value) return;
router.delete(route("settings.actions.destroy", { id: toDelete.value.id }), {
preserveScroll: true,
onFinish: () => cancelDelete(),
});
};
</script>
<template>
<div class="p-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="flex gap-3 items-center">
<AppPopover align="start" side="bottom" content-class="w-80">
<template #trigger>
<Button variant="outline" size="sm">
<FilterIcon class="w-4 h-4 mr-2" />
Filtri
</Button>
</template>
<div class="p-1">
<div>
<InputLabel for="searchFilter" value="Iskanje" class="mb-1" />
<Input
id="searchFilter"
v-model="search"
placeholder="Iskanje..."
class="w-full"
/>
</div>
<div>
<InputLabel for="segmentFilter" value="Segment" class="mb-1" />
<AppCombobox
id="segmentFilter"
v-model="selectedSegment"
:items="segmentOptions"
placeholder="Filter po segmentu"
button-class="w-full"
/>
</div>
</div>
</AppPopover>
</div>
<Button @click="openCreateDrawer">+ Dodaj akcijo</Button>
</div>
<div>
<DataTableClient
:columns="columns"
:rows="filtered"
:sort="sort"
:search="''"
:page="page"
:pageSize="pageSize"
:showToolbar="false"
:showPagination="true"
@update:sort="(v) => (sort = v)"
@update:page="(v) => (page = v)"
@update:pageSize="(v) => (pageSize = v)"
>
<template #cell-color_tag="{ row }">
<div class="flex items-center gap-2">
<span
v-if="row.color_tag"
class="inline-block h-4 w-4 rounded"
:style="{ backgroundColor: row.color_tag }"
></span>
<span>{{ row.color_tag || "" }}</span>
</div>
</template>
<template #cell-decisions="{ row }">
{{ row.decisions?.length ?? 0 }}
</template>
<template #cell-segment="{ row }">
<span>
{{ row.segment?.name || "" }}
</span>
</template>
<template #actions="{ row }">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="ghost" size="icon">
<MoreHorizontal class="w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem @click="openEditDrawer(row)">
<Pencil class="w-4 h-4 mr-2" />
Uredi
</DropdownMenuItem>
<DropdownMenuItem
:disabled="(row.activities_count ?? 0) > 0"
@click="confirmDelete(row)"
class="text-red-600 focus:text-red-600"
>
<Trash class="w-4 h-4 mr-2" />
Izbriši
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>
</DataTableClient>
</div>
<Dialog v-model:open="drawerEdit">
<DialogContent>
<DialogHeader>
<DialogTitle>Spremeni akcijo</DialogTitle>
</DialogHeader>
<div class="space-y-4">
<div>
<InputLabel for="name">Ime</InputLabel>
<Input id="name" ref="nameInput" v-model="form.name" type="text" />
</div>
<div>
<InputLabel for="colorTag">Barva</InputLabel>
<div class="mt-1">
<InlineColorPicker v-model="form.color_tag" />
</div>
</div>
<div>
<InputLabel for="segmentEdit">Segment</InputLabel>
<AppCombobox
id="segmentEdit"
v-model="form.segment_id"
:items="segmentOptions"
placeholder="Izberi segment"
button-class="w-full"
/>
</div>
<div>
<InputLabel for="decisions">Odločitve</InputLabel>
<AppMultiSelect
id="decisions"
v-model="form.decisions"
:items="selectOptions"
placeholder="Dodaj odločitev"
content-class="p-0 w-full"
/>
</div>
<div v-if="form.recentlySuccessful" class="text-sm text-green-600">
Shranjuje.
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="closeEditDrawer">Cancel</Button>
<Button @click="update" :disabled="form.processing">Shrani</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog v-model:open="drawerCreate">
<DialogContent>
<DialogHeader>
<DialogTitle>Dodaj akcijo</DialogTitle>
</DialogHeader>
<div class="space-y-4">
<div>
<InputLabel for="nameCreate">Ime</InputLabel>
<Input id="nameCreate" v-model="createForm.name" type="text" />
</div>
<div>
<InputLabel for="colorTagCreate">Barva</InputLabel>
<div class="mt-1">
<InlineColorPicker v-model="createForm.color_tag" />
</div>
</div>
<div>
<InputLabel for="segmentCreate">Segment</InputLabel>
<AppCombobox
id="segmentCreate"
v-model="createForm.segment_id"
:items="segmentOptions"
placeholder="Izberi segment"
button-class="w-full"
/>
</div>
<div>
<InputLabel for="decisionsCreate">Odločitve</InputLabel>
<AppMultiSelect
id="decisionsCreate"
v-model="createForm.decisions"
:items="selectOptions"
placeholder="Dodaj odločitev"
content-class="p-0 w-full"
/>
</div>
<div v-if="createForm.recentlySuccessful" class="text-sm text-green-600">
Shranjuje.
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="closeCreateDrawer">Cancel</Button>
<Button @click="store" :disabled="createForm.processing">Dodaj</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<AlertDialog v-model:open="showDelete">
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete action</AlertDialogTitle>
</AlertDialogHeader>
<div class="text-sm text-muted-foreground">
Are you sure you want to delete action "{{ toDelete?.name }}"? This cannot be
undone.
</div>
<AlertDialogFooter>
<Button variant="outline" @click="cancelDelete">Cancel</Button>
<Button variant="destructive" @click="destroyAction">Delete</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</template>