getCookie 제거
This commit is contained in:
49
frontend/components/common/ToastContainer.vue
Normal file
49
frontend/components/common/ToastContainer.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1100;">
|
||||
<div
|
||||
v-for="toast in toasts"
|
||||
:key="toast.id"
|
||||
class="toast show"
|
||||
role="alert"
|
||||
>
|
||||
<div class="toast-header" :class="headerClass(toast.type)">
|
||||
<i :class="iconClass(toast.type)" class="me-2"></i>
|
||||
<strong class="me-auto">{{ toast.title }}</strong>
|
||||
<button type="button" class="btn-close btn-close-white" @click="remove(toast.id)"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
{{ toast.message }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { toasts, remove } = useToast()
|
||||
|
||||
function headerClass(type: string) {
|
||||
const classes: Record<string, string> = {
|
||||
success: 'bg-success text-white',
|
||||
error: 'bg-danger text-white',
|
||||
warning: 'bg-warning text-dark',
|
||||
info: 'bg-primary text-white'
|
||||
}
|
||||
return classes[type] || classes.info
|
||||
}
|
||||
|
||||
function iconClass(type: string) {
|
||||
const icons: Record<string, string> = {
|
||||
success: 'bi bi-check-circle-fill',
|
||||
error: 'bi bi-x-circle-fill',
|
||||
warning: 'bi bi-exclamation-triangle-fill',
|
||||
info: 'bi bi-info-circle-fill'
|
||||
}
|
||||
return icons[type] || icons.info
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toast {
|
||||
min-width: 300px;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="container-fluid">
|
||||
<NuxtLink class="navbar-brand" to="/">
|
||||
<i class="bi bi-clipboard-check me-2"></i>
|
||||
주간업무보고
|
||||
업무관리프로그램
|
||||
</NuxtLink>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
|
||||
60
frontend/composables/useToast.ts
Normal file
60
frontend/composables/useToast.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Bootstrap Toast Composable
|
||||
* 전역 토스트 메시지 관리
|
||||
*/
|
||||
interface ToastMessage {
|
||||
id: number
|
||||
type: 'success' | 'error' | 'warning' | 'info'
|
||||
title?: string
|
||||
message: string
|
||||
duration?: number
|
||||
}
|
||||
|
||||
const toasts = ref<ToastMessage[]>([])
|
||||
let toastId = 0
|
||||
|
||||
export function useToast() {
|
||||
function show(message: string, type: ToastMessage['type'] = 'info', title?: string, duration = 3000) {
|
||||
const id = ++toastId
|
||||
toasts.value.push({ id, type, title, message, duration })
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
remove(id)
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
|
||||
function success(message: string, title?: string, duration = 3000) {
|
||||
show(message, 'success', title || '성공', duration)
|
||||
}
|
||||
|
||||
function error(message: string, title?: string, duration = 5000) {
|
||||
show(message, 'error', title || '오류', duration)
|
||||
}
|
||||
|
||||
function warning(message: string, title?: string, duration = 4000) {
|
||||
show(message, 'warning', title || '경고', duration)
|
||||
}
|
||||
|
||||
function info(message: string, title?: string, duration = 3000) {
|
||||
show(message, 'info', title || '알림', duration)
|
||||
}
|
||||
|
||||
function remove(id: number) {
|
||||
const idx = toasts.value.findIndex(t => t.id === id)
|
||||
if (idx > -1) {
|
||||
toasts.value.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
toasts,
|
||||
show,
|
||||
success,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
remove
|
||||
}
|
||||
}
|
||||
@@ -457,6 +457,7 @@ const { fetchCurrentUser } = useAuth()
|
||||
const route = useRoute()
|
||||
const { getWeekInfo, getWeekDates, getLastWeekInfo, getActualCurrentWeekInfo, getMonday, changeWeek: calcChangeWeek } = useWeekCalc()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
|
||||
interface TaskItem {
|
||||
projectId: number
|
||||
@@ -656,7 +657,7 @@ async function setDefaultWeek(userId: number) {
|
||||
const isFutureWeek = year > currentWeek.year || (year === currentWeek.year && week > currentWeek.week)
|
||||
|
||||
if (isFutureWeek) {
|
||||
alert('작성할 수 없는 주차입니다.')
|
||||
toast.warning('작성할 수 없는 주차입니다.')
|
||||
router.replace('/report/weekly')
|
||||
return false
|
||||
}
|
||||
@@ -860,7 +861,7 @@ function formatHoursDisplay(hours: number): string {
|
||||
async function handleSubmit() {
|
||||
const validTasks = form.value.tasks.filter(t => t.description.trim())
|
||||
if (validTasks.length === 0) {
|
||||
alert('최소 1개 이상의 Task를 입력해주세요.')
|
||||
toast.warning('최소 1개 이상의 Task를 입력해주세요.')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -885,10 +886,10 @@ async function handleSubmit() {
|
||||
remarkDescription: form.value.remarkDescription
|
||||
}
|
||||
})
|
||||
alert('주간보고가 작성되었습니다.')
|
||||
toast.success('주간보고가 작성되었습니다.')
|
||||
router.push('/report/weekly')
|
||||
} catch (e: any) {
|
||||
alert(e.data?.message || '저장에 실패했습니다.')
|
||||
toast.error(e.data?.message || '저장에 실패했습니다.')
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
@@ -1000,11 +1001,11 @@ async function runAiParse() {
|
||||
}
|
||||
aiStep.value = 'matching'
|
||||
} else {
|
||||
alert('분석된 내용이 없습니다.')
|
||||
toast.warning('분석된 내용이 없습니다.')
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('=== AI 분석 에러 ===', e)
|
||||
alert(e.data?.message || 'AI 분석에 실패했습니다.')
|
||||
toast.error(e.data?.message || 'AI 분석에 실패했습니다.')
|
||||
} finally {
|
||||
isAiParsing.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user