Initial Version

This commit is contained in:
root
2025-12-21 10:09:54 -05:00
commit 2fbddd7dbc
366 changed files with 41999 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
/**
* Core composables - reusable composition functions
*/
export { useClipboard } from './useClipboard'
export { usePreferences } from './usePreferences'

View File

@@ -0,0 +1,45 @@
import { ref } from 'vue'
/**
* Composable for clipboard operations with visual feedback
*
* @param timeout - Duration in ms to show success state (default: 2000)
* @returns Object containing copiedKey ref and copyToClipboard function
*
* @example
* ```vue
* <script setup>
* import { useClipboard } from '@KTXC/composables/useClipboard'
*
* const { copiedKey, copyToClipboard } = useClipboard()
* </script>
*
* <template>
* <v-btn
* :icon="copiedKey === index ? 'mdi-check' : 'mdi-content-copy'"
* :color="copiedKey === index ? 'success' : undefined"
* @click="copyToClipboard(text, index)"
* />
* </template>
* ```
*/
export function useClipboard<T = number>(timeout = 2000) {
const copiedKey = ref<T | null>(null)
const copyToClipboard = async (text: string, key: T): Promise<boolean> => {
try {
await navigator.clipboard.writeText(text)
copiedKey.value = key
setTimeout(() => { copiedKey.value = null }, timeout)
return true
} catch (err) {
console.error('Failed to copy to clipboard:', err)
return false
}
}
return {
copiedKey,
copyToClipboard
}
}

View File

@@ -0,0 +1,208 @@
import { computed, ref } from 'vue';
import { usePreferencesStore, type PreferencesState } from '@KTXC/stores/preferencesStore';
import { preferenceService } from '@KTXC/services/preferenceService';
/**
* Composable for managing user preferences
* Provides reactive access to preferences with automatic sync to server
*/
export function usePreferences() {
const store = usePreferencesStore();
const saving = ref(false);
const error = ref<string | null>(null);
/**
* Get all preferences
*/
const preferences = computed(() => store.preferences);
/**
* Get locked preference keys
*/
const locks = computed(() => store.locks);
/**
* Check if a preference is locked by tenant admin
*/
const isLocked = (key: keyof PreferencesState): boolean => {
return store.isLocked(key);
};
/**
* Get a single preference value
*/
const get = <K extends keyof PreferencesState>(key: K): PreferencesState[K] => {
return store.getPreference(key);
};
/**
* Set a single preference and sync to server
*/
const set = async <K extends keyof PreferencesState>(
key: K,
value: PreferencesState[K],
syncToServer = true
): Promise<boolean> => {
error.value = null;
// Update local state first
const success = store.setPreference(key, value);
if (!success) {
error.value = `Preference "${key}" is locked by administrator`;
return false;
}
// Sync to server if requested
if (syncToServer) {
saving.value = true;
try {
const response = await preferenceService.setPreference(key, value);
// Update store with server response to ensure consistency
store.setPreferences(response.effective);
store.setLocks(response.locks);
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to save preference';
return false;
} finally {
saving.value = false;
}
}
return true;
};
/**
* Update multiple preferences and sync to server
*/
const update = async (
prefs: Partial<PreferencesState>,
syncToServer = true
): Promise<{ success: boolean; rejected: string[] }> => {
error.value = null;
// Update local state
store.setPreferences(prefs);
if (syncToServer) {
saving.value = true;
try {
const response = await preferenceService.updatePreferences(prefs);
store.setPreferences(response.effective);
store.setLocks(response.locks);
return {
success: true,
rejected: response.rejectedKeys ?? [],
};
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to save preferences';
return { success: false, rejected: [] };
} finally {
saving.value = false;
}
}
return { success: true, rejected: [] };
};
/**
* Reset preferences to tenant defaults
*/
const reset = async (): Promise<boolean> => {
error.value = null;
saving.value = true;
try {
const response = await preferenceService.resetPreferences();
store.setPreferences(response.effective);
store.setLocks(response.locks);
return true;
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to reset preferences';
return false;
} finally {
saving.value = false;
}
};
/**
* Refresh preferences from server
*/
const refresh = async (): Promise<boolean> => {
error.value = null;
store.setLoading(true);
try {
const response = await preferenceService.getPreferences();
store.setPreferences(response.effective);
store.setLocks(response.locks);
return true;
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to load preferences';
return false;
} finally {
store.setLoading(false);
}
};
// Individual preference computed refs for convenience
const theme = computed({
get: () => store.preferences.theme,
set: (value: string) => set('theme', value),
});
const language = computed({
get: () => store.preferences.language,
set: (value: string) => set('language', value),
});
const timezone = computed({
get: () => store.preferences.timezone,
set: (value: string) => set('timezone', value),
});
const dateFormat = computed({
get: () => store.preferences.date_format,
set: (value: string) => set('date_format', value),
});
const timeFormat = computed({
get: () => store.preferences.time_format,
set: (value: string) => set('time_format', value),
});
const weekStart = computed({
get: () => store.preferences.week_start,
set: (value: string) => set('week_start', value),
});
const defaultModule = computed({
get: () => store.preferences.default_module ?? '',
set: (value: string) => set('default_module', value),
});
return {
// State
preferences,
locks,
saving,
error,
loading: computed(() => store.loading),
// Methods
get,
set,
update,
reset,
refresh,
isLocked,
// Individual preference refs
theme,
language,
timezone,
dateFormat,
timeFormat,
weekStart,
defaultModule,
};
}