From 29a529be4dae9c40a4e3878370b43db6c9f5b96d Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Thu, 13 Feb 2025 16:44:51 +0900 Subject: [PATCH 01/26] =?UTF-8?q?:recycle:=20[fix]=20:=20=ED=9E=88?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=20=EA=B8=B8=EC=9D=B4=EC=A0=9C?= =?UTF-8?q?=ED=95=9C=20+=20=EC=A0=84=EC=86=A1=EC=8B=9C=EC=97=90=EB=8F=84?= =?UTF-8?q?=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task-detail/TaskDetailHistoryInput.vue | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/task-detail/TaskDetailHistoryInput.vue b/src/components/task-detail/TaskDetailHistoryInput.vue index 2dcc29a..7e65318 100644 --- a/src/components/task-detail/TaskDetailHistoryInput.vue +++ b/src/components/task-detail/TaskDetailHistoryInput.vue @@ -2,15 +2,18 @@
@@ -34,7 +37,9 @@ @click="sendMessage" />
-

({{ inputLength }}/{{ 254 }})

+

+ ({{ inputLength }}/{{ 200 }}) +

history.length !== 0 && + inputLength.value < 200 && (info.value.role !== 'ROLE_USER' || info.value.nickname === requestorName) ) From 2be62e01445951231180a351898c596441c0f214 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 09:39:44 +0900 Subject: [PATCH 02/26] =?UTF-8?q?:recycle:=20[fix]=20:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=EB=A6=84=20=EA=B8=B8=EC=9D=B4=20=EC=B6=95?= =?UTF-8?q?=EC=95=BD=ED=95=B4=EC=84=9C=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request-task/RequestTaskFileInput.vue | 14 ++++++-------- .../task-detail/TaskDetailHistoryInput.vue | 9 +++++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/request-task/RequestTaskFileInput.vue b/src/components/request-task/RequestTaskFileInput.vue index 33d3308..e4d8ac5 100644 --- a/src/components/request-task/RequestTaskFileInput.vue +++ b/src/components/request-task/RequestTaskFileInput.vue @@ -63,17 +63,14 @@ const handleModal = () => { isModalVisible.value = !isModalVisible.value } -const truncateFilename = (name: string, maxLength: number) => { - return [...name].slice(0, maxLength).join('') -} - const handleFileUpload = (event: Event) => { const target = event.target as HTMLInputElement if (target.files && target.files.length > 0) { const newFiles = Array.from(target.files) .map(file => { - const truncatedName = truncateFilename(file.name, 35) - const newFile = new File([file], truncatedName, { type: file.type }) + const normalizedFileName = file.name.normalize('NFC') + const newFileName = normalizedFileName.slice(0, 18) + const newFile = new File([file], newFileName, { type: file.type }) return newFile.size <= 5 * 1024 * 1024 ? newFile : null }) .filter(file => file !== null) as File[] @@ -98,8 +95,9 @@ const handleDrop = (event: DragEvent) => { if (files && files.length > 0) { const newFiles = Array.from(files) .map(file => { - const truncatedName = truncateFilename(file.name, 35) - const newFile = new File([file], truncatedName, { type: file.type }) + const normalizedFileName = file.name.normalize('NFC') + const newFileName = normalizedFileName.slice(0, 18) + const newFile = new File([file], newFileName, { type: file.type }) return newFile.size <= 5 * 1024 * 1024 ? newFile : null }) .filter(file => file !== null) as File[] diff --git a/src/components/task-detail/TaskDetailHistoryInput.vue b/src/components/task-detail/TaskDetailHistoryInput.vue index 7e65318..d3c604e 100644 --- a/src/components/task-detail/TaskDetailHistoryInput.vue +++ b/src/components/task-detail/TaskDetailHistoryInput.vue @@ -114,7 +114,6 @@ const handleFileUpload = async (event: Event) => { const target = event.target as HTMLInputElement const file = target.files?.[0] if (!file) return - if (file.size > 5 * 1024 * 1024) { handleModal() target.value = '' @@ -122,7 +121,13 @@ const handleFileUpload = async (event: Event) => { } const formData = new FormData() - formData.append('attachment', file) + + const normalizedFileName = file.name.normalize('NFC') + const truncatedFileName = normalizedFileName.slice(0, 18) + + const renamedFile = new File([file], truncatedFileName, { type: file.type }) + formData.append('attachment', renamedFile) + await postCommentAttachment(taskId, formData) queryClient.invalidateQueries({ queryKey: ['historyData', taskId] }) target.value = '' From 3410d0646b961d70b13c5a0e29cf1d996d9dd116 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 10:46:57 +0900 Subject: [PATCH 03/26] =?UTF-8?q?:recycle:=20[fix]=20:=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EC=B5=9C=EC=8B=A0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member-management/MemberManagementList.vue | 8 +++++--- src/components/user-manage/UserUpdate.vue | 18 +++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/member-management/MemberManagementList.vue b/src/components/member-management/MemberManagementList.vue index c828b06..b426299 100644 --- a/src/components/member-management/MemberManagementList.vue +++ b/src/components/member-management/MemberManagementList.vue @@ -7,7 +7,9 @@ @@ -22,18 +24,18 @@ From 004b14ba00e360922cba189b2bca85ba08bdfcc8 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 14:27:41 +0900 Subject: [PATCH 07/26] =?UTF-8?q?:recycle:=20[fix]=20:=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=20=EC=88=98=EC=A0=95=20=EC=8B=9C=EC=97=90=EB=8F=84=20?= =?UTF-8?q?=EA=B8=B8=EC=9D=B4=20=EC=B8=A1=EC=A0=95=20=EB=B0=8F=20=EC=A0=9C?= =?UTF-8?q?=ED=95=9C=EB=90=9C=20=EA=B8=B8=EC=9D=B4=20=EB=82=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=84=EB=8B=AC=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/task-management/LabelManagement.vue | 1 - .../task-management/LabelManagementLine.vue | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/task-management/LabelManagement.vue b/src/components/task-management/LabelManagement.vue index abe862d..ce828d7 100644 --- a/src/components/task-management/LabelManagement.vue +++ b/src/components/task-management/LabelManagement.vue @@ -35,7 +35,6 @@ @updateColor="updateLabelColor" /> diff --git a/src/components/task-management/LabelManagementLine.vue b/src/components/task-management/LabelManagementLine.vue index e1f560f..89c37fc 100644 --- a/src/components/task-management/LabelManagementLine.vue +++ b/src/components/task-management/LabelManagementLine.vue @@ -5,7 +5,7 @@ :key="label.labelId" class="flex w-full flex-col">
-
+
-

+

{{ label.labelName }}

+

+ {{ editValue.labelName.length }}/10 +

- - {{ name.length }} / 10 +
+ + + {{ name.length }} / 10 + +

아이디

From aa1c761f8d5fec963f13a5821ed1f79b9edb60e3 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 19:59:24 +0900 Subject: [PATCH 13/26] =?UTF-8?q?:bug:=20[fix]=20:=20csv=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=ED=98=95=EC=8B=9D=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/axios.ts | 2 ++ src/utils/getErorr.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/utils/axios.ts b/src/utils/axios.ts index 19d109c..1ea2c9a 100644 --- a/src/utils/axios.ts +++ b/src/utils/axios.ts @@ -89,6 +89,8 @@ const setInterceptors = (instance: AxiosInstance) => { return Promise.reject(new Error('MEMBER_REVIEWER')) } else if (error.response.data === 'MEMBER_014') { return Promise.reject(new Error('NICKNAME_EMAIL_DIFFERENT')) + } else if (error.response.data === 'MEMBER_016') { + return Promise.reject(new Error('WRONG_NICKNAME')) } else { setError('잘못된 요청입니다', '다시 시도해주세요') } diff --git a/src/utils/getErorr.ts b/src/utils/getErorr.ts index 3f5f20f..2ac453e 100644 --- a/src/utils/getErorr.ts +++ b/src/utils/getErorr.ts @@ -8,6 +8,7 @@ export const getErrorCSV = (error: unknown): string => { NICKNAME_EMAIL_DIFFERENT: '닉네임과 이메일이 일치하지 않습니다', NO_DEPARTMENT: '부서를 찾을 수 없습니다', PARSSING_ERROR: 'CSV 데이터 파싱 중 오류가 발생했습니다', + WRONG_NICKNAME: '잘못된 닉네임 형식입니다' } return errorMessages[error.message] || '알 수 없는 오류가 발생했습니다' From 709dd5e259c54b1e0e7b7e0ca8523390dd1d3792 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 20:47:54 +0900 Subject: [PATCH 14/26] =?UTF-8?q?:bug:=20[fix]=20:=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20=ED=95=9C=EB=B2=88=EB=A7=8C=20=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/request-approve/RequestApprove.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/request-approve/RequestApprove.vue b/src/components/request-approve/RequestApprove.vue index 8488f5c..a94fca5 100644 --- a/src/components/request-approve/RequestApprove.vue +++ b/src/components/request-approve/RequestApprove.vue @@ -84,6 +84,7 @@ const subCategoryArr = ref([]) const afterSubCategoryArr = ref([]) const approveData = ref(INITIAL_REQUEST_APPROVE_DATA) const isInvalidate = ref('') +const isApproving = ref(false) const isFirst = ref(true) const router = useRouter() @@ -143,6 +144,7 @@ const handleCancel = () => { } const handleSubmit = async () => { + if (isApproving.value || isModalVisible.value) return if (!category1.value) { isInvalidate.value = 'category1' return @@ -163,6 +165,7 @@ const handleSubmit = async () => { isInvalidate.value = '' return } + isApproving.value = true const requestData = { categoryId: category2.value.subCategoryId, @@ -172,6 +175,7 @@ const handleSubmit = async () => { : null, labelId: approveData.value.label?.labelId || null } + await postTaskApprove(requestId, requestData) isModalVisible.value = true } From dd8e6b257a416889e1d1effd6245b00194d0d797 Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Fri, 14 Feb 2025 21:53:34 +0900 Subject: [PATCH 15/26] =?UTF-8?q?:bug:=20[fix]=20:=20=EC=83=88=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=20=EC=B6=94=EA=B0=80=20=EC=8B=9C=20=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=EC=9E=90=20=EA=B6=8C=ED=95=9C=EC=97=AC=EB=B6=80=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20+=20=EC=97=AD=ED=95=A0=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user-manage/DepartmentDropDown.vue | 22 ++++++++++---- .../user-manage/UserRegistration.vue | 29 +++++++++++++++++-- src/constants/admin.ts | 2 +- src/types/admin.ts | 3 +- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/components/user-manage/DepartmentDropDown.vue b/src/components/user-manage/DepartmentDropDown.vue index ecaf73d..eddfbf1 100644 --- a/src/components/user-manage/DepartmentDropDown.vue +++ b/src/components/user-manage/DepartmentDropDown.vue @@ -10,7 +10,10 @@ class="flex w-full h-11 items-center rounded p-4 border border-border-1 bg-white cursor-pointer" @click="toggleDropdown()">

- {{ dePartments.find(department => department.departmentId === modelValue)?.name }} + {{ + dePartments.find(department => department.departmentId === modelValue?.departmentId) + ?.name + }}

- {{ department.name }} +

{{ department.name }}

+

+ {{ department.isManager ? '담당자 권한 O' : '담당자 권한 X' }} +

@@ -41,7 +47,10 @@ import CommonIcons from '../common/CommonIcons.vue' const dePartments = ref([]) const dropdownOpen = ref(false) const emit = defineEmits(['update:modelValue']) -const { modelValue, isInvalidate } = defineProps<{ modelValue: number; isInvalidate: string }>() +const { modelValue, isInvalidate } = defineProps<{ + modelValue: DepartmentType | null + isInvalidate: string +}>() const isInvalidateState = computed(() => isInvalidate) const toggleDropdown = () => { @@ -49,11 +58,14 @@ const toggleDropdown = () => { } const selectOption = (option: DepartmentType) => { - emit('update:modelValue', option.departmentId) + emit('update:modelValue', option) dropdownOpen.value = false } onMounted(async () => { dePartments.value = await getDepartmentsAdmin() + if (dePartments.value.length > 0) { + emit('update:modelValue', dePartments.value[0]) + } }) diff --git a/src/components/user-manage/UserRegistration.vue b/src/components/user-manage/UserRegistration.vue index 53680c4..63b289c 100644 --- a/src/components/user-manage/UserRegistration.vue +++ b/src/components/user-manage/UserRegistration.vue @@ -29,11 +29,11 @@ :is-not-required="false" /> { router.back() } +const filteredRoleKeys = computed(() => { + if (userRegistrationForm.value.department?.isManager) { + return RoleKeys + } + return RoleKeys.filter(role => role !== '담당자') +}) + +watch( + () => userRegistrationForm.value.department?.isManager, + newValue => { + if (!newValue && userRegistrationForm.value.role === '담당자') { + userRegistrationForm.value.role = '사용자' + } + } +) + const handleSubmit = async () => { try { if (!userRegistrationForm.value.name) { @@ -108,12 +124,19 @@ const handleSubmit = async () => { isInvalidate.value = 'wrongEmail' return } + if (!userRegistrationForm.value.department?.departmentId) { + isInvalidate.value = 'depertmentEmpty' + return + } + const { department, ...restForm } = userRegistrationForm.value const formData = { - ...userRegistrationForm.value, + ...restForm, + departmentId: department?.departmentId, isReviewer: isManager.value ? userRegistrationForm.value.isReviewer : false, role: RoleTypeMapping[userRegistrationForm.value.role], email: userRegistrationForm.value.nickname + userRegistrationForm.value.email } + await addMemberAdmin(formData) isModalVisible.value = true } catch (error) { diff --git a/src/constants/admin.ts b/src/constants/admin.ts index f45be1a..2bba7e0 100644 --- a/src/constants/admin.ts +++ b/src/constants/admin.ts @@ -47,7 +47,7 @@ export const INITIAL_USER_REGISTRATION: UserRegistrationProps = { nickname: '', isReviewer: false, role: '사용자', - departmentId: 1, + department: null, departmentRole: '' } diff --git a/src/types/admin.ts b/src/types/admin.ts index b584acd..d2470e9 100644 --- a/src/types/admin.ts +++ b/src/types/admin.ts @@ -38,7 +38,7 @@ export interface UserRegistrationProps { email: string nickname: string isReviewer: boolean - departmentId: number + department: DepartmentType | null role: RoleTypes departmentRole: string } @@ -105,6 +105,7 @@ export interface ApiLogsResponse { export interface DepartmentType { departmentId: number name: string + isManager: boolean } export interface UserRegistrationApiProps { From 7d3b54bcf4b76144b3615749d989ae7f4313a83f Mon Sep 17 00:00:00 2001 From: Minkyu0424 Date: Sat, 15 Feb 2025 01:16:26 +0900 Subject: [PATCH 16/26] =?UTF-8?q?:bug:=20[fix]=20:=20=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=91=9C=EC=8B=9C=20+=20=EC=97=AD?= =?UTF-8?q?=ED=95=A0=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request-task/RequestTaskDropdown.vue | 4 +-- src/components/user-manage/UserUpdate.vue | 36 +++++++++++++++---- src/types/admin.ts | 1 + 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/components/request-task/RequestTaskDropdown.vue b/src/components/request-task/RequestTaskDropdown.vue index ceccc72..e72dcf5 100644 --- a/src/components/request-task/RequestTaskDropdown.vue +++ b/src/components/request-task/RequestTaskDropdown.vue @@ -21,7 +21,7 @@
+ class="absolute w-full max-h-40 overflow-y-auto scrollbar-hide top-[52px] flex flex-col gap-2 p-2 bg-white rounded z-10 shadow border-t border-t-border-2">
import { dropdownIcon } from '@/constants/iconPath' +import { useOutsideClick } from '@/hooks/useOutsideClick' import type { RequestTaskDropdownProps } from '@/types/user' import { ref } from 'vue' import CommonIcons from '../common/CommonIcons.vue' -import { useOutsideClick } from '@/hooks/useOutsideClick' const { placeholderText, options, labelName, modelValue, isLabel, disabled, isInvalidate } = defineProps() diff --git a/src/components/user-manage/UserUpdate.vue b/src/components/user-manage/UserUpdate.vue index 99957b3..c386819 100644 --- a/src/components/user-manage/UserUpdate.vue +++ b/src/components/user-manage/UserUpdate.vue @@ -30,11 +30,11 @@ :is-not-required="false" />