KINDNICK 5fb8655a47 v2.11.0: UI 전면 다크 리디자인 + 라인 아이콘 + 적립 가드/삭제
- 모던 다크 미니멀 테마(NanumSquare 번들, 단일 accent #4DABF7, 8px radius, flat 버튼, 다크 기본값)
- 라인 아이콘 시스템(ui/icons.py, QtSvg) — 앱 전반 이모지 교체
- 다크 깨짐 수정: 테이블 헤더/코너 흰색, 도움말 탭 흰 라인, 트레이/미니위젯 메뉴
- fix: 자동 적립 OFF가 자동 퇴근 경로에서 무시되던 버그(게이팅)
- feat: 연장근무 적립 기록 삭제(우클릭)
- 테스트 3건 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 18:21:54 +09:00

193 lines
6.7 KiB
Python

"""
사용 설명 가이드 창.
i18n 사전(_HELP_HTML)에서 ko/en HTML을 가져와 6개 탭으로 표시.
도전과제/통계 다이얼로그와 동일한 다크 톤.
"""
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QWidget, QTabWidget, QTextBrowser)
from PyQt5.QtCore import Qt
from core.i18n import tr, tr_html
from ui.styles import apply_dark_titlebar
from ui.dark_components import (
dialog_qss, tabs_qss, button_qss,
DARK_BG, DARK_PANEL, DARK_BORDER, DARK_TEXT, ACCENT_GOLD,
)
class HelpView(QDialog):
"""사용 설명 가이드 다이얼로그"""
# (사전 키, 탭 라벨 키)
_TABS = [
('help.html.intro', 'help.tab_intro'),
('help.html.work_hours', 'help.tab_work_hours'),
('help.html.overtime', 'help.tab_overtime'),
('help.html.leave', 'help.tab_leave'),
('help.html.break', 'help.tab_break'),
('help.html.faq', 'help.tab_faq'),
]
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle(tr('window.help'))
self.setModal(True)
self.setMinimumSize(720, 720)
self.resize(820, 760)
self.setStyleSheet(dialog_qss())
self.init_ui()
apply_dark_titlebar(self, dark=True)
def init_ui(self):
main_layout = QVBoxLayout()
main_layout.setContentsMargins(20, 16, 20, 14)
main_layout.setSpacing(10)
# 다크 타이틀
title = QLabel(tr('window.help'))
title.setStyleSheet(
f"font-size: 18pt; font-weight: bold; color: {DARK_TEXT}; "
f"background: transparent; border: none; padding: 4px 0;"
)
main_layout.addWidget(title)
tabs = QTabWidget()
tabs.setStyleSheet(tabs_qss())
for html_key, tab_label_key in self._TABS:
tabs.addTab(self._make_tab(tr_html(html_key)), tr(tab_label_key))
main_layout.addWidget(tabs, 1)
button_layout = QHBoxLayout()
button_layout.setContentsMargins(0, 6, 0, 0)
# 온보딩 다시 보기 (왼쪽, ghost 스타일)
onboarding_button = QPushButton("온보딩 다시 보기")
onboarding_button.setMinimumHeight(36)
onboarding_button.setStyleSheet(button_qss('ghost'))
onboarding_button.clicked.connect(self._reopen_onboarding)
button_layout.addWidget(onboarding_button)
button_layout.addStretch()
close_button = QPushButton(tr('btn.close'))
close_button.setMinimumHeight(36)
close_button.setMinimumWidth(120)
close_button.setStyleSheet(button_qss('primary'))
close_button.clicked.connect(self.close)
button_layout.addWidget(close_button)
main_layout.addLayout(button_layout)
self.setLayout(main_layout)
def _reopen_onboarding(self):
"""부모 윈도우의 show_onboarding 호출 후 도움말 닫음."""
self.close()
if self.parent() and hasattr(self.parent(), 'show_onboarding'):
self.parent().show_onboarding()
def _make_tab(self, html: str) -> QWidget:
container = QWidget()
container.setStyleSheet(f"background: {DARK_PANEL};")
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
browser = QTextBrowser()
browser.setOpenExternalLinks(False)
# HTML 내부에 다크 톤 스타일 주입
styled_html = self._inject_dark_styles(html)
browser.setHtml(styled_html)
browser.setStyleSheet(f"""
QTextBrowser {{
background: {DARK_PANEL};
color: {DARK_TEXT};
border: none;
padding: 16px 20px;
font-size: 10.5pt;
selection-background-color: {ACCENT_GOLD};
selection-color: #1a1a26;
}}
QScrollBar:vertical {{
background: {DARK_PANEL}; width: 10px; border-radius: 5px;
}}
QScrollBar::handle:vertical {{
background: {DARK_BORDER}; border-radius: 5px; min-height: 30px;
}}
QScrollBar::handle:vertical:hover {{ background: {ACCENT_GOLD}; }}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ height: 0; }}
""")
layout.addWidget(browser)
container.setLayout(layout)
return container
def _inject_dark_styles(self, html: str) -> str:
"""HelpHTML 내용에 다크 톤 CSS 주입 (제목/링크/코드/테이블)."""
css = f"""
<style>
body, p, li {{
color: #e8e8f4;
font-size: 14px;
line-height: 1.65;
}}
h1, h2, h3, h4 {{
color: #ffd24a;
margin-top: 1.2em;
margin-bottom: 0.5em;
}}
h2 {{ font-size: 16pt; border-bottom: 2px solid #44446a; padding-bottom: 6px; }}
h3 {{ font-size: 13pt; color: #6b9eff; }}
h4 {{ font-size: 11pt; color: #4ade80; }}
b, strong {{ color: #ff90b8; }}
code {{
background: #1c1c28;
color: #ffd24a;
padding: 2px 6px;
border-radius: 4px;
font-family: Consolas, monospace;
font-size: 12px;
}}
pre {{
background: #1c1c28;
border: 1px solid #2a2a3a;
border-radius: 6px;
padding: 10px;
color: #e8e8f4;
}}
ul, ol {{ margin-left: 0; padding-left: 24px; }}
li {{ margin-bottom: 4px; }}
a {{ color: #4adef0; text-decoration: none; }}
a:hover {{ text-decoration: underline; }}
table {{ border-collapse: collapse; margin: 10px 0; }}
th {{
background: #2a2a3a;
color: #ffd24a;
padding: 8px 12px;
border: 1px solid #44446a;
text-align: left;
}}
td {{
padding: 6px 12px;
border: 1px solid #2a2a3a;
color: #e8e8f4;
}}
hr {{ border: none; border-top: 1px solid #2a2a3a; margin: 16px 0; }}
blockquote {{
border-left: 3px solid #6b9eff;
margin-left: 0;
padding: 4px 16px;
color: #a0a0b8;
background: rgba(107, 158, 255, 0.05);
}}
</style>
"""
return css + html
# 단독 실행 테스트
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
dialog = HelpView()
dialog.exec_()