diff --git a/app/Http/Controllers/ImportTemplateController.php b/app/Http/Controllers/ImportTemplateController.php index 0ac8546..4ed9f21 100644 --- a/app/Http/Controllers/ImportTemplateController.php +++ b/app/Http/Controllers/ImportTemplateController.php @@ -23,6 +23,16 @@ public function index() ->orderBy('name') ->get(); + // Preload options for import mapping + $clients = Client::query() + ->join('person', 'person.id', '=', 'clients.person_id') + ->orderBy('person.full_name') + ->get(['clients.uuid', DB::raw('person.full_name as name')]); + + $segments = Segment::query()->orderBy('name')->get(['id', 'name']); + $decisions = Decision::query()->orderBy('name')->get(['id', 'name']); + $actions = Action::query()->orderBy('name')->get(['id', 'name']); + return Inertia::render('Imports/Templates/Index', [ 'templates' => $templates->map(fn ($t) => [ 'uuid' => $t->uuid, @@ -35,6 +45,10 @@ public function index() 'name' => $t->client->person?->full_name, ] : null, ]), + 'clients' => $clients, + 'segments' => $segments, + 'decisions' => $decisions, + 'actions' => $actions, ]); } @@ -664,4 +678,138 @@ public function destroy(ImportTemplate $template) return redirect()->route('importTemplates.index')->with('success', 'Template deleted'); } + + // Export template as JSON file + public function export(ImportTemplate $template) + { + $template->load('mappings'); + + $data = [ + 'name' => $template->name, + 'description' => $template->description, + 'source_type' => $template->source_type, + 'default_record_type' => $template->default_record_type, + 'sample_headers' => $template->sample_headers, + 'is_active' => $template->is_active, + 'reactivate' => $template->reactivate, + 'meta' => $template->meta, + 'mappings' => $template->mappings->map(fn($m) => [ + 'source_column' => $m->source_column, + 'entity' => $m->entity, + 'target_field' => $m->target_field, + 'transform' => $m->transform, + 'apply_mode' => $m->apply_mode, + 'options' => $m->options, + 'position' => $m->position, + ])->values()->toArray(), + ]; + + $filename = Str::slug($template->name) . '-' . now()->format('Y-m-d') . '.json'; + + return response()->json($data) + ->header('Content-Disposition', 'attachment; filename="' . $filename . '"'); + } + + // Import template from JSON file + public function import(Request $request) + { + $data = $request->validate([ + 'file' => 'required|file|mimes:json,txt|max:10240', + 'client_uuid' => 'nullable|string|exists:clients,uuid', + 'segment_id' => 'nullable|integer|exists:segments,id', + 'decision_id' => 'nullable|integer|exists:decisions,id', + 'action_id' => 'nullable|integer|exists:actions,id', + 'activity_action_id' => 'nullable|integer|exists:actions,id', + 'activity_decision_id' => 'nullable|integer|exists:decisions,id', + ]); + + $file = $request->file('file'); + $contents = file_get_contents($file->getRealPath()); + $json = json_decode($contents, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + return back()->withErrors(['file' => 'Invalid JSON file']); + } + + // Validate structure + $validator = validator($json, [ + 'name' => 'required|string|max:100', + 'description' => 'nullable|string|max:255', + 'source_type' => 'required|string|in:csv,xml,xls,xlsx,json', + 'default_record_type' => 'nullable|string|max:50', + 'sample_headers' => 'nullable|array', + 'is_active' => 'nullable|boolean', + 'reactivate' => 'nullable|boolean', + 'meta' => 'nullable|array', + 'mappings' => 'nullable|array', + 'mappings.*.source_column' => 'required|string', + 'mappings.*.entity' => 'nullable|string', + 'mappings.*.target_field' => 'nullable|string', + 'mappings.*.transform' => 'nullable|string', + 'mappings.*.apply_mode' => 'nullable|string|in:insert,update,both,keyref', + 'mappings.*.options' => 'nullable|array', + 'mappings.*.position' => 'nullable|integer', + ]); + + if ($validator->fails()) { + return back()->withErrors($validator)->withInput(); + } + + $clientId = null; + if (!empty($data['client_uuid'])) { + $clientId = Client::where('uuid', $data['client_uuid'])->value('id'); + } + + // Replace IDs in meta if provided + $meta = $json['meta'] ?? []; + if (!empty($data['segment_id'])) { + $meta['segment_id'] = $data['segment_id']; + } + if (!empty($data['decision_id'])) { + $meta['decision_id'] = $data['decision_id']; + } + if (!empty($data['action_id'])) { + $meta['action_id'] = $data['action_id']; + } + if (!empty($data['activity_action_id'])) { + $meta['activity_action_id'] = $data['activity_action_id']; + } + if (!empty($data['activity_decision_id'])) { + $meta['activity_decision_id'] = $data['activity_decision_id']; + } + + $template = null; + DB::transaction(function () use (&$template, $request, $json, $clientId, $meta) { + $template = ImportTemplate::create([ + 'uuid' => (string) Str::uuid(), + 'name' => $json['name'], + 'description' => $json['description'] ?? null, + 'source_type' => $json['source_type'], + 'default_record_type' => $json['default_record_type'] ?? null, + 'sample_headers' => $json['sample_headers'] ?? null, + 'user_id' => $request->user()?->id, + 'client_id' => $clientId, + 'is_active' => $json['is_active'] ?? true, + 'reactivate' => $json['reactivate'] ?? false, + 'meta' => $meta, + ]); + + foreach (($json['mappings'] ?? []) as $m) { + ImportTemplateMapping::create([ + 'import_template_id' => $template->id, + 'entity' => $m['entity'] ?? null, + 'source_column' => $m['source_column'], + 'target_field' => $m['target_field'] ?? null, + 'transform' => $m['transform'] ?? null, + 'apply_mode' => $m['apply_mode'] ?? 'both', + 'options' => $m['options'] ?? null, + 'position' => $m['position'] ?? null, + ]); + } + }); + + return redirect() + ->route('importTemplates.edit', ['template' => $template->uuid]) + ->with('success', 'Template imported successfully'); + } } diff --git a/resources/js/Pages/Imports/Templates/Index.vue b/resources/js/Pages/Imports/Templates/Index.vue index d2c739e..09b01b6 100644 --- a/resources/js/Pages/Imports/Templates/Index.vue +++ b/resources/js/Pages/Imports/Templates/Index.vue @@ -1,7 +1,7 @@ @@ -89,9 +174,15 @@ const props = defineProps({ }} - +