Skip to content

Commit

Permalink
feat: implement files uploading integration (#1065)
Browse files Browse the repository at this point in the history
  • Loading branch information
raichev-dima authored Nov 22, 2024
1 parent e83ed7b commit 42fa1cb
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 49 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"register-service-worker": "^1.7.1",
"sass": "^1.69.5",
"serve": "^14.2.1",
"uuid": "^11.0.3",
"vue": "3.5.8",
"vue-chartjs": "^5.3.0",
"vue-i18n": "^9.6.2",
Expand Down
25 changes: 23 additions & 2 deletions src/data/pages/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,36 @@ export const addUser = async (user: User) => {
const headers = new Headers()
headers.append('Content-Type', 'application/json')

return fetch(api.allUsers(), { method: 'POST', body: JSON.stringify(user), headers }).then((r) => r.json())
const result = await fetch(api.allUsers(), { method: 'POST', body: JSON.stringify(user), headers }).then((r) =>
r.json(),
)

if (!result.error) {
return result
}

throw new Error(result.error)
}

export const updateUser = async (user: User) => {
const headers = new Headers()
headers.append('Content-Type', 'application/json')
return fetch(api.user(user.id), { method: 'PUT', body: JSON.stringify(user), headers }).then((r) => r.json())

const result = await fetch(api.user(user.id), { method: 'PUT', body: JSON.stringify(user), headers }).then((r) =>
r.json(),
)

if (!result.error) {
return result
}

throw new Error(result.error)
}

export const removeUser = async (user: User) => {
return fetch(api.user(user.id), { method: 'DELETE' })
}

export const uploadAvatar = async (body: FormData) => {
return fetch(api.avatars(), { method: 'POST', body, redirect: 'follow' }).then((r) => r.json())
}
40 changes: 30 additions & 10 deletions src/pages/users/UsersPage.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ref, watchEffect } from 'vue'
import UsersTable from './widgets/UsersTable.vue'
import EditUserForm from './widgets/EditUserForm.vue'
import { User } from './types'
Expand All @@ -9,7 +9,7 @@ import { useProjects } from '../projects/composables/useProjects'
const doShowEditUserModal = ref(false)
const { users, isLoading, filters, sorting, pagination, ...usersApi } = useUsers()
const { users, isLoading, filters, sorting, pagination, error, ...usersApi } = useUsers()
const { projects } = useProjects()
const userToEdit = ref<User | null>(null)
Expand All @@ -26,19 +26,39 @@ const showAddUserModal = () => {
const { init: notify } = useToast()
watchEffect(() => {
if (error.value) {
notify({
message: error.value.message,
color: 'danger',
})
}
})
const onUserSaved = async (user: User) => {
if (user.avatar.startsWith('blob:')) {
const blob = await fetch(user.avatar).then((r) => r.blob())
const { publicUrl } = await usersApi.uploadAvatar(blob)
user.avatar = publicUrl
}
if (userToEdit.value) {
await usersApi.update(user)
notify({
message: `${user.fullname} has been updated`,
color: 'success',
})
if (!error.value) {
notify({
message: `${user.fullname} has been updated`,
color: 'success',
})
}
} else {
await usersApi.add(user)
notify({
message: `${user.fullname} has been created`,
color: 'success',
})
if (!error.value) {
notify({
message: `${user.fullname} has been created`,
color: 'success',
})
}
}
}
Expand Down
56 changes: 42 additions & 14 deletions src/pages/users/composables/useUsers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Ref, ref, unref, watch, computed } from 'vue'
import { v4 as uuid } from 'uuid'
import type { Filters, Pagination, Sorting } from '../../../data/pages/users'
import { User } from '../types'
import { useUsersStore } from '../../../stores/users'
Expand All @@ -13,20 +14,23 @@ export const useUsers = (options?: {
filters?: Ref<Partial<Filters>>
}) => {
const isLoading = ref(false)
const error = ref()
const usersStore = useUsersStore()

const { filters = makeFiltersRef(), sorting = makeSortingRef(), pagination = makePaginationRef() } = options || {}

const fetch = async () => {
isLoading.value = true
await usersStore.getAll({
filters: unref(filters),
sorting: unref(sorting),
pagination: unref(pagination),
})

isLoading.value = false
pagination.value = usersStore.pagination
try {
await usersStore.getAll({
filters: unref(filters),
sorting: unref(sorting),
pagination: unref(pagination),
})
pagination.value = usersStore.pagination
} finally {
isLoading.value = false
}
}

watch(
Expand Down Expand Up @@ -72,6 +76,7 @@ export const useUsers = (options?: {
})

return {
error,
isLoading,
filters,
sorting,
Expand All @@ -83,20 +88,43 @@ export const useUsers = (options?: {

async add(user: User) {
isLoading.value = true
await usersStore.add(user)
isLoading.value = false
try {
return await usersStore.add(user)
} catch (e) {
error.value = e
} finally {
isLoading.value = false
}
},

async update(user: User) {
isLoading.value = true
await usersStore.update(user)
isLoading.value = false
try {
return await usersStore.update(user)
} catch (e) {
error.value = e
} finally {
isLoading.value = false
}
},

async remove(user: User) {
isLoading.value = true
await usersStore.remove(user)
isLoading.value = false
try {
return await usersStore.remove(user)
} catch (e) {
error.value = e
} finally {
isLoading.value = false
}
},

async uploadAvatar(avatar: Blob) {
const formData = new FormData()
formData.append('avatar', avatar)
formData.append('id', uuid())

return usersStore.uploadAvatar(formData)
},
}
}
15 changes: 12 additions & 3 deletions src/pages/users/widgets/UserAvatar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { PropType } from 'vue'
import { User } from '../types'
import { type PropType } from 'vue'
import { type User } from '../types'
const avatarColor = (userName: string) => {
const colors = ['primary', '#FFD43A', '#ADFF00', '#262824', 'danger']
Expand All @@ -22,13 +22,22 @@ defineProps({
const isUrl = (avatar: string) => {
return avatar.startsWith('http') || avatar.startsWith('blob:')
}
const fallback = (fullname: string) => {
try {
const [firstName, lastName] = fullname.split(' ')
return `${firstName[0]}${lastName[0]}`
} catch {
return fullname[0]
}
}
</script>

<template>
<VaAvatar
:size="size"
:src="isUrl(user.avatar) ? user.avatar : ''"
:fallback-text="user.avatar || user.fullname[0]"
:fallback-text="fallback(user.fullname)"
:color="avatarColor(user.fullname)"
/>
</template>
1 change: 1 addition & 0 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export default {
project: (id: string) => `${apiBaseUrl}/projects/${id}`,
projects: ({ page, pageSize }: { page: number; pageSize: number }) =>
`${apiBaseUrl}/projects/?page=${page}&pageSize=${pageSize}`,
avatars: () => `${apiBaseUrl}/avatars`,
}
19 changes: 17 additions & 2 deletions src/stores/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { defineStore } from 'pinia'
import { addUser, type Filters, getUsers, Pagination, removeUser, Sorting, updateUser } from '../data/pages/users'
import {
addUser,
type Filters,
getUsers,
Pagination,
removeUser,
Sorting,
updateUser,
uploadAvatar,
} from '../data/pages/users'
import { User } from '../pages/users/types'

export const useUsersStore = defineStore('users', {
Expand All @@ -23,13 +32,15 @@ export const useUsersStore = defineStore('users', {

async add(user: User) {
const [newUser] = await addUser(user)
this.items.push(newUser)
this.items.unshift(newUser)
return newUser
},

async update(user: User) {
const [updatedUser] = await updateUser(user)
const index = this.items.findIndex(({ id }) => id === user.id)
this.items.splice(index, 1, updatedUser)
return updatedUser
},

async remove(user: User) {
Expand All @@ -40,5 +51,9 @@ export const useUsersStore = defineStore('users', {
this.items.splice(index, 1)
}
},

async uploadAvatar(formData: FormData) {
return uploadAvatar(formData)
},
},
})
25 changes: 7 additions & 18 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8168,16 +8168,7 @@ [email protected]:
resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz"
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==

"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -8202,14 +8193,7 @@ string_decoder@^1.1.1, string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -8726,6 +8710,11 @@ [email protected]:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==

uuid@^11.0.3:
version "11.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.3.tgz#248451cac9d1a4a4128033e765d137e2b2c49a3d"
integrity sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==

uuid@^9.0.0:
version "9.0.1"
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
Expand Down

0 comments on commit 42fa1cb

Please sign in to comment.