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

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

@@ -1,8 +1,11 @@
/**
* ISO 8601 주차 계산 composable
* - 1월 4일이 포함된 주 = 1주차
* - 주의 시작 = 월요일
* - 예: 2026년 2주차 = 2026-01-05(월) ~ 2026-01-11(일)
*/
interface WeekInfo {
export interface WeekInfo {
year: number
week: number
startDate: Date
@@ -13,28 +16,114 @@ interface WeekInfo {
}
export function useWeekCalc() {
/**
* 날짜를 YYYY-MM-DD 형식으로 포맷
*/
function formatDate(date: Date): string {
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
return `${y}-${m}-${d}`
}
/**
* 한국어 날짜 포맷 (M월 D일)
*/
function formatDateKr(date: Date): string {
return `${date.getMonth() + 1}${date.getDate()}`
}
/**
* 해당 날짜의 월요일 반환
*/
function getMonday(date: Date): Date {
const d = new Date(date)
d.setHours(0, 0, 0, 0)
const day = d.getDay()
const diff = d.getDate() - day + (day === 0 ? -6 : 1)
d.setDate(diff)
return d
}
/**
* 해당 날짜의 일요일 반환
*/
function getSunday(date: Date): Date {
const monday = getMonday(date)
const sunday = new Date(monday)
sunday.setDate(monday.getDate() + 6)
return sunday
}
/**
* 해당 연도의 1주차 월요일 반환 (ISO 8601)
* - 1월 4일이 포함된 주의 월요일
*/
function getWeek1Monday(year: number): Date {
const jan4 = new Date(year, 0, 4)
return getMonday(jan4)
}
/**
* 해당 연도의 총 주차 수 반환
*/
function getWeeksInYear(year: number): number {
const dec31 = new Date(year, 11, 31)
const weekInfo = getWeekNumber(dec31)
// 12월 31일이 다음 해 1주차면 52주, 아니면 해당 주차
return weekInfo.year === year ? weekInfo.week : 52
}
/**
* 특정 날짜의 ISO 주차 번호 반환
*/
function getWeekNumber(date: Date): { year: number; week: number } {
const d = new Date(date)
d.setHours(0, 0, 0, 0)
// 목요일 기준으로 연도 판단 (ISO 8601)
const thursday = new Date(d)
thursday.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7))
const year = thursday.getFullYear()
const week1Monday = getWeek1Monday(year)
const diffTime = getMonday(d).getTime() - week1Monday.getTime()
const diffDays = Math.round(diffTime / (24 * 60 * 60 * 1000))
const week = Math.floor(diffDays / 7) + 1
return { year, week }
}
/**
* 특정 날짜의 ISO 주차 정보 반환
*/
function getWeekInfo(date: Date = new Date()): WeekInfo {
const target = new Date(date)
target.setHours(0, 0, 0, 0)
const monday = getMonday(date)
const sunday = getSunday(date)
const { year, week } = getWeekNumber(date)
// 목요일 기준으로 연도 판단 (ISO 규칙)
const thursday = new Date(target)
thursday.setDate(target.getDate() - ((target.getDay() + 6) % 7) + 3)
return {
year,
week,
startDate: monday,
endDate: sunday,
startDateStr: formatDate(monday),
endDateStr: formatDate(sunday),
weekString: `${year}-W${week.toString().padStart(2, '0')}`
}
}
/**
* 연도/주차로 날짜 범위 반환
*/
function getWeekDates(year: number, week: number): WeekInfo {
const week1Monday = getWeek1Monday(year)
const year = thursday.getFullYear()
const firstThursday = new Date(year, 0, 4)
firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3)
const monday = new Date(week1Monday)
monday.setDate(week1Monday.getDate() + (week - 1) * 7)
const week = Math.ceil((thursday.getTime() - firstThursday.getTime()) / (7 * 24 * 60 * 60 * 1000)) + 1
// 해당 주의 월요일
const monday = new Date(target)
monday.setDate(target.getDate() - ((target.getDay() + 6) % 7))
// 해당 주의 일요일
const sunday = new Date(monday)
sunday.setDate(monday.getDate() + 6)
@@ -49,6 +138,24 @@ export function useWeekCalc() {
}
}
/**
* 주차 이동 (delta: +1 다음주, -1 이전주)
*/
function changeWeek(year: number, week: number, delta: number): { year: number; week: number } {
let newYear = year
let newWeek = week + delta
if (newWeek < 1) {
newYear--
newWeek = getWeeksInYear(newYear)
} else if (newWeek > getWeeksInYear(newYear)) {
newYear++
newWeek = 1
}
return { year: newYear, week: newWeek }
}
/**
* 이번 주 정보 (보고서 기준)
* - 금~일: 현재 주차
@@ -85,13 +192,6 @@ export function useWeekCalc() {
return getWeekInfo(lastWeek)
}
/**
* 날짜 포맷 (YYYY-MM-DD)
*/
function formatDate(date: Date): string {
return date.toISOString().split('T')[0]
}
/**
* 주차 문자열 파싱
*/
@@ -102,39 +202,43 @@ export function useWeekCalc() {
}
/**
* 주차별 날짜 범위 텍스트
* 주차별 날짜 범위 텍스트 (예: "1월 5일 ~ 1월 11일")
*/
function getWeekRangeText(year: number, week: number): string {
// 해당 연도 첫 번째 목요일 찾기
const jan4 = new Date(year, 0, 4)
const firstThursday = new Date(jan4)
firstThursday.setDate(jan4.getDate() - ((jan4.getDay() + 6) % 7) + 3)
// 해당 주차의 월요일
const monday = new Date(firstThursday)
monday.setDate(firstThursday.getDate() - 3 + (week - 1) * 7)
const sunday = new Date(monday)
sunday.setDate(monday.getDate() + 6)
return `${formatDateKr(monday)} ~ ${formatDateKr(sunday)}`
const { startDate, endDate } = getWeekDates(year, week)
return `${formatDateKr(startDate)} ~ ${formatDateKr(endDate)}`
}
/**
* 한국어 날짜 포맷 (M월 D일)
* 주차별 날짜 범위 텍스트 - ISO 형식 (예: "2026-01-05 ~ 2026-01-11")
*/
function formatDateKr(date: Date): string {
return `${date.getMonth() + 1}${date.getDate()}`
function getWeekRangeTextISO(year: number, week: number): string {
const { startDateStr, endDateStr } = getWeekDates(year, week)
return `${startDateStr} ~ ${endDateStr}`
}
return {
// 기본 유틸
formatDate,
formatDateKr,
getMonday,
getSunday,
// 주차 계산
getWeekNumber,
getWeekInfo,
getWeekDates,
getWeeksInYear,
changeWeek,
// 현재/지난주
getCurrentWeekInfo,
getActualCurrentWeekInfo,
getLastWeekInfo,
formatDate,
// 파싱/포맷
parseWeekString,
getWeekRangeText,
formatDateKr
getWeekRangeTextISO
}
}