Changes to import / template pages frontend updated design
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<script setup>
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/Components/ui/card";
|
||||
import { Label } from "@/Components/ui/label";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/Components/ui/select";
|
||||
import { Checkbox } from "@/Components/ui/checkbox";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import AppMultiSelect from "@/Components/app/ui/AppMultiSelect.vue";
|
||||
|
||||
const props = defineProps({
|
||||
form: { type: Object, required: true },
|
||||
clients: { type: Array, default: () => [] },
|
||||
segments: { type: Array, default: () => [] },
|
||||
actions: { type: Array, default: () => [] },
|
||||
decisions: { type: Array, default: () => [] },
|
||||
canChangeClient: { type: Boolean, default: true },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["save"]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Osnovne informacije</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-6">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="name">Ime predloge</Label>
|
||||
<Input id="name" v-model="form.name" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="source_type">Vir</Label>
|
||||
<Select v-model="form.source_type">
|
||||
<SelectTrigger id="source_type">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="csv">CSV</SelectItem>
|
||||
<SelectItem value="xml">XML</SelectItem>
|
||||
<SelectItem value="xls">XLS</SelectItem>
|
||||
<SelectItem value="xlsx">XLSX</SelectItem>
|
||||
<SelectItem value="json">JSON</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="default_record_type">Privzeti tip zapisa</Label>
|
||||
<Input
|
||||
id="default_record_type"
|
||||
v-model="form.default_record_type"
|
||||
type="text"
|
||||
placeholder="npr.: account, person"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="client">Naročnik (izbirno)</Label>
|
||||
<Select v-model="form.client_uuid" :disabled="!canChangeClient">
|
||||
<SelectTrigger id="client">
|
||||
<SelectValue placeholder="Globalno (brez naročnika)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">Globalno (brez naročnika)</SelectItem>
|
||||
<SelectItem v-for="c in clients || []" :key="c.uuid" :value="c.uuid">
|
||||
{{ c.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p v-if="!canChangeClient" class="text-xs text-amber-600">
|
||||
Ni mogoče spremeniti naročnika, ker ta predloga že vsebuje preslikave.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="delimiter">Privzeti ločilni znak (CSV)</Label>
|
||||
<Select v-model="form.meta.delimiter">
|
||||
<SelectTrigger id="delimiter">
|
||||
<SelectValue placeholder="(Auto-detect)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
<SelectItem value=",">Vejica ,</SelectItem>
|
||||
<SelectItem value=";">Podpičje ;</SelectItem>
|
||||
<SelectItem value="\t">Tab \t</SelectItem>
|
||||
<SelectItem value="|">Pipe |</SelectItem>
|
||||
<SelectItem value=" ">Presledek ␠</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Pusti prazno za samodejno zaznavo. Uporabi, ko zaznavanje ne deluje pravilno.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="segment">Privzeti segment</Label>
|
||||
<Select v-model="form.meta.segment_id">
|
||||
<SelectTrigger id="segment">
|
||||
<SelectValue placeholder="(brez)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">(brez)</SelectItem>
|
||||
<SelectItem v-for="s in segments || []" :key="s.id" :value="s.id">
|
||||
{{ s.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="action">Privzeto dejanje (post-contract activity)</Label>
|
||||
<Select v-model="form.meta.action_id">
|
||||
<SelectTrigger id="action">
|
||||
<SelectValue placeholder="(brez)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">(brez)</SelectItem>
|
||||
<SelectItem v-for="a in actions || []" :key="a.id" :value="a.id">
|
||||
{{ a.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="decision">Privzeta odločitev (post-contract)</Label>
|
||||
<Select v-model="form.meta.decision_id" :disabled="!form.meta.action_id">
|
||||
<SelectTrigger id="decision">
|
||||
<SelectValue placeholder="(brez)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem :value="null">(brez)</SelectItem>
|
||||
<SelectItem v-for="d in decisions || []" :key="d.id" :value="d.id">
|
||||
{{ d.name }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p v-if="!form.meta.action_id" class="text-xs text-muted-foreground">
|
||||
Najprej izberi dejanje, nato odločitev.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
:checked="form.is_active"
|
||||
@update:checked="form.is_active = $event"
|
||||
/>
|
||||
<Label for="is_active" class="cursor-pointer">Aktivna</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="reactivate"
|
||||
:checked="form.reactivate"
|
||||
@update:checked="form.reactivate = $event"
|
||||
/>
|
||||
<Label for="reactivate" class="cursor-pointer">Reaktivacija</Label>
|
||||
</div>
|
||||
<Button @click="emit('save')" class="ml-auto">Shrani</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
@@ -0,0 +1,301 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { router } from "@inertiajs/vue3";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/Components/ui/accordion";
|
||||
import { Card, CardContent } from "@/Components/ui/card";
|
||||
import { Label } from "@/Components/ui/label";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
import { Textarea } from "@/Components/ui/textarea";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/Components/ui/select";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
import { ArrowUp, ArrowDown } from "lucide-vue-next";
|
||||
|
||||
const props = defineProps({
|
||||
entities: { type: Array, default: () => [] },
|
||||
entityOptions: { type: Array, default: () => [] },
|
||||
fieldOptions: { type: Object, default: () => ({}) },
|
||||
mappings: { type: Array, default: () => [] },
|
||||
templateUuid: { type: String, required: true },
|
||||
allSourceColumns: { type: Array, default: () => [] },
|
||||
entityAliases: { type: Object, default: () => ({}) },
|
||||
actions: { type: Array, default: () => [] },
|
||||
decisions: { type: Array, default: () => [] },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["refresh"]);
|
||||
|
||||
const newRows = ref({});
|
||||
const bulkRows = ref({});
|
||||
|
||||
function addRow(entity) {
|
||||
const row = newRows.value[entity];
|
||||
if (!row || !row.source || !row.field) return;
|
||||
const target_field = `${entity}.${row.field}`;
|
||||
const opts = {};
|
||||
if (row.group) opts.group = row.group;
|
||||
if (row.field === "meta" && row.metaKey) {
|
||||
opts.key = String(row.metaKey).trim();
|
||||
if (row.metaType) opts.type = String(row.metaType).trim();
|
||||
}
|
||||
const payload = {
|
||||
source_column: row.source,
|
||||
target_field,
|
||||
transform: row.transform || "",
|
||||
apply_mode: row.apply_mode || "both",
|
||||
options: Object.keys(opts).length ? opts : null,
|
||||
position: (props.mappings?.length || 0) + 1,
|
||||
};
|
||||
router.post(
|
||||
route("importTemplates.mappings.add", { template: props.templateUuid }),
|
||||
payload,
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
newRows.value[entity] = {};
|
||||
emit("refresh");
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateMapping(m) {
|
||||
const payload = {
|
||||
source_column: m.source_column,
|
||||
target_field: m.target_field,
|
||||
transform: m.transform || "",
|
||||
apply_mode: m.apply_mode || "both",
|
||||
options: m.options || null,
|
||||
position: m.position,
|
||||
};
|
||||
router.put(
|
||||
route("importTemplates.mappings.update", {
|
||||
template: props.templateUuid,
|
||||
mapping: m.id,
|
||||
}),
|
||||
payload,
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => emit("refresh"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function deleteMapping(m) {
|
||||
router.delete(
|
||||
route("importTemplates.mappings.delete", {
|
||||
template: props.templateUuid,
|
||||
mapping: m.id,
|
||||
}),
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => emit("refresh"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function reorder(entity, direction, m) {
|
||||
const all = [...props.mappings];
|
||||
const aliases = (props.entityAliases[entity] || [entity]).map((a) => a + ".");
|
||||
const entityMaps = all.filter((x) => {
|
||||
const tf = x.target_field || "";
|
||||
return aliases.some((prefix) => tf.startsWith(prefix));
|
||||
});
|
||||
const idx = entityMaps.findIndex((x) => x.id === m.id);
|
||||
if (idx < 0) return;
|
||||
const swapIdx = direction === "up" ? idx - 1 : idx + 1;
|
||||
if (swapIdx < 0 || swapIdx >= entityMaps.length) return;
|
||||
const a = entityMaps[idx];
|
||||
const b = entityMaps[swapIdx];
|
||||
const ordered = all.map((x) => x.id);
|
||||
const ai = ordered.indexOf(a.id);
|
||||
const bi = ordered.indexOf(b.id);
|
||||
if (ai < 0 || bi < 0) return;
|
||||
[ordered[ai], ordered[bi]] = [ordered[bi], ordered[ai]];
|
||||
router.post(
|
||||
route("importTemplates.mappings.reorder", { template: props.templateUuid }),
|
||||
{ order: ordered },
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => emit("refresh"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getEntityMappings(entity) {
|
||||
const aliases = (props.entityAliases[entity] || [entity]).map((a) => a + ".");
|
||||
return (props.mappings || []).filter((m) => {
|
||||
const tf = m.target_field || "";
|
||||
return aliases.some((prefix) => tf.startsWith(prefix));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<CardContent class="p-0">
|
||||
<Accordion type="multiple" collapsible class="w-full">
|
||||
<AccordionItem v-for="entity in entities" :key="entity" :value="entity">
|
||||
<AccordionTrigger class="px-4 hover:no-underline">
|
||||
<span class="font-medium">
|
||||
{{ entityOptions.find((e) => e.key === entity)?.label || entity }}
|
||||
</span>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent class="px-4 pb-4 space-y-4">
|
||||
<!-- Existing mappings -->
|
||||
<div v-if="getEntityMappings(entity).length > 0" class="space-y-2">
|
||||
<div
|
||||
v-for="m in getEntityMappings(entity)"
|
||||
:key="m.id"
|
||||
class="p-3 border rounded-lg bg-muted/30"
|
||||
>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-5 gap-2 items-center">
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">Izvor</Label>
|
||||
<Input v-model="m.source_column" class="text-sm" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">Cilj</Label>
|
||||
<Input v-model="m.target_field" class="text-sm" />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">Transform</Label>
|
||||
<Select v-model="m.transform">
|
||||
<SelectTrigger class="text-sm">
|
||||
<SelectValue placeholder="Brez" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="trim">trim</SelectItem>
|
||||
<SelectItem value="upper">upper</SelectItem>
|
||||
<SelectItem value="lower">lower</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">Način</Label>
|
||||
<Select v-model="m.apply_mode">
|
||||
<SelectTrigger class="text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="both">both</SelectItem>
|
||||
<SelectItem value="insert">insert</SelectItem>
|
||||
<SelectItem value="update">update</SelectItem>
|
||||
<SelectItem value="keyref">keyref</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
class="h-6 w-6"
|
||||
@click="reorder(entity, 'up', m)"
|
||||
>
|
||||
<ArrowUp class="h-3 w-3" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
class="h-6 w-6"
|
||||
@click="reorder(entity, 'down', m)"
|
||||
>
|
||||
<ArrowDown class="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" @click="updateMapping(m)">Shrani</Button>
|
||||
<Button size="sm" variant="destructive" @click="deleteMapping(m)">
|
||||
Izbriši
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-sm text-muted-foreground py-4 text-center">
|
||||
Ni definiranih preslikav za to entiteto.
|
||||
</div>
|
||||
|
||||
<!-- Add new mapping -->
|
||||
<div class="p-3 bg-muted/50 rounded-lg border">
|
||||
<div class="space-y-3">
|
||||
<div class="text-sm font-medium">Dodaj novo preslikavo</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-4 gap-3">
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs">Izvorno polje</Label>
|
||||
<Input
|
||||
v-model="(newRows[entity] ||= {}).source"
|
||||
placeholder="npr.: reference"
|
||||
list="`src-opts-${entity}`"
|
||||
/>
|
||||
<datalist :id="`src-opts-${entity}`">
|
||||
<option v-for="s in allSourceColumns" :key="s" :value="s" />
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs">Polje</Label>
|
||||
<Select v-model="(newRows[entity] ||= {}).field">
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-for="f in fieldOptions[entity] || []"
|
||||
:key="f"
|
||||
:value="f"
|
||||
>
|
||||
{{ f }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs">Transform</Label>
|
||||
<Select v-model="(newRows[entity] ||= {}).transform">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Brez" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="trim">trim</SelectItem>
|
||||
<SelectItem value="upper">upper</SelectItem>
|
||||
<SelectItem value="lower">lower</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs">Način</Label>
|
||||
<Select v-model="(newRows[entity] ||= {}).apply_mode">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="both" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="both">both</SelectItem>
|
||||
<SelectItem value="insert">insert</SelectItem>
|
||||
<SelectItem value="update">update</SelectItem>
|
||||
<SelectItem value="keyref">keyref</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<Button @click="addRow(entity)" size="sm">Dodaj preslikavo</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script setup>
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/Components/ui/card";
|
||||
import { Label } from "@/Components/ui/label";
|
||||
import { Checkbox } from "@/Components/ui/checkbox";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/Components/ui/select";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
|
||||
const props = defineProps({
|
||||
form: { type: Object, required: true },
|
||||
entities: { type: Array, default: () => [] },
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Nastavitve načina uvoza</CardTitle>
|
||||
<CardDescription>
|
||||
Konfiguriraj način uvoza za zgodovino ali plačila
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="flex items-center gap-6">
|
||||
<label class="inline-flex items-center gap-2 cursor-pointer">
|
||||
<Checkbox
|
||||
:checked="form.meta.history_import"
|
||||
@update:checked="form.meta.history_import = $event"
|
||||
/>
|
||||
<span class="text-sm font-medium">Uvoz zgodovine</span>
|
||||
</label>
|
||||
<label class="inline-flex items-center gap-2 cursor-pointer">
|
||||
<Checkbox
|
||||
:checked="form.meta.payments_import"
|
||||
@update:checked="form.meta.payments_import = $event"
|
||||
/>
|
||||
<span class="text-sm font-medium">Uvoz plačil</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Zgodovina dovoljuje oseba/naslov/telefon/pogodbe/aktivnosti/primeri strank; računi so
|
||||
samodejno dodani s pogodbami. Plačila zaklene entitete na Pogodbe → Računi → Plačila.
|
||||
</p>
|
||||
|
||||
<div v-if="form.meta.payments_import" class="space-y-2 pt-2">
|
||||
<Label for="contract_key">Ključ ujemanja pogodb</Label>
|
||||
<Select v-model="form.meta.contract_key_mode">
|
||||
<SelectTrigger id="contract_key">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="reference">
|
||||
Referenca (uporabi samo contract.reference za iskanje zapisov)
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Preslika stolpec CSV na contract.reference za reševanje pogodb za tega naročnika.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Entities locked info for payments mode -->
|
||||
<div v-if="form.meta.payments_import" class="p-3 bg-emerald-50 rounded-lg border border-emerald-200">
|
||||
<div class="text-sm text-emerald-900 mb-2 font-medium">Entitete so zaklenjene:</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Badge variant="secondary" class="bg-emerald-100 text-emerald-800">Pogodbe</Badge>
|
||||
<Badge variant="secondary" class="bg-emerald-100 text-emerald-800">Računi</Badge>
|
||||
<Badge variant="secondary" class="bg-emerald-100 text-emerald-800">Plačila</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
@@ -0,0 +1,253 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useForm } from "@inertiajs/vue3";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
} from "@/Components/ui/card";
|
||||
import { Label } from "@/Components/ui/label";
|
||||
import { Input } from "@/Components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/Components/ui/select";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import { Badge } from "@/Components/ui/badge";
|
||||
|
||||
const props = defineProps({
|
||||
unassigned: { type: Array, default: () => [] },
|
||||
templateUuid: { type: String, required: true },
|
||||
entityOptions: { type: Array, default: () => [] },
|
||||
fieldOptions: { type: Object, default: () => ({}) },
|
||||
suggestions: { type: Object, default: () => ({}) },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["refresh"]);
|
||||
|
||||
const unassignedState = ref({});
|
||||
|
||||
function saveUnassigned(m) {
|
||||
const st = unassignedState.value[m.id] || {};
|
||||
if (st.entity && st.field) {
|
||||
m.target_field = `${st.entity}.${st.field}`;
|
||||
} else {
|
||||
m.target_field = null;
|
||||
}
|
||||
if (st.group) {
|
||||
m.options = m.options && typeof m.options === "object" ? m.options : {};
|
||||
m.options.group = st.group;
|
||||
}
|
||||
if (st.field === "meta") {
|
||||
if (st.metaKey && String(st.metaKey).trim() !== "") {
|
||||
m.options = m.options && typeof m.options === "object" ? m.options : {};
|
||||
m.options.key = String(st.metaKey).trim();
|
||||
}
|
||||
if (st.metaType && String(st.metaType).trim() !== "") {
|
||||
m.options = m.options && typeof m.options === "object" ? m.options : {};
|
||||
m.options.type = String(st.metaType).trim();
|
||||
}
|
||||
}
|
||||
const payload = {
|
||||
source_column: m.source_column,
|
||||
target_field: m.target_field,
|
||||
transform: m.transform,
|
||||
apply_mode: m.apply_mode,
|
||||
options: m.options || null,
|
||||
position: m.position,
|
||||
};
|
||||
useForm(payload).put(
|
||||
route("importTemplates.mappings.update", {
|
||||
template: props.templateUuid,
|
||||
mapping: m.id,
|
||||
}),
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => emit("refresh"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function deleteMapping(m) {
|
||||
useForm({}).delete(
|
||||
route("importTemplates.mappings.delete", {
|
||||
template: props.templateUuid,
|
||||
mapping: m.id,
|
||||
}),
|
||||
{
|
||||
preserveScroll: true,
|
||||
onSuccess: () => emit("refresh"),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function applySuggestion(m, suggestion) {
|
||||
if (!suggestion || !suggestion.entity || !suggestion.field) return;
|
||||
const state = unassignedState.value[m.id] || {};
|
||||
state.entity = suggestion.entity;
|
||||
state.field = suggestion.field;
|
||||
unassignedState.value[m.id] = state;
|
||||
saveUnassigned(m);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card v-if="unassigned && unassigned.length > 0" class="border-amber-200">
|
||||
<CardHeader class="bg-amber-50">
|
||||
<CardTitle class="text-amber-900">
|
||||
Nedodeljene preslikave ({{ unassigned.length }})
|
||||
</CardTitle>
|
||||
<CardDescription class="text-amber-700">
|
||||
Te preslikave nimajo dodeljene ciljne entitete in polja
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="p-4 space-y-3">
|
||||
<div v-for="m in unassigned" :key="m.id" class="p-3 bg-white border rounded-lg">
|
||||
<div class="space-y-3">
|
||||
<!-- Source column with suggestion -->
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<Label class="text-xs text-muted-foreground">Izvorno polje</Label>
|
||||
<div class="font-medium">{{ m.source_column }}</div>
|
||||
<div v-if="suggestions && suggestions[m.source_column]" class="mt-1">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="link"
|
||||
class="h-auto p-0 text-xs text-indigo-700"
|
||||
@click="applySuggestion(m, suggestions[m.source_column])"
|
||||
>
|
||||
Predlog: {{ suggestions[m.source_column].entity }}.{{
|
||||
suggestions[m.source_column].field
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entity and Field selection -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div class="space-y-2">
|
||||
<Label for="entity">Entiteta</Label>
|
||||
<Select v-model="(unassignedState[m.id] ||= {}).entity">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="(izberi)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-for="opt in entityOptions"
|
||||
:key="opt.key"
|
||||
:value="opt.key"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="field">Polje</Label>
|
||||
<Select
|
||||
v-model="(unassignedState[m.id] ||= {}).field"
|
||||
:disabled="!(unassignedState[m.id] || {}).entity"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="(izberi)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-for="f in fieldOptions[(unassignedState[m.id] || {}).entity] || []"
|
||||
:key="f"
|
||||
:value="f"
|
||||
>
|
||||
{{ f }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transform, Apply Mode, Group -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<div class="space-y-2">
|
||||
<Label>Transform</Label>
|
||||
<Select v-model="m.transform">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Brez" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="trim">trim</SelectItem>
|
||||
<SelectItem value="upper">upper</SelectItem>
|
||||
<SelectItem value="lower">lower</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Način</Label>
|
||||
<Select v-model="m.apply_mode">
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="both">both</SelectItem>
|
||||
<SelectItem value="insert">insert</SelectItem>
|
||||
<SelectItem value="update">update</SelectItem>
|
||||
<SelectItem value="keyref">keyref</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>Skupina</Label>
|
||||
<Input
|
||||
v-model="(unassignedState[m.id] ||= {}).group"
|
||||
placeholder="1, 2, home, work"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta fields if applicable -->
|
||||
<div
|
||||
v-if="(unassignedState[m.id] || {}).field === 'meta'"
|
||||
class="grid grid-cols-1 sm:grid-cols-2 gap-3"
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<Label>Meta ključ</Label>
|
||||
<Input
|
||||
v-model="(unassignedState[m.id] ||= {}).metaKey"
|
||||
placeholder="npr.: note, category"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label>Meta tip</Label>
|
||||
<Select v-model="(unassignedState[m.id] ||= {}).metaType">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="(auto/string)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="string">string</SelectItem>
|
||||
<SelectItem value="number">number</SelectItem>
|
||||
<SelectItem value="date">date</SelectItem>
|
||||
<SelectItem value="boolean">boolean</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex items-center gap-2 pt-2">
|
||||
<Button size="sm" @click="saveUnassigned(m)">Shrani</Button>
|
||||
<Button size="sm" variant="destructive" @click="deleteMapping(m)"
|
||||
>Izbriši</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
Reference in New Issue
Block a user