# 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)