From b302ec095147fa558479c9d6dcc5c31f594ca047 Mon Sep 17 00:00:00 2001 From: king Date: Wed, 2 Sep 2026 02:52:59 +0900 Subject: [PATCH] =?UTF-8?q?feat(cupang):=20=EB=8B=AC=EB=A0=A5=20=EC=B9=B8?= =?UTF-8?q?=EC=97=90=20=EA=B3=B5=ED=9C=B4=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C(=EB=8C=80=EC=B2=B4=EA=B3=B5=ED=9C=B4?= =?UTF-8?q?=EC=9D=BC=20=ED=8F=AC=ED=95=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - holidays.py 를 이름까지 담도록 확장(holiday_name), is_holiday 는 이름 유무로 판정 - 대체공휴일은 원래 명칭 대신 "대체공휴일"로 표기, 겹치는 날은 "어린이날·부처님오신날" - 달력 칸에 날짜 아래 빨강 소형 라벨 + title 툴팁, 칸 최소 높이 62→70px Co-Authored-By: Claude Opus 5 --- app/modules/cupang/holidays.py | 101 +++++++++++------- app/modules/cupang/router.py | 3 +- .../cupang/templates/cupang/box_calc.html | 2 +- .../cupang/templates/cupang/box_rules.html | 2 +- .../cupang/templates/cupang/index.html | 7 +- .../cupang/templates/cupang/products.html | 2 +- app/modules/cupang/templates/cupang/view.html | 2 +- app/static/cupang.css | 12 ++- 8 files changed, 86 insertions(+), 45 deletions(-) diff --git a/app/modules/cupang/holidays.py b/app/modules/cupang/holidays.py index e47f7f7..16ea91e 100644 --- a/app/modules/cupang/holidays.py +++ b/app/modules/cupang/holidays.py @@ -1,8 +1,9 @@ -"""대한민국 공휴일 판정 (달력 색상용). +"""대한민국 공휴일 판정 + 이름 (달력 색상·표기용). - 고정 양력 공휴일은 매년 동일 → 연도 무관 판정. - 음력 공휴일(설날/부처님오신날/추석)과 대체공휴일은 매년 달라짐 → 연도별 dict(`_LUNAR_AND_SUBSTITUTE`)에 명시. 새 연도는 KASI 발표값을 추가한다. +- 대체공휴일은 원래 공휴일 이름 대신 "대체공휴일"로 표기한다. 미수록 연도는 고정 양력 공휴일만 빨강 처리된다(음력/대체는 누락). """ @@ -11,52 +12,78 @@ from __future__ import annotations from datetime import date -# 매년 동일한 양력 공휴일 (month, day) -_FIXED_SOLAR: set[tuple[int, int]] = { - (1, 1), # 신정 - (3, 1), # 삼일절 - (5, 5), # 어린이날 - (6, 6), # 현충일 - (8, 15), # 광복절 - (10, 3), # 개천절 - (10, 9), # 한글날 - (12, 25), # 성탄절 +SUBSTITUTE = "대체공휴일" + +# 매년 동일한 양력 공휴일 (month, day) -> 이름 +_FIXED_SOLAR: dict[tuple[int, int], str] = { + (1, 1): "신정", + (3, 1): "삼일절", + (5, 5): "어린이날", + (6, 6): "현충일", + (8, 15): "광복절", + (10, 3): "개천절", + (10, 9): "한글날", + (12, 25): "성탄절", } -# 연도별 음력 공휴일 + 대체공휴일 (ISO 날짜 문자열). KASI 발표 기준. -_LUNAR_AND_SUBSTITUTE: dict[int, set[str]] = { +# 연도별 음력 공휴일 + 대체공휴일 (ISO 날짜 -> 이름). KASI 발표 기준. +_LUNAR_AND_SUBSTITUTE: dict[int, dict[str, str]] = { 2025: { - "2025-01-28", "2025-01-29", "2025-01-30", # 설날 연휴 - "2025-03-03", # 삼일절 대체(3/1 토) - "2025-05-06", # 부처님오신날 대체(5/5 겹침) - "2025-05-05", # 부처님오신날(어린이날과 동일일) - "2025-10-06", "2025-10-07", "2025-10-08", # 추석 연휴 - "2025-10-08", # 추석 대체 가능 + "2025-01-28": "설날 연휴", + "2025-01-29": "설날", + "2025-01-30": "설날 연휴", + "2025-03-03": SUBSTITUTE, # 삼일절(3/1 토) + "2025-05-05": "부처님오신날", # 어린이날과 같은 날 + "2025-05-06": SUBSTITUTE, # 부처님오신날 겹침 + "2025-10-06": "추석", + "2025-10-07": "추석 연휴", + "2025-10-08": SUBSTITUTE, # 추석 연휴 겹침 }, 2026: { - "2026-02-16", "2026-02-17", "2026-02-18", # 설날 연휴 (설날 2/17) - "2026-03-02", # 삼일절 대체 (3/1 일) - "2026-05-24", # 부처님오신날 (일) - "2026-05-25", # 부처님오신날 대체 - "2026-08-17", # 광복절 대체 (8/15 토) - "2026-09-24", "2026-09-25", "2026-09-26", # 추석 연휴 (추석 9/25) - "2026-09-28", # 추석 대체 (9/26 토) - "2026-10-05", # 개천절 대체 (10/3 토) + "2026-02-16": "설날 연휴", + "2026-02-17": "설날", + "2026-02-18": "설날 연휴", + "2026-03-02": SUBSTITUTE, # 삼일절(3/1 일) + "2026-05-24": "부처님오신날", + "2026-05-25": SUBSTITUTE, + "2026-08-17": SUBSTITUTE, # 광복절(8/15 토) + "2026-09-24": "추석 연휴", + "2026-09-25": "추석", + "2026-09-26": "추석 연휴", + "2026-09-28": SUBSTITUTE, + "2026-10-05": SUBSTITUTE, # 개천절(10/3 토) }, 2027: { - "2027-02-06", "2027-02-07", "2027-02-08", # 설날 연휴 (설날 2/7) - "2027-02-09", # 설날 대체 (2/7 일) - "2027-05-13", # 부처님오신날 (목) - "2027-08-16", # 광복절 대체 (8/15 일) - "2027-09-14", "2027-09-15", "2027-09-16", # 추석 연휴 (추석 9/15) - "2027-10-04", # 개천절 대체 (10/3 일) - "2027-10-11", # 한글날 대체 (10/9 토) + "2027-02-06": "설날 연휴", + "2027-02-07": "설날", + "2027-02-08": "설날 연휴", + "2027-02-09": SUBSTITUTE, + "2027-05-13": "부처님오신날", + "2027-08-16": SUBSTITUTE, # 광복절(8/15 일) + "2027-09-14": "추석 연휴", + "2027-09-15": "추석", + "2027-09-16": "추석 연휴", + "2027-10-04": SUBSTITUTE, # 개천절(10/3 일) + "2027-10-11": SUBSTITUTE, # 한글날(10/9 토) }, } +def holiday_name(d: date) -> str: + """그날의 공휴일 이름. 공휴일이 아니면 빈 문자열. + + 한 날에 두 공휴일이 겹치면 "어린이날·부처님오신날" 처럼 이어 붙인다. + """ + names: list[str] = [] + fixed = _FIXED_SOLAR.get((d.month, d.day)) + if fixed: + names.append(fixed) + extra = _LUNAR_AND_SUBSTITUTE.get(d.year, {}).get(d.isoformat()) + if extra and extra not in names: + names.append(extra) + return "·".join(names) + + def is_holiday(d: date) -> bool: """공휴일(일요일 제외)이면 True. 일/토 색상은 요일로 따로 판정한다.""" - if (d.month, d.day) in _FIXED_SOLAR: - return True - return d.isoformat() in _LUNAR_AND_SUBSTITUTE.get(d.year, set()) + return bool(holiday_name(d)) diff --git a/app/modules/cupang/router.py b/app/modules/cupang/router.py index ca4a6c4..db4ea09 100644 --- a/app/modules/cupang/router.py +++ b/app/modules/cupang/router.py @@ -21,7 +21,7 @@ from app.timezone import today_kst from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, Request, UploadFile from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse -from .holidays import is_holiday +from .holidays import holiday_name, is_holiday from .store import SHIP_METHODS router = APIRouter(prefix="/cupang", tags=["cupang"]) @@ -181,6 +181,7 @@ async def index(request: Request) -> HTMLResponse: "is_sunday": d.weekday() == 6, "is_saturday": d.weekday() == 5, "is_holiday": is_holiday(d), + "holiday_name": holiday_name(d), "counts": counts.get(d.isoformat(), {}), } for d in week diff --git a/app/modules/cupang/templates/cupang/box_calc.html b/app/modules/cupang/templates/cupang/box_calc.html index ce94733..e174b91 100644 --- a/app/modules/cupang/templates/cupang/box_calc.html +++ b/app/modules/cupang/templates/cupang/box_calc.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/box_rules.html b/app/modules/cupang/templates/cupang/box_rules.html index cf82529..33cb22b 100644 --- a/app/modules/cupang/templates/cupang/box_rules.html +++ b/app/modules/cupang/templates/cupang/box_rules.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/index.html b/app/modules/cupang/templates/cupang/index.html index 08bc714..2a04df2 100644 --- a/app/modules/cupang/templates/cupang/index.html +++ b/app/modules/cupang/templates/cupang/index.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
@@ -56,8 +56,11 @@ {% if cell.counts.ship %}has-ship{% endif %} {% if cell.is_sunday or cell.is_holiday %}is-red{% elif cell.is_saturday %}is-blue{% endif %}" href="/cupang/?year={{ year }}&month={{ month }}&date={{ cell.date }}" - title="{{ cell.date }}{% if cell.counts.ship %} — 출고 {{ cell.counts.ship }}건 / 센터 {{ cell.counts.centers }}곳{% endif %}"> + title="{{ cell.date }}{% if cell.holiday_name %} — {{ cell.holiday_name }}{% endif %}{% if cell.counts.ship %} — 출고 {{ cell.counts.ship }}건 / 센터 {{ cell.counts.centers }}곳{% endif %}"> {{ cell.day }} + {% if cell.holiday_name %} + {{ cell.holiday_name }} + {% endif %} {% if cell.counts.ship %} {{ cell.counts.ship }}건{{ cell.counts.centers }}곳 {% endif %} diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 6ed9bfb..8eb8fca 100644 --- a/app/modules/cupang/templates/cupang/products.html +++ b/app/modules/cupang/templates/cupang/products.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/view.html b/app/modules/cupang/templates/cupang/view.html index 2522262..2f1e915 100644 --- a/app/modules/cupang/templates/cupang/view.html +++ b/app/modules/cupang/templates/cupang/view.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %} {# 출고 묶음 보기 — 상자 계산 화면과 같은 3열 구성. 읽기 전용(수정 없음). #} diff --git a/app/static/cupang.css b/app/static/cupang.css index 03d2aef..ef823ca 100644 --- a/app/static/cupang.css +++ b/app/static/cupang.css @@ -1112,7 +1112,7 @@ body.cpg-dragging { cursor: grabbing; user-select: none; } .cpg-mcal-cell { position: relative; display: flex; flex-direction: column; align-items: center; gap: 4px; - min-height: 62px; min-width: 0; padding: 6px 4px 7px; + min-height: 70px; min-width: 0; padding: 6px 4px 7px; border: 1px solid var(--color-subtle-ash); border-radius: 8px; background: #fff; @@ -1141,6 +1141,16 @@ body.cpg-dragging { cursor: grabbing; user-select: none; } box-shadow: inset 0 0 0 1.5px var(--color-deep-black); } +/* 공휴일 이름 — 날짜 아래 작은 빨강 글씨 */ +.cpg-mcal-holi { + max-width: 100%; + font-size: 11px; font-weight: 700; line-height: 1.3; + color: var(--color-callout-red); + text-align: center; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.cpg-mcal-cell.is-out .cpg-mcal-holi { color: #e3a59b; } + /* 출고 있는 날 — 건수/센터수 태그 */ .cpg-mcal-tag { display: inline-flex; align-items: center; gap: 4px;