Files
ss-tools/frontend/src/routes/settings/git/+page.svelte
2026-01-23 21:58:32 +03:00

182 lines
7.0 KiB
Svelte

<!-- [DEF:GitSettingsPage:Component] -->
<!--
@SEMANTICS: git, settings, configuration, integration
@PURPOSE: Manage Git server configurations for dashboard versioning.
@LAYER: Page
@RELATION: USES -> gitService
@RELATION: USES -> Button, Input, Card, PageHeader, Select
@INVARIANT: All configurations must be validated via connection test.
-->
<script lang="ts">
// [SECTION: IMPORTS]
import { onMount } from 'svelte';
import { gitService } from '../../../services/gitService';
import { addToast as toast } from '../../../lib/toasts.js';
import { t } from '$lib/i18n';
import { Button, Input, Card, PageHeader, Select } from '$lib/ui';
// [/SECTION: IMPORTS]
// [SECTION: STATE]
let configs = [];
let newConfig = {
name: '',
provider: 'GITHUB',
url: 'https://github.com',
pat: '',
default_repository: ''
};
let testing = false;
// [/SECTION: STATE]
// [DEF:loadConfigs:Function]
/**
* @purpose Fetches existing git configurations.
* @pre Component is mounted.
* @post configs state is populated.
*/
async function loadConfigs() {
try {
configs = await gitService.getConfigs();
} catch (e) {
toast(e.message, 'error');
}
}
// [/DEF:loadConfigs:Function]
onMount(loadConfigs);
// [DEF:handleTest:Function]
/**
* @purpose Tests connection to a git server with current form data.
* @pre newConfig contains valid provider, url, and pat.
* @post testing state is managed; toast shown with result.
*/
async function handleTest() {
testing = true;
try {
const result = await gitService.testConnection(newConfig);
if (result.status === 'success') {
toast('Connection successful', 'success');
} else {
toast(result.message || 'Connection failed', 'error');
}
} catch (e) {
toast('Connection failed', 'error');
} finally {
testing = false;
}
}
// [/DEF:handleTest:Function]
// [DEF:handleSave:Function]
/**
* @purpose Saves a new git configuration.
* @pre newConfig is valid and tested.
* @post New config is saved to DB and added to configs list.
*/
async function handleSave() {
try {
const saved = await gitService.createConfig(newConfig);
configs = [...configs, saved];
toast('Configuration saved', 'success');
newConfig = { name: '', provider: 'GITHUB', url: 'https://github.com', pat: '', default_repository: '' };
} catch (e) {
toast(e.message, 'error');
}
}
// [/DEF:handleSave:Function]
// [DEF:handleDelete:Function]
/**
* @purpose Deletes a git configuration by ID.
* @param {string} id - Configuration ID.
* @pre id is valid; user confirmed deletion.
* @post Configuration is removed from DB and local state.
*/
async function handleDelete(id) {
if (!confirm('Are you sure you want to delete this Git configuration?')) return;
try {
await gitService.deleteConfig(id);
configs = configs.filter(c => c.id !== id);
toast('Configuration deleted', 'success');
} catch (e) {
toast(e.message, 'error');
}
}
// [/DEF:handleDelete:Function]
</script>
<!-- [SECTION: TEMPLATE] -->
<div class="p-6 max-w-6xl mx-auto">
<PageHeader title="Git Integration Settings" />
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- List of Configs -->
<Card title="Configured Servers">
{#if configs.length === 0}
<p class="text-gray-500">No Git servers configured.</p>
{:else}
<ul class="divide-y divide-gray-100">
{#each configs as config}
<li class="py-4 flex justify-between items-center">
<div>
<div class="flex items-center gap-2">
<span class="font-medium text-gray-900">{config.name}</span>
<span class="text-xs font-mono bg-gray-50 text-gray-500 px-1.5 py-0.5 rounded">{config.provider}</span>
</div>
<div class="text-xs text-gray-400 mt-1">{config.url}</div>
</div>
<div class="flex items-center space-x-4">
<span class="px-2 py-1 text-xs font-medium rounded {config.status === 'CONNECTED' ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'}">
{config.status}
</span>
<button on:click={() => handleDelete(config.id)} class="text-gray-400 hover:text-red-600 transition-colors" title="Delete">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</button>
</div>
</li>
{/each}
</ul>
{/if}
</Card>
<!-- Add New Config -->
<Card title="Add Git Server">
<div class="space-y-6">
<Input label="Display Name" bind:value={newConfig.name} placeholder="e.g. My GitHub" />
<Select
label="Provider"
bind:value={newConfig.provider}
options={[
{ value: 'GITHUB', label: 'GitHub' },
{ value: 'GITLAB', label: 'GitLab' },
{ value: 'GITEA', label: 'Gitea' }
]}
/>
<Input label="Server URL" bind:value={newConfig.url} />
<Input label="Personal Access Token (PAT)" type="password" bind:value={newConfig.pat} />
<Input label="Default Repository (Optional)" bind:value={newConfig.default_repository} placeholder="org/repo" />
<div class="flex gap-3 pt-2">
<Button variant="secondary" on:click={handleTest} isLoading={testing}>
Test Connection
</Button>
<Button variant="primary" on:click={handleSave}>
Save Configuration
</Button>
</div>
</div>
</Card>
</div>
</div>
<!-- [/SECTION: TEMPLATE] -->
<style>
/* Styles are handled by Tailwind */
</style>
<!-- [/DEF:GitSettingsPage:Component] -->