Skip to content

Commit 14622e4

Browse files
authored
Merge pull request #145 from TaskFlow-CLAP/CLAP-354
Clap-354 오류사항 Moya
2 parents 182ce9e + 88d69ce commit 14622e4

26 files changed

+400
-219
lines changed

src/api/user.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const changeProcessor = async (taskID: number, processorId: number) => {
3838
}
3939

4040
export const patchChangeStatus = async (taskID: number, status: Status) => {
41-
const response = await axiosInstance.patch(`/api/tasks/${taskID}/status`, { status })
41+
const response = await axiosInstance.patch(`/api/tasks/${taskID}/status`, { taskStatus: status })
4242
return response.data
4343
}
4444

src/components/common/ModalView.vue

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
v-if="type == 'successType'"
1515
:name="successIcon" />
1616
<CommonIcons
17-
v-if="type == 'failType' || type == 'inputType'"
17+
v-if="type == 'failType' || type == 'inputType' || type === 'terminate'"
1818
:name="failIcon" />
1919
<CommonIcons
2020
v-if="type == 'warningType'"
@@ -32,9 +32,11 @@
3232
</div>
3333

3434
<textarea
35-
v-if="type == 'inputType'"
35+
v-if="type == 'inputType' || type === 'terminate'"
3636
v-model="textValue"
37-
placeholder="반려 사유를 입력해주세요"
37+
:placeholder="
38+
type === 'terminate' ? '종료 사유를 입력해주세요' : '반려 사유를 입력해주세요'
39+
"
3840
class="flex border w-full border-border-1 px-4 py-3 focus:outline-none resize-none h-[120px]" />
3941
</div>
4042

@@ -56,7 +58,7 @@
5658

5759
<div
5860
class="flex items-center gap-6"
59-
v-if="type == 'warningType' || type == 'inputType'">
61+
v-if="type == 'warningType' || type == 'inputType' || type === 'terminate'">
6062
<button
6163
type="button"
6264
class="button-large-default"
@@ -67,7 +69,7 @@
6769
type="button"
6870
class="button-large-red"
6971
@click="confirmModal">
70-
{{ type === 'inputType' ? '반려' : '삭제' }}
72+
{{ type === 'inputType' ? '반려' : type === 'terminate' ? '종료' : '삭제' }}
7173
</button>
7274
</div>
7375
</div>

src/components/request-approve/DueDateInput.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22
<input
33
:type="inputType"
44
:value="modelValue"
5-
:min="new Date().toISOString().split('T')[0]"
65
class="w-full border border-gray-300 rounded px-3 py-2 cursor-pointer focus:outline-none text-center"
6+
:min="inputType === 'date' ? minValue : undefined"
77
@focus="e => (e.target as HTMLInputElement).showPicker()"
88
@input="updateValue(($event.target as HTMLInputElement).value)" />
99
</template>
1010

1111
<script lang="ts" setup>
1212
import type { DueDateInputProps } from '@/types/common'
13-
import { defineEmits, defineProps, onMounted } from 'vue'
13+
import { computed, defineEmits, defineProps, onMounted } from 'vue'
1414
1515
const { modelValue, inputType } = defineProps<DueDateInputProps>()
1616
1717
const emit = defineEmits(['update:modelValue'])
18+
19+
const minValue = computed(() => {
20+
const now = new Date()
21+
return now.toISOString().split('T')[0]
22+
})
23+
1824
const updateValue = (value: string) => {
1925
emit('update:modelValue', value)
2026
}

