대시보드와 주간보고 기능 업데이트

This commit is contained in:
2026-01-10 14:40:01 +09:00
parent 0dd4b561f0
commit e4627caa4c
26 changed files with 3329 additions and 1720 deletions

View File

@@ -64,9 +64,11 @@
<div
class="upload-zone p-5 text-center border rounded"
:class="{ 'border-primary bg-light': isDragging }"
tabindex="0"
@dragover.prevent="isDragging = true"
@dragleave.prevent="isDragging = false"
@drop.prevent="handleDrop"
@paste="handlePaste"
@click="($refs.fileInput as HTMLInputElement).click()"
>
<input
@@ -80,7 +82,7 @@
<i class="bi bi-cloud-arrow-up display-4 text-muted"></i>
<p class="mt-2 mb-0 text-muted">
이미지를 드래그하거나 클릭해서 업로드<br>
<small>(최대 10, PNG/JPG)</small>
<small>또는 <strong>Ctrl+V</strong> 붙여넣기 (최대 10)</small>
</p>
</div>
</div>
@@ -358,6 +360,7 @@
<script setup lang="ts">
const { fetchCurrentUser } = useAuth()
const { getWeekInfo, getWeekDates, getLastWeekInfo, getActualCurrentWeekInfo, changeWeek: calcChangeWeek } = useWeekCalc()
const router = useRouter()
const step = ref(1)
@@ -400,12 +403,6 @@ onMounted(async () => {
router.push('/login')
return
}
if (user.employeeEmail !== 'coziny@gmail.com') {
alert('관리자만 접근할 수 있습니다.')
router.push('/')
return
}
})
function getHeaderClass(report: any) {
@@ -449,14 +446,36 @@ function handleDrop(e: DragEvent) {
if (files) processFiles(files)
}
function handlePaste(e: ClipboardEvent) {
const items = e.clipboardData?.items
if (!items) return
const imageFiles: File[] = []
for (let i = 0; i < items.length; i++) {
if (items[i].type.startsWith('image/')) {
const file = items[i].getAsFile()
if (file) imageFiles.push(file)
}
}
if (imageFiles.length > 0) {
e.preventDefault()
processImageFiles(imageFiles)
}
}
function handleFileSelect(e: Event) {
const input = e.target as HTMLInputElement
if (input.files) processFiles(input.files)
}
function processFiles(files: FileList) {
processImageFiles(Array.from(files))
}
function processImageFiles(files: File[]) {
const maxFiles = 10 - uploadedImages.value.length
const toProcess = Array.from(files).slice(0, maxFiles)
const toProcess = files.slice(0, maxFiles)
toProcess.forEach(file => {
if (!file.type.startsWith('image/')) return
@@ -491,67 +510,34 @@ function removeParsedTask(taskArray: any[], idx: number) {
}
}
// 주차 계산 함수들
function getMonday(date: Date): Date {
const d = new Date(date)
const day = d.getDay()
const diff = d.getDate() - day + (day === 0 ? -6 : 1)
d.setDate(diff)
return d
}
function getSunday(monday: Date): Date {
const d = new Date(monday)
d.setDate(d.getDate() + 6)
return d
}
function formatDate(date: Date): string {
return date.toISOString().split('T')[0]
}
function getWeekNumber(date: Date): { year: number; week: number } {
const d = new Date(date)
d.setHours(0, 0, 0, 0)
d.setDate(d.getDate() + 3 - (d.getDay() + 6) % 7)
const week1 = new Date(d.getFullYear(), 0, 4)
const weekNum = 1 + Math.round(((d.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7)
return { year: d.getFullYear(), week: weekNum }
}
function setWeekDates(monday: Date) {
const sunday = getSunday(monday)
const weekInfo = getWeekNumber(monday)
parsedData.value.weekStartDate = formatDate(monday)
parsedData.value.weekEndDate = formatDate(sunday)
parsedData.value.reportYear = weekInfo.year
parsedData.value.reportWeek = weekInfo.week
// 주차 관련 함수들 (useWeekCalc 사용)
function setWeekFromInfo(info: { year: number; week: number; startDateStr: string; endDateStr: string }) {
parsedData.value.reportYear = info.year
parsedData.value.reportWeek = info.week
parsedData.value.weekStartDate = info.startDateStr
parsedData.value.weekEndDate = info.endDateStr
}
function changeWeek(delta: number) {
const currentMonday = new Date(parsedData.value.weekStartDate)
currentMonday.setDate(currentMonday.getDate() + (delta * 7))
setWeekDates(currentMonday)
const { year, week } = calcChangeWeek(parsedData.value.reportYear, parsedData.value.reportWeek, delta)
const weekInfo = getWeekDates(year, week)
setWeekFromInfo(weekInfo)
}
function setLastWeek() {
const today = new Date()
const lastWeekMonday = getMonday(today)
lastWeekMonday.setDate(lastWeekMonday.getDate() - 7)
setWeekDates(lastWeekMonday)
const lastWeek = getLastWeekInfo()
setWeekFromInfo(lastWeek)
}
function setThisWeek() {
const today = new Date()
const thisWeekMonday = getMonday(today)
setWeekDates(thisWeekMonday)
const thisWeek = getActualCurrentWeekInfo()
setWeekFromInfo(thisWeek)
}
function updateWeekFromDate() {
const startDate = new Date(parsedData.value.weekStartDate)
const monday = getMonday(startDate)
setWeekDates(monday)
const weekInfo = getWeekInfo(startDate)
setWeekFromInfo(weekInfo)
}
// 분석 결과 처리
@@ -568,6 +554,11 @@ function handleParseResult(res: any) {
}))
p.planTasks = p.planTasks || []
})
// 내용 없는 프로젝트 제외
r.projects = r.projects.filter((p: any) =>
(p.workTasks && p.workTasks.some((t: any) => t.description?.trim())) ||
(p.planTasks && p.planTasks.some((t: any) => t.description?.trim()))
)
})
employees.value = res.employees
projects.value = res.projects
@@ -616,12 +607,14 @@ async function bulkRegister() {
employeeId: r.createNewEmployee ? null : r.matchedEmployeeId,
employeeName: r.employeeName,
employeeEmail: r.employeeEmail,
projects: r.projects.map((p: any) => ({
projectId: p.matchedProjectId,
projectName: p.projectName,
workTasks: (p.workTasks || []).filter((t: any) => t.description?.trim()),
planTasks: (p.planTasks || []).filter((t: any) => t.description?.trim())
})),
projects: r.projects
.map((p: any) => ({
projectId: p.matchedProjectId,
projectName: p.projectName,
workTasks: (p.workTasks || []).filter((t: any) => t.description?.trim()),
planTasks: (p.planTasks || []).filter((t: any) => t.description?.trim())
}))
.filter((p: any) => p.workTasks.length > 0 || p.planTasks.length > 0), // 내용 없는 프로젝트 제외
issueDescription: r.issueDescription,
vacationDescription: r.vacationDescription,
remarkDescription: r.remarkDescription