Skip to content

Commit b70ecfa

Browse files
authored
Merge pull request #197 from TaskFlow-CLAP/CLAP-435
CLAP-435 QA사항 반영 2차 Moya
2 parents b7490d8 + 2f77c18 commit b70ecfa

19 files changed

+272
-126
lines changed

src/api/user.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,8 @@ export const getSubCategoryDetail = async (categoryId: number) => {
8888
const response = await axiosInstance.get(`/api/sub-categories/${categoryId}`)
8989
return response.data
9090
}
91+
92+
export const terminateTaskUser = async (taskId: number, reason: string) => {
93+
const response = await axiosInstance.patch(`/api/tasks/${taskId}/terminate`, { reason })
94+
return response.data
95+
}

src/components/my-request/MyRequestList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<template #listCards>
88
<MyRequestListCard
99
v-for="info in data?.content"
10-
:key="info.taskStatus + info.processorName + info.taskId"
10+
:key="info.taskStatus + info.processorName + info.taskId + info.title + info.categoryName"
1111
:info="info" />
1212
<NoContent v-if="data?.content.length === 0" />
1313
</template>

src/components/request-approve/RequestApprove.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import CategoryDropDown from '../request-task/CategoryDropDown.vue'
7575
import DueDateInput from './DueDateInput.vue'
7676
import LabelDropdown from './LabelDropdown.vue'
7777
import ManagerDropdown from './ManagerDropdown.vue'
78+
import getPossibleCategory from '@/utils/possibleCategory'
7879
7980
const isModalVisible = ref(false)
8081
const category1 = ref<Category | null>(null)
@@ -119,7 +120,12 @@ onMounted(async () => {
119120
setError('존재하지 않는 요청입니다', '', () => redirectToLogin('/requested'))
120121
return
121122
}
122-
mainCategoryArr.value = await getMainCategory()
123+
const mainCategory = await getMainCategory()
124+
const mainIds = await getPossibleCategory()
125+
const filteredMainCategory = mainCategory.filter((category: Category) =>
126+
mainIds.includes(category.mainCategoryId)
127+
)
128+
mainCategoryArr.value = filteredMainCategory
123129
subCategoryArr.value = await getSubCategory()
124130
const data = await getTaskDetailUser(requestId)
125131
const selected = mainCategoryArr.value.find(ct => ct.name === data.mainCategoryName) || null

src/components/request-task/ReRequestTask.vue

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
:limit-length="200" />
2828
<RequestTaskFileInput
2929
v-model="file"
30+
:initFileArr="initFileArr"
3031
:isEdit="true" />
3132
<FormButtonContainer
3233
:handleCancel="handleCancel"
@@ -46,14 +47,26 @@
4647
<template #header>작업{{ statusText }}을 실패했습니다</template>
4748
<template #body>잠시후 시도해주세요</template>
4849
</ModalView>
50+
<ModalView
51+
:isOpen="isModalVisible === 'loading'"
52+
type="loadingType">
53+
<template #header>작업을 요청 중입니다...</template>
54+
<template #body>잠시만 기다려주세요</template>
55+
</ModalView>
4956
</div>
5057
</template>
5158

5259
<script lang="ts" setup>
5360
import { getMainCategory, getSubCategory } from '@/api/common'
54-
import { getTaskDetailUser, patchTaskRequest, postTaskRequest } from '@/api/user'
61+
import {
62+
getSubCategoryDetail,
63+
getTaskDetailUser,
64+
patchTaskRequest,
65+
postTaskRequest
66+
} from '@/api/user'
5567
import type { Category, SubCategory } from '@/types/common'
5668
import type { AttachmentResponse } from '@/types/user'
69+
import getPossibleCategory from '@/utils/possibleCategory'
5770
import { computed, onMounted, ref, watch } from 'vue'
5871
import { useRouter } from 'vue-router'
5972
import FormButtonContainer from '../common/FormButtonContainer.vue'
@@ -91,7 +104,12 @@ const handleCancel = () => {
91104
}
92105
93106
onMounted(async () => {
94-
mainCategoryArr.value = await getMainCategory()
107+
const mainCategory = await getMainCategory()
108+
const mainIds = await getPossibleCategory()
109+
const filteredMainCategory = mainCategory.filter((category: Category) =>
110+
mainIds.includes(category.mainCategoryId)
111+
)
112+
mainCategoryArr.value = filteredMainCategory
95113
subCategoryArr.value = await getSubCategory()
96114
afterSubCategoryArr.value = await getSubCategory()
97115
const data = await getTaskDetailUser(Number(id))
@@ -103,6 +121,7 @@ onMounted(async () => {
103121
)
104122
title.value = data.title
105123
description.value = data.description
124+
106125
file.value = data.attachmentResponses.map((attachment: AttachmentResponse) => {
107126
return new File([attachment.fileUrl], attachment.fileName, { type: 'application/pdf' })
108127
})
@@ -120,6 +139,13 @@ watch(category1, async newValue => {
120139
)
121140
})
122141
142+
watch(category2, async newVal => {
143+
if (newVal) {
144+
const res = await getSubCategoryDetail(newVal.subCategoryId)
145+
description.value = res.descriptionExample
146+
}
147+
})
148+
123149
const handleSubmit = async () => {
124150
if (isSubmitting.value || isModalVisible.value) return
125151
@@ -140,11 +166,9 @@ const handleSubmit = async () => {
140166
return
141167
}
142168
143-
isSubmitting.value = true
144-
145169
const formData = new FormData()
146-
147170
isSubmitting.value = true
171+
isModalVisible.value = 'loading'
148172
149173
const attachmentsToDelete = initFileArr.value
150174
.filter(initFile => !file.value?.some(f => f.name === initFile.fileName))
@@ -161,7 +185,7 @@ const handleSubmit = async () => {
161185
attachmentsToDelete: attachmentsToDelete
162186
}
163187
164-
const jsonTaskInfo = JSON.stringify(taskInfoEdit)
188+
const jsonTaskInfo = JSON.stringify(reqType === 'edit' ? taskInfoEdit : taskInfo)
165189
const newBlob = new Blob([jsonTaskInfo], { type: 'application/json' })
166190
formData.append('taskInfo', newBlob)
167191
@@ -177,13 +201,16 @@ const handleSubmit = async () => {
177201
formData.append('attachment', f)
178202
})
179203
}
180-
181-
if (reqType === 're') {
182-
await postTaskRequest(formData)
183-
} else {
184-
await patchTaskRequest(id, formData)
204+
try {
205+
if (reqType === 're') {
206+
await postTaskRequest(formData)
207+
} else {
208+
await patchTaskRequest(id, formData)
209+
}
210+
isModalVisible.value = 'success'
211+
} finally {
212+
isSubmitting.value = false
213+
if (isModalVisible.value !== 'success') isModalVisible.value = ''
185214
}
186-
isModalVisible.value = 'success'
187-
isSubmitting.value = false
188215
}
189216
</script>

src/components/request-task/RequestTask.vue

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import { getMainCategory, getSubCategory } from '@/api/common'
5353
import { getSubCategoryDetail, postTaskRequest } from '@/api/user'
5454
import type { Category, SubCategory } from '@/types/common'
55+
import getPossibleCategory from '@/utils/possibleCategory'
5556
import { onMounted, ref, watch } from 'vue'
5657
import { useRouter } from 'vue-router'
5758
import FormButtonContainer from '../common/FormButtonContainer.vue'
@@ -79,7 +80,12 @@ const subCategoryArr = ref<SubCategory[]>([])
7980
const afterSubCategoryArr = ref<SubCategory[]>([])
8081
8182
onMounted(async () => {
82-
mainCategoryArr.value = await getMainCategory()
83+
const mainCategory = await getMainCategory()
84+
const mainIds = await getPossibleCategory()
85+
const filteredMainCategory = mainCategory.filter((category: Category) =>
86+
mainIds.includes(category.mainCategoryId)
87+
)
88+
mainCategoryArr.value = filteredMainCategory
8389
subCategoryArr.value = await getSubCategory()
8490
afterSubCategoryArr.value = await getSubCategory()
8591
})
@@ -147,9 +153,14 @@ const handleSubmit = async () => {
147153
if (file.value && file.value.length > 0) {
148154
file.value.forEach(f => formData.append('attachment', f))
149155
}
150-
await postTaskRequest(formData)
151-
isModalVisible.value = 'success'
152-
isSubmitting.value = false
153-
isUploading.value = false
156+
157+
try {
158+
await postTaskRequest(formData)
159+
isModalVisible.value = 'success'
160+
} finally {
161+
if (isModalVisible.value !== 'success') isModalVisible.value = ''
162+
isSubmitting.value = false
163+
isUploading.value = false
164+
}
154165
}
155166
</script>

src/components/request-task/RequestTaskFileInput.vue

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
<RequestTaskFileInputAfter
1212
:files="modelValue"
1313
:isEdit
14-
:removeFile="removeFile" />
14+
:removeFile="removeFile"
15+
:initFileArr="initFileArr" />
1516
</label>
1617
<div
1718
v-else
@@ -33,34 +34,42 @@
3334
</label>
3435
</div>
3536
<ModalView
36-
:is-open="isModalVisible"
37+
:is-open="isModalVisible === 'invalidate'"
3738
type="failType"
38-
@close="handleModal">
39+
@close="closeModal">
3940
<template #header>파일추가를 실패했습니다</template>
4041
<template #body>최대 5개, 각 5mb까지 가능합니다</template>
4142
</ModalView>
43+
<ModalView
44+
:is-open="isModalVisible === 'duplicated'"
45+
type="failType"
46+
@close="closeModal">
47+
<template #header>중복된 파일입니다</template>
48+
</ModalView>
4249
</div>
4350
</template>
4451

4552
<script lang="ts" setup>
4653
import CommonIcons from '@/components/common/CommonIcons.vue'
4754
import { uploadIcon } from '@/constants/iconPath'
55+
import type { AttachmentResponse } from '@/types/user'
4856
import { computed, ref } from 'vue'
4957
import ModalView from '../common/ModalView.vue'
5058
import RequestTaskFileInputAfter from './RequestTaskFileInputAfter.vue'
5159
52-
const { modelValue } = defineProps<{
60+
const { modelValue, isEdit, initFileArr } = defineProps<{
5361
modelValue: File[] | null
5462
isEdit?: boolean
63+
initFileArr?: AttachmentResponse[]
5564
}>()
5665
const emit = defineEmits(['update:modelValue'])
5766
5867
const hasFiles = computed(() => modelValue && modelValue.length > 0)
5968
const isDragging = ref(false)
60-
const isModalVisible = ref(false)
69+
const isModalVisible = ref('')
6170
62-
const handleModal = () => {
63-
isModalVisible.value = !isModalVisible.value
71+
const closeModal = () => {
72+
isModalVisible.value = ''
6473
}
6574
6675
const handleFileUpload = (event: Event) => {
@@ -76,12 +85,19 @@ const handleFileUpload = (event: Event) => {
7685
.filter(file => file !== null) as File[]
7786
7887
if (newFiles.length !== target.files.length) {
79-
handleModal()
88+
isModalVisible.value = 'invalidate'
89+
return
90+
}
91+
92+
const existingFileNames = modelValue ? modelValue.map(file => file.name) : []
93+
if (newFiles.some(file => existingFileNames.includes(file.name))) {
94+
isModalVisible.value = 'duplicated'
8095
return
8196
}
97+
8298
const updatedFiles = modelValue ? [...modelValue, ...newFiles] : newFiles
8399
if (updatedFiles.length > 5) {
84-
handleModal()
100+
isModalVisible.value = 'invalidate'
85101
return
86102
}
87103
emit('update:modelValue', updatedFiles.length === 1 ? [updatedFiles[0]] : updatedFiles)
@@ -101,13 +117,21 @@ const handleDrop = (event: DragEvent) => {
101117
return newFile.size <= 5 * 1024 * 1024 ? newFile : null
102118
})
103119
.filter(file => file !== null) as File[]
120+
104121
if (newFiles.length !== files.length) {
105-
handleModal()
122+
isModalVisible.value = 'invalidate'
123+
return
124+
}
125+
126+
const existingFileNames = modelValue ? modelValue.map(file => file.name) : []
127+
if (newFiles.some(file => existingFileNames.includes(file.name))) {
128+
isModalVisible.value = 'duplicated'
106129
return
107130
}
131+
108132
const updatedFiles = modelValue ? [...modelValue, ...newFiles] : newFiles
109133
if (updatedFiles.length > 5) {
110-
handleModal()
134+
isModalVisible.value = 'invalidate'
111135
return
112136
}
113137
emit('update:modelValue', updatedFiles.length === 1 ? [updatedFiles[0]] : updatedFiles)

src/components/request-task/RequestTaskFileInputAfter.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
class="flex w-full justify-between items-center h-8 text-xs border-b border-b-border-2 px-4 shrink-0">
2020
<p class="flex truncate mr-3">{{ file.name }}</p>
2121
<div class="flex gap-6">
22-
<p class="w-[60px]">{{ isEdit ? file.size : formatFileSize(file.size) }}</p>
22+
<p class="w-[60px]">
23+
{{ isEdit ? getFileSizeByName(file.name, file.size) : formatFileSize(file.size) }}
24+
</p>
2325
<p class="w-36">{{ new Date().toLocaleString() }}</p>
2426
<div class="w-10 flex items-center justify-center cursor-pointer">
2527
<CommonIcons
@@ -47,5 +49,10 @@ import type { RequestTaskFileInputProps } from '@/types/user'
4749
import { formatFileSize } from '@/utils/unit'
4850
import CommonIcons from '../common/CommonIcons.vue'
4951
50-
const { files, removeFile, isEdit } = defineProps<RequestTaskFileInputProps>()
52+
const { files, removeFile, isEdit, initFileArr } = defineProps<RequestTaskFileInputProps>()
53+
54+
const getFileSizeByName = (fileName: string, size: number): string | number => {
55+
const file = initFileArr?.find(file => file.fileName === fileName)
56+
return file ? file.fileSize : formatFileSize(size)
57+
}
5158
</script>

src/components/requested/RequestedListCard.vue

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@
3535
:is-open="isModalVisible.reject"
3636
@update:model-value="value => (rejectReason = value || '')"
3737
type="inputType"
38-
@close="closeModal"
38+
@close="closeAllModal"
3939
@click="rejectRequest">
4040
<template #header>반려 사유를 입력해주세요</template>
4141
</ModalView>
4242
<ModalView
4343
:is-open="isModalVisible.success"
4444
type="successType"
45-
@close="closeModal">
45+
@close="closeAllModal">
4646
<template #header>반려가 완료되었습니다</template>
4747
</ModalView>
4848
<ModalView
@@ -75,13 +75,6 @@ const requestedTabList: ListCardProps[] = [
7575
{ content: info.title },
7676
{ content: info.requesterName, width: 120, profileImg: info.requesterImg }
7777
]
78-
79-
const selectedID = ref<number | null>(null)
80-
81-
const handleModal = (id: number | null) => {
82-
selectedID.value = id
83-
}
84-
8578
const router = useRouter()
8679
const queryClient = useQueryClient()
8780
@@ -90,24 +83,38 @@ const isModalVisible = ref({
9083
fail: false,
9184
success: false
9285
})
86+
const backModal = ref(false)
9387
const modalError = ref('')
9488
const rejectReason = ref('')
89+
const selectedID = ref<number | null>(null)
90+
91+
const handleModal = (id: number | null) => {
92+
selectedID.value = id
93+
}
94+
9595
const toggleModal = (key: keyof typeof isModalVisible.value) => {
9696
isModalVisible.value = Object.fromEntries(
9797
Object.keys(isModalVisible.value).map(k => [k, k === key])
9898
) as typeof isModalVisible.value
9999
}
100100
const closeModal = () => {
101+
isModalVisible.value = { reject: backModal.value ? true : false, fail: false, success: false }
102+
}
103+
104+
const closeAllModal = () => {
101105
const prevSuccess = isModalVisible.value.success
102106
isModalVisible.value = { reject: false, fail: false, success: false }
103107
if (prevSuccess) queryClient.invalidateQueries({ queryKey: ['requested'] })
104108
}
109+
105110
const rejectRequest = async () => {
106111
if (rejectReason.value.length === 0) {
107112
toggleModal('fail')
108113
modalError.value = '반려 사유를 입력해주세요'
114+
backModal.value = true
109115
return
110116
}
117+
backModal.value = false
111118
await axiosInstance.patch(`/api/tasks/${info.taskId}/terminate`, {
112119
reason: DOMPurify.sanitize(rejectReason.value)
113120
})

0 commit comments

Comments
 (0)