70 lines
2.1 KiB
Svelte
70 lines
2.1 KiB
Svelte
<!-- [DEF:FrontendComponentShot:Component] -->
|
|
<script>
|
|
/**
|
|
* @TIER: CRITICAL
|
|
* @SEMANTICS: Task, Button, Action, UX
|
|
* @PURPOSE: Action button to spawn a new task with full UX feedback cycle.
|
|
* @LAYER: UI (Presentation)
|
|
* @RELATION: CALLS -> postApi
|
|
* @INVARIANT: Must prevent double-submission while loading.
|
|
*
|
|
* @TEST_DATA: idle_state -> {"isLoading": false}
|
|
* @TEST_DATA: loading_state -> {"isLoading": true}
|
|
*
|
|
* @UX_STATE: Idle -> Button enabled, primary color.
|
|
* @UX_STATE: Loading -> Button disabled, spinner visible.
|
|
* @UX_STATE: Error -> Toast notification triggers.
|
|
*
|
|
* @UX_FEEDBACK: Toast success/error.
|
|
* @UX_TEST: Idle -> {click: spawnTask, expected: isLoading=true}
|
|
* @UX_TEST: Success -> {api_resolve: 200, expected: toast.success called}
|
|
*/
|
|
import { postApi } from "$lib/api.js";
|
|
import { t } from "$lib/i18n";
|
|
import { toast } from "$lib/stores/toast";
|
|
|
|
export let plugin_id = "";
|
|
export let params = {};
|
|
|
|
let isLoading = false;
|
|
|
|
// [DEF:spawnTask:Function]
|
|
async function spawnTask() {
|
|
isLoading = true;
|
|
console.log("[FrontendComponentShot][Loading] Spawning task...");
|
|
|
|
try {
|
|
// 1. Action: API Call
|
|
const response = await postApi("/api/tasks", {
|
|
plugin_id,
|
|
params
|
|
});
|
|
|
|
// 2. Feedback: Success
|
|
if (response.task_id) {
|
|
console.log("[FrontendComponentShot][Success] Task created.");
|
|
toast.success($t.tasks.spawned_success);
|
|
}
|
|
} catch (error) {
|
|
// 3. Recovery: User notification
|
|
console.log("[FrontendComponentShot][Error] Failed:", error);
|
|
toast.error(`${$t.errors.task_failed}: ${error.message}`);
|
|
} finally {
|
|
isLoading = false;
|
|
}
|
|
}
|
|
// [/DEF:spawnTask:Function]
|
|
</script>
|
|
|
|
<button
|
|
on:click={spawnTask}
|
|
disabled={isLoading}
|
|
class="btn-primary flex items-center gap-2"
|
|
aria-busy={isLoading}
|
|
>
|
|
{#if isLoading}
|
|
<span class="animate-spin" aria-label="Loading">🌀</span>
|
|
{/if}
|
|
<span>{$t.actions.start_task}</span>
|
|
</button>
|
|
<!-- [/DEF:FrontendComponentShot:Component] --> |