From 97c5c8659251567d43dfc21d641b9ffe78ac2bee Mon Sep 17 00:00:00 2001 From: borissal1207 Date: Tue, 4 Aug 2026 15:55:57 +0900 Subject: [PATCH 1/4] =?UTF-8?q?style:=20#49=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=99=94=EB=A9=B4=20=EB=B0=8F=20planner=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planner/templates/planner/dashboard.html | 181 +++++++++++++++ .../planner/includes/empty_state.html | 27 +++ .../templates/planner/includes/fit_bar.html | 17 ++ .../templates/planner/includes/sidebar.html | 48 ++++ static/css/planner.css | 209 ++++++++++++++++++ static/js/planner.js | 15 ++ templates/includes/icons.html | 32 +++ 7 files changed, 529 insertions(+) create mode 100644 planner/templates/planner/includes/empty_state.html create mode 100644 planner/templates/planner/includes/fit_bar.html create mode 100644 planner/templates/planner/includes/sidebar.html create mode 100644 static/css/planner.css create mode 100644 static/js/planner.js create mode 100644 templates/includes/icons.html diff --git a/planner/templates/planner/dashboard.html b/planner/templates/planner/dashboard.html index e69de29..6fe1e7e 100644 --- a/planner/templates/planner/dashboard.html +++ b/planner/templates/planner/dashboard.html @@ -0,0 +1,181 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}대시보드 · PLAN B{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block body %} +
+ + {% include "planner/includes/sidebar.html" with nav="dashboard" %} + +
+
+ {# 사이드바가 접혔을 때 다시 여는 버튼 #} + +

대시보드

+
+ +
+ + {% if not exam_period %} + {# ===== 시험기간이 없을 때 ===== #} +
+ {% include "planner/includes/empty_state.html" with icon="i-calendar" title="아직 등록된 시험기간이 없습니다" desc="시험기간을 만들고 과목과 공부 가능시간을 입력하면, PLAN B가 날짜별 계획을 대신 계산해 드립니다." btn_label="시험기간 만들기" show_steps=True %} +
+ + {% else %} + {# ===== 평소 대시보드 ===== #} +
+
+

{{ today|date:"n월 j일 l" }}

+
+ 시험까지 남은 공부 가능일 {{ remaining_days }}일 + · 오늘 배정 {{ today_minutes }}분 +
+
+ {# TODO: {% url 'planner:today' %} 로 교체 #} + + 오늘의 공부 시작 + +
+ + {% if pending_recovery %} + + {% endif %} + + {# ---- 전체 판정 ---- #} +
+
+
+
+ 시험기간 전체 판정 + + {{ overall.status_label }} + +
+ + {{ overall.calculated_at|date:"n.j H:i" }} 기준 + +
+ + {% include "planner/includes/fit_bar.html" with status=overall.status min_pct=overall.min_pct band_pct=overall.band_pct mark_pct=overall.mark_pct min_minutes=overall.min_minutes max_minutes=overall.max_minutes available_minutes=overall.available_minutes axis_max=overall.axis_max %} + +
+
+
남은 가능시간
+
{{ overall.available_minutes }}
+
+
+
최소 필요시간
+
{{ overall.min_minutes }}
+
+
+
최대 필요시간
+
{{ overall.max_minutes }}
+
+
+
최대 기준 부족
+
+ {% if overall.shortage_minutes %}-{{ overall.shortage_minutes }}{% else %}0{% endif %} +
+ {% if overall.daily_extra_minutes %} +
하루 평균 +{{ overall.daily_extra_minutes }}분이면 해소
+ {% endif %} +
+
+
+
+ + {# ---- 과목별 판정 ---- #} +
+
+

과목별 판정

+ 같은 눈금 · 0~{{ subject_axis_max }}분 +
+ + 전체 계획 보기 + +
+ +
+ {% for s in subject_status %} +
+
+ {{ s.subject_name }} + + {{ s.status_label }} + +
+
+ 시험 {{ s.exam_date|date:"n/j" }} · D-{{ s.d_day }} +
+ + {% include "planner/includes/fit_bar.html" with compact=True status=s.status min_pct=s.min_pct band_pct=s.band_pct mark_pct=s.mark_pct %} + +
+ 필요 {{ s.min_minutes }}~{{ s.max_minutes }}분 + 가능 {{ s.available_minutes }}분 +
+
+ {% endfor %} +
+ + {# ---- 진행 상황 ---- #} +
+

진행 상황

+
+ +
+
+
오늘 완료율
+
{{ progress.today_percent }}%
+
+
{{ progress.today_done_minutes }} / {{ progress.today_total_minutes }}분
+
+ +
+
전체 진행률
+
{{ progress.total_percent }}%
+
+
{{ progress.total_done_minutes }} / {{ progress.total_minutes }}분
+
+ +
+
남은 CORE 작업
+
{{ progress.core_left }}
+
+ {% for c in progress.core_by_subject %} + {{ c.name }} {{ c.count }} + {% endfor %} +
+
+
+ {% endif %} + +
+
+
+ +{% include "includes/icons.html" %} +{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/planner/templates/planner/includes/empty_state.html b/planner/templates/planner/includes/empty_state.html new file mode 100644 index 0000000..9358742 --- /dev/null +++ b/planner/templates/planner/includes/empty_state.html @@ -0,0 +1,27 @@ +
+
+ +

{{ title }}

+ {% if desc %}

{{ desc }}

{% endif %} + + {% if btn_label %} + {{ btn_label }} + {% endif %} + + {% if show_steps %} +
+
+
STEP 1
시험기간과 과목 등록 + 기간, 과목명, 시험일을 입력합니다 +
+
+
STEP 2
공부 가능시간 입력 + 날짜별 전체 시간만 넣으면 됩니다 +
+
+
STEP 3
시험 범위 넣기 + PDF 또는 직접 입력 +
+
+ {% endif %} +
\ No newline at end of file diff --git a/planner/templates/planner/includes/fit_bar.html b/planner/templates/planner/includes/fit_bar.html new file mode 100644 index 0000000..d2cd984 --- /dev/null +++ b/planner/templates/planner/includes/fit_bar.html @@ -0,0 +1,17 @@ +
+
+
+
+
+
+ {% if not compact %}가능 {{ available_minutes }}분{% endif %} +
+
+ +{% if not compact %} +
+ 0분 + 필요 {{ min_minutes }} ~ {{ max_minutes }}분 + {{ axis_max }}분 +
+{% endif %} \ No newline at end of file diff --git a/planner/templates/planner/includes/sidebar.html b/planner/templates/planner/includes/sidebar.html new file mode 100644 index 0000000..69aa3e7 --- /dev/null +++ b/planner/templates/planner/includes/sidebar.html @@ -0,0 +1,48 @@ + \ No newline at end of file diff --git a/static/css/planner.css b/static/css/planner.css new file mode 100644 index 0000000..966d5e4 --- /dev/null +++ b/static/css/planner.css @@ -0,0 +1,209 @@ +.ico{width:18px;height:18px;flex:none;display:block; + stroke:currentColor;fill:none;stroke-width:1.8; + stroke-linecap:round;stroke-linejoin:round;overflow:visible} + +.app{display:grid;grid-template-columns:220px 1fr;min-height:100vh} +.app.collapsed{grid-template-columns:0 1fr} + +.side{ + background:var(--neutral1);border-right:1px solid var(--neutral4); + display:flex;flex-direction:column;position:sticky;top:0;height:100vh; + padding:0 12px 18px;overflow:hidden;transition:.2s; +} +.app.collapsed .side{padding:0;border-right:0} +.app.collapsed .side>*{display:none} + +.brand{ + height:60px;display:flex;align-items:center; + margin:0 -12px 12px;padding:0 22px; + border-bottom:1px solid var(--neutral4); + font-size:17px;font-weight:800;color:var(--neutral11);letter-spacing:-0.035em; +} +.brand em{font-style:normal;color:var(--brand4);margin-left:5px} + +.nav-i{ + display:flex;align-items:center;gap:11px;height:38px;padding:0 10px; + border-radius:9px;font-size:14px;font-weight:550;color:var(--neutral9);transition:.16s; +} +.nav-i .ico{width:17px;height:17px;color:var(--neutral7)} +.nav-i:hover{background:var(--neutral3)} +.nav-i.on{background:var(--neutral11);color:#fff} +.nav-i.on .ico{color:#fff} +.nav-i.off{color:var(--neutral6);pointer-events:none} +.nav-i .count{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--neutral7)} + +.side-label{font-size:11px;font-weight:700;color:var(--neutral7); + letter-spacing:.08em;padding:20px 10px 9px} +.side-box{padding:14px;border:1px solid var(--neutral5);border-radius:12px;background:var(--neutral2)} +.side-box.dashed{border-style:dashed;text-align:center} +.side-box h4{font-size:13px;font-weight:650;color:var(--neutral10)} +.side-box p{font-size:12px;color:var(--neutral7);line-height:1.6} +.side-box.alert{background:var(--judge2-bg);border-color:var(--judge2-line);border-style:solid} +.side-box.alert h4{color:var(--judge2);display:flex;align-items:center;gap:7px} +.side-box.alert p{color:var(--judge2);margin-top:6px} +.side-foot{margin-top:auto;padding-top:16px;border-top:1px solid var(--neutral4)} + +.main{min-width:0;display:flex;flex-direction:column} +.topbar{ + height:60px;background:var(--neutral1);border-bottom:1px solid var(--neutral4); + display:flex;align-items:center;gap:12px;padding:0 28px;position:sticky;top:0;z-index:20; +} +.topbar h2{font-size:15.5px;font-weight:700;color:var(--neutral11)} +.content{padding:34px 28px 44px;flex:1} + +.page-head{display:flex;align-items:flex-end;margin-bottom:26px} +.page-head h1{font-size:26px;font-weight:800;color:var(--neutral11);letter-spacing:-0.038em} +.page-head .sub{font-size:13.5px;color:var(--neutral8);margin-top:7px} +.page-head .sub b{color:var(--neutral9)} + +.section-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px} +.section-head h3{font-size:15px;font-weight:700;color:var(--neutral10)} +.section-head .note{font-family:var(--mono);font-size:11.5px;color:var(--neutral7); + padding:3px 8px;background:var(--neutral3);border-radius:6px} + + +.btn{ + display:inline-flex;align-items:center;justify-content:center;gap:7px; + height:40px;padding:0 16px;border-radius:var(--r-sm); + font-size:14px;font-weight:650;border:1px solid transparent;transition:.16s; +} +.btn .ico{width:16px;height:16px} +.btn-primary{background:var(--neutral11);color:#fff;box-shadow:var(--shadow)} +.btn-primary:hover{background:#1D2338} +.btn-secondary{background:var(--neutral1);color:var(--neutral10);border-color:var(--neutral5)} +.btn-secondary:hover{background:var(--neutral2)} +.btn-ghost{color:var(--neutral8)} +.btn-ghost:hover{background:var(--neutral3)} +.btn-accent{background:var(--brand4);color:#fff} /* 복구안 적용 전용 */ +.btn-accent:hover{background:var(--brand5)} +.btn-lg{height:44px;padding:0 20px} +.btn-sm{height:32px;padding:0 12px;font-size:13px;border-radius:8px} +.btn-sm .ico{width:14px;height:14px} + +.icon-btn{width:34px;height:34px;border-radius:9px;color:var(--neutral8);position:relative; + display:inline-flex;align-items:center;justify-content:center} +.icon-btn:hover{background:var(--neutral3)} + + +.card{background:var(--neutral1);border:1px solid var(--neutral5); + border-radius:var(--r-md);box-shadow:var(--shadow)} +.card-body{padding:24px} +.card-pad{padding:20px} + +.tag{display:inline-flex;align-items:center;height:22px;padding:0 8px;border-radius:6px; + font-size:11.5px;font-weight:650;background:var(--neutral11);color:#fff} + +.badge{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 11px; + border-radius:999px;font-size:12px;font-weight:700;border:1px solid} +.badge-ok{background:var(--judge1-bg);color:var(--judge1);border-color:var(--judge1-line)} +.badge-warn{background:var(--judge2-bg);color:var(--judge2);border-color:var(--judge2-line)} +.badge-bad{background:var(--judge3-bg);color:var(--judge3);border-color:var(--judge3-line)} +.dot{width:6px;height:6px;border-radius:50%} +.dot-ok{background:var(--judge1)} +.dot-warn{background:var(--judge2)} +.dot-bad{background:var(--judge3)} + + +.banner{display:flex;gap:14px;padding:18px 20px;border-radius:var(--r-md); + border:1px solid var(--judge2-line);background:var(--judge2-bg);color:var(--judge2)} +.banner .ico{width:19px;height:19px;margin-top:1px} +.banner .body{flex:1} +.banner h4{font-size:14.5px;font-weight:700;margin-bottom:5px} +.banner p{font-size:13.5px;line-height:1.65} +.banner .actions{display:flex;gap:8px;align-self:center} + + +/* ===== 숫자 ===== */ +.kpi-label{font-size:12px;color:var(--neutral8)} +.kpi-value{font-family:var(--mono);font-size:24px;font-weight:600; + color:var(--neutral11);letter-spacing:-0.035em;margin-top:7px} +.kpi-value small{font-family:var(--ui);font-size:14px;color:var(--neutral7);margin-left:3px} +.kpi-value.warn{color:var(--judge2)} +.kpi-sub{font-size:11.5px;color:var(--neutral7);margin-top:6px} + +.meter{height:6px;background:var(--neutral4);border-radius:999px;overflow:hidden} +.meter i{display:block;height:100%;background:var(--neutral11)} + + +/* ===== Fit Bar ===== + 검은 세로선(가능시간)이 띠(필요시간)보다 + 오른쪽=가능 / 띠 안=위험 / 왼쪽=불가능 */ +.fit{position:relative;padding-top:24px} +.fit.compact{padding-top:0} +.fit-scale{position:relative;height:44px;border-radius:var(--r-sm);overflow:hidden; + background:var(--neutral3);border:1px solid var(--neutral4)} +.fit.compact .fit-scale{height:32px} + +.fit-band{position:absolute;top:0;bottom:0} +.fit-min{position:absolute;top:0;bottom:0;left:0;opacity:.5} +.fit-band.ok{background:var(--judge1-line)} .fit-min.ok{background:var(--judge1)} +.fit-band.warn{background:var(--judge2-line)} .fit-min.warn{background:var(--judge2)} +.fit-band.bad{background:var(--judge3-line)} .fit-min.bad{background:var(--judge3)} + +.fit-mark{position:absolute;top:20px;bottom:-4px;width:2px;background:var(--neutral11);z-index:2} +.fit.compact .fit-mark{top:-2px;bottom:-2px} +.fit-mark span{position:absolute;left:50%;transform:translateX(-50%);top:-21px; + font-family:var(--mono);font-size:10.5px;font-weight:600; + color:var(--neutral11);white-space:nowrap} + +.fit-caption{position:relative;height:15px;margin-top:9px; + font-family:var(--mono);font-size:11px;color:var(--neutral7)} +.fit-caption .start{position:absolute;left:0} +.fit-caption .end{position:absolute;right:0} +.fit-caption .need{position:absolute;transform:translateX(-50%);color:var(--neutral8)} + + +.empty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:64px 40px} +.empty-icon{width:64px;height:64px;border-radius:20px;margin-bottom:24px; + background:var(--neutral3);border:1px solid var(--neutral5); + display:flex;align-items:center;justify-content:center} +.empty-icon .ico{width:28px;height:28px;color:var(--neutral7)} +.empty h3{font-size:19px;font-weight:750;color:var(--neutral11);margin-bottom:10px} +.empty p{font-size:14px;color:var(--neutral8);line-height:1.7;max-width:420px;margin-bottom:26px} + +.steps{display:flex;margin-top:36px;width:100%;max-width:720px} +.step{flex:1;text-align:left;padding:0 18px;border-right:1px solid var(--neutral4)} +.step:last-child{border-right:0} +.step .no{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--brand4);margin-bottom:7px} +.step b{display:block;font-size:13.5px;color:var(--neutral9);margin-bottom:4px} +.step span{font-size:12px;color:var(--neutral7);line-height:1.55} + + +/* ===== 유틸 ===== */ +.row{display:flex;align-items:center;gap:12px} +.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:18px} +.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:22px} +.ml-auto{margin-left:auto} + + +/* ===== 반응형 ===== */ +@media (max-width:1024px){ + .grid-3,.grid-4{grid-template-columns:repeat(2,1fr)} + .content{padding:28px 20px 40px} +} + +@media (max-width:767px){ + .app,.app.collapsed{grid-template-columns:1fr} + .side,.app.collapsed .side{ + position:fixed;left:0;right:0;bottom:0;top:auto;height:auto; + flex-direction:row;border:0;border-top:1px solid var(--neutral4); + padding:8px 6px 12px;z-index:50; + } + .app.collapsed .side>*{display:flex} + .brand,.side-label,.side-box,.side-foot{display:none!important} + .side nav{display:flex;flex:1} + .nav-i{flex:1;flex-direction:column;gap:4px;height:auto;padding:6px 0; + font-size:10.5px;background:transparent!important;color:var(--neutral7)} + .nav-i.on,.nav-i.on .ico{color:var(--neutral11)} + .nav-i .count{display:none} + + .content{padding:20px 16px 96px} + .grid-3,.grid-4{grid-template-columns:1fr} + .page-head{flex-direction:column;align-items:flex-start;gap:16px} + .page-head h1{font-size:23px} + .page-head .ml-auto{margin-left:0;width:100%} + .page-head .btn{width:100%} + .topbar{padding:0 16px} + .steps{flex-direction:column} + .step{border-right:0} +} \ No newline at end of file diff --git a/static/js/planner.js b/static/js/planner.js new file mode 100644 index 0000000..bbf3e0b --- /dev/null +++ b/static/js/planner.js @@ -0,0 +1,15 @@ +document.addEventListener('DOMContentLoaded', function () { + const app = document.querySelector('.app'); + const btn = document.getElementById('toggleSide'); + const open = document.getElementById('openSide'); + + if (sessionStorage.getItem('sideCollapsed') === '1') app.classList.add('collapsed'); + + function toggle() { + app.classList.toggle('collapsed'); + sessionStorage.setItem('sideCollapsed', app.classList.contains('collapsed') ? '1' : '0'); + } + + if (btn) btn.addEventListener('click', toggle); + if (open) open.addEventListener('click', toggle); +}); \ No newline at end of file diff --git a/templates/includes/icons.html b/templates/includes/icons.html new file mode 100644 index 0000000..34b4259 --- /dev/null +++ b/templates/includes/icons.html @@ -0,0 +1,32 @@ +{# 아이콘 (Lucide). 사용법: #} + + \ No newline at end of file From de131bbfa47e0075ba3f77cf1e51343776a84b7c Mon Sep 17 00:00:00 2001 From: borissal1207 Date: Tue, 4 Aug 2026 17:14:13 +0900 Subject: [PATCH 2/4] =?UTF-8?q?style:=20#49=20comment=20=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=207=EA=B0=80=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planner/templates/planner/dashboard.html | 74 ++++++++++--------- .../templates/planner/includes/fit_bar.html | 16 ++-- .../templates/planner/includes/sidebar.html | 1 - .../templates/planner/includes/task_row.html | 72 ++++++++++++++++++ static/css/planner.css | 30 +++++--- 5 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 planner/templates/planner/includes/task_row.html diff --git a/planner/templates/planner/dashboard.html b/planner/templates/planner/dashboard.html index 6fe1e7e..fdd78d4 100644 --- a/planner/templates/planner/dashboard.html +++ b/planner/templates/planner/dashboard.html @@ -14,7 +14,6 @@
- {# 사이드바가 접혔을 때 다시 여는 버튼 #} @@ -24,18 +23,30 @@

대시보드

{% if not exam_period %} - {# ===== 시험기간이 없을 때 ===== #} + {# ===== 1단계 : 시험기간 없음 ===== #}
{% include "planner/includes/empty_state.html" with icon="i-calendar" title="아직 등록된 시험기간이 없습니다" desc="시험기간을 만들고 과목과 공부 가능시간을 입력하면, PLAN B가 날짜별 계획을 대신 계산해 드립니다." btn_label="시험기간 만들기" show_steps=True %}
+ {% elif not has_plan %} + {# ===== 2단계 : 시험기간은 있지만 계획이 아직 없음 ===== #} +
+
+

{{ exam_period.title }}

+
{{ exam_period.start_date|date:"n월 j일" }} ~ {{ exam_period.end_date|date:"n월 j일" }}
+
+
+
+ {% include "planner/includes/empty_state.html" with icon="i-calendar" title="아직 계획이 만들어지지 않았습니다" desc="과목과 시험 범위를 입력하고 학습 작업을 확정하면 계획을 생성할 수 있습니다." btn_label=next_step_label btn_url=next_step_url %} +
+ {% else %} - {# ===== 평소 대시보드 ===== #} + {# ===== 3단계 : 평소 대시보드 ===== #}

{{ today|date:"n월 j일 l" }}

- 시험까지 남은 공부 가능일 {{ remaining_days }}일 + 공부할 수 있는 날 {{ remaining_days }}일 남음 · 오늘 배정 {{ today_minutes }}분
@@ -54,7 +65,7 @@

적용 대기 중인 복구안이 있습니다

지금 계획은 {{ overall.status_label }} 상태 그대로입니다.

- + {# TODO: 복구안 비교 화면 URL 로 교체 (pending_recovery.group_id 사용) #} 복구안 보기
@@ -75,7 +86,7 @@

적용 대기 중인 복구안이 있습니다

- {% include "planner/includes/fit_bar.html" with status=overall.status min_pct=overall.min_pct band_pct=overall.band_pct mark_pct=overall.mark_pct min_minutes=overall.min_minutes max_minutes=overall.max_minutes available_minutes=overall.available_minutes axis_max=overall.axis_max %} + {% include "planner/includes/fit_bar.html" with status=overall.status min_pct=overall.min_pct band_pct=overall.band_pct mark_pct=overall.mark_pct need_label_pct=overall.need_label_pct min_minutes=overall.min_minutes max_minutes=overall.max_minutes available_minutes=overall.available_minutes axis_max=overall.axis_max %}
@@ -103,43 +114,44 @@

적용 대기 중인 복구안이 있습니다

- {# ---- 과목별 판정 ---- #} + {% comment %} + 과목 목록 + 과목별 가용시간은 ExamPeriod 단위라 배분 정책 없이는 계산할 수 없습니다. + 그래서 과목별 Fit Bar와 판정 배지는 넣지 않고 + 시험일 / 남은 예상시간 / 남은 작업 수만 보여줍니다. + {% endcomment %}
-
-

과목별 판정

- 같은 눈금 · 0~{{ subject_axis_max }}분 -
+

과목

+ {# TODO: {% url 'planner:calendar' %} 로 교체 #} 전체 계획 보기
-
- {% for s in subject_status %} -
-
- {{ s.subject_name }} - - {{ s.status_label }} - +
+ {% for s in subject_summary %} +
+ D-{{ s.d_day }} +
+
{{ s.subject_name }}
+
시험 {{ s.exam_date|date:"n월 j일" }}
-
- 시험 {{ s.exam_date|date:"n/j" }} · D-{{ s.d_day }} +
+
{{ s.remaining_minutes }}분
+
남은 예상시간
- - {% include "planner/includes/fit_bar.html" with compact=True status=s.status min_pct=s.min_pct band_pct=s.band_pct mark_pct=s.mark_pct %} - -
- 필요 {{ s.min_minutes }}~{{ s.max_minutes }}분 - 가능 {{ s.available_minutes }}분 +
+
{{ s.remaining_task_count }}개
+
남은 작업
{% endfor %}
{# ---- 진행 상황 ---- #} -
+

진행 상황

+ 완료 환산 기준
@@ -160,11 +172,7 @@

진행 상황

남은 CORE 작업
{{ progress.core_left }}
-
- {% for c in progress.core_by_subject %} - {{ c.name }} {{ c.count }} - {% endfor %} -
+
필수 학습만 남긴 기준
{% endif %} diff --git a/planner/templates/planner/includes/fit_bar.html b/planner/templates/planner/includes/fit_bar.html index d2cd984..0ffae53 100644 --- a/planner/templates/planner/includes/fit_bar.html +++ b/planner/templates/planner/includes/fit_bar.html @@ -1,17 +1,15 @@ -
+
- {% if not compact %}가능 {{ available_minutes }}분{% endif %} + 가능 {{ available_minutes }}분
-{% if not compact %} -
- 0분 - 필요 {{ min_minutes }} ~ {{ max_minutes }}분 - {{ axis_max }}분 -
-{% endif %} \ No newline at end of file +
+ 0분 + 필요 {{ min_minutes }} ~ {{ max_minutes }}분 + {{ axis_max }}분 +
\ No newline at end of file diff --git a/planner/templates/planner/includes/sidebar.html b/planner/templates/planner/includes/sidebar.html index 69aa3e7..45c2d24 100644 --- a/planner/templates/planner/includes/sidebar.html +++ b/planner/templates/planner/includes/sidebar.html @@ -1,5 +1,4 @@
- {# TODO: 복구안 비교 화면 URL 로 교체 (pending_recovery.group_id 사용) #} 복구안 보기
@@ -82,7 +81,7 @@

적용 대기 중인 복구안이 있습니다

- {{ overall.calculated_at|date:"n.j H:i" }} 기준 + {{ overall.viewed_at|date:"n.j H:i" }} 조회 기준
@@ -102,27 +101,20 @@

적용 대기 중인 복구안이 있습니다

{{ overall.max_minutes }}
-
최대 기준 부족
+
판정 기준 부족
{% if overall.shortage_minutes %}-{{ overall.shortage_minutes }}{% else %}0{% endif %}
{% if overall.daily_extra_minutes %} -
하루 평균 +{{ overall.daily_extra_minutes }}분이면 해소
+
남은 {{ remaining_days }}일 동안 하루 +{{ overall.daily_extra_minutes }}분이면 해소
{% endif %}
- {% comment %} - 과목 목록 - 과목별 가용시간은 ExamPeriod 단위라 배분 정책 없이는 계산할 수 없습니다. - 그래서 과목별 Fit Bar와 판정 배지는 넣지 않고 - 시험일 / 남은 예상시간 / 남은 작업 수만 보여줍니다. - {% endcomment %}

과목

- {# TODO: {% url 'planner:calendar' %} 로 교체 #} 전체 계획 보기 @@ -132,7 +124,7 @@

과목

{% for s in subject_summary %}
D-{{ s.d_day }} -
+
{{ s.subject_name }}
시험 {{ s.exam_date|date:"n월 j일" }}
@@ -148,7 +140,6 @@

과목

{% endfor %}
- {# ---- 진행 상황 ---- #}

진행 상황

완료 환산 기준 diff --git a/planner/templates/planner/includes/task_row.html b/planner/templates/planner/includes/task_row.html deleted file mode 100644 index 4264699..0000000 --- a/planner/templates/planner/includes/task_row.html +++ /dev/null @@ -1,72 +0,0 @@ -{% comment %} -status (core/choices.py ProgressStatus) -done / partial / not_done -pending 아직 결과 입력 전 -excluded 복구안에서 빠진 작업 -{% endcomment %} - -
- - - {% if task.status == 'done' %} - - {% elif task.status == 'partial' %} - - {% elif task.status == 'not_done' %} - - {% elif task.status == 'excluded' %} - - {% else %} - {% endif %} - - -
-
- {{ task.subject_name }} - {% if task.depth == 'core' %}CORE - {% elif task.depth == 'basic' %}BASIC - {% elif task.depth == 'optional' %}OPTIONAL{% endif %} - {% if task.importance %}{{ task.importance|upper }}{% endif %} -
- -
{{ task.title }}
- - {% if task.status == 'partial' %} -
-
-
- {% endif %} -
- -
- {% if task.status == 'done' %} -
{{ task.actual_minutes }}분
-
완료 · 계획 {{ task.planned_minutes }}분
- {% elif task.status == 'partial' %} -
{{ task.actual_minutes }}분
-
일부완료 · 남음 {{ task.remaining_minutes }}분
- {% elif task.status == 'not_done' %} -
0분
-
못함 · 내일로 이동
- {% elif task.status == 'excluded' %} - 계획에서 제외 - {% else %} -
{{ task.planned_minutes }}분
-
예정
- {% endif %} -
- - {% if show_action and task.status != 'excluded' %} - {% if task.status and task.status != 'pending' %} - - {% else %} - - {% endif %} - {% endif %} -
\ No newline at end of file diff --git a/static/css/planner.css b/static/css/planner.css index fd04955..6398cf7 100644 --- a/static/css/planner.css +++ b/static/css/planner.css @@ -85,6 +85,7 @@ .icon-btn:hover{background:var(--neutral3)} +/* ===== 카드 · 태그 · 배지 ===== */ .card{background:var(--neutral1);border:1px solid var(--neutral5); border-radius:var(--r-md);box-shadow:var(--shadow)} .card-body{padding:24px} @@ -94,7 +95,8 @@ font-size:11.5px;font-weight:650;background:var(--neutral11);color:var(--neutral1)} .badge{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 11px; - border-radius:999px;font-size:12px;font-weight:700;border:1px solid} + border-radius:999px;font-size:12px;font-weight:700;border:1px solid; + white-space:nowrap;flex:none} .badge-ok{background:var(--judge1-bg);color:var(--judge1);border-color:var(--judge1-line)} .badge-warn{background:var(--judge2-bg);color:var(--judge2);border-color:var(--judge2-line)} .badge-bad{background:var(--judge3-bg);color:var(--judge3);border-color:var(--judge3-line)} @@ -124,9 +126,6 @@ .meter i{display:block;height:100%;background:var(--neutral11)} -/* ===== Fit Bar ===== - 검은 세로선(가능시간)이 띠(필요시간)보다 - 오른쪽=가능 / 띠 안=위험 / 왼쪽=불가능 */ .fit{position:relative;padding-top:24px} .fit-scale{position:relative;height:44px;border-radius:var(--r-sm);overflow:hidden; background:var(--neutral3);border:1px solid var(--neutral4)} @@ -152,16 +151,18 @@ .subject-card{display:flex;align-items:center;gap:16px;padding:16px 20px; border-bottom:1px solid var(--neutral4)} .subject-card:last-child{border-bottom:0} +.subject-card .info{flex:1;min-width:0} .subject-card .name{font-size:15px;font-weight:650;color:var(--neutral10)} .subject-card .date{font-family:var(--mono);font-size:11.5px;color:var(--neutral7);margin-top:3px} .subject-card .dday{font-family:var(--mono);font-size:13px;font-weight:600; color:var(--neutral9);padding:4px 9px; background:var(--neutral3);border-radius:6px} .subject-card .dday.near{background:var(--judge2-bg);color:var(--judge2)} -.subject-card .left{text-align:right;flex:none;min-width:96px} +.subject-card .left{text-align:right;flex:none;min-width:88px} .subject-card .left .v{font-family:var(--mono);font-size:15px;font-weight:600;color:var(--neutral10)} .subject-card .left .l{font-size:11.5px;color:var(--neutral7);margin-top:2px} + .empty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:64px 40px} .empty-icon{width:64px;height:64px;border-radius:20px;margin-bottom:24px; background:var(--neutral3);border:1px solid var(--neutral5); @@ -214,4 +215,13 @@ .topbar{padding:0 16px} .steps{flex-direction:column} .step{border-right:0} + + .subject-card{flex-wrap:wrap;gap:12px;padding:16px} + .subject-card .info{flex:1 1 calc(100% - 62px);min-width:0} + .subject-card .name{font-size:14.5px} + .subject-card .left{ + flex:1 1 calc(50% - 6px);min-width:0;text-align:left; + padding-top:12px;border-top:1px solid var(--neutral4); + } + .subject-card .left .v{font-size:14px} } \ No newline at end of file From 3307aeebc8999bd55c9b036fbb16088cc705c4b2 Mon Sep 17 00:00:00 2001 From: borissal1207 Date: Wed, 5 Aug 2026 18:17:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?style:=20#49=20=EB=A6=AC=EB=B7=B0=203?= =?UTF-8?q?=EC=B0=A8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- planner/templates/planner/dashboard.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/planner/templates/planner/dashboard.html b/planner/templates/planner/dashboard.html index fb539b2..f895c96 100644 --- a/planner/templates/planner/dashboard.html +++ b/planner/templates/planner/dashboard.html @@ -101,12 +101,12 @@

적용 대기 중인 복구안이 있습니다

{{ overall.max_minutes }}
-
판정 기준 부족
-
- {% if overall.shortage_minutes %}-{{ overall.shortage_minutes }}{% else %}0{% endif %} +
가능까지 부족
+
+ {% if overall.max_shortage_minutes %}-{{ overall.max_shortage_minutes }}{% else %}0{% endif %}
{% if overall.daily_extra_minutes %} -
남은 {{ remaining_days }}일 동안 하루 +{{ overall.daily_extra_minutes }}분이면 해소
+
남은 {{ remaining_days }}일 동안 하루 +{{ overall.daily_extra_minutes }}분이면 가능
{% endif %}