167 lines
5.8 KiB
Vue
167 lines
5.8 KiB
Vue
<script setup>
|
|
import { computed } from "vue";
|
|
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/Components/ui/select";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { Label } from "@/Components/ui/label";
|
|
import { Input } from "@/Components/ui/input";
|
|
import { Badge } from "@/Components/ui/badge";
|
|
|
|
const props = defineProps({
|
|
isCompleted: Boolean,
|
|
hasHeader: Boolean,
|
|
delimiterState: Object,
|
|
selectedTemplateOption: Object,
|
|
filteredTemplates: Array,
|
|
templateApplied: Boolean,
|
|
form: Object, // reactive object reference from parent
|
|
});
|
|
const emits = defineEmits([
|
|
"update:hasHeader",
|
|
"update:delimiterMode",
|
|
"update:delimiterCustom",
|
|
"apply-template",
|
|
"preview",
|
|
]);
|
|
|
|
function onHeaderChange(val) {
|
|
emits("update:hasHeader", val === "true");
|
|
}
|
|
function onDelimiterMode(val) {
|
|
emits("update:delimiterMode", val);
|
|
}
|
|
function onDelimiterCustom(e) {
|
|
emits("update:delimiterCustom", e.target.value);
|
|
}
|
|
|
|
// Proxy selected template object <-> form.import_template_id (which stores the id)
|
|
const selectedTemplateProxy = computed({
|
|
get() {
|
|
return props.selectedTemplateOption || null;
|
|
},
|
|
set(opt) {
|
|
props.form.import_template_id = opt ? opt.id : null;
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-4">
|
|
<div class="flex items-start justify-between gap-4">
|
|
<div class="flex-1">
|
|
<Label class="text-sm font-medium">Template</Label>
|
|
<Select
|
|
:model-value="selectedTemplateProxy?.id?.toString()"
|
|
@update:model-value="(val) => {
|
|
const tpl = filteredTemplates.find(t => t.id.toString() === val);
|
|
selectedTemplateProxy = tpl || null;
|
|
}"
|
|
>
|
|
<SelectTrigger class="mt-1">
|
|
<SelectValue placeholder="Izberi predlogo...">
|
|
<div v-if="selectedTemplateProxy" class="flex items-center gap-2">
|
|
<span>{{ selectedTemplateProxy.name }}</span>
|
|
<span v-if="selectedTemplateProxy.source_type" class="text-xs text-muted-foreground">({{ selectedTemplateProxy.source_type }})</span>
|
|
<Badge variant="outline" class="text-[10px]">{{ selectedTemplateProxy.client_id ? 'Client' : 'Global' }}</Badge>
|
|
</div>
|
|
</SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectItem v-for="option in filteredTemplates" :key="option.id" :value="option.id.toString()">
|
|
<div class="flex items-center justify-between w-full gap-3">
|
|
<div class="flex items-center gap-2">
|
|
<span>{{ option.name }}</span>
|
|
<span v-if="option.source_type" class="text-xs text-muted-foreground">({{ option.source_type }})</span>
|
|
</div>
|
|
<Badge variant="outline" class="text-[10px]">{{
|
|
option.client_id ? "Client" : "Global"
|
|
}}</Badge>
|
|
</div>
|
|
</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
<div v-if="isCompleted" class="mt-2">
|
|
<Button
|
|
variant="default"
|
|
size="sm"
|
|
class="w-full sm:w-auto"
|
|
@click="$emit('preview')"
|
|
>
|
|
Ogled CSV
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="!isCompleted" class="flex flex-col gap-3">
|
|
<div class="flex flex-col sm:flex-row gap-3">
|
|
<div class="flex-1">
|
|
<Label class="text-xs font-medium">Header row</Label>
|
|
<Select
|
|
:model-value="hasHeader.toString()"
|
|
@update:model-value="onHeaderChange"
|
|
>
|
|
<SelectTrigger class="mt-1">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectItem value="true">Has header</SelectItem>
|
|
<SelectItem value="false">No header (positional)</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div class="flex-1">
|
|
<Label class="text-xs font-medium">Delimiter</Label>
|
|
<Select
|
|
:model-value="delimiterState.mode"
|
|
@update:model-value="onDelimiterMode"
|
|
>
|
|
<SelectTrigger class="mt-1">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectGroup>
|
|
<SelectItem value="auto">Auto-detect</SelectItem>
|
|
<SelectItem value="comma">Comma ,</SelectItem>
|
|
<SelectItem value="semicolon">Semicolon ;</SelectItem>
|
|
<SelectItem value="tab">Tab \t</SelectItem>
|
|
<SelectItem value="pipe">Pipe |</SelectItem>
|
|
<SelectItem value="space">Space ␠</SelectItem>
|
|
<SelectItem value="custom">Custom…</SelectItem>
|
|
</SelectGroup>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<div v-if="delimiterState.mode === 'custom'" class="flex items-end gap-3">
|
|
<div class="w-40">
|
|
<Label class="text-xs font-medium">Custom delimiter</Label>
|
|
<Input
|
|
:model-value="delimiterState.custom"
|
|
@input="onDelimiterCustom"
|
|
maxlength="4"
|
|
placeholder=","
|
|
class="mt-1"
|
|
/>
|
|
</div>
|
|
<p class="text-xs text-muted-foreground">
|
|
Template default: {{ selectedTemplateOption?.meta?.delimiter || "auto" }}
|
|
</p>
|
|
</div>
|
|
<p v-else class="text-xs text-muted-foreground">
|
|
Template default: {{ selectedTemplateOption?.meta?.delimiter || "auto" }}
|
|
</p>
|
|
</div>
|
|
<Button
|
|
v-if="!isCompleted && form.import_template_id"
|
|
variant="default"
|
|
@click="$emit('apply-template')"
|
|
class="w-full"
|
|
>
|
|
{{ templateApplied ? 'Re-apply Template' : 'Apply Template' }}
|
|
</Button>
|
|
</div>
|
|
</template>
|