impement and fix user settings
This commit is contained in:
@@ -36,24 +36,26 @@ class UserProfileController extends ControllerAbstract
|
|||||||
* Update user profile fields
|
* Update user profile fields
|
||||||
* Only editable fields can be updated. Provider-managed fields are automatically filtered out.
|
* Only editable fields can be updated. Provider-managed fields are automatically filtered out.
|
||||||
*
|
*
|
||||||
* @param array $profile Key-value pairs of profile fields to update
|
* @param array $data Key-value pairs of profile fields to update
|
||||||
*
|
*
|
||||||
* @example request body:
|
* @example request body:
|
||||||
* {
|
* {
|
||||||
* "name_given": "John",
|
* "data": {
|
||||||
* "name_family": "Doe",
|
* "name_given": "John",
|
||||||
* "phone": "+1234567890"
|
* "name_family": "Doe",
|
||||||
|
* "phone": "+1234567890"
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @return JsonResponse Updated profile data
|
* @return JsonResponse Updated profile data
|
||||||
*/
|
*/
|
||||||
#[AuthenticatedRoute('/user/profile', name: 'user.profile.update', methods: ['PUT', 'PATCH'])]
|
#[AuthenticatedRoute('/user/profile', name: 'user.profile.update', methods: ['PUT', 'PATCH'])]
|
||||||
public function update(array $profile = []): JsonResponse
|
public function update(array $data): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $this->userIdentity->identifier();
|
$userId = $this->userIdentity->identifier();
|
||||||
|
|
||||||
// storeProfile automatically filters out provider-managed fields
|
// storeProfile automatically filters out provider-managed fields
|
||||||
$this->userService->storeProfile($userId, $profile);
|
$this->userService->storeProfile($userId, $data);
|
||||||
|
|
||||||
// Return updated profile
|
// Return updated profile
|
||||||
$updatedProfile = $this->userService->fetchProfile($userId);
|
$updatedProfile = $this->userService->fetchProfile($userId);
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ class UserSettingsController extends ControllerAbstract
|
|||||||
#[AuthenticatedRoute('/user/settings', name: 'user.settings.read', methods: ['GET'])]
|
#[AuthenticatedRoute('/user/settings', name: 'user.settings.read', methods: ['GET'])]
|
||||||
public function read(): JsonResponse
|
public function read(): JsonResponse
|
||||||
{
|
{
|
||||||
// Fetch all settings
|
// Fetch all settings (no filter)
|
||||||
$settings = $this->userService->fetchSettings([]);
|
$settings = $this->userService->fetchSettings();
|
||||||
|
|
||||||
return new JsonResponse($settings, JsonResponse::HTTP_OK);
|
return new JsonResponse($settings, JsonResponse::HTTP_OK);
|
||||||
}
|
}
|
||||||
@@ -35,24 +35,26 @@ class UserSettingsController extends ControllerAbstract
|
|||||||
/**
|
/**
|
||||||
* Update user settings
|
* Update user settings
|
||||||
*
|
*
|
||||||
* @param array $settings Key-value pairs of settings to update
|
* @param array $data Key-value pairs of settings to update
|
||||||
*
|
*
|
||||||
* @example request body:
|
* @example request body:
|
||||||
* {
|
* {
|
||||||
* "theme": "dark",
|
* "data": {
|
||||||
* "language": "en",
|
* "theme": "dark",
|
||||||
* "notifications": true
|
* "language": "en",
|
||||||
|
* "notifications": true
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @return JsonResponse Updated settings data
|
* @return JsonResponse Updated settings data
|
||||||
*/
|
*/
|
||||||
#[AuthenticatedRoute('/user/settings', name: 'user.settings.update', methods: ['PUT', 'PATCH'])]
|
#[AuthenticatedRoute('/user/settings', name: 'user.settings.update', methods: ['PUT', 'PATCH'])]
|
||||||
public function update(array $settings = []): JsonResponse
|
public function update(array $data): JsonResponse
|
||||||
{
|
{
|
||||||
$this->userService->storeSettings($settings);
|
$this->userService->storeSettings($data);
|
||||||
|
|
||||||
// Return updated settings
|
// Return updated settings
|
||||||
$updatedSettings = $this->userService->fetchSettings(array_keys($settings));
|
$updatedSettings = $this->userService->fetchSettings(array_keys($data));
|
||||||
|
|
||||||
return new JsonResponse($updatedSettings, JsonResponse::HTTP_OK);
|
return new JsonResponse($updatedSettings, JsonResponse::HTTP_OK);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,8 @@ class UserStore
|
|||||||
['$set' => $updates]
|
['$set' => $updates]
|
||||||
);
|
);
|
||||||
|
|
||||||
return $result->getModifiedCount() > 0;
|
// Return true if document was matched (exists), even if not modified
|
||||||
|
return $result->getMatchedCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,4 +4,17 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterView } from 'vue-router';
|
import { RouterView } from 'vue-router';
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
import { useTheme } from 'vuetify';
|
||||||
|
import { useLayoutStore } from '@KTXC/stores/layoutStore';
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
|
// Apply saved theme on mount
|
||||||
|
onMounted(() => {
|
||||||
|
if (layoutStore.theme) {
|
||||||
|
theme.global.name.value = layoutStore.theme;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const userStore = useUserStore();
|
|||||||
const integrationStore = useIntegrationStore();
|
const integrationStore = useIntegrationStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
const identityData = computed(() => userStore.user);
|
const identityData = computed(() => userStore.auth);
|
||||||
const profileMenuItems = computed(() => integrationStore.getItems('profile_menu'));
|
const profileMenuItems = computed(() => integrationStore.getItems('profile_menu'));
|
||||||
|
|
||||||
// Theme toggle
|
// Theme toggle
|
||||||
@@ -20,7 +20,7 @@ const isDarkMode = computed(() => theme.global.name.value === 'dark');
|
|||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
const newTheme = theme.global.name.value === 'light' ? 'dark' : 'light';
|
const newTheme = theme.global.name.value === 'light' ? 'dark' : 'light';
|
||||||
theme.global.name.value = newTheme;
|
theme.global.name.value = newTheme;
|
||||||
localStorage.setItem('theme', newTheme);
|
layoutStore.setTheme(newTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigate to settings
|
// Navigate to settings
|
||||||
@@ -45,7 +45,7 @@ const goToSettings = () => {
|
|||||||
<h6 class="text-h6 mb-0">
|
<h6 class="text-h6 mb-0">
|
||||||
{{ identityData?.label || 'User Name' }}
|
{{ identityData?.label || 'User Name' }}
|
||||||
</h6>
|
</h6>
|
||||||
<p class="text-caption mb-0">{{ identityData?.email || '' }}</p>
|
<p class="text-caption mb-0">{{ userStore.getProfileField('email') || '' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const userService = {
|
|||||||
profileUpdateTimer = null;
|
profileUpdateTimer = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchWrapper.put('/user/profile', updates);
|
await fetchWrapper.put('/user/profile', { data: updates });
|
||||||
resolve();
|
resolve();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@@ -59,7 +59,7 @@ export const userService = {
|
|||||||
const updates = { ...pendingProfileUpdates, ...fields };
|
const updates = { ...pendingProfileUpdates, ...fields };
|
||||||
pendingProfileUpdates = {};
|
pendingProfileUpdates = {};
|
||||||
|
|
||||||
return fetchWrapper.put('/user/profile', updates);
|
return fetchWrapper.put('/user/profile', { data: updates });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +74,7 @@ export const userService = {
|
|||||||
if (Object.keys(pendingProfileUpdates).length > 0) {
|
if (Object.keys(pendingProfileUpdates).length > 0) {
|
||||||
const updates = { ...pendingProfileUpdates };
|
const updates = { ...pendingProfileUpdates };
|
||||||
pendingProfileUpdates = {};
|
pendingProfileUpdates = {};
|
||||||
return fetchWrapper.put('/user/profile', updates);
|
return fetchWrapper.put('/user/profile', { data: updates });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ export const userService = {
|
|||||||
settingsUpdateTimer = null;
|
settingsUpdateTimer = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchWrapper.put('/user/settings', updates);
|
await fetchWrapper.put('/user/settings', { data: updates });
|
||||||
resolve();
|
resolve();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@@ -122,7 +122,7 @@ export const userService = {
|
|||||||
const updates = { ...pendingSettingsUpdates, ...settings };
|
const updates = { ...pendingSettingsUpdates, ...settings };
|
||||||
pendingSettingsUpdates = {};
|
pendingSettingsUpdates = {};
|
||||||
|
|
||||||
return fetchWrapper.put('/user/settings', updates);
|
return fetchWrapper.put('/user/settings', { data: updates });
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +137,7 @@ export const userService = {
|
|||||||
if (Object.keys(pendingSettingsUpdates).length > 0) {
|
if (Object.keys(pendingSettingsUpdates).length > 0) {
|
||||||
const updates = { ...pendingSettingsUpdates };
|
const updates = { ...pendingSettingsUpdates };
|
||||||
pendingSettingsUpdates = {};
|
pendingSettingsUpdates = {};
|
||||||
return fetchWrapper.put('/user/settings', updates);
|
return fetchWrapper.put('/user/settings', { data: updates });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ref } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import config from '@KTXC/config';
|
import config from '@KTXC/config';
|
||||||
|
import { useUserStore } from './userStore';
|
||||||
|
|
||||||
export type MenuMode = 'apps' | 'settings';
|
export type MenuMode = 'apps' | 'settings';
|
||||||
|
|
||||||
@@ -8,14 +9,32 @@ export const useLayoutStore = defineStore('layout', () => {
|
|||||||
// Loading state
|
// Loading state
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
// Sidebar state
|
// Sidebar state - initialize from settings or config
|
||||||
const sidebarDrawer = ref(config.Sidebar_drawer);
|
const userStore = useUserStore();
|
||||||
const miniSidebar = ref(config.mini_sidebar);
|
const sidebarDrawer = ref(userStore.getSetting('sidebar_drawer') ?? config.Sidebar_drawer);
|
||||||
|
const miniSidebar = ref(userStore.getSetting('mini_sidebar') ?? config.mini_sidebar);
|
||||||
const menuMode = ref<MenuMode>('apps');
|
const menuMode = ref<MenuMode>('apps');
|
||||||
|
|
||||||
// Theme state
|
// Theme state - initialize from settings or config
|
||||||
const theme = ref(config.actTheme);
|
const theme = ref(userStore.getSetting('theme') ?? config.actTheme);
|
||||||
const font = ref(config.fontTheme);
|
const font = ref(userStore.getSetting('font') ?? config.fontTheme);
|
||||||
|
|
||||||
|
// Watch and sync sidebar state to settings
|
||||||
|
watch(sidebarDrawer, (value) => {
|
||||||
|
userStore.setSetting('sidebar_drawer', value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(miniSidebar, (value) => {
|
||||||
|
userStore.setSetting('mini_sidebar', value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(theme, (value) => {
|
||||||
|
userStore.setSetting('theme', value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(font, (value) => {
|
||||||
|
userStore.setSetting('font', value);
|
||||||
|
});
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
function toggleSidebarDrawer() {
|
function toggleSidebarDrawer() {
|
||||||
|
|||||||
Reference in New Issue
Block a user