feat: implement plugin architecture and application settings with Svelte UI
- Added plugin base and loader for backend extensibility - Implemented application settings management with config persistence - Created Svelte-based frontend with Dashboard and Settings pages - Added API routes for plugins, tasks, and settings - Updated documentation and specifications - Improved project structure and developer tools
This commit is contained in:
207
frontend/src/pages/Settings.svelte
Executable file
207
frontend/src/pages/Settings.svelte
Executable file
@@ -0,0 +1,207 @@
|
||||
<!--
|
||||
[DEF:Settings:Component]
|
||||
@SEMANTICS: settings, ui, configuration
|
||||
@PURPOSE: The main settings page for the application, allowing management of environments and global settings.
|
||||
@LAYER: UI
|
||||
@RELATION: CALLS -> api.js
|
||||
@RELATION: USES -> stores.js
|
||||
|
||||
@PROPS:
|
||||
None
|
||||
@EVENTS:
|
||||
None
|
||||
@INVARIANT: Settings changes must be saved to the backend.
|
||||
-->
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { getSettings, updateGlobalSettings, getEnvironments, addEnvironment, updateEnvironment, deleteEnvironment, testEnvironmentConnection } from '../lib/api';
|
||||
import { addToast } from '../lib/toasts';
|
||||
|
||||
let settings = {
|
||||
environments: [],
|
||||
settings: {
|
||||
backup_path: '',
|
||||
default_environment_id: null
|
||||
}
|
||||
};
|
||||
|
||||
let newEnv = {
|
||||
id: '',
|
||||
name: '',
|
||||
url: '',
|
||||
username: '',
|
||||
password: '',
|
||||
is_default: false
|
||||
};
|
||||
|
||||
let editingEnvId = null;
|
||||
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const data = await getSettings();
|
||||
settings = data;
|
||||
} catch (error) {
|
||||
addToast('Failed to load settings', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveGlobal() {
|
||||
try {
|
||||
await updateGlobalSettings(settings.settings);
|
||||
addToast('Global settings saved', 'success');
|
||||
} catch (error) {
|
||||
addToast('Failed to save global settings', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddOrUpdateEnv() {
|
||||
try {
|
||||
if (editingEnvId) {
|
||||
await updateEnvironment(editingEnvId, newEnv);
|
||||
addToast('Environment updated', 'success');
|
||||
} else {
|
||||
await addEnvironment(newEnv);
|
||||
addToast('Environment added', 'success');
|
||||
}
|
||||
resetEnvForm();
|
||||
await loadSettings();
|
||||
} catch (error) {
|
||||
addToast('Failed to save environment', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteEnv(id) {
|
||||
if (confirm('Are you sure you want to delete this environment?')) {
|
||||
try {
|
||||
await deleteEnvironment(id);
|
||||
addToast('Environment deleted', 'success');
|
||||
await loadSettings();
|
||||
} catch (error) {
|
||||
addToast('Failed to delete environment', 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTestEnv(id) {
|
||||
try {
|
||||
const result = await testEnvironmentConnection(id);
|
||||
if (result.status === 'success') {
|
||||
addToast('Connection successful', 'success');
|
||||
} else {
|
||||
addToast(`Connection failed: ${result.message}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
addToast('Failed to test connection', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function editEnv(env) {
|
||||
newEnv = { ...env };
|
||||
editingEnvId = env.id;
|
||||
}
|
||||
|
||||
function resetEnvForm() {
|
||||
newEnv = {
|
||||
id: '',
|
||||
name: '',
|
||||
url: '',
|
||||
username: '',
|
||||
password: '',
|
||||
is_default: false
|
||||
};
|
||||
editingEnvId = null;
|
||||
}
|
||||
|
||||
onMount(loadSettings);
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto p-4">
|
||||
<h1 class="text-2xl font-bold mb-6">Settings</h1>
|
||||
|
||||
<section class="mb-8 bg-white p-6 rounded shadow">
|
||||
<h2 class="text-xl font-semibold mb-4">Global Settings</h2>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<div>
|
||||
<label for="backup_path" class="block text-sm font-medium text-gray-700">Backup Storage Path</label>
|
||||
<input type="text" id="backup_path" bind:value={settings.settings.backup_path} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<button on:click={handleSaveGlobal} class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 w-max">
|
||||
Save Global Settings
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mb-8 bg-white p-6 rounded shadow">
|
||||
<h2 class="text-xl font-semibold mb-4">Superset Environments</h2>
|
||||
|
||||
<div class="mb-6 overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">URL</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Username</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Default</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
{#each settings.environments as env}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.name}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.url}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.username}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">{env.is_default ? 'Yes' : 'No'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<button on:click={() => handleTestEnv(env.id)} class="text-green-600 hover:text-green-900 mr-4">Test</button>
|
||||
<button on:click={() => editEnv(env)} class="text-indigo-600 hover:text-indigo-900 mr-4">Edit</button>
|
||||
<button on:click={() => handleDeleteEnv(env.id)} class="text-red-600 hover:text-red-900">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 p-4 rounded">
|
||||
<h3 class="text-lg font-medium mb-4">{editingEnvId ? 'Edit' : 'Add'} Environment</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="env_id" class="block text-sm font-medium text-gray-700">ID</label>
|
||||
<input type="text" id="env_id" bind:value={newEnv.id} disabled={!!editingEnvId} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_name" class="block text-sm font-medium text-gray-700">Name</label>
|
||||
<input type="text" id="env_name" bind:value={newEnv.name} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_url" class="block text-sm font-medium text-gray-700">URL</label>
|
||||
<input type="text" id="env_url" bind:value={newEnv.url} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_user" class="block text-sm font-medium text-gray-700">Username</label>
|
||||
<input type="text" id="env_user" bind:value={newEnv.username} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="env_pass" class="block text-sm font-medium text-gray-700">Password</label>
|
||||
<input type="password" id="env_pass" bind:value={newEnv.password} class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm p-2" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox" id="env_default" bind:checked={newEnv.is_default} class="h-4 w-4 text-blue-600 border-gray-300 rounded" />
|
||||
<label for="env_default" class="ml-2 block text-sm text-gray-900">Default Environment</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex gap-2">
|
||||
<button on:click={handleAddOrUpdateEnv} class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
|
||||
{editingEnvId ? 'Update' : 'Add'} Environment
|
||||
</button>
|
||||
{#if editingEnvId}
|
||||
<button on:click={resetEnvForm} class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
|
||||
Cancel
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<!-- [/DEF:Settings] -->
|
||||
Reference in New Issue
Block a user