Teren-app/resources/js/Pages/Imports/Templates/Create.vue
2025-10-02 22:09:05 +02:00

311 lines
11 KiB
Vue

<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import { ref } from "vue";
import { useForm } from "@inertiajs/vue3";
import Multiselect from "vue-multiselect";
import { computed, watch } from "vue";
const props = defineProps({
clients: Array,
segments: Array,
decisions: Array,
actions: Array,
});
const form = useForm({
name: "",
description: "",
source_type: "csv",
default_record_type: "",
is_active: true,
client_uuid: null,
entities: [],
meta: {
segment_id: null,
decision_id: null,
action_id: null,
delimiter: "",
// Payments import mode
payments_import: false,
// For payments mode: how to locate Contract - use single key 'reference'
contract_key_mode: null,
},
});
const decisionsForSelectedAction = computed(() => {
const act = (props.actions || []).find((a) => a.id === form.meta.action_id);
return act?.decisions || [];
});
watch(
() => form.meta.action_id,
() => {
// Clear decision when action changes to enforce valid pair
form.meta.decision_id = null;
}
);
function submit() {
form.post(route("importTemplates.store"), {
onSuccess: () => {
// You can redirect or show a success message here
},
});
}
// Payments mode: lock entities to Contract -> Account -> Payment and provide key mode
const prevEntities = ref([]);
watch(
() => form.meta.payments_import,
(enabled) => {
if (enabled) {
// Save current selection and lock to the required chain
prevEntities.value = Array.isArray(form.entities) ? [...form.entities] : [];
form.entities = ["contracts", "accounts", "payments"];
// default contract key mode to 'reference'
if (!form.meta.contract_key_mode) {
form.meta.contract_key_mode = "reference";
}
} else {
// Restore previous selection when turning off
form.entities = prevEntities.value?.length ? [...prevEntities.value] : [];
form.meta.contract_key_mode = null;
}
}
);
</script>
<template>
<AppLayout title="Create Import Template">
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Create Import Template
</h2>
</template>
<div class="py-6">
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white shadow sm:rounded-lg p-6 space-y-6">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700"
>Client (optional)</label
>
<Multiselect
v-model="form.client_uuid"
:options="props.clients || []"
:reduce="(c) => c.uuid"
track-by="uuid"
label="name"
placeholder="Global (no client)"
:searchable="true"
:allow-empty="true"
class="mt-1"
/>
<p class="text-xs text-gray-500 mt-1">
Leave empty to make this template global (visible to all clients).
</p>
</div>
<div>
<div class="flex items-center justify-between">
<label class="block text-sm font-medium text-gray-700"
>Entities (tables)</label
>
<label class="inline-flex items-center gap-2 text-sm">
<input
type="checkbox"
v-model="form.meta.payments_import"
class="rounded"
/>
<span>Payments import</span>
</label>
</div>
<template v-if="!form.meta.payments_import">
<Multiselect
v-model="form.entities"
:options="[
{ value: 'person', label: 'Person' },
{ value: 'person_addresses', label: 'Person Addresses' },
{ value: 'person_phones', label: 'Person Phones' },
{ value: 'emails', label: 'Emails' },
{ value: 'accounts', label: 'Accounts' },
{ value: 'contracts', label: 'Contracts' },
{ value: 'payments', label: 'Payments' },
]"
:multiple="true"
track-by="value"
label="label"
:reduce="(o) => o.value"
placeholder="Select one or more entities"
:searchable="false"
class="mt-1"
/>
</template>
<template v-else>
<div class="mt-1">
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center rounded-full bg-emerald-100 text-emerald-800 px-3 py-1 text-xs font-medium">Contracts</span>
<span class="inline-flex items-center rounded-full bg-emerald-100 text-emerald-800 px-3 py-1 text-xs font-medium">Accounts</span>
<span class="inline-flex items-center rounded-full bg-emerald-100 text-emerald-800 px-3 py-1 text-xs font-medium">Payments</span>
</div>
</div>
</template>
<p class="text-xs text-gray-500 mt-1">
Choose which tables this template targets. You can still define per-column
mappings later.
</p>
<div v-if="form.meta.payments_import" class="mt-2 text-xs text-gray-600">
Payments mode locks entities to:
<span class="font-medium">Contracts → Accounts → Payments</span> and
optimizes matching for payments import.
</div>
<div v-if="form.meta.payments_import" class="mt-3">
<label class="block text-sm font-medium text-gray-700"
>Contract match key</label
>
<select
v-model="form.meta.contract_key_mode"
class="mt-1 block w-full border rounded p-2"
>
<option value="reference">
Reference (use only contract.reference to locate records)
</option>
</select>
<p class="text-xs text-gray-500 mt-1">
When importing payments, Contract records are located using the selected
key. Use your CSV mapping to map the appropriate column to the contract
reference.
</p>
</div>
</div>
</div>
<!-- Defaults: Segment / Decision / Action -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700"
>Default Segment</label
>
<select
v-model="form.meta.segment_id"
class="mt-1 block w-full border rounded p-2"
>
<option :value="null">(none)</option>
<option v-for="s in props.segments || []" :key="s.id" :value="s.id">
{{ s.name }}
</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"
>Default Action (for Activity)</label
>
<select
v-model="form.meta.action_id"
class="mt-1 block w-full border rounded p-2"
>
<option :value="null">(none)</option>
<option v-for="a in props.actions || []" :key="a.id" :value="a.id">
{{ a.name }}
</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"
>Default Decision</label
>
<select
v-model="form.meta.decision_id"
class="mt-1 block w-full border rounded p-2"
:disabled="!form.meta.action_id"
>
<option :value="null">(none)</option>
<option v-for="d in decisionsForSelectedAction" :key="d.id" :value="d.id">
{{ d.name }}
</option>
</select>
<p v-if="!form.meta.action_id" class="text-xs text-gray-500 mt-1">
Select an Action to see its Decisions.
</p>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Name</label>
<input
v-model="form.name"
type="text"
class="mt-1 block w-full border rounded p-2"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Description</label>
<textarea
v-model="form.description"
class="mt-1 block w-full border rounded p-2"
rows="3"
/>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Source Type</label>
<select
v-model="form.source_type"
class="mt-1 block w-full border rounded p-2"
>
<option value="csv">CSV</option>
<option value="xml">XML</option>
<option value="xls">XLS</option>
<option value="xlsx">XLSX</option>
<option value="json">JSON</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700"
>Default Record Type (optional)</label
>
<input
v-model="form.default_record_type"
type="text"
class="mt-1 block w-full border rounded p-2"
placeholder="e.g., account, person"
/>
</div>
</div>
<div class="flex items-center gap-2">
<input
id="is_active"
v-model="form.is_active"
type="checkbox"
class="rounded"
/>
<label for="is_active" class="text-sm font-medium text-gray-700"
>Active</label
>
</div>
<div class="pt-4">
<button
@click.prevent="submit"
class="px-4 py-2 bg-emerald-600 text-white rounded"
:disabled="form.processing"
>
{{ form.processing ? "Saving…" : "Create Template" }}
</button>
</div>
<div
v-if="form.errors && Object.keys(form.errors).length"
class="text-sm text-red-600"
>
<div v-for="(msg, key) in form.errors" :key="key">{{ msg }}</div>
</div>
</div>
</div>
</div>
</AppLayout>
</template>