This commit is contained in:
Simon Pocrnjič
2025-12-26 22:39:58 +01:00
parent f8623a6071
commit dea7432deb
55 changed files with 7977 additions and 1983 deletions
@@ -1,6 +1,10 @@
<script setup>
import Multiselect from "vue-multiselect";
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,
@@ -19,11 +23,11 @@ const emits = defineEmits([
"preview",
]);
function onHeaderChange(e) {
emits("update:hasHeader", e.target.value === "true");
function onHeaderChange(val) {
emits("update:hasHeader", val === "true");
}
function onDelimiterMode(e) {
emits("update:delimiterMode", e.target.value);
function onDelimiterMode(val) {
emits("update:delimiterMode", val);
}
function onDelimiterCustom(e) {
emits("update:delimiterCustom", e.target.value);
@@ -44,116 +48,119 @@ const selectedTemplateProxy = computed({
<div class="space-y-4">
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700">Template</label>
<Multiselect
v-model="selectedTemplateProxy"
:options="filteredTemplates"
track-by="id"
label="name"
placeholder="Izberi predlogo..."
:searchable="true"
:allow-empty="true"
class="mt-1"
:custom-label="(o) => o.name"
:disabled="filteredTemplates?.length === 0"
:show-no-results="true"
:clear-on-select="false"
<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;
}"
>
<template #option="{ option }">
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-2">
<span>{{ option.name }}</span>
<span v-if="option.source_type" class="ml-2 text-xs text-gray-500"
>({{ option.source_type }})</span
>
<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>
<span class="text-[10px] px-1.5 py-0.5 rounded bg-gray-100 text-gray-600">{{
option.client_id ? "Client" : "Global"
}}</span>
</div>
</template>
<template #singleLabel="{ option }">
<div class="flex items-center gap-2">
<span>{{ option.name }}</span>
<span v-if="option.source_type" class="ml-1 text-xs text-gray-500"
>({{ option.source_type }})</span
>
<span class="text-[10px] px-1.5 py-0.5 rounded bg-gray-100 text-gray-600">{{
option.client_id ? "Client" : "Global"
}}</span>
</div>
</template>
<template #noResult>
<div class="px-2 py-1 text-xs text-gray-500">Ni predlog.</div>
</template>
</Multiselect>
</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
type="button"
<Button
variant="default"
size="sm"
class="w-full sm:w-auto"
@click="$emit('preview')"
class="px-3 py-1.5 bg-indigo-600 text-white rounded text-sm hover:bg-indigo-500 w-full sm:w-auto"
>
Ogled CSV
</button>
</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="block text-xs font-medium text-gray-600">Header row</label>
<select
:value="hasHeader"
@change="onHeaderChange"
class="mt-1 block w-full border rounded p-2 text-sm"
<Label class="text-xs font-medium">Header row</Label>
<Select
:model-value="hasHeader.toString()"
@update:model-value="onHeaderChange"
>
<option value="true">Has header</option>
<option value="false">No header (positional)</option>
</select>
<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="block text-xs font-medium text-gray-600">Delimiter</label>
<select
:value="delimiterState.mode"
@change="onDelimiterMode"
class="mt-1 block w-full border rounded p-2 text-sm"
<Label class="text-xs font-medium">Delimiter</Label>
<Select
:model-value="delimiterState.mode"
@update:model-value="onDelimiterMode"
>
<option value="auto">Auto-detect</option>
<option value="comma">Comma ,</option>
<option value="semicolon">Semicolon ;</option>
<option value="tab">Tab \t</option>
<option value="pipe">Pipe |</option>
<option value="space">Space ␠</option>
<option value="custom">Custom…</option>
</select>
<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="block text-xs font-medium text-gray-600">Custom delimiter</label>
<input
:value="delimiterState.custom"
<Label class="text-xs font-medium">Custom delimiter</Label>
<Input
:model-value="delimiterState.custom"
@input="onDelimiterCustom"
maxlength="4"
placeholder=","
class="mt-1 block w-full border rounded p-2 text-sm"
class="mt-1"
/>
</div>
<p class="text-xs text-gray-500">
<p class="text-xs text-muted-foreground">
Template default: {{ selectedTemplateOption?.meta?.delimiter || "auto" }}
</p>
</div>
<p v-else class="text-xs text-gray-500">
<p v-else class="text-xs text-muted-foreground">
Template default: {{ selectedTemplateOption?.meta?.delimiter || "auto" }}
</p>
</div>
<button
v-if="!isCompleted"
class="px-3 py-1.5 bg-emerald-600 text-white rounded text-sm"
:disabled="!form.import_template_id"
<Button
v-if="!isCompleted && form.import_template_id"
variant="default"
@click="$emit('apply-template')"
class="w-full"
>
{{ templateApplied ? "Ponovno uporabi predlogo" : "Uporabi predlogo" }}
</button>
{{ templateApplied ? 'Re-apply Template' : 'Apply Template' }}
</Button>
</div>
</template>