165 lines
7.5 KiB
Svelte
165 lines
7.5 KiB
Svelte
<!-- [DEF:MapperTool:Component] -->
|
|
<!--
|
|
@SEMANTICS: mapper, tool, dataset, postgresql, excel
|
|
@PURPOSE: UI component for mapping dataset column verbose names using the MapperPlugin.
|
|
@LAYER: UI
|
|
@RELATION: USES -> frontend/src/services/toolsService.js
|
|
@RELATION: USES -> frontend/src/services/connectionService.js
|
|
-->
|
|
<script>
|
|
// [SECTION: IMPORTS]
|
|
import { onMount } from 'svelte';
|
|
import { runTask } from '../../services/toolsService.js';
|
|
import { getConnections } from '../../services/connectionService.js';
|
|
import { selectedTask } from '../../lib/stores.js';
|
|
import { addToast } from '../../lib/toasts.js';
|
|
// [/SECTION]
|
|
|
|
let envs = [];
|
|
let connections = [];
|
|
let selectedEnv = '';
|
|
let datasetId = '';
|
|
let source = 'postgres';
|
|
let selectedConnection = '';
|
|
let tableName = '';
|
|
let tableSchema = 'public';
|
|
let excelPath = '';
|
|
let isRunning = false;
|
|
|
|
// [DEF:fetchData:Function]
|
|
// @PURPOSE: Fetches environments and saved connections.
|
|
// @PRE: None.
|
|
// @POST: envs and connections arrays are populated.
|
|
async function fetchData() {
|
|
try {
|
|
const envsRes = await fetch('/api/environments');
|
|
envs = await envsRes.json();
|
|
connections = await getConnections();
|
|
} catch (e) {
|
|
addToast('Failed to fetch data', 'error');
|
|
}
|
|
}
|
|
// [/DEF:fetchData:Function]
|
|
|
|
// [DEF:handleRunMapper:Function]
|
|
// @PURPOSE: Triggers the MapperPlugin task.
|
|
// @PRE: selectedEnv and datasetId are set; source-specific fields are valid.
|
|
// @POST: Mapper task is started and selectedTask is updated.
|
|
async function handleRunMapper() {
|
|
if (!selectedEnv || !datasetId) {
|
|
addToast('Please fill in required fields', 'warning');
|
|
return;
|
|
}
|
|
|
|
if (source === 'postgres' && (!selectedConnection || !tableName)) {
|
|
addToast('Connection and Table Name are required for postgres source', 'warning');
|
|
return;
|
|
}
|
|
|
|
if (source === 'excel' && !excelPath) {
|
|
addToast('Excel path is required for excel source', 'warning');
|
|
return;
|
|
}
|
|
|
|
isRunning = true;
|
|
try {
|
|
const env = envs.find(e => e.id === selectedEnv);
|
|
const task = await runTask('dataset-mapper', {
|
|
env: env.name,
|
|
dataset_id: parseInt(datasetId),
|
|
source,
|
|
connection_id: selectedConnection,
|
|
table_name: tableName,
|
|
table_schema: tableSchema,
|
|
excel_path: excelPath
|
|
});
|
|
|
|
selectedTask.set(task);
|
|
addToast('Mapper task started', 'success');
|
|
} catch (e) {
|
|
addToast(e.message, 'error');
|
|
} finally {
|
|
isRunning = false;
|
|
}
|
|
}
|
|
// [/DEF:handleRunMapper:Function]
|
|
|
|
onMount(fetchData);
|
|
</script>
|
|
|
|
<!-- [SECTION: TEMPLATE] -->
|
|
<div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">Dataset Column Mapper</h3>
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="mapper-env" class="block text-sm font-medium text-gray-700">Environment</label>
|
|
<select id="mapper-env" bind:value={selectedEnv} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
|
|
<option value="" disabled>-- Select Environment --</option>
|
|
{#each envs as env}
|
|
<option value={env.id}>{env.name}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="mapper-ds-id" class="block text-sm font-medium text-gray-700">Dataset ID</label>
|
|
<input type="number" id="mapper-ds-id" bind:value={datasetId} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Mapping Source</label>
|
|
<div class="mt-2 flex space-x-4">
|
|
<label class="inline-flex items-center">
|
|
<input type="radio" bind:group={source} value="postgres" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" />
|
|
<span class="ml-2 text-sm text-gray-700">PostgreSQL</span>
|
|
</label>
|
|
<label class="inline-flex items-center">
|
|
<input type="radio" bind:group={source} value="excel" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" />
|
|
<span class="ml-2 text-sm text-gray-700">Excel</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{#if source === 'postgres'}
|
|
<div class="space-y-4 p-4 bg-gray-50 rounded-md border border-gray-100">
|
|
<div>
|
|
<label for="mapper-conn" class="block text-sm font-medium text-gray-700">Saved Connection</label>
|
|
<select id="mapper-conn" bind:value={selectedConnection} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
|
|
<option value="" disabled>-- Select Connection --</option>
|
|
{#each connections as conn}
|
|
<option value={conn.id}>{conn.name}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="mapper-table" class="block text-sm font-medium text-gray-700">Table Name</label>
|
|
<input type="text" id="mapper-table" bind:value={tableName} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
|
|
</div>
|
|
<div>
|
|
<label for="mapper-schema" class="block text-sm font-medium text-gray-700">Table Schema</label>
|
|
<input type="text" id="mapper-schema" bind:value={tableSchema} class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
<div class="p-4 bg-gray-50 rounded-md border border-gray-100">
|
|
<label for="mapper-excel" class="block text-sm font-medium text-gray-700">Excel File Path</label>
|
|
<input type="text" id="mapper-excel" bind:value={excelPath} placeholder="/path/to/mapping.xlsx" class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="flex justify-end">
|
|
<button
|
|
on:click={handleRunMapper}
|
|
disabled={isRunning}
|
|
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
|
|
>
|
|
{isRunning ? 'Starting...' : 'Run Mapper'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- [/SECTION] -->
|
|
<!-- [/DEF:MapperTool:Component] --> |