From 919bc82ca16b69804277081da4447fb3f3151b8e Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Sun, 26 Oct 2025 13:02:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=98=84=ED=99=A9=ED=8C=90=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=B9=B4=EB=93=9C=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=EB=B3=84=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ“Š 톡계 μΉ΄λ“œ μž¬κ΅¬μ„±: - 전체 μ§„ν–‰ 쀑: λͺ¨λ“  μ§„ν–‰ 쀑인 이슈 수 - 였늘 μ‹ κ·œ: 였늘 μˆ˜μ‹ ν•¨μ—μ„œ μ§„ν–‰μ€‘μœΌλ‘œ λ„˜μ–΄μ˜¨ 이슈 - μ™„λ£Œ λŒ€κΈ°: μ™„λ£Œ μ‹ μ²­λœ 이슈 (completion_requested_at 쑴재) - μ§€μ—° 쀑: 마감일이 μ§€λ‚œ 이슈 🎨 UI κ°œμ„ : - μ™„λ£Œ λŒ€κΈ°: 보라색 λ°°κ²½ + λͺ¨λž˜μ‹œκ³„ μ•„μ΄μ½˜ - μ§€μ—° 쀑: 빨간색 λ°°κ²½ + μ‹œκ³„ μ•„μ΄μ½˜ - 각 μΉ΄λ“œλ³„ μ• λ‹ˆλ©”μ΄μ…˜ 점 효과 μœ μ§€ πŸ”§ 둜직 κ°œμ„ : - reviewed_at κΈ°μ€€μœΌλ‘œ 였늘 μ‹ κ·œ 계산 - completion_requested_at ν•„λ“œλ‘œ μ™„λ£Œ λŒ€κΈ° μƒνƒœ νŒλ³„ - expected_completion_date κΈ°μ€€μœΌλ‘œ μ§€μ—° μƒνƒœ νŒλ³„ - μ‹€μ‹œκ°„ 톡계 μ—…λ°μ΄νŠΈ πŸ’‘ μ‚¬μš©μž κ²½ν—˜: - ν•œλˆˆμ— νŒŒμ•… κ°€λŠ₯ν•œ μƒνƒœλ³„ λΆ„λ₯˜ - 색상 μ½”λ”©μœΌλ‘œ μš°μ„ μˆœμœ„ ꡬ뢄 - 직관적인 μ•„μ΄μ½˜ μ‚¬μš© Expected Result: βœ… 전체 μ§„ν–‰ 쀑 | 였늘 μ‹ κ·œ | μ™„λ£Œ λŒ€κΈ° | μ§€μ—° 쀑 βœ… μ‹€μ‹œκ°„ μƒνƒœλ³„ 톡계 ν‘œμ‹œ βœ… μ‹œκ°μ μœΌλ‘œ κ΅¬λΆ„λ˜λŠ” 색상 체계 βœ… κ΄€λ¦¬μžκ°€ μš°μ„ μˆœμœ„λ₯Ό μ‰½κ²Œ νŒŒμ•… --- .../__pycache__/issues.cpython-311.pyc | Bin 17180 -> 17981 bytes backend/routers/issues.py | 12 +++- .../__pycache__/file_service.cpython-311.pyc | Bin 3866 -> 3924 bytes backend/services/file_service.py | 6 +- frontend/issues-dashboard.html | 55 +++++++++--------- 5 files changed, 43 insertions(+), 30 deletions(-) diff --git a/backend/routers/__pycache__/issues.cpython-311.pyc b/backend/routers/__pycache__/issues.cpython-311.pyc index 0973e38ee4d28eae312699860882147b79647be3..067221c44f018417cc8590e954f15ed2968594e6 100644 GIT binary patch delta 1299 zcmZ`%Z%h+s7=PckcfRf271}}}6vqKFN*JvUWB*~hIkb?);#g4jNnH=Z8Vu&z3NB@C z>NW%x-QHxnjTzZMX55xBG~tV1{2+cYxhD64EFtOl9?9{+gqRrj-j#yY_~gCc{qA{x zzvp@Gy_@+4KA48q1&*@-5(_iWhyHF~w4UE@IBr_Hvi}IIPE~3fg%EaSz%M{R8AF1> z&s%B}f_a?Ezywdp>8UTBMcxjw2aoh13ACE(=4p@cjn;U2d z1V?Hg{)(+;&jGO}Ly6U9kZQ;Kd;w#HP)XG7HuE_B1w9H!!I<$AFbZ3oc+t0dA?+rO zIu9S}iw1e+r_1@@r+H=Smh$gkpiOSisJdtpml=-kF1JDnN|^17io&C6Al>(@x(x%KHk)_ngnRrfv5e=LgjN(gyqmD6+o+t1hJv!SA-&fp*-wy_W&35eUt;@n4z~&* zM@-H-i9eck#$;zqDvv#-f!QiqJz3}9 z`|;)a#ld@nk~K_L7DZ$fkx*n6S*|jZ%oT2$o8WRx!~Edv(A<#3c(m2tMG()1j)fgs zxy{YZ>j9CJ3HcLE$ z&AdQxvZA^Y8zVyv^8%sCf!rdK?WKg6QaC3sG?$j-n!^&zpv+LB2Gh&Hpu$iTQp+}3 z-&~WOwMc@Kp+tLfvV|oxcM8ws`4+~4?6qulY~_sQ3>D0g4CRcPyptbW++g!7Dgc@= z`LI>mxj)}tzFK=v^%RyY35 z{`M7&G9X88<8N%3M6q@^lp<{} zJ(M|=`U5!>El5Ghv6mhSw$y?xfx=2pr3Y_<*Oqo>li235^UZs2zIpTJ&D)KpLQ~tW z(`f@#cDH`dKCuGugBfL!O3L}8=WVxoT#BUH6)PP!l~?VaG7BU`#l9{~IuwU$PjHHJ zou70nFbaZI2d{Twzc81K&!~p;Zal4quEx?CJhwDsh$*Zl=2i_6tC?lIG|gRv1HeLc zM8~;7H0~+nh=8uNa61ryJvxd~Y3WEqB#MF%5gmCwyoTn>$pb}PFoWX;$~i!70@C=L zgVex!pGLR+N3@iPjT*{9Mj$PGx1ZP1Qga25UtrmRQb0F(%vW2G1~-9XColNEI!9%4 zrvE2DghX8+9YU{1*r5sSUEEj~l`7pr&vdghIn6?d(2Y>8v1p3qyWqVkY=Uhr3R*!X zRH^`-E&=SQYuxtkIH2B4Tpb%dTMpdrs`8aQcVJcn}(V zGM-*AU~J7HPsmAGkw@e)IeNt7i}-lTo&wNyqa`;MU9m@!`y?1sGn^~pXZ-R5)7Y-!0kV-o2K9P>X!C0 zyOsSG2z?EN_Oo9C!-c?baqV;cQR7G7k*^S$IG&t3zCA^;5V=!aq(}WkS=fmy<=L6i4ZRNTir{S<;c9%T9e1;d(H zUR;c&;+a`|jl6XP;AsBDVT0q)ymruqSuf)@2CQccF>^mf+aL4H)5ARIM&vpcnAy(2 zJh;kSpnr;44LnLNyZp9s>SXPo{zRg#2c9#~0qb*7bJ}k;r*|VCqVLDZiEH35cx>WS delta 903 zcmY*Y(MwZN7(eHpbMM{m?xt;SV}`o4VIw98B3Rfo!6l?r^C5*8H_x3@wb?eQi(Q z*Q-M}4sRw8ZzVZahbB**9NACj>(J<60)PXcCA!gp=1p{>gHD-q8n5hWuORGVX? zk6%i~bWlFQK+IY0-hGpbR?$lwqL{`7zaqTO^inpPS~RQ-9p?LjXnmL)^$4$vm|4>@ zQ|Y;^NyjRZTZ26w%6kE5M1Yu$nqoaz Optional[str]: +def save_base64_image(base64_string: str, prefix: str = "image") -> Optional[str]: """Base64 이미지λ₯Ό 파일둜 μ €μž₯ν•˜κ³  경둜 λ°˜ν™˜""" try: ensure_upload_dir() @@ -40,8 +40,8 @@ def save_base64_image(base64_string: str) -> Optional[str]: elif image.mode != 'RGB': image = image.convert('RGB') - # 파일λͺ… 생성 (κ°•μ œλ‘œ .jpg) - filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}.jpg" + # 파일λͺ… 생성 (prefix 포함) + filename = f"{prefix}_{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}.jpg" filepath = os.path.join(UPLOAD_DIR, filename) # 이미지 μ €μž₯ (μ΅œλŒ€ 크기 μ œν•œ) diff --git a/frontend/issues-dashboard.html b/frontend/issues-dashboard.html index d7feb4d..0483cbd 100644 --- a/frontend/issues-dashboard.html +++ b/frontend/issues-dashboard.html @@ -181,29 +181,29 @@ -
-
-
-

