feat: Implement user profile preferences for start page, Git identity, and task drawer auto-open, alongside Git server default branch configuration.
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
taskDrawerStore,
|
||||
openDrawerForTask,
|
||||
openDrawer,
|
||||
setTaskDrawerAutoOpenPreference,
|
||||
} from "$lib/stores/taskDrawer.js";
|
||||
import { sidebarStore, toggleMobileSidebar } from "$lib/stores/sidebar.js";
|
||||
import { t } from "$lib/i18n";
|
||||
@@ -112,6 +113,19 @@
|
||||
toggleAssistantChat();
|
||||
}
|
||||
|
||||
async function hydrateTaskDrawerPreference() {
|
||||
try {
|
||||
const response = await api.getProfilePreferences();
|
||||
const autoOpenTaskDrawer = response?.preference?.auto_open_task_drawer;
|
||||
setTaskDrawerAutoOpenPreference(autoOpenTaskDrawer !== false);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"[TopNavbar][REFLECT] Failed to hydrate task drawer preference, fallback to local preference cache",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearchFocus() {
|
||||
isSearchFocused = true;
|
||||
showSearchDropdown = groupedSearchResults.length > 0;
|
||||
@@ -320,6 +334,7 @@
|
||||
|
||||
onMount(() => {
|
||||
void initializeEnvironmentContext();
|
||||
void hydrateTaskDrawerPreference();
|
||||
if (typeof document !== "undefined") {
|
||||
document.addEventListener("click", handleDocumentClick);
|
||||
}
|
||||
|
||||
@@ -401,8 +401,15 @@
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profile",
|
||||
"description": "Manage your dashboard filter preferences.",
|
||||
"description": "Manage your profile preferences, Git integration, and access view.",
|
||||
"dashboard_preferences": "Dashboard Preferences",
|
||||
"security_access": "Security & Access",
|
||||
"read_only": "Read-only",
|
||||
"security_read_only_note": "This section is read-only. Role changes are managed in Admin → Users.",
|
||||
"current_role": "Current Role",
|
||||
"role_source": "Role Source",
|
||||
"permissions": "Permissions",
|
||||
"permission_none": "No permissions available",
|
||||
"superset_environment": "Superset Environment",
|
||||
"superset_environment_placeholder": "Select environment",
|
||||
"superset_account": "Your Apache Superset Account",
|
||||
@@ -415,6 +422,27 @@
|
||||
"save_error": "Failed to save preferences. Please try again.",
|
||||
"invalid_username": "Username should not contain spaces. Please enter a valid Apache Superset username.",
|
||||
"username_required": "Superset username is required when default filter is enabled.",
|
||||
"invalid_git_email": "Git email should be a valid email address.",
|
||||
"git_integration": "Git Integration (PAT)",
|
||||
"git_username": "Git Username",
|
||||
"git_username_placeholder": "Enter git username",
|
||||
"git_email": "Git Email",
|
||||
"git_email_placeholder": "Enter git email",
|
||||
"git_token": "GitLab / GitHub Token",
|
||||
"git_token_clear": "Clear token",
|
||||
"git_token_placeholder": "Enter new personal access token",
|
||||
"git_token_hint": "Token is never returned in plain text. Leave empty to keep current token.",
|
||||
"git_token_masked_label": "Current token",
|
||||
"git_token_not_set": "Token is not set",
|
||||
"user_preferences": "User Preferences",
|
||||
"start_page": "Start Page",
|
||||
"start_page_dashboards": "Dashboards",
|
||||
"start_page_datasets": "Datasets",
|
||||
"start_page_reports": "Reports / Logs",
|
||||
"table_density": "Table Density",
|
||||
"table_density_compact": "Compact",
|
||||
"table_density_comfortable": "Comfortable",
|
||||
"auto_open_task_drawer": "Automatically open task drawer for long-running tasks",
|
||||
"filter_badge_active": "My Dashboards Only",
|
||||
"filter_badge_override": "Showing all dashboards temporarily",
|
||||
"filter_empty_state": "No dashboards found for your account. Try adjusting your filter settings.",
|
||||
|
||||
@@ -399,8 +399,15 @@
|
||||
},
|
||||
"profile": {
|
||||
"title": "Профиль",
|
||||
"description": "Управляйте настройками фильтра дашбордов.",
|
||||
"description": "Управляйте настройками профиля, Git-интеграцией и просмотром доступа.",
|
||||
"dashboard_preferences": "Настройки дашбордов",
|
||||
"security_access": "Безопасность и доступ",
|
||||
"read_only": "Только чтение",
|
||||
"security_read_only_note": "Этот раздел только для чтения. Изменение ролей выполняется в Админ → Users.",
|
||||
"current_role": "Текущая роль",
|
||||
"role_source": "Источник роли",
|
||||
"permissions": "Права доступа",
|
||||
"permission_none": "Права доступа отсутствуют",
|
||||
"superset_environment": "Окружение Superset",
|
||||
"superset_environment_placeholder": "Выберите окружение",
|
||||
"superset_account": "Ваш аккаунт Apache Superset",
|
||||
@@ -413,6 +420,27 @@
|
||||
"save_error": "Не удалось сохранить настройки. Попробуйте снова.",
|
||||
"invalid_username": "Имя пользователя не должно содержать пробелы. Введите корректное имя пользователя Apache Superset.",
|
||||
"username_required": "Имя пользователя Superset обязательно, когда фильтр по умолчанию включен.",
|
||||
"invalid_git_email": "Git email должен быть корректным адресом электронной почты.",
|
||||
"git_integration": "Интеграция с Git (PAT)",
|
||||
"git_username": "Имя пользователя Git",
|
||||
"git_username_placeholder": "Введите имя пользователя Git",
|
||||
"git_email": "Git Email",
|
||||
"git_email_placeholder": "Введите Git Email",
|
||||
"git_token": "Токен GitLab / GitHub",
|
||||
"git_token_clear": "Очистить токен",
|
||||
"git_token_placeholder": "Введите новый персональный токен доступа",
|
||||
"git_token_hint": "Токен никогда не возвращается в открытом виде. Оставьте поле пустым, чтобы сохранить текущий токен.",
|
||||
"git_token_masked_label": "Текущий токен",
|
||||
"git_token_not_set": "Токен не задан",
|
||||
"user_preferences": "Пользовательские настройки",
|
||||
"start_page": "Стартовая страница",
|
||||
"start_page_dashboards": "Дашборды",
|
||||
"start_page_datasets": "Датасеты",
|
||||
"start_page_reports": "Отчеты / Логи",
|
||||
"table_density": "Плотность таблиц",
|
||||
"table_density_compact": "Компактная",
|
||||
"table_density_comfortable": "Комфортная",
|
||||
"auto_open_task_drawer": "Автоматически открывать Task Drawer для долгих задач",
|
||||
"filter_badge_active": "Только мои дашборды",
|
||||
"filter_badge_override": "Временно показаны все дашборды",
|
||||
"filter_empty_state": "Для вашего аккаунта дашборды не найдены. Попробуйте изменить настройки фильтра.",
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { get } from 'svelte/store';
|
||||
import { taskDrawerStore, openDrawerForTask, closeDrawer, updateResourceTask } from '../taskDrawer.js';
|
||||
import {
|
||||
taskDrawerStore,
|
||||
openDrawerForTask,
|
||||
openDrawerForTaskIfPreferred,
|
||||
closeDrawer,
|
||||
updateResourceTask,
|
||||
setTaskDrawerAutoOpenPreference
|
||||
} from '../taskDrawer.js';
|
||||
|
||||
describe('taskDrawerStore', () => {
|
||||
beforeEach(() => {
|
||||
setTaskDrawerAutoOpenPreference(true);
|
||||
taskDrawerStore.set({
|
||||
isOpen: false,
|
||||
activeTaskId: null,
|
||||
@@ -26,6 +34,26 @@ describe('taskDrawerStore', () => {
|
||||
expect(state.activeTaskId).toBe(null);
|
||||
});
|
||||
|
||||
it('should open drawer via preference-aware helper when auto-open is enabled', () => {
|
||||
setTaskDrawerAutoOpenPreference(true);
|
||||
const opened = openDrawerForTaskIfPreferred('task-123');
|
||||
const state = get(taskDrawerStore);
|
||||
|
||||
expect(opened).toBe(true);
|
||||
expect(state.isOpen).toBe(true);
|
||||
expect(state.activeTaskId).toBe('task-123');
|
||||
});
|
||||
|
||||
it('should skip opening drawer via preference-aware helper when auto-open is disabled', () => {
|
||||
setTaskDrawerAutoOpenPreference(false);
|
||||
const opened = openDrawerForTaskIfPreferred('task-123');
|
||||
const state = get(taskDrawerStore);
|
||||
|
||||
expect(opened).toBe(false);
|
||||
expect(state.isOpen).toBe(false);
|
||||
expect(state.activeTaskId).toBe(null);
|
||||
});
|
||||
|
||||
it('should update resource task mapping for running task', () => {
|
||||
updateResourceTask('dash-1', 'task-1', 'RUNNING');
|
||||
const state = get(taskDrawerStore);
|
||||
|
||||
@@ -21,7 +21,21 @@
|
||||
// @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, derived } from 'svelte/store';
|
||||
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,
|
||||
@@ -37,12 +51,54 @@ export const taskDrawerStore = writable(initialState);
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user