src/components/request-approve/RequestApprove.vue

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,26 @@
2323
:placeholderText="'담당자를 선택해주세요'"
2424
:is-invalidate="isInvalidate" />
2525
<div class="flex flex-col gap-2">
26-
<div class="flex gap-1">
26+
<div class="flex gap-2">
2727
<p class="text-body text-xs font-bold">마감기한</p>
2828
<p
29-
v-if="isInvalidate === 'date'"
29+
v-if="!isDueDateValid && approveData.dueDate && approveData.dueTime"
30+
class="text-red-1 text-xs">
31+
현재 시간 이후로 설정해주세요
32+
</p>
33+
<p
34+
v-else-if="isInvalidate === 'date'"
3035
class="text-red-1 text-xs">
3136
기한정보를 모두 입력하세요
3237
</p>
3338
</div>
39+
3440
<div class="flex w-full justify-center gap-6">
3541
<DueDateInput
3642
v-model="approveData.dueDate"
37-
:is-invalidate="isInvalidate"
3843
inputType="date" />
3944
<DueDateInput
4045
v-model="approveData.dueTime"
41-
:is-invalidate="isInvalidate"
4246
inputType="time" />
4347
</div>
4448
</div>
@@ -58,15 +62,15 @@ import { getMainCategory, getSubCategory } from '@/api/common'
5862
import { getTaskDetailUser, postTaskApprove } from '@/api/user'
5963
import { INITIAL_REQUEST_APPROVE_DATA } from '@/constants/manager'
6064
import type { Category, SubCategory } from '@/types/common'
61-
import { convertToISO } from '@/utils/date'
62-
import { onMounted, ref, watch } from 'vue'
65+
import { convertToISO, isAfterNow } from '@/utils/date'
66+
import { computed, onMounted, ref, watch } from 'vue'
6367
import { onBeforeRouteLeave, useRouter } from 'vue-router'
6468
import FormButtonContainer from '../common/FormButtonContainer.vue'
69+
import ModalView from '../common/ModalView.vue'
6570
import CategoryDropDown from '../request-task/CategoryDropDown.vue'
6671
import DueDateInput from './DueDateInput.vue'
6772
import LabelDropdown from './LabelDropdown.vue'
6873
import ManagerDropdown from './ManagerDropdown.vue'
69-
import ModalView from '../common/ModalView.vue'
7074
7175
const isModalVisible = ref(false)
7276
const category1 = ref<Category | null>(null)
@@ -75,7 +79,6 @@ const mainCategoryArr = ref<Category[]>([])
7579
const subCategoryArr = ref<SubCategory[]>([])
7680
const afterSubCategoryArr = ref<SubCategory[]>([])
7781
const approveData = ref(INITIAL_REQUEST_APPROVE_DATA)
78-
7982
const isInvalidate = ref('')
8083
const isFirst = ref(true)
8184
@@ -85,6 +88,21 @@ const requestId = Array.isArray(route.query.requestId)
8588
? Number(route.query.requestId[0])
8689
: Number(route.query.requestId)
8790
91+
const isTimeFilled = computed(() => {
92+
return (
93+
(approveData.value.dueDate && !approveData.value.dueTime) ||
94+
(!approveData.value.dueDate && approveData.value.dueTime)
95+
)
96+
})
97+
98+
const isTimeComplete = computed(() => {
99+
return approveData.value.dueDate && approveData.value.dueTime
100+
})
101+
102+
const isDueDateValid = computed(() => {
103+
return isAfterNow(approveData.value.dueDate, approveData.value.dueTime)
104+
})
105+
88106
onBeforeRouteLeave((to, from, next) => {
89107
approveData.value = INITIAL_REQUEST_APPROVE_DATA
90108
next()
@@ -103,11 +121,8 @@ onMounted(async () => {
103121
})
104122
105123
watch(category1, async newValue => {
106-
if (isFirst.value) {
107-
isFirst.value = false
108-
} else {
109-
category2.value = null
110-
}
124+
if (isFirst.value) isFirst.value = false
125+
else category2.value = null
111126
afterSubCategoryArr.value = subCategoryArr.value.filter(
112127
subCategory => subCategory.mainCategoryId === newValue?.id
113128
)
@@ -127,21 +142,22 @@ const handleSubmit = async () => {
127142
isInvalidate.value = 'manager'
128143
return
129144
}
130-
if (
131-
(approveData.value.dueDate && !approveData.value.dueTime) ||
132-
(!approveData.value.dueDate && approveData.value.dueTime)
133-
) {
145+
if (isTimeFilled.value) {
134146
isInvalidate.value = 'date'
135147
return
136148
}
137-
149+
if (isTimeComplete.value && !isDueDateValid.value) {
150+
isInvalidate.value = ''
151+
return
152+
}
138153
const requestData = {
139154
categoryId: category2.value.id,
140155
processorId: approveData.value.processor.memberId,
141-
dueDate: convertToISO(approveData.value.dueDate, approveData.value.dueTime),
156+
dueDate: isTimeFilled.value
157+
? convertToISO(approveData.value.dueDate, approveData.value.dueTime)
158+
: null,
142159
labelId: approveData.value.label?.labelId || null
143160
}
144-
145161
try {
146162
await postTaskApprove(requestId, requestData)
147163
isModalVisible.value = true

src/components/request-task/ReRequestTask.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
:is-invalidate="isInvalidate" />
2222
<RequestTaskTextArea
2323
v-model="description"
24+
:is-invalidate="isInvalidate"
2425
:placeholderText="'부가 정보를 입력해주세요'" />
2526
<RequestTaskFileInput v-model="file" />
2627
<FormButtonContainer
@@ -45,11 +46,11 @@ import type { AttachmentResponse } from '@/types/user'
4546
import { onMounted, ref, watch } from 'vue'
4647
import { useRouter } from 'vue-router'
4748
import FormButtonContainer from '../common/FormButtonContainer.vue'
49+
import ModalView from '../common/ModalView.vue'
4850
import CategoryDropDown from './CategoryDropDown.vue'
4951
import RequestTaskFileInput from './RequestTaskFileInput.vue'
5052
import RequestTaskInput from './RequestTaskInput.vue'
5153
import RequestTaskTextArea from './RequestTaskTextArea.vue'
52-
import ModalView from '../common/ModalView.vue'
5354
5455
const category1 = ref<Category | null>(null)
5556
const category2 = ref<Category | null>(null)
@@ -110,6 +111,12 @@ const handleSubmit = async () => {
110111
} else if (!title.value) {
111112
isInvalidate.value = 'input'
112113
return
114+
} else if (title.value.length > 30) {
115+
isInvalidate.value = 'title'
116+
return
117+
} else if (description.value.length > 200) {
118+
isInvalidate.value = 'description'
119+
return
113120
}
114121
const formData = new FormData()
115122

src/components/request-task/RequestTask.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
:is-invalidate="isInvalidate" />
2222
<RequestTaskTextArea
2323
v-model="description"
24+
:is-invalidate="isInvalidate"
2425
:placeholderText="'부가 정보를 입력해주세요'" />
2526
<RequestTaskFileInput v-model="file" />
2627
<FormButtonContainer
@@ -44,11 +45,11 @@ import type { Category, SubCategory } from '@/types/common'
4445
import { onMounted, ref, watch } from 'vue'
4546
import { useRouter } from 'vue-router'
4647
import FormButtonContainer from '../common/FormButtonContainer.vue'
48+
import ModalView from '../common/ModalView.vue'
4749
import CategoryDropDown from './CategoryDropDown.vue'
4850
import RequestTaskFileInput from './RequestTaskFileInput.vue'
4951
import RequestTaskInput from './RequestTaskInput.vue'
5052
import RequestTaskTextArea from './RequestTaskTextArea.vue'
51-
import ModalView from '../common/ModalView.vue'
5253
5354
const category1 = ref<Category | null>(null)
5455
const category2 = ref<Category | null>(null)
@@ -101,6 +102,12 @@ const handleSubmit = async () => {
101102
} else if (!title.value) {
102103
isInvalidate.value = 'input'
103104
return
105+
} else if (title.value.length > 30) {
106+
isInvalidate.value = 'title'
107+
return
108+
} else if (description.value.length > 200) {
109+
isInvalidate.value = 'description'
110+
return
104111
}
105112
const formData = new FormData()
106113
const taskInfo = {

src/components/request-task/RequestTaskDropdown.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
<template>
22
<div>
3-
<div class="flex text-xs gap-x-1 mb-2">
3+
<div class="flex text-xs text-red-1 gap-x-1 mb-2">
44
<p class="text-body font-bold">{{ labelName }}</p>
5-
<p
6-
v-if="!isLabel"
7-
class="text-red-1">
8-
*
9-
</p>
5+
<p v-if="!isLabel">*</p>
106
</div>
117
<div class="relative flex">
128
<div

src/components/request-task/RequestTaskFileInput.vue

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,25 @@
77
id="file"
88
multiple
99
@change="handleFileUpload" />
10-
<RequestTaskFileInputAfter
10+
<label
1111
v-if="hasFiles"
12-
:files="modelValue"
13-
:removeFile="removeFile" />
12+
for="file">
13+
<RequestTaskFileInputAfter
14+
:files="modelValue"
15+
:removeFile="removeFile" />
16+
</label>
1417
<div
1518
v-else
16-
class="w-full h-32 flex flex-col gap-4 items-center justify-center mt-2 rounded py-[31px] bg-white border-2 border-border-1 border-dashed">
17-
<div class="text-sm text-disabled font-bold">첨부할 파일을 끌어 놓으세요</div>
19+
:class="[
20+
'w-full h-32 flex flex-col gap-4 items-center justify-center mt-2 rounded py-[31px] bg-white border-2 border-dashed',
21+
isDragging ? 'border-primary1' : 'border-border-1'
22+
]"
23+
@dragover.prevent="isDragging = true"
24+
@dragleave.prevent="isDragging = false"
25+
@drop.prevent="handleDrop">
26+
<div :class="['text-sm font-bold', isDragging ? 'text-primary1' : 'text-disabled']">
27+
첨부할 파일을 끌어 놓으세요
28+
</div>
1829
<label
1930
for="file"
2031
class="button-medium-primary">
@@ -36,15 +47,16 @@
3647
import CommonIcons from '@/components/common/CommonIcons.vue'
3748
import { uploadIcon } from '@/constants/iconPath'
3849
import { computed, ref } from 'vue'
39-
import RequestTaskFileInputAfter from './RequestTaskFileInputAfter.vue'
4050
import ModalView from '../common/ModalView.vue'
51+
import RequestTaskFileInputAfter from './RequestTaskFileInputAfter.vue'
4152
4253
const { modelValue } = defineProps<{
4354
modelValue: File[] | null
4455
}>()
4556
const emit = defineEmits(['update:modelValue'])
4657
4758
const hasFiles = computed(() => modelValue && modelValue.length > 0)
59+
const isDragging = ref(false)
4860
const isModalVisible = ref(false)
4961
5062
const handleModal = () => {
@@ -68,6 +80,23 @@ const handleFileUpload = (event: Event) => {
6880
}
6981
}
7082
83+
const handleDrop = (event: DragEvent) => {
84+
const files = event.dataTransfer?.files
85+
if (files && files.length > 0) {
86+
const newFiles = Array.from(files).filter(file => file.size <= 5 * 1024 * 1024)
87+
if (newFiles.length !== files.length) {
88+
handleModal()
89+
return
90+
}
91+
const updatedFiles = modelValue ? [...modelValue, ...newFiles] : newFiles
92+
if (updatedFiles.length > 5) {
93+
handleModal()
94+
return
95+
}
96+
emit('update:modelValue', updatedFiles.length === 1 ? [updatedFiles[0]] : updatedFiles)
97+
}
98+
}
99+
71100
const removeFile = (index: number) => {
72101
if (modelValue) {
73102
const updatedFiles = [...modelValue]

src/components/request-task/RequestTaskFileInputAfter.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<div class="flex gap-6">
88
<p class="w-[60px]">용량</p>
99
<p class="w-36">최종 업로드 시각</p>
10-
<p class="w-10">다운로드</p>
10+
<p class="w-10">파일삭제</p>
1111
</div>
1212
</div>
1313
<div

src/components/request-task/RequestTaskInput.vue

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
<template>
22
<div class="relative w-full">
3-
<div class="text-xs flex gap-x-1 mb-2">
3+
<div class="text-xs flex gap-x-1 mb-2 text-red-1">
44
<p class="text-body font-bold">{{ labelName }}</p>
5-
<p
6-
v-if="!isNotRequired"
7-
class="text-red-1">
8-
*
9-
</p>
10-
<p
11-
v-if="isInvalidateState === 'input'"
12-
class="text-red-1 text-xs">
13-
{{ labelName }}을 입력해주세요
14-
</p>
5+
<p v-if="!isNotRequired">*</p>
6+
<p v-if="isInvalidateState === 'input'">{{ labelName }}을 입력해주세요</p>
7+
<p v-if="isInvalidateState === 'duplicate'">회원아이디가 중복되었습니다</p>
8+
<p v-if="isInvalidateState === 'title'">제목은 30자 이내로 적어주세요</p>
159
</div>
1610
<input
1711
class="w-full h-11 border border-border-1 px-4 focus:outline-none rounded"
@@ -21,7 +15,6 @@
2115
:placeholder="placeholderText"
2216
:class="{ 'text-gray-500': isEdit }"
2317
:maxlength="labelName === '제목' ? 30 : undefined" />
24-
2518
<p
2619
v-if="isInvalidateState === 'code'"
2720
class="text-red-1 text-xs absolute top-[calc(100%+4px)]">

0 commit comments

Comments
 (0)