Skip to content

Commit 019e0f4

Browse files
authored
Merge pull request #61 from TaskFlow-CLAP/CLAP-191
Clap-191 요청 생성 API 연결
2 parents c02caa2 + 6f14671 commit 019e0f4

File tree

21 files changed

+282
-125
lines changed

21 files changed

+282
-125
lines changed

src/api/common.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { axiosInstance } from '../utils/axios'
2+
3+
export const getMainCategory = async () => {
4+
const response = await axiosInstance.get('/api/main-category')
5+
return response.data
6+
}
7+
8+
export const getSubCategory = async () => {
9+
const response = await axiosInstance.get('/api/sub-category')
10+
return response.data
11+
}

src/api/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import axiosInstance from '../utils/axios'
1+
import { axiosInstance } from '../utils/axios'
22

33
export const getNotifications = async () => {
44
const response = await axiosInstance.get('/api/notifications?page=0&size=5')

src/api/user.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { formDataAxiosInstance } from '@/utils/axios'
2+
3+
export const postTaskRequest = async (formdata: FormData) => {
4+
const response = await formDataAxiosInstance.post('/api/tasks', formdata)
5+
return response.data
6+
}

src/components/my-request/MyRequestFilterBar.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@
3333
</template>
3434

3535
<script setup lang="ts">
36-
import FilterDropdown from '../filters/FilterDropdown.vue'
36+
import { PAGE_SIZE_LIST, TASK_STATUS_LIST, TERM_LIST } from '@/constants/common'
37+
import { useRequestParamsStore } from '@/stores/params'
38+
import { axiosInstance } from '@/utils/axios'
39+
import { useQuery } from '@tanstack/vue-query'
3740
import FilterCategory from '../filters/FilterCategory.vue'
38-
import FilterInput from '../filters/FilterInput.vue'
41+
import FilterDropdown from '../filters/FilterDropdown.vue'
3942
import FilterDropdownMulti from '../filters/FilterDropdownMulti.vue'
40-
import { useRequestParamsStore } from '@/stores/params'
41-
import { PAGE_SIZE_LIST, TASK_STATUS_LIST, TERM_LIST } from '@/constants/common'
43+
import FilterInput from '../filters/FilterInput.vue'
4244
import { useRequestParamsChange } from '../hooks/useRequestParamsChange'
43-
import axiosInstance from '@/utils/axios'
44-
import { useQuery } from '@tanstack/vue-query'
4545
4646
const store = useRequestParamsStore()
4747
store.$reset()

src/components/my-request/MyRequestList.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@
2222
</template>
2323

2424
<script setup lang="ts">
25-
import MyRequestListBar from './MyRequestListBar.vue'
26-
import MyRequestListCard from './MyRequestListCard.vue'
27-
import ListPagination from '../lists/ListPagination.vue'
28-
import ListContainer from '../lists/ListContainer.vue'
2925
import { useRequestParamsStore } from '@/stores/params'
30-
import axiosInstance from '@/utils/axios'
31-
import { useQuery } from '@tanstack/vue-query'
32-
import { useParseParams } from '../hooks/useParseParams'
3326
import type { MyRequestResponse } from '@/types/user'
27+
import { axiosInstance } from '@/utils/axios'
28+
import { useQuery } from '@tanstack/vue-query'
3429
import { ref, watch } from 'vue'
30+
import { useParseParams } from '../hooks/useParseParams'
31+
import ListContainer from '../lists/ListContainer.vue'
32+
import ListPagination from '../lists/ListPagination.vue'
3533
import NoContent from '../lists/NoContent.vue'
34+
import MyRequestListBar from './MyRequestListBar.vue'
35+
import MyRequestListCard from './MyRequestListCard.vue'
3636
3737
const { params } = useRequestParamsStore()
3838
const onPageChange = (value: number) => {

src/components/my-task/MyTaskFilterBar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { useRequestParamsStore } from '@/stores/params'
4141
import { PAGE_SIZE_LIST, TASK_STATUS_LIST, TERM_LIST } from '@/constants/common'
4242
import { useRequestParamsChange } from '../hooks/useRequestParamsChange'
4343
import { useQuery } from '@tanstack/vue-query'
44-
import axiosInstance from '@/utils/axios'
44+
import {axiosInstance} from '@/utils/axios'
4545
4646
const store = useRequestParamsStore()
4747
store.$reset()

src/components/request-history/RequestHistoryFilterBar.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@
3333
</template>
3434

3535
<script setup lang="ts">
36-
import FilterDropdown from '../filters/FilterDropdown.vue'
37-
import FilterCategory from '../filters/FilterCategory.vue'
38-
import FilterInput from '../filters/FilterInput.vue'
3936
import { PAGE_SIZE_LIST, TASK_STATUS_LIST, TERM_LIST } from '@/constants/common'
40-
import FilterDropdownMulti from '../filters/FilterDropdownMulti.vue'
4137
import { useRequestParamsStore } from '@/stores/params'
42-
import { useRequestParamsChange } from '../hooks/useRequestParamsChange'
38+
import { axiosInstance } from '@/utils/axios'
4339
import { useQuery } from '@tanstack/vue-query'
44-
import axiosInstance from '@/utils/axios'
40+
import FilterCategory from '../filters/FilterCategory.vue'
41+
import FilterDropdown from '../filters/FilterDropdown.vue'
42+
import FilterDropdownMulti from '../filters/FilterDropdownMulti.vue'
43+
import FilterInput from '../filters/FilterInput.vue'
44+
import { useRequestParamsChange } from '../hooks/useRequestParamsChange'
4545
4646
const store = useRequestParamsStore()
4747
store.$reset()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<template>
2+
<div>
3+
<div class="flex text-xs gap-x-1 mb-2">
4+
<p class="text-body font-bold">{{ labelName }}</p>
5+
<p
6+
v-if="!isLabel"
7+
class="text-red-1">
8+
*
9+
</p>
10+
<p
11+
v-if="isInvalidateState === 'category'"
12+
class="text-red-1">
13+
카테고리를 선택해주세요
14+
</p>
15+
</div>
16+
<div class="relative flex text-base">
17+
<div
18+
class="flex w-full h-11 items-center rounded p-4 bg-white border border-border-1 cursor-pointer text-black"
19+
@click="toggleDropdown">
20+
<p :class="{ 'text-disabled': !modelValue?.name }">
21+
{{ modelValue?.name ?? labelName + '를 선택해주세요' }}
22+
</p>
23+
<CommonIcons
24+
:name="dropdownIcon"
25+
:class="['ml-auto', { 'rotate-180': dropdownOpen }]" />
26+
</div>
27+
<div
28+
v-if="dropdownOpen"
29+
class="absolute w-full h-40 overflow-y-auto top-[52px] flex flex-col gap-2 p-2 bg-white rounded z-10 shadow border-t border-t-border-2 text-black">
30+
<div
31+
v-for="option in options"
32+
:key="option.id"
33+
class="w-full flex items-center h-11 p-2 rounded hover:bg-background-2 cursor-pointer"
34+
@click="selectOption(option)">
35+
{{ option.name }}
36+
</div>
37+
</div>
38+
</div>
39+
</div>
40+
</template>
41+
42+
<script lang="ts" setup>
43+
import { dropdownIcon } from '@/constants/iconPath'
44+
import type { CategoryDropdownProps, MainCategoryTypes, SubCategoryTypes } from '@/types/common'
45+
import { computed, ref } from 'vue'
46+
import CommonIcons from '../common/CommonIcons.vue'
47+
48+
const { options, labelName, modelValue, isLabel, isDisabled, isInvalidate } =
49+
defineProps<CategoryDropdownProps>()
50+
51+
const isInvalidateState = computed(() => isInvalidate)
52+
53+
const emit = defineEmits(['update:modelValue'])
54+
const dropdownOpen = ref(false)
55+
56+
const toggleDropdown = () => {
57+
if (isDisabled) return
58+
dropdownOpen.value = !dropdownOpen.value
59+
}
60+
61+
const selectOption = (option: MainCategoryTypes | SubCategoryTypes) => {
62+
emit('update:modelValue', option)
63+
dropdownOpen.value = false
64+
}
65+
</script>
Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,120 @@
11
<template>
22
<div class="w-full flex flex-col gap-y-6">
3-
<RequestTaskDropdown
3+
<CategoryDropDown
44
v-model="category1"
5-
:options="DUMMY_REQUEST_TASK_CATEGORIES"
5+
:options="mainCategoryArr"
66
:label-name="'1차 카테고리'"
7-
:placeholderText="'1차 카테고리를 선택해주세요'" />
8-
<RequestTaskDropdown
7+
:isInvalidate="isInvalidate"
8+
:isDisabled="false" />
9+
<CategoryDropDown
910
v-model="category2"
10-
:options="DUMMY_REQUEST_TASK_CATEGORIES"
11+
:options="afterSubCategoryArr"
1112
:label-name="'2차 카테고리'"
12-
:placeholderText="'2차 카테고리를 선택해주세요'" />
13+
:is-invalidate="isInvalidate"
14+
:isDisabled="!category1" />
1315
<RequestTaskInput
1416
v-model="title"
15-
:placeholderText="TITLE_PLACEHOLDER"
16-
:label-name="'제목'" />
17+
:placeholderText="'제목을 입력해주세요'"
18+
:label-name="'제목'"
19+
:is-invalidate="isInvalidate" />
1720
<RequestTaskTextArea
1821
v-model="description"
19-
:placeholderText="EXPLANATION_PLACEHOLDER" />
22+
:placeholderText="'부가 정보를 입력해주세요'" />
2023
<RequestTaskFileInput v-model="file" />
2124
<FormButtonContainer
2225
:handleCancel="handleCancel"
2326
:handleSubmit="handleSubmit"
2427
cancelText="취소"
2528
submitText="요청" />
29+
<ModalView
30+
:isOpen="isModalVisible"
31+
:type="'successType'"
32+
@close="handleCancel">
33+
<template #header>작업이 요청되었습니다</template>
34+
</ModalView>
2635
</div>
2736
</template>
2837

2938
<script lang="ts" setup>
30-
import { EXPLANATION_PLACEHOLDER, TITLE_PLACEHOLDER } from '@/constants/user'
31-
import { DUMMY_REQUEST_TASK_CATEGORIES } from '@/datas/taskdetail'
32-
import { ref } from 'vue'
39+
import { getMainCategory, getSubCategory } from '@/api/common'
40+
import { postTaskRequest } from '@/api/user'
41+
import type { MainCategoryTypes, SubCategoryTypes } from '@/types/common'
42+
import { onMounted, ref, watch } from 'vue'
43+
import { useRouter } from 'vue-router'
3344
import FormButtonContainer from '../common/FormButtonContainer.vue'
34-
import RequestTaskDropdown from './RequestTaskDropdown.vue'
45+
import ModalView from '../ModalView.vue'
46+
import CategoryDropDown from './CategoryDropDown.vue'
3547
import RequestTaskFileInput from './RequestTaskFileInput.vue'
3648
import RequestTaskInput from './RequestTaskInput.vue'
3749
import RequestTaskTextArea from './RequestTaskTextArea.vue'
38-
import { useRouter } from 'vue-router'
3950
40-
const category1 = ref('1차 카테고리를 선택해주세요')
41-
const category2 = ref('2차 카테고리를 선택해주세요')
51+
const category1 = ref<MainCategoryTypes | null>(null)
52+
const category2 = ref<MainCategoryTypes | null>(null)
53+
4254
const title = ref('')
4355
const description = ref('')
4456
const file = ref(null as File[] | null)
57+
const isInvalidate = ref('')
58+
const isModalVisible = ref(false)
59+
60+
const mainCategoryArr = ref<MainCategoryTypes[]>([])
61+
const subCategoryArr = ref<SubCategoryTypes[]>([])
62+
const afterSubCategoryArr = ref<SubCategoryTypes[]>([])
63+
64+
onMounted(async () => {
65+
mainCategoryArr.value = await getMainCategory()
66+
subCategoryArr.value = await getSubCategory()
67+
afterSubCategoryArr.value = await getSubCategory()
68+
})
69+
70+
watch(category1, async newValue => {
71+
category2.value = null
72+
afterSubCategoryArr.value = subCategoryArr.value.filter(
73+
subCategory => subCategory.mainCategoryId === newValue?.id
74+
)
75+
})
4576
4677
const router = useRouter()
78+
4779
const handleCancel = () => {
48-
category1.value = ''
49-
category2.value = ''
80+
category1.value = null
81+
category2.value = null
5082
title.value = ''
5183
description.value = ''
52-
file.value = null
84+
file.value = []
5385
router.back()
5486
}
5587
56-
const handleSubmit = () => {
88+
const handleSubmit = async () => {
89+
if (!category1.value || !category2.value) {
90+
isInvalidate.value = 'category'
91+
console.log(isInvalidate.value, '변경됨')
92+
return
93+
} else if (!title.value) {
94+
isInvalidate.value = 'input'
95+
return
96+
}
5797
const formData = new FormData()
58-
formData.append('category1', category1.value)
59-
formData.append('category2', category2.value)
60-
formData.append('title', title.value)
61-
formData.append('description', description.value)
62-
if (file.value) {
63-
file.value.forEach(f => {
64-
formData.append('file', f)
65-
})
98+
const taskInfo = {
99+
categoryId: category2.value.id,
100+
title: title.value,
101+
description: description.value
102+
}
103+
104+
const jsonTaskInfo = JSON.stringify(taskInfo)
105+
const newBlob = new Blob([jsonTaskInfo], { type: 'application/json' })
106+
107+
formData.append('taskInfo', newBlob)
108+
109+
if (file.value && file.value.length > 0) {
110+
file.value.forEach(f => formData.append('attachment', f))
111+
}
112+
try {
113+
const res = await postTaskRequest(formData)
114+
isModalVisible.value = true
115+
console.error('요청 성공:', res)
116+
} catch (error) {
117+
console.error('요청 실패:', error)
66118
}
67-
console.log(Object.fromEntries(formData))
68119
}
69120
</script>

src/components/request-task/RequestTaskFileInput.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@change="handleFileUpload" />
1010
<RequestTaskFileInputAfter
1111
v-if="hasFiles"
12-
:files="props.modelValue"
12+
:files="modelValue"
1313
:removeFile="removeFile" />
1414
<div
1515
v-else
@@ -31,25 +31,25 @@ import { uploadIcon } from '@/constants/iconPath'
3131
import { computed } from 'vue'
3232
import RequestTaskFileInputAfter from './RequestTaskFileInputAfter.vue'
3333
34-
const props = defineProps<{
34+
const { modelValue } = defineProps<{
3535
modelValue: File[] | null
3636
}>()
3737
const emit = defineEmits(['update:modelValue'])
3838
39-
const hasFiles = computed(() => props.modelValue && props.modelValue.length > 0)
39+
const hasFiles = computed(() => modelValue && modelValue.length > 0)
4040
4141
const handleFileUpload = (event: Event) => {
4242
const target = event.target as HTMLInputElement
4343
if (target.files && target.files.length > 0) {
4444
const newFiles = Array.from(target.files)
45-
const updatedFiles = props.modelValue ? [...props.modelValue, ...newFiles] : newFiles
45+
const updatedFiles = modelValue ? [...modelValue, ...newFiles] : newFiles
4646
emit('update:modelValue', updatedFiles)
4747
}
4848
}
4949
5050
const removeFile = (index: number) => {
51-
if (props.modelValue) {
52-
const updatedFiles = [...props.modelValue]
51+
if (modelValue) {
52+
const updatedFiles = [...modelValue]
5353
updatedFiles.splice(index, 1)
5454
emit('update:modelValue', updatedFiles)
5555
}

0 commit comments

Comments
 (0)