From 761e7152a228388f528282f0438df7a78ae67371 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Fri, 22 Dec 2023 02:03:16 +0530 Subject: [PATCH] feat: hook and dropdown menu for user sort management --- .../nc-gui/components/account/UserList.vue | 39 ++++- .../nc-gui/components/account/UserMenu.vue | 89 ++++++++++++ packages/nc-gui/composables/useUserSorts.ts | 133 ++++++++++++++++++ packages/nc-gui/lang/en.json | 4 +- packages/nc-gui/lib/types.ts | 6 + 5 files changed, 263 insertions(+), 8 deletions(-) create mode 100644 packages/nc-gui/components/account/UserMenu.vue create mode 100644 packages/nc-gui/composables/useUserSorts.ts diff --git a/packages/nc-gui/components/account/UserList.vue b/packages/nc-gui/components/account/UserList.vue index c5d5293651..a516ffc5a8 100644 --- a/packages/nc-gui/components/account/UserList.vue +++ b/packages/nc-gui/components/account/UserList.vue @@ -2,7 +2,16 @@ import { OrgUserRoles } from 'nocodb-sdk' import type { OrgUserReqType, RequestParams, UserType } from 'nocodb-sdk' import type { User } from '#imports' -import { extractSdkResponseErrorMsg, iconMap, useApi, useCopy, useDashboard, useDebounceFn, useNuxtApp } from '#imports' +import { + extractSdkResponseErrorMsg, + iconMap, + useApi, + useCopy, + useDashboard, + useDebounceFn, + useNuxtApp, + useUserSorts, +} from '#imports' const { api, isLoading } = useApi() @@ -19,8 +28,14 @@ const { user: loggedInUser } = useGlobal() const { copy } = useCopy() +const { sorts, sortDirection, loadSorts, saveOrUpdate, handleGetSortsData } = useUserSorts() + const users = ref([]) +const sortedUsers = computed(() => { + return handleGetSortsData(users.value, sorts.value) as UserType[] +}) + const currentPage = ref(1) const currentLimit = ref(10) @@ -64,6 +79,7 @@ const loadUsers = useDebounceFn(async (page = currentPage.value, limit = current onMounted(() => { loadUsers() + loadSorts() }) const updateRole = async (userId: string, roles: string) => { @@ -176,10 +192,21 @@ const openDeleteModal = (user: UserType) => {
-
- {{ $t('labels.email') }} +
+ + {{ $t('labels.email') }} + + +
+
+ + {{ $t('objects.role') }} + +
-
{{ $t('objects.role') }}
{{ $t('labels.action') }}
@@ -193,7 +220,7 @@ const openDeleteModal = (user: UserType) => {
{
diff --git a/packages/nc-gui/components/account/UserMenu.vue b/packages/nc-gui/components/account/UserMenu.vue new file mode 100644 index 0000000000..af99ebc0e6 --- /dev/null +++ b/packages/nc-gui/components/account/UserMenu.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/packages/nc-gui/composables/useUserSorts.ts b/packages/nc-gui/composables/useUserSorts.ts new file mode 100644 index 0000000000..15ccccd3e1 --- /dev/null +++ b/packages/nc-gui/composables/useUserSorts.ts @@ -0,0 +1,133 @@ +import rfdc from 'rfdc' +import type { UsersSortType } from '~/lib' +import { useGlobal } from '#imports' + +/** + * Hook for managing user sorts and sort configurations. + * @returns An object containing reactive values and functions related to user sorts. + */ +export function useUserSorts() { + const clone = rfdc() + + const { user } = useGlobal() + + const sorts = ref([]) + + // Key for storing user sort configurations in local storage + const userSortConfigKey = 'userSortConfig' + + // Default user ID if no user found (fallback) + const defaultUserId = 'default' + + /** + * Computed property that returns a record of sort directions based on the current sort configurations. + * @type {ComputedRef>} + */ + const sortDirection: ComputedRef> = computed(() => { + return sorts.value.reduce((acc, curr) => { + acc = { ...acc, [curr.field]: curr.direction } + return acc + }, {} as Record) + }) + + /** + * Loads user sort configurations from local storage based on the current user ID. + */ + function loadSorts(): void { + try { + // Retrieve sort configuration from local storage + const storedConfig = localStorage.getItem(userSortConfigKey) + + const sortConfig = storedConfig ? JSON.parse(storedConfig) : {} + sorts.value = sortConfig + + // Load user-specific sort configurations or default configurations + sorts.value = user.value?.id ? sortConfig[user.value.id] || [] : sortConfig[defaultUserId] || [] + } catch (error) { + console.error('Error while retrieving sort configuration from local storage:', error) + // Set sorts to an empty array in case of an error + sorts.value = [] + } + } + + /** + * Saves or updates a user sort configuration and updates local storage. + * @param {UsersSortType} newSortConfig - The new sort configuration to save or update. + */ + function saveOrUpdate(newSortConfig: UsersSortType): void { + try { + const fieldIndex = sorts.value.findIndex((sort) => sort.field === newSortConfig.field) + if (newSortConfig.direction) { + if (fieldIndex !== -1) { + // Update the direction if the field exists + sorts.value = [ + ...clone(sorts.value).map((sort) => { + if (sort.field === newSortConfig.field) { + sort.direction = newSortConfig.direction + } + return sort + }), + ] + } else { + // Add a new sort configuration if the field does not exist + sorts.value = [...clone(sorts.value), newSortConfig] + } + } else { + if (fieldIndex !== -1) { + // Remove the sort configuration if the field exists and direction is not present + sorts.value = [...clone(sorts.value).filter((sort) => sort.field !== newSortConfig.field)] + } + } + + // Update local storage with the new sort configurations + const storedConfig = localStorage.getItem(userSortConfigKey) + const sortConfig = storedConfig ? JSON.parse(storedConfig) : {} + + if (user.value?.id) { + // Save or delete user-specific sort configurations + if (sorts.value.length) { + sortConfig[user.value.id] = sorts.value + } else { + delete sortConfig[user.value.id] + } + } else { + // Save or delete default user sort configurations + sortConfig[defaultUserId] = sorts.value + } + + localStorage.setItem(userSortConfigKey, JSON.stringify(sortConfig)) + } catch (error) { + console.error('Error while retrieving sort configuration from local storage:', error) + } + } + + /** + * Sorts and returns a deep copy of an array of objects based on the provided sort configurations. + * + * @param data - The array of objects to be sorted. + * @param sortsConfig - The array of sort configurations. + * @returns A new array containing sorted objects. + * @template T - The type of objects in the input array. + */ + function handleGetSortsData>(data: T[], sortsConfig: UsersSortType[] = sorts.value): T[] { + const sortedData = clone(data).sort((a, b) => { + let sortCondition = 0 + + for (const { field, direction } of sortsConfig) { + if (a[field]) continue + + if (direction === 'asc') { + sortCondition = sortCondition || a[field]?.localeCompare(b[field]) + } else if (direction === 'desc') { + sortCondition = sortCondition || b[field]?.localeCompare(a[field]) + } + } + + return sortCondition + }) + + return sortedData + } + + return { sorts, sortDirection, loadSorts, saveOrUpdate, handleGetSortsData } +} diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index b2875c9f0e..ae26001223 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -189,7 +189,8 @@ "shift": "Shift", "enter": "Enter", "seconds": "Seconds", - "paste": "Paste" + "paste": "Paste", + "resetSort": "Reset Sort" }, "objects": { "workspace": "Workspace", @@ -1212,7 +1213,6 @@ "thankYou": "Thank you!", "submittedFormData": "You have successfully submitted the form data.", "editingSystemKeyNotSupported": "Editing system key not supported" - }, "error": { "nameRequired": "Name Required", diff --git a/packages/nc-gui/lib/types.ts b/packages/nc-gui/lib/types.ts index 7643f681ce..0cde7912e3 100644 --- a/packages/nc-gui/lib/types.ts +++ b/packages/nc-gui/lib/types.ts @@ -176,6 +176,11 @@ interface SidebarTableNode extends TableType { isViewsLoading?: boolean } +interface UsersSortType { + field: string + direction?: 'asc' | 'desc' +} + export type { User, ProjectMetaInfo, @@ -201,4 +206,5 @@ export type { ViewPageType, NcButtonSize, SidebarTableNode, + UsersSortType, }