- μ§€μ—° μœ„ν—˜ -

-

-

0

-
- -
-
-

- ν™œμ„± ν”„λ‘œμ νŠΈ + μ™„λ£Œ λŒ€κΈ°

-

0

+

0

- + +
+
+ +
+
+
+

+ μ§€μ—° 쀑 +

+

+

0

+
+
@@ -385,26 +385,29 @@ // 톡계 μ—…λ°μ΄νŠΈ function updateStatistics() { const today = new Date().toDateString(); + + // 였늘 μ‹ κ·œ (였늘 μˆ˜μ‹ ν•¨μ—μ„œ μ§„ν–‰μ€‘μœΌλ‘œ λ„˜μ–΄μ˜¨ 것듀) const todayIssues = allIssues.filter(issue => - new Date(issue.report_date).toDateString() === today + issue.reviewed_at && new Date(issue.reviewed_at).toDateString() === today ); - // μ§€μ—° μœ„ν—˜ 계산 (μ˜ˆμƒμΌμ΄ μ§€λ‚¬κ±°λ‚˜ 3일 이내) - const delayRiskIssues = allIssues.filter(issue => { + // μ™„λ£Œ λŒ€κΈ° (μ™„λ£Œ 신청이 된 것듀) + const pendingCompletionIssues = allIssues.filter(issue => + issue.completion_requested_at && issue.review_status === 'in_progress' + ); + + // μ§€μ—° 쀑 (마감일이 μ§€λ‚œ 것듀) + const overdueIssues = allIssues.filter(issue => { if (!issue.expected_completion_date) return false; const expectedDate = new Date(issue.expected_completion_date); const now = new Date(); - const diffDays = (expectedDate - now) / (1000 * 60 * 60 * 24); - return diffDays <= 3; // 3일 이내 λ˜λŠ” μ§€μ—° + return expectedDate < now; // 마감일 지남 }); - // ν™œμ„± ν”„λ‘œμ νŠΈ (μ§„ν–‰ 쀑인 뢀적합이 μžˆλŠ” ν”„λ‘œμ νŠΈ) - const activeProjectIds = new Set(allIssues.map(issue => issue.project_id)); - document.getElementById('totalInProgress').textContent = allIssues.length; document.getElementById('todayNew').textContent = todayIssues.length; - document.getElementById('delayRisk').textContent = delayRiskIssues.length; - document.getElementById('activeProjects').textContent = activeProjectIds.size; + document.getElementById('pendingCompletion').textContent = pendingCompletionIssues.length; + document.getElementById('overdue').textContent = overdueIssues.length; } // 이슈 μΉ΄λ“œ μ—…λ°μ΄νŠΈ (관리함 μŠ€νƒ€μΌ - λ‚ μ§œλ³„ κ·Έλ£Ήν™”)