Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions src/api/admin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { NewLabelTypes, UserRegistrationProps } from '@/types/admin'
import type { NewLabelTypes, UserRegistrationApiProps, UserUpdateValue } from '@/types/admin'
import type { LabelDataTypes } from '@/types/common'
import { axiosInstance } from '@/utils/axios'
import { axiosInstance, formDataAxiosInstance } from '@/utils/axios'

export const deleteLabelAdmin = async (id: number) => {
const response = await axiosInstance.delete(`/api/managements/labels/${id}`)
Expand All @@ -25,7 +25,27 @@ export const deleteCategoryAdmin = async (id: number) => {
return response.data
}

export const addMemberAdmin = async (memberData: UserRegistrationProps) => {
export const addMemberAdmin = async (memberData: UserRegistrationApiProps) => {
const response = await axiosInstance.post('/api/managements/members', memberData)
return response.data
}

export const getDepartmentsAdmin = async () => {
const response = await axiosInstance.get('/api/managements/departments')
return response.data
}

export const addMemberAdminByCsv = async (formdata: FormData) => {
const response = await formDataAxiosInstance.post('/api/managements/members/upload', formdata)
return response.data
}

export const getMemberDetailAdmin = async (id: string) => {
const response = await axiosInstance.get(`api/managements/members/${id}/details`)
return response.data
}

export const updateMemberAdmin = async (id: string, data: UserUpdateValue) => {
const response = await axiosInstance.post(`api/managements/members/${id}`, data)
return response.data
}
5 changes: 5 additions & 0 deletions src/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ export const patchTaskRequest = async (taskId: string, formdata: FormData) => {
const response = await formDataAxiosInstance.patch(`/api/tasks/${taskId}`, formdata)
return response.data
}

export const cancelTaskUser = async (taskId: number) => {
const response = await axiosInstance.patch(`/api/tasks/${taskId}/cancel`)
return response.data
}
19 changes: 16 additions & 3 deletions src/components/common/FormCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
<div class="text-xs font-bold">
<p class="text-body mb-2">{{ labelName }}</p>
<div
class="w-fit flex gap-2 items-center cursor-pointer"
@click="updateValue">
@click="!isDisabled && updateValue()"
:class="[
'w-fit flex gap-2 items-center',
{ 'cursor-pointer': !isDisabled, 'cursor-not-allowed': isDisabled }
]">
<CommonIcons
:name="checkBoxIcon"
:class="[
Expand All @@ -19,12 +22,22 @@
<script lang="ts" setup>
import { checkBoxIcon } from '@/constants/iconPath'
import type { FormCheckboxProps } from '@/types/common'
import { watch } from 'vue'
import CommonIcons from './CommonIcons.vue'

const { labelName, checkButtonName, isChecked } = defineProps<FormCheckboxProps>()
const { labelName, checkButtonName, isChecked, isDisabled } = defineProps<FormCheckboxProps>()
const emit = defineEmits(['update:modelValue'])

const updateValue = () => {
emit('update:modelValue', !isChecked)
}

watch(
() => isDisabled,
newVal => {
if (newVal) {
emit('update:modelValue', false)
}
}
)
</script>
52 changes: 52 additions & 0 deletions src/components/member-management/MemberManagementAddByCsv.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<template>
<input
class="hidden"
type="file"
id="file"
accept=".csv"
@change="handleFileUpload" />
<label
for="file"
class="cursor-pointer flex items-center gap-1 text-xs font-bold text-primary1">
<CommonIcons
:name="plusIcon"
:style="{ fill: '#7879EB' }" />
파일로 일괄 추가
</label>
<ModalView
:isOpen="isModalVisible"
:type="'successType'"
@close="handleCancel">
<template #header> 회원이 추가되었습니다 </template>
</ModalView>
</template>

<script setup lang="ts">
import { addMemberAdminByCsv } from '@/api/admin'
import { plusIcon } from '@/constants/iconPath'
import { useMemberManagementParamsStore } from '@/stores/params'
import { useQueryClient } from '@tanstack/vue-query'
import { ref } from 'vue'
import CommonIcons from '../common/CommonIcons.vue'
import ModalView from '../ModalView.vue'

const queryClient = useQueryClient()
const isModalVisible = ref(false)
const { params } = useMemberManagementParamsStore()

const handleFileUpload = async (event: Event) => {
const target = event.target as HTMLInputElement
const file = target.files?.[0]
if (!file) return
const formData = new FormData()
formData.append('file', file)
await addMemberAdminByCsv(formData)
queryClient.invalidateQueries({ queryKey: ['member', { ...params }] })
isModalVisible.value = true
target.value = ''
}

const handleCancel = () => {
isModalVisible.value = false
}
</script>
13 changes: 6 additions & 7 deletions src/components/member-management/MemberManagementList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,21 @@
</template>

<script setup lang="ts">
import ListPagination from '../lists/ListPagination.vue'
import ListContainer from '../lists/ListContainer.vue'
import { useMemberManagementParamsStore } from '@/stores/params'
import MemberManagementListBar from './MemberManagementListBar.vue'
import MemberManagementListCard from './MemberManagementListCard.vue'
import NoContent from '../lists/NoContent.vue'
import type { MemberManagementResponse } from '@/types/admin'
import { axiosInstance } from '@/utils/axios'
import { useQuery } from '@tanstack/vue-query'
import { computed } from 'vue'
import type { MemberManagementResponse } from '@/types/admin'
import ListContainer from '../lists/ListContainer.vue'
import ListPagination from '../lists/ListPagination.vue'
import NoContent from '../lists/NoContent.vue'
import MemberManagementListBar from './MemberManagementListBar.vue'
import MemberManagementListCard from './MemberManagementListCard.vue'

const { params } = useMemberManagementParamsStore()
const onPageChange = (value: number) => {
params.page = value
}

const fetchMemberList = async () => {
const response = await axiosInstance.get('/api/managements/members', { params })
return response.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
:is-status="tab.isStatus" />
<div class="w-[180px] flex gap-2 justify-center items-center shrink-0">
<button
@click="router.push(`/user-update`)"
@click="router.push(`/user-update?id=${info.memberId}`)"
class="button-medium-primary">
수정
</button>
Expand Down
5 changes: 3 additions & 2 deletions src/components/request-task/RequestTaskInput.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="relative">
<div class="relative w-full">
<div class="text-xs flex gap-x-1 mb-2">
<p class="text-body font-bold">{{ labelName }}</p>
<p
Expand All @@ -13,7 +13,8 @@
:value="modelValue"
:disabled="isEdit"
@input="updateValue(($event.target as HTMLInputElement).value)"
:placeholder="placeholderText" />
:placeholder="placeholderText"
:class="{ 'text-gray-500': isEdit, 'text-black': !isEdit }" />
<p
v-if="isInvalidateState === 'input'"
class="text-red-1 text-xs absolute top-[calc(100%+4px)]">
Expand Down
2 changes: 1 addition & 1 deletion src/components/task-detail/TaskDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { useMemberStore } from '@/stores/member'
import type { TaskDetailDatas, TaskDetailHistoryData, TaskDetailProps } from '@/types/user'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { storeToRefs } from 'pinia'
import { computed, watch, ref } from 'vue'
import { computed, ref, watch } from 'vue'
import TaskDetailHistory from './TaskDetailHistory.vue'
import TaskDetailLeft from './TaskDetailLeft.vue'
import TaskDetailRight from './TaskDetailRight.vue'
Expand Down
2 changes: 1 addition & 1 deletion src/components/task-detail/TaskDetailHistoryInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
:disabled="!isPossible"
@compositionstart="isComposing = true"
@compositionend="isComposing = false"
@keyup.enter.prevent="handleEnterKey"></textarea>
@keydown.enter.stop.prevent="handleEnterKey"></textarea>
<input
class="hidden"
type="file"
Expand Down
29 changes: 21 additions & 8 deletions src/components/task-detail/TaskDetailTopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<p class="text-primary1">요청 승인</p>
</div>
<div
@click="cancelTask"
@click="toggleModal('cancel')"
v-if="!isApproved && (isRequestor || isProcessor)"
class="flex gap-1 items-center cursor-pointer">
<CommonIcons :name="cancelIcon" />
Expand All @@ -35,16 +35,24 @@
@click="closeTaskDetail"
class="cursor-pointer" />
<ModalView
type="inputType"
:is-open="isModalOpen.cancel"
@click="cancelTask"
@close="cancelTask">
<template #header>거부 사유를 입력해주세요</template>
type="warningType"
@close="closeCancelModal()"
@click="cancelTask">
<template #header>요청을 취소 하시겠습니까?</template>
<template #body>요청하신 내용은 사라집니다</template>
</ModalView>
<ModalView
:is-open="isModalOpen.success"
type="successType"
@close="toggleModal('success')">
<template #header>요청이 취소되었습니다</template>
</ModalView>
</div>
</template>

<script setup lang="ts">
import { cancelTaskUser } from '@/api/user'
import {
approveIcon,
cancelIcon,
Expand All @@ -64,19 +72,24 @@ const { isApproved, closeTaskDetail, id, isProcessor, isRequestor } =

const isModalOpen = ref({
cancel: false,
approve: false
success: false
})

const toggleModal = (key: keyof typeof isModalOpen.value) => {
isModalOpen.value[key] = !isModalOpen.value[key]
}

const cancelTask = () => {
const closeCancelModal = () => {
isModalOpen.value.cancel = false
}

const cancelTask = async () => {
await cancelTaskUser(id)
toggleModal('cancel')
toggleModal('success')
}

const ApproveTask = () => {
toggleModal('approve')
router.push(`/request-approve/${id}`)
}
</script>
61 changes: 61 additions & 0 deletions src/components/user-manage/DepartmentDropDown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<div>
<div class="flex text-xs gap-x-1 mb-2">
<p class="text-body font-bold">부서</p>
<p class="text-red-1">*</p>
</div>
<div class="relative flex">
<div
class="flex w-full h-11 items-center rounded p-4 border border-border-1 bg-white text-black cursor-pointer"
@click="toggleDropdown()">
<p>
{{ dePartments.find(department => department.departmentId === modelValue)?.name }}
</p>
<CommonIcons
:name="dropdownIcon"
:class="['ml-auto', { 'rotate-180': dropdownOpen }]" />
</div>
<div
v-if="dropdownOpen"
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">
<div
v-for="department in dePartments"
:key="department.departmentId"
class="w-full flex items-center h-11 p-2 rounded hover:bg-background-2 cursor-pointer"
@click="selectOption(department)">
{{ department.name }}
</div>
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import { getDepartmentsAdmin } from '@/api/admin'
import { dropdownIcon } from '@/constants/iconPath'
import type { DepartmentType } from '@/types/admin'
import { onMounted, ref } from 'vue'
import CommonIcons from '../common/CommonIcons.vue'

const dePartments = ref<DepartmentType[]>([])
const dropdownOpen = ref(false)
const emit = defineEmits(['update:modelValue'])
const { modelValue } = defineProps<{ modelValue: number }>()

const toggleDropdown = () => {
dropdownOpen.value = !dropdownOpen.value
}

const selectOption = (option: DepartmentType) => {
emit('update:modelValue', option.departmentId)
dropdownOpen.value = false
}

onMounted(async () => {
try {
dePartments.value = await getDepartmentsAdmin()
} catch (error) {
console.error('부서가져오기 실패:', error)
}
})
</script>
Loading