274 lines
12 KiB
Python
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)
|