Files
ss-tools/frontend/src/lib/stores/taskDrawer.js

165 lines
5.1 KiB
JavaScript

// [DEF:taskDrawer:Store]
// @TIER: CRITICAL
// @PURPOSE: Manage Task Drawer visibility and resource-to-task mapping
// @LAYER: UI
// @INVARIANT: resourceTaskMap always reflects current task associations
//
// @UX_STATE: Closed -> Drawer hidden, no active task
// @UX_STATE: Open -> Drawer visible, logs streaming
// @UX_STATE: InputRequired -> Interactive form rendered in drawer
//
// @TEST_CONTRACT: TaskDrawerStore ->
// {
// required_fields: {isOpen: boolean, activeTaskId: string|null, resourceTaskMap: Object},
// invariants: [
// "Updates isOpen and activeTaskId properly on openDrawerForTask",
// "Updates isOpen and activeTaskId=null on openDrawer",
// "Properly sets isOpen=false on closeDrawer",
// "Maintains mapping in resourceTaskMap correctly via updateResourceTask"
// ]
// }
// @TEST_FIXTURE: valid_store_state -> {"isOpen": true, "activeTaskId": "test_1", "resourceTaskMap": {"res1": {"taskId": "test_1", "status": "RUNNING"}}}
// @TEST_INVARIANT: state_management -> verifies: [valid_store_state]
import { writable } from 'svelte/store';
const TASK_DRAWER_AUTO_OPEN_STORAGE_KEY = "ss_tools.profile.auto_open_task_drawer";
function readAutoOpenTaskDrawerPreference() {
if (typeof window === "undefined") {
return true;
}
const rawValue = window.localStorage.getItem(TASK_DRAWER_AUTO_OPEN_STORAGE_KEY);
if (rawValue === "false") return false;
if (rawValue === "true") return true;
return true;
}
let autoOpenTaskDrawerPreference = readAutoOpenTaskDrawerPreference();
const initialState = {
isOpen: false,
activeTaskId: null,
resourceTaskMap: {}
};
export const taskDrawerStore = writable(initialState);
/**
* Open drawer for a specific task
* @param {string} taskId - The task ID to show in drawer
* @UX_STATE: Open -> Drawer visible, logs streaming
*/
export function openDrawerForTask(taskId) {
if (!taskId) {
console.log("[taskDrawer.openDrawerForTask][Action] Skip open: taskId is empty");
return false;
}
console.log(`[taskDrawer.openDrawerForTask][Action] Opening drawer for task ${taskId}`);
taskDrawerStore.update(state => ({
...state,
isOpen: true,
activeTaskId: taskId
}));
return true;
}
/**
* Update user preference for automatic drawer opening.
* @param {boolean} enabled - Whether automatic opening is enabled.
*/
export function setTaskDrawerAutoOpenPreference(enabled) {
autoOpenTaskDrawerPreference = enabled !== false;
if (typeof window !== "undefined") {
window.localStorage.setItem(
TASK_DRAWER_AUTO_OPEN_STORAGE_KEY,
autoOpenTaskDrawerPreference ? "true" : "false",
);
}
}
/**
* Read current user preference for automatic drawer opening.
* @returns {boolean} true if automatic drawer opening is enabled.
*/
export function getTaskDrawerAutoOpenPreference() {
return autoOpenTaskDrawerPreference;
}
/**
* Open drawer for a task only when user preference allows auto-open.
* @param {string} taskId - The task ID to show in drawer.
* @returns {boolean} true if drawer was opened.
*/
export function openDrawerForTaskIfPreferred(taskId) {
if (autoOpenTaskDrawerPreference !== true) {
console.log(
`[taskDrawer.openDrawerForTaskIfPreferred][Action] Skip auto-open for task ${taskId}`,
);
return false;
}
return openDrawerForTask(taskId);
}
/**
* Open drawer in list mode (no specific task)
* @UX_STATE: Open -> Drawer visible, showing recent task list
*/
export function openDrawer() {
console.log('[taskDrawer.openDrawer][Action] Opening drawer in list mode');
taskDrawerStore.update(state => ({
...state,
isOpen: true,
activeTaskId: null
}));
}
/**
* Close the drawer (task continues running)
* @UX_STATE: Closed -> Drawer hidden, no active task
*/
export function closeDrawer() {
console.log('[taskDrawer.closeDrawer][Action] Closing drawer');
taskDrawerStore.update(state => ({
...state,
isOpen: false,
activeTaskId: null
}));
}
/**
* Update resource-to-task mapping
* @param {string} resourceId - Resource ID (dashboard uuid, dataset id, etc.)
* @param {string} taskId - Task ID associated with this resource
* @param {string} status - Task status (IDLE, RUNNING, WAITING_INPUT, SUCCESS, ERROR)
*/
export function updateResourceTask(resourceId, taskId, status) {
console.log(`[taskDrawer.updateResourceTask][Action] Updating resource ${resourceId} -> task ${taskId}, status ${status}`);
taskDrawerStore.update(state => {
const newMap = { ...state.resourceTaskMap };
if (status === 'IDLE' || status === 'SUCCESS' || status === 'ERROR') {
// Remove mapping when task completes
delete newMap[resourceId];
} else {
// Add or update mapping
newMap[resourceId] = { taskId, status };
}
return { ...state, resourceTaskMap: newMap };
});
}
/**
* Get task status for a specific resource
* @param {string} resourceId - Resource ID
* @returns {Object|null} Task info or null if no active task
*/
export function getTaskForResource(resourceId) {
let result = null;
taskDrawerStore.subscribe(state => {
result = state.resourceTaskMap[resourceId] || null;
})();
return result;
}
// [/DEF:taskDrawer:Store]