diff --git a/.env.example b/.env.example index c61cc7529..854aae654 100644 --- a/.env.example +++ b/.env.example @@ -5,3 +5,5 @@ VITE_APP_INCLUDE_DEMOS= VITE_APP_ROUTER_MODE_HISTORY= VITE_APP_BUILD_VERSION= + +VITE_API_BASE_URL= diff --git a/.gitignore b/.gitignore index c3bb0dbc3..f9d0c5bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +.env # Editor directories and files .vscode/* @@ -25,3 +26,4 @@ dist-ssr # Local Netlify folder .netlify + diff --git a/src/data/pages/users.ts b/src/data/pages/users.ts index 80285863f..a8e9d5134 100644 --- a/src/data/pages/users.ts +++ b/src/data/pages/users.ts @@ -1,23 +1,5 @@ -import { sleep } from '../../services/utils' -import { User } from './../../pages/users/types' -import usersDb from './users-db.json' -import projectsDb from './projects-db.json' -import { Project } from '../../pages/projects/types' - -export const users = usersDb as User[] - -const getUserProjects = (userId: number | string) => { - return projectsDb - .filter((project) => project.team.includes(Number(userId))) - .map((project) => ({ - ...project, - project_owner: users.find((user) => user.id === project.project_owner)!, - team: project.team.map((userId) => users.find((user) => user.id === userId)!), - status: project.status as Project['status'], - })) -} - -// Simulate API calls +import { User } from '../../pages/users/types' +import api from '../../services/api' export type Pagination = { page: number @@ -37,25 +19,22 @@ export type Filters = { const getSortItem = (obj: any, sortBy: string) => { if (sortBy === 'projects') { - return obj.projects.map((project: any) => project.project_name).join(', ') + return obj.projects.map((project: any) => project).join(', ') } return obj[sortBy] } export const getUsers = async (filters: Partial) => { - await sleep(1000) const { isActive, search, sortBy, sortingOrder } = filters - let filteredUsers = users + let filteredUsers: User[] = await fetch(api.getAllUsers()).then((r) => r.json()) - filteredUsers = filteredUsers.filter((user) => user.active === isActive) + filteredUsers = filteredUsers.filter((user) => user.isActive === isActive) if (search) { - filteredUsers = filteredUsers.filter((user) => user.fullname.toLowerCase().includes(search.toLowerCase())) + filteredUsers = filteredUsers.filter((user) => user.fullName.toLowerCase().includes(search.toLowerCase())) } - filteredUsers = filteredUsers.map((user) => ({ ...user, projects: getUserProjects(user.id) })) - if (sortBy && sortingOrder) { filteredUsers = filteredUsers.sort((a, b) => { const first = getSortItem(a, sortBy) @@ -82,20 +61,18 @@ export const getUsers = async (filters: Partial) } export const addUser = async (user: User) => { - await sleep(1000) - users.unshift(user) + const headers = new Headers() + headers.append('Content-Type', 'application/json') + + return fetch(api.getAllUsers(), { method: 'POST', body: JSON.stringify(user), headers }) } export const updateUser = async (user: User) => { - await sleep(1000) - const index = users.findIndex((u) => u.id === user.id) - users[index] = user + const headers = new Headers() + headers.append('Content-Type', 'application/json') + return fetch(api.getUser(user.id), { method: 'PUT', body: JSON.stringify(user), headers }) } export const removeUser = async (user: User) => { - await sleep(1000) - users.splice( - users.findIndex((u) => u.id === user.id), - 1, - ) + return fetch(api.getUser(user.id), { method: 'DELETE' }) } diff --git a/src/pages/users/UsersPage.vue b/src/pages/users/UsersPage.vue index 56cb3c061..ed5e55d20 100644 --- a/src/pages/users/UsersPage.vue +++ b/src/pages/users/UsersPage.vue @@ -28,13 +28,13 @@ const onUserSaved = async (user: User) => { if (userToEdit.value) { await usersApi.update(user) notify({ - message: `${user.fullname} has been updated`, + message: `${user.fullName} has been updated`, color: 'success', }) } else { - usersApi.add(user) + await usersApi.add(user) notify({ - message: `${user.fullname} has been created`, + message: `${user.fullName} has been created`, color: 'success', }) } @@ -43,7 +43,7 @@ const onUserSaved = async (user: User) => { const onUserDelete = async (user: User) => { await usersApi.remove(user) notify({ - message: `${user.fullname} has been deleted`, + message: `${user.fullName} has been deleted`, color: 'success', }) } diff --git a/src/pages/users/composables/useUsers.ts b/src/pages/users/composables/useUsers.ts index 1d051f97d..bab35c89e 100644 --- a/src/pages/users/composables/useUsers.ts +++ b/src/pages/users/composables/useUsers.ts @@ -4,7 +4,7 @@ import { User } from '../types' import { watchIgnorable } from '@vueuse/core' const makePaginationRef = () => ref({ page: 1, perPage: 10, total: 0 }) -const makeSortingRef = () => ref({ sortBy: 'fullname', sortingOrder: null }) +const makeSortingRef = () => ref({ sortBy: 'fullName', sortingOrder: null }) const makeFiltersRef = () => ref>({ isActive: true, search: '' }) export const useUsers = (options?: { diff --git a/src/pages/users/types.ts b/src/pages/users/types.ts index 3b167c85e..882efbb5b 100644 --- a/src/pages/users/types.ts +++ b/src/pages/users/types.ts @@ -1,16 +1,15 @@ -import { Project } from '../projects/types' +export type UserRole = 'ADMIN' | 'USER' | 'OWNER' -export type UserRole = 'admin' | 'user' | 'owner' +export type UUID = `${string}-${string}-${string}-${string}-${string}` -export type BaseUser = { - id: number - fullname: string +export type User = { + id: UUID + fullName: string email: string username: string role: UserRole avatar: string + projects: string[] notes: string - active: boolean + isActive: boolean } - -export type User = BaseUser & { projects: Project[] } diff --git a/src/pages/users/widgets/EditUserForm.vue b/src/pages/users/widgets/EditUserForm.vue index 3b5713d31..d5ed5bd4d 100644 --- a/src/pages/users/widgets/EditUserForm.vue +++ b/src/pages/users/widgets/EditUserForm.vue @@ -17,19 +17,18 @@ const props = defineProps({ }, }) -const defaultNewUser: User = { - id: -1, +const defaultNewUser: Omit = { avatar: '', - fullname: '', - role: 'user', + fullName: '', + role: 'USER', username: '', notes: '', email: '', - active: true, + isActive: true, projects: [], } -const newUser = ref({ ...defaultNewUser }) +const newUser = ref({ ...defaultNewUser } as User) const isFormHasUnsavedChanges = computed(() => { return Object.keys(newUser.value).some((key) => { @@ -37,7 +36,9 @@ const isFormHasUnsavedChanges = computed(() => { return false } - return newUser.value[key as keyof User] !== (props.user ?? defaultNewUser)?.[key as keyof User] + return ( + newUser.value[key as keyof Omit] !== (props.user ?? defaultNewUser)?.[key as keyof Omit] + ) }) }) @@ -80,10 +81,10 @@ const onSave = () => { } } -const roleSelectOptions: { text: Capitalize; value: UserRole }[] = [ - { text: 'Admin', value: 'admin' }, - { text: 'User', value: 'user' }, - { text: 'Owner', value: 'owner' }, +const roleSelectOptions: { text: Capitalize>; value: UserRole }[] = [ + { text: 'Admin', value: 'ADMIN' }, + { text: 'User', value: 'USER' }, + { text: 'Owner', value: 'OWNER' }, ] const { projects } = useProjects({ pagination: ref({ page: 1, perPage: 9999, total: 10 }) }) @@ -112,11 +113,11 @@ const { projects } = useProjects({ pagination: ref({ page: 1, perPage: 9999, tot
@@ -162,7 +161,7 @@ const { projects } = useProjects({ pagination: ref({ page: 1, perPage: 9999, tot
- +
diff --git a/src/pages/users/widgets/UserAvatar.vue b/src/pages/users/widgets/UserAvatar.vue index 5c8a7b5a6..b64f5f21c 100644 --- a/src/pages/users/widgets/UserAvatar.vue +++ b/src/pages/users/widgets/UserAvatar.vue @@ -28,7 +28,7 @@ const isUrl = (avatar: string) => { diff --git a/src/pages/users/widgets/UsersTable.vue b/src/pages/users/widgets/UsersTable.vue index 701e498fc..22ff8a3f1 100644 --- a/src/pages/users/widgets/UsersTable.vue +++ b/src/pages/users/widgets/UsersTable.vue @@ -8,7 +8,7 @@ import { useVModel } from '@vueuse/core' import { Project } from '../../projects/types' const columns = defineVaDataTableColumns([ - { label: 'Full Name', key: 'fullname', sortable: true }, + { label: 'Full Name', key: 'fullName', sortable: true }, { label: 'Email', key: 'email', sortable: true }, { label: 'Username', key: 'username', sortable: true }, { label: 'Role', key: 'role', sortable: true }, @@ -24,7 +24,7 @@ const props = defineProps({ loading: { type: Boolean, default: false }, pagination: { type: Object as PropType, required: true }, sortBy: { type: String as PropType, required: true }, - sortingOrder: { type: String as PropType, required: true }, + sortingOrder: { type: String as PropType, default: null }, }) const emit = defineEmits<{ @@ -39,9 +39,9 @@ const sortByVModel = useVModel(props, 'sortBy', emit) const sortingOrderVModel = useVModel(props, 'sortingOrder', emit) const roleColors: Record = { - admin: 'danger', - user: 'background-element', - owner: 'warning', + ADMIN: 'danger', + USER: 'background-element', + OWNER: 'warning', } const totalPages = computed(() => Math.ceil(props.pagination.total / props.pagination.perPage)) @@ -51,7 +51,7 @@ const { confirm } = useModal() const onUserDelete = async (user: User) => { const agreed = await confirm({ title: 'Delete user', - message: `Are you sure you want to delete ${user.fullname}?`, + message: `Are you sure you want to delete ${user.fullName}?`, okText: 'Delete', cancelText: 'Cancel', size: 'small', @@ -66,13 +66,13 @@ const onUserDelete = async (user: User) => { const formatProjectNames = (projects: Project[]) => { if (projects.length === 0) return 'No projects' if (projects.length <= 2) { - return projects.map((project) => project.project_name).join(', ') + return projects.map((project) => project).join(', ') } return ( projects .slice(0, 2) - .map((project) => project.project_name) + .map((project) => project) .join(', ') + ' + ' + (projects.length - 2) + @@ -89,10 +89,10 @@ const formatProjectNames = (projects: Project[]) => { :items="users" :loading="$props.loading" > -