AutoPercenty3/admin/admin_client.py

274 lines
12 KiB
Python

# admin_client.py
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QTabWidget, QWidget, QHBoxLayout, QLabel,
QTextEdit, QLineEdit, QCheckBox, QPushButton, QMessageBox, QScrollArea
)
from PySide6.QtCore import Qt
from collapsible_announcement import CollapsibleAnnouncement # 아래에 정의됨
from sp_manager import SupabaseManager # Supabase 관련 CRUD 메서드 포함
from settings_manager import SettingsManager # QSettings 관리
class AnnouncementManagementTab(QWidget):
"""
공지사항 관리 탭: 공지사항 목록을 스크롤 가능한 영역에 CollapsibleAnnouncement 위젯으로 표시하고,
새 공지사항 작성, 수정, 삭제, 표시/숨김 전환 기능을 제공합니다.
"""
def __init__(self, supabase_manager, parent=None):
super().__init__(parent)
self.supabase_manager = supabase_manager
self.setup_ui()
self.load_announcements()
def setup_ui(self):
main_layout = QVBoxLayout(self)
# 스크롤 영역으로 공지사항 목록을 표시
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.ann_container = QWidget()
self.ann_layout = QVBoxLayout(self.ann_container)
self.ann_container.setLayout(self.ann_layout)
self.scroll_area.setWidget(self.ann_container)
main_layout.addWidget(self.scroll_area)
# 버튼 영역
btn_layout = QHBoxLayout()
self.btn_new = QPushButton("새 공지 작성")
self.btn_edit = QPushButton("수정")
self.btn_delete = QPushButton("삭제")
self.btn_toggle_visible = QPushButton("숨김/보임 전환")
btn_layout.addWidget(self.btn_new)
btn_layout.addWidget(self.btn_edit)
btn_layout.addWidget(self.btn_delete)
btn_layout.addWidget(self.btn_toggle_visible)
main_layout.addLayout(btn_layout)
self.btn_new.clicked.connect(self.new_announcement)
self.btn_edit.clicked.connect(self.edit_selected)
self.btn_delete.clicked.connect(self.delete_selected)
self.btn_toggle_visible.clicked.connect(self.toggle_visibility_selected)
self.setLayout(main_layout)
self.setStyleSheet("""
QWidget { background-color: #ffffff; }
QPushButton {
background-color: #1877f2;
color: white;
border-radius: 4px;
padding: 6px;
font-size: 14px;
}
QPushButton:hover {
background-color: #166fe5;
}
QLabel { font-size: 14px; }
""")
def load_announcements(self):
"""공지사항 목록을 Supabase에서 로드합니다."""
try:
self.announcements = self.supabase_manager.get_announcements() # 리스트: dict, 각 dict에 position, title, content, visible, important, id
# Clear container
for i in reversed(range(self.ann_layout.count())):
widget = self.ann_layout.itemAt(i).widget()
if widget:
widget.setParent(None)
if not self.announcements:
self.ann_layout.addWidget(QLabel("공지사항이 없습니다."))
else:
# 정렬: position 기준 오름차순
sorted_ann = sorted(self.announcements, key=lambda x: x["position"])
for ann in sorted_ann:
widget = CollapsibleAnnouncement(
position=ann["position"],
title=ann["title"],
content=ann["content"]
)
# 저장해 두기: 위젯의 property에 공지 ID 저장
widget.setProperty("announcement_id", ann["id"])
# 만약 중요 공지라면 타이틀 색상을 빨간색 등으로 처리
if ann.get("important"):
widget.toggle_button.setStyleSheet("""
QToolButton {
font-size: 16px;
font-weight: bold;
background-color: #d32f2f;
color: white;
border: none;
border-radius: 4px;
padding: 5px;
}
QToolButton:checked {
background-color: #b71c1c;
}
""")
self.ann_layout.addWidget(widget)
except Exception as e:
QMessageBox.warning(self, "오류", f"공지사항 로드 중 오류 발생: {e}")
def new_announcement(self):
dialog = AnnouncementEditDialog(self.supabase_manager, mode="new", parent=self)
if dialog.exec():
self.load_announcements()
def edit_selected(self):
# 간단하게 첫 번째 공지사항을 선택하는 예시
if self.ann_layout.count() == 0:
QMessageBox.warning(self, "오류", "수정할 공지사항이 없습니다.")
return
widget = self.ann_layout.itemAt(0).widget()
ann_id = widget.property("announcement_id")
ann = self.supabase_manager.get_announcement_by_id(ann_id)
dialog = AnnouncementEditDialog(self.supabase_manager, mode="edit", announcement=ann, parent=self)
if dialog.exec():
self.load_announcements()
def delete_selected(self):
if self.ann_layout.count() == 0:
QMessageBox.warning(self, "오류", "삭제할 공지사항이 없습니다.")
return
widget = self.ann_layout.itemAt(0).widget()
ann_id = widget.property("announcement_id")
reply = QMessageBox.question(self, "삭제 확인", "이 공지사항을 삭제하시겠습니까?", QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
self.supabase_manager.delete_announcement(ann_id)
self.load_announcements()
def toggle_visibility_selected(self):
if self.ann_layout.count() == 0:
QMessageBox.warning(self, "오류", "토글할 공지사항이 없습니다.")
return
widget = self.ann_layout.itemAt(0).widget()
ann_id = widget.property("announcement_id")
self.supabase_manager.toggle_announcement_visibility(ann_id)
self.load_announcements()
class AnnouncementEditDialog(QDialog):
"""
공지사항 작성/수정 다이얼로그.
mode: "new" 또는 "edit"
announcement: mode가 "edit"인 경우 기존 공지사항 dict
"""
def __init__(self, supabase_manager, mode="new", announcement=None, parent=None):
super().__init__(parent)
self.supabase_manager = supabase_manager
self.mode = mode
self.announcement = announcement
self.setWindowTitle("공지사항 " + ("작성" if mode=="new" else "수정"))
self.setMinimumSize(500, 600)
self.setup_ui()
if self.mode == "edit" and announcement:
self.load_announcement()
def setup_ui(self):
layout = QVBoxLayout(self)
self.position_input = QLineEdit()
self.position_input.setPlaceholderText("공지번호 (1~5)")
self.title_input = QLineEdit()
self.title_input.setPlaceholderText("공지 제목")
self.content_input = QTextEdit()
self.content_input.setPlaceholderText("공지 내용을 HTML 형식으로 입력하세요")
self.visible_checkbox = QCheckBox("표시")
self.visible_checkbox.setChecked(True)
self.important_checkbox = QCheckBox("중요 공지")
layout.addWidget(QLabel("공지번호:"))
layout.addWidget(self.position_input)
layout.addWidget(QLabel("제목:"))
layout.addWidget(self.title_input)
layout.addWidget(QLabel("내용:"))
layout.addWidget(self.content_input)
layout.addWidget(self.visible_checkbox)
layout.addWidget(self.important_checkbox)
btn_layout = QHBoxLayout()
self.save_button = QPushButton("저장")
self.cancel_button = QPushButton("취소")
btn_layout.addWidget(self.save_button)
btn_layout.addWidget(self.cancel_button)
layout.addLayout(btn_layout)
self.setLayout(layout)
self.save_button.clicked.connect(self.handle_save)
self.cancel_button.clicked.connect(self.reject)
def load_announcement(self):
self.position_input.setText(str(self.announcement.get("position", "")))
self.title_input.setText(self.announcement.get("title", ""))
self.content_input.setHtml(self.announcement.get("content", ""))
self.visible_checkbox.setChecked(self.announcement.get("visible", True))
self.important_checkbox.setChecked(self.announcement.get("important", False))
def handle_save(self):
try:
ann_data = {
"position": int(self.position_input.text().strip()),
"title": self.title_input.text().strip(),
"content": self.content_input.toHtml(),
"visible": self.visible_checkbox.isChecked(),
"important": self.important_checkbox.isChecked()
}
if self.mode == "new":
self.supabase_manager.create_announcement(ann_data)
else:
ann_id = self.announcement.get("id")
self.supabase_manager.update_announcement(ann_id, ann_data)
self.accept()
except Exception as e:
QMessageBox.warning(self, "저장 오류", f"공지사항 저장 중 오류 발생: {e}")
class LicenseManagementTab(QWidget):
def __init__(self, supabase_manager, parent=None):
super().__init__(parent)
self.supabase_manager = supabase_manager
self.setup_ui()
self.load_license()
def setup_ui(self):
layout = QVBoxLayout(self)
self.current_version_label = QLabel("현재 버전: ")
layout.addWidget(self.current_version_label)
self.license_text = QTextEdit()
self.license_text.setMinimumHeight(300)
layout.addWidget(self.license_text)
btn_layout = QHBoxLayout()
self.save_license_button = QPushButton("라이센스 저장")
btn_layout.addWidget(self.save_license_button)
layout.addLayout(btn_layout)
self.save_license_button.clicked.connect(self.handle_save_license)
self.setLayout(layout)
self.setStyleSheet("""
QWidget { background-color: #ffffff; }
QPushButton { background-color: #1877f2; color: white; border-radius: 4px; padding: 6px; font-size: 14px; }
QPushButton:hover { background-color: #166fe5; }
QLabel { font-size: 14px; }
QTextEdit { font-size: 14px; border: 1px solid #ccc; border-radius: 4px; }
""")
def load_license(self):
license_content = self.supabase_manager.get_license_content()
version = self.supabase_manager.get_latest_license_version()
self.current_version_label.setText(f"현재 버전: {version}")
self.license_text.setHtml(license_content)
def handle_save_license(self):
new_content = self.license_text.toHtml()
from datetime import datetime
new_version = datetime.utcnow().strftime("v%Y%m%d%H%M%S")
self.supabase_manager.create_or_update_license(new_content, new_version)
QMessageBox.information(self, "저장 완료", f"라이센스가 저장되었습니다. 새 버전: {new_version}")
self.load_license()
class AdminClientDialog(QDialog):
def __init__(self, supabase_manager, parent=None):
super().__init__(parent)
self.supabase_manager = supabase_manager
self.setWindowTitle("관리자 클라이언트")
self.setMinimumSize(700, 600)
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
self.tab_widget = QTabWidget()
self.announcement_tab = AnnouncementManagementTab(self.supabase_manager)
self.license_tab = LicenseManagementTab(self.supabase_manager)
self.tab_widget.addTab(self.announcement_tab, "공지사항 관리")
self.tab_widget.addTab(self.license_tab, "라이센스 관리")
layout.addWidget(self.tab_widget)
self.setLayout(layout)