Skip to content

Commit dc43c34

Browse files
authored
Merge pull request #201 from TaskFlow-CLAP/CLAP-444
CLAP-444 QA사항 반영 3차 Moya
2 parents 0b27e53 + 9bf3fd5 commit dc43c34

File tree

11 files changed

+78
-37
lines changed

11 files changed

+78
-37
lines changed

src/api/user.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const patchChangeStatus = async (taskID: number, status: Status) => {
4343
return response.data
4444
}
4545

46-
export const changeLabel = async (taskID: number, labelId: number) => {
46+
export const changeLabel = async (taskID: number, labelId: number | null) => {
4747
const response = await axiosInstance.patch(`/api/tasks/${taskID}/label`, { labelId })
4848
return response.data
4949
}

src/components/request-approve/LabelDropdown.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
class="absolute w-full 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">
2020
<div
2121
v-for="option in labelArr"
22-
:key="option.labelId"
22+
:key="option.labelId || option.labelName"
2323
class="w-full flex items-center h-11 p-2 rounded hover:bg-background-2 cursor-pointer"
2424
@click="selectOption(option)">
2525
{{ option.labelName }}

src/components/task-detail/TaskDetail.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
</div>
2525
<div class="w-[1px] bg-border-1"></div>
2626
<TaskDetailRight
27-
:data
27+
:data="data"
2828
:isProcessor="info.role !== 'ROLE_USER'" />
2929
</div>
3030
</div>
3131
</template>
3232

3333
<script setup lang="ts">
3434
import { getHistory, getTaskDetailManager, getTaskDetailUser } from '@/api/user'
35+
import { useIsOverlayOpenStore } from '@/stores/isOverlayOpen'
3536
import { useMemberStore } from '@/stores/member'
3637
import type { TaskDetailDatas, TaskDetailHistoryData, TaskDetailProps } from '@/types/user'
3738
import { useQuery } from '@tanstack/vue-query'
@@ -41,7 +42,6 @@ import TaskDetailHistory from './TaskDetailHistory.vue'
4142
import TaskDetailLeft from './TaskDetailLeft.vue'
4243
import TaskDetailRight from './TaskDetailRight.vue'
4344
import TaskDetailTopBar from './TaskDetailTopBar.vue'
44-
import { useIsOverlayOpenStore } from '@/stores/isOverlayOpen'
4545
4646
const { closeTaskDetail, selectedId } = defineProps<TaskDetailProps>()
4747
@@ -54,7 +54,8 @@ const { data } = useQuery<TaskDetailDatas>({
5454
info.value.role === 'ROLE_USER'
5555
? () => getTaskDetailUser(selectedId)
5656
: () => getTaskDetailManager(selectedId),
57-
refetchOnMount: true
57+
refetchOnMount: 'always',
58+
staleTime: 0
5859
})
5960
6061
const { data: historyData } = useQuery<TaskDetailHistoryData>({

src/components/task-detail/TaskDetailLabelDropdown.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@
1717
class="absolute w-full pb-6 top-12">
1818
<div
1919
class="w-full h-32 overflow-y-auto flex flex-col gap-2 p-2 bg-white rounded z-10 shadow-custom">
20+
<div
21+
class="w-full flex text-sm items-center h-10 p-1.5 rounded hover:bg-background-2 cursor-pointer text-disabled"
22+
@click="selectOption({ labelId: null, labelName: '', labelColor: '' })">
23+
구분 없음
24+
</div>
2025
<div
2126
v-for="option in labelArr"
22-
:key="option.labelId"
27+
:key="option.labelId || option.labelName"
2328
class="w-full flex text-sm items-center h-10 p-1.5 rounded hover:bg-background-2 cursor-pointer"
2429
@click="selectOption(option)">
2530
{{ option.labelName }}
@@ -55,7 +60,7 @@ const toggleDropdown = () => {
5560
5661
const selectOption = async (option: LabelDataTypes) => {
5762
emit('update:modelValue', option)
58-
await changeLabel(taskId || 0, option.labelId || 0)
63+
await changeLabel(taskId || 0, option.labelId || null)
5964
queryClient.invalidateQueries({ queryKey: ['taskDetailUser', taskId] })
6065
dropdownOpen.value = false
6166
}

src/components/task-detail/TaskDetailManagerDropdown.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import ImageContainer from '../common/ImageContainer.vue'
5151
5252
const { modelValue } = defineProps<{ modelValue: ManagerTypes; taskId: number }>()
5353
const emit = defineEmits(['update:modelValue'])
54-
console.log(modelValue, '현재 담당자')
5554
5655
const dropdownOpen = ref(false)
5756
const managerArr = ref<ManagerTypes[]>([])

src/components/task-detail/TaskDetailRight.vue

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
class="w-fit">
2323
<TaskStatus :status="data.taskStatus" />
2424
</div>
25-
<div v-else>
25+
<div v-else-if="taskStatus">
2626
<TaskStatusList
2727
v-model="taskStatus"
2828
:isProcessor="info.isReviewer || isProcessor"
@@ -40,7 +40,7 @@
4040
</div>
4141
<div>
4242
<p class="task-detail">담당자</p>
43-
<div v-if="data.taskStatus !== 'REQUESTED' && isProcessor">
43+
<div v-if="data.taskStatus !== 'REQUESTED' && isProcessor && newManager">
4444
<TaskDetailManagerDropdown
4545
v-model="newManager"
4646
:task-id="data.taskId" />
@@ -55,7 +55,7 @@
5555
<p class="text-sm">{{ data.processorNickName || '-' }}</p>
5656
</div>
5757
</div>
58-
<div v-if="data.taskStatus !== 'REQUESTED' && info.isReviewer">
58+
<div v-if="data.taskStatus !== 'REQUESTED' && info.role === 'ROLE_MANAGER'">
5959
<p class="task-detail">마감기한</p>
6060
<div v-if="data.dueDate">
6161
<div class="w-full flex justify-between items-center">
@@ -65,7 +65,7 @@
6565
</div>
6666
<div v-else>-</div>
6767
</div>
68-
<div v-if="data.taskStatus !== 'REQUESTED' && info.isReviewer">
68+
<div v-if="data.taskStatus !== 'REQUESTED' && info.role === 'ROLE_MANAGER'">
6969
<p class="task-detail">구분</p>
7070
<TaskDetailLabelDropdown
7171
v-model="taskLabel"
@@ -78,6 +78,7 @@
7878
<script setup lang="ts">
7979
import { changeProcessor } from '@/api/user'
8080
import { useMemberStore } from '@/stores/member'
81+
import type { Status } from '@/types/common'
8182
import type { ManagerTypes } from '@/types/manager'
8283
import type { TaskDetailDatas } from '@/types/user'
8384
import { formatDateAndTime, formatDaysBefore, formatDueDate } from '@/utils/date'
@@ -92,27 +93,41 @@ import TaskStatusList from './TaskStatusList.vue'
9293
9394
const { data, isProcessor } = defineProps<{ data: TaskDetailDatas; isProcessor: boolean }>()
9495
95-
const selectedManager = ref({
96+
const selectedManager = ref<ManagerTypes>({
9697
memberId: -1,
97-
nickname: data.processorNickName,
98-
imageUrl: data.processorImageUrl,
98+
nickname: '',
99+
imageUrl: '',
99100
remainingTasks: -1
100-
} as ManagerTypes)
101+
})
101102
102-
const taskStatus = ref(data.taskStatus)
103-
const newManager = ref(selectedManager.value)
103+
const taskStatus = ref<Status | null>(null)
104+
const newManager = ref<ManagerTypes | null>(null)
104105
const queryClient = useQueryClient()
105106
const memberStore = useMemberStore()
106107
const { info } = storeToRefs(memberStore)
107108
108109
const taskLabel = ref({
109110
labelId: -1,
110-
labelName: data.labelName || '',
111+
labelName: '',
111112
labelColor: ''
112113
})
113114
114115
watchEffect(() => {
115-
taskStatus.value = data.taskStatus
116+
if (data) {
117+
taskStatus.value = data.taskStatus
118+
selectedManager.value = {
119+
memberId: -1,
120+
nickname: data.processorNickName || '',
121+
imageUrl: data.processorImageUrl || '',
122+
remainingTasks: -1
123+
}
124+
newManager.value = { ...selectedManager.value }
125+
taskLabel.value = {
126+
labelId: -1,
127+
labelName: data.labelName || '',
128+
labelColor: ''
129+
}
130+
}
116131
})
117132
118133
watch(newManager, async newValue => {

src/components/task-management/CategoryAdd.vue

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,34 @@
2626
v-model="categoryForm.name"
2727
placeholder-text="카테고리명을 입력해주세요"
2828
:label-name="`${categoryStep}차 카테고리명`"
29-
:is-invalidate="errorMessage.categoryName" />
29+
:is-invalidate="errorMessage.categoryName"
30+
:limitLength="30" />
3031
<RequestTaskInput
3132
v-model="categoryForm.code"
3233
placeholder-text="카테고리의 작업코드를 입력해주세요"
3334
label-name="작업코드 (대문자 영어 2글자까지)"
3435
:is-invalidate="errorMessage.categoryCode === 'noCode' ? 'noCode' : isCodeInvalidate" />
35-
3636
<div
3737
v-if="categoryStep === '2'"
38-
class="flex flex-col gap-2">
39-
<p class="text-body text-xs font-semibold">부가설명 템플릿</p>
38+
class="flex flex-col gap-2 relative">
39+
<div class="flex gap-1 text-xs">
40+
<p class="text-body font-semibold">부가설명 템플릿</p>
41+
<p
42+
class="text-red-1"
43+
v-if="errorMessage.description === 'tooLong'">
44+
템플릿은 100자 이내로 적어주세요
45+
</p>
46+
</div>
4047
<textarea
4148
class="w-full h-32 border border-border-1 px-4 py-2 resize-none focus:outline-none rounded"
4249
:value="categoryForm.descriptionExample"
50+
:maxlength="100"
4351
:placeholder="'부가설명 템플릿을 작성해주세요'"
4452
@input="onValueChange">
4553
</textarea>
54+
<p class="absolute text-xs top-[calc(100%+4px)] w-full flex justify-end text-body">
55+
{{ categoryForm.descriptionExample?.length || 0 }}/{{ 100 }}
56+
</p>
4657
</div>
4758

4859
<FormButtonContainer
@@ -54,17 +65,17 @@
5465
</template>
5566

5667
<script lang="ts" setup>
68+
import { getMainCategory } from '@/api/common'
5769
import { CATEGORY_FORM } from '@/constants/admin'
70+
import type { Category, CategoryForm } from '@/types/common'
71+
import { axiosInstance } from '@/utils/axios'
72+
import DOMPurify from 'dompurify'
5873
import { computed, onMounted, ref, watch } from 'vue'
5974
import { useRoute, useRouter } from 'vue-router'
6075
import FormButtonContainer from '../common/FormButtonContainer.vue'
76+
import ModalView from '../common/ModalView.vue'
6177
import RequestTaskDropdown from '../request-task/RequestTaskDropdown.vue'
6278
import RequestTaskInput from '../request-task/RequestTaskInput.vue'
63-
import { axiosInstance } from '@/utils/axios'
64-
import { getMainCategory } from '@/api/common'
65-
import type { Category, CategoryForm } from '@/types/common'
66-
import ModalView from '../common/ModalView.vue'
67-
import DOMPurify from 'dompurify'
6879
6980
const router = useRouter()
7081
const route = useRoute()
@@ -74,11 +85,10 @@ const { categoryStep } = defineProps<{
7485
}>()
7586
7687
const isModalVisible = ref({ add: false, cancel: false, fail: false })
77-
const errorMessage = ref({ categoryName: '', categoryCode: '' })
88+
const errorMessage = ref({ categoryName: '', categoryCode: '', description: '' })
7889
const hasMainCategory = ref(true)
7990
8091
const categoryForm = ref<CategoryForm>(CATEGORY_FORM)
81-
8292
const handleAddModal = () => {
8393
isModalVisible.value.add = false
8494
handleGoBack()
@@ -97,7 +107,7 @@ const handleGoBack = () => {
97107
98108
const handleSubmit = async () => {
99109
hasMainCategory.value = true
100-
errorMessage.value = { categoryCode: '', categoryName: '' }
110+
errorMessage.value = { categoryCode: '', categoryName: '', description: '' }
101111
if (!categoryForm.value.mainCategoryId && categoryStep === '2') {
102112
hasMainCategory.value = false
103113
return
@@ -110,6 +120,9 @@ const handleSubmit = async () => {
110120
} else if (categoryForm.value.code.length === 0) {
111121
errorMessage.value.categoryCode = 'noCode'
112122
return
123+
} else if ((categoryForm.value.descriptionExample ?? '').length > 100) {
124+
errorMessage.value.description = 'tooLong'
125+
return
113126
}
114127
115128
categoryForm.value.name = DOMPurify.sanitize(categoryForm.value.name)

src/components/user-manage/UserRegistration.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
? 'empty'
1616
: isInvalidate === 'wrongNickname'
1717
? isInvalidate
18-
: ''
18+
: isInvalidate === 'duplicate'
19+
? isInvalidate
20+
: ''
1921
"
2022
:placeholderText="'회원의 아이디를 입력해주세요'"
2123
:labelName="'아이디'" />
@@ -34,7 +36,9 @@
3436
</div>
3537
<DepartmentDropDown
3638
v-model="userRegistrationForm.department"
37-
:is-invalidate="isInvalidate === 'departmentEmpty' ? isInvalidate : ''" />
39+
:is-invalidate="
40+
isInvalidate === 'departmentEmpty' || isInvalidate === 'reviewer' ? isInvalidate : ''
41+
" />
3842
<RequestTaskDropdown
3943
v-model="userRegistrationForm.role"
4044
:options="filteredRoleKeys"
@@ -69,6 +73,7 @@
6973
<script lang="ts" setup>
7074
import { addMemberAdmin } from '@/api/admin'
7175
import { INITIAL_USER_REGISTRATION, RoleKeys, RoleTypeMapping } from '@/constants/admin'
76+
import DOMPurify from 'dompurify'
7277
import { computed, onMounted, ref, watch } from 'vue'
7378
import { useRouter } from 'vue-router'
7479
import FormButtonContainer from '../common/FormButtonContainer.vue'
@@ -77,7 +82,6 @@ import ModalView from '../common/ModalView.vue'
7782
import RequestTaskDropdown from '../request-task/RequestTaskDropdown.vue'
7883
import RequestTaskInput from '../request-task/RequestTaskInput.vue'
7984
import DepartmentDropDown from './DepartmentDropDown.vue'
80-
import DOMPurify from 'dompurify'
8185
8286
const router = useRouter()
8387

src/components/user-manage/UserUpdate.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
</div>
2222
<DepartmentDropDown
2323
v-model="userRegistrationForm.department"
24-
:is-invalidate="isInvalidate === 'departmentEmpty' ? isInvalidate : ''" />
24+
:is-invalidate="
25+
isInvalidate === 'departmentEmpty' || isInvalidate === 'reviewer' ? isInvalidate : ''
26+
" />
2527
<RequestTaskDropdown
2628
v-model="userRegistrationForm.role"
2729
:options="filteredRoleKeys"

src/types/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export interface CategoryDropdownProps {
9999
}
100100

101101
export interface LabelDataTypes {
102-
labelId: number
102+
labelId: number | null
103103
labelName: string
104104
labelColor: string
105105
}

0 commit comments

Comments
 (0)