--- /dev/null
+from __future__ import annotations
+from PyQt6.QtWidgets import (
+ QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
+ QListWidget, QListWidgetItem, QLabel, QPushButton,
+)
+from PyQt6.QtCore import Qt, pyqtSignal
+from PyQt6.QtGui import QColor
+from core.models import Article
+
+class ArticleItem(QListWidgetItem):
+ def __init__(self, article: Article):
+ super().__init__()
+ self.article = article
+ lang_other = "EN" if article.lang == "it" else "IT"
+ if article.has_translation:
+ badge = f"🇬🇧 ✓" if article.lang == "it" else "🇮🇹 ✓"
+ status = f"{article.slug} [{badge}]"
+ else:
+ badge = f"🇬🇧 ✗" if article.lang == "it" else "🇮🇹 ✗"
+ status = f"{article.slug} [{badge}]"
+ self.setText(status)
+ if not article.has_translation:
+ self.setForeground(QColor("#ff6b6b"))
+
+class ArticlesView(QWidget):
+ article_selected = pyqtSignal(object) # Article
+ translate_requested = pyqtSignal(object) # Article
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._articles: list[Article] = []
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout(self)
+ layout.setContentsMargins(0, 0, 0, 0)
+ self._tabs = QTabWidget()
+ self._tabs.setStyleSheet("QTabBar::tab { padding: 6px 16px; }")
+ self._list_it = QListWidget()
+ self._list_en = QListWidget()
+ self._tabs.addTab(self._list_it, "🇮🇹 Italiano")
+ self._tabs.addTab(self._list_en, "🇬🇧 English")
+ layout.addWidget(self._tabs)
+ self._list_it.itemClicked.connect(lambda item: self.article_selected.emit(item.article))
+ self._list_en.itemClicked.connect(lambda item: self.article_selected.emit(item.article))
+
+ def set_articles(self, articles: list[Article]):
+ self._articles = articles
+ self._list_it.clear()
+ self._list_en.clear()
+ for a in articles:
+ item = ArticleItem(a)
+ if a.lang == "it":
+ self._list_it.addItem(item)
+ else:
+ self._list_en.addItem(item)
+
+class MissingTranslationView(QWidget):
+ article_selected = pyqtSignal(object) # Article
+ translate_requested = pyqtSignal(object) # Article
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout(self)
+ header = QLabel("⚠️ Articoli senza traduzione")
+ header.setStyleSheet("color: #ff6b6b; font-weight: bold; font-size: 13px; padding: 10px;")
+ layout.addWidget(header)
+ self._list = QListWidget()
+ layout.addWidget(self._list)
+ self._list.itemClicked.connect(lambda item: self.article_selected.emit(item.article))
+
+ def set_articles(self, articles: list[Article]):
+ self._list.clear()
+ missing = [a for a in articles if not a.has_translation]
+ for a in missing:
+ lang_other = "🇬🇧" if a.lang == "it" else "🇮🇹"
+ item = ArticleItem(a)
+ item.setText(f"{a.slug} [manca {lang_other}] ({a.lang.upper()})")
+ self._list.addItem(item)
from __future__ import annotations
+import subprocess
from PyQt6.QtWidgets import (
QMainWindow, QWidget, QHBoxLayout, QVBoxLayout,
QLabel, QPushButton, QStackedWidget, QFrame,
self._stack = QStackedWidget()
root.addWidget(self._stack, stretch=1)
- # Placeholder pages (replaced in later tasks)
- for name in ["articles", "no_translation", "new_article", "taxonomy",
- "media", "translations", "git", "hugo"]:
+ from ui.articles_view import ArticlesView, MissingTranslationView
+
+ self._articles_view = ArticlesView()
+ self._articles_view.article_selected.connect(self._on_article_selected)
+ self._stack.addWidget(self._articles_view)
+ self._page_articles = self._stack.count() - 1
+
+ self._missing_view = MissingTranslationView()
+ self._missing_view.article_selected.connect(self._on_article_selected)
+ self._stack.addWidget(self._missing_view)
+ self._page_no_translation = self._stack.count() - 1
+
+ from ui.article_detail import ArticleDetailView
+
+ self._detail_view = ArticleDetailView()
+ self._detail_view.open_typora.connect(self._do_open_typora)
+ self._detail_view.open_frontmatter.connect(self._do_open_frontmatter)
+ self._detail_view.translate.connect(self._do_translate)
+ self._detail_view.push_master.connect(lambda a: self._do_git_push("master"))
+ self._detail_view.publish.connect(lambda a: self._do_git_push("production"))
+ self._detail_view.delete_article.connect(self._do_delete_article)
+ self._stack.addWidget(self._detail_view)
+ self._page_detail = self._stack.count() - 1
+
+ # Remaining placeholders
+ for name in ["new_article", "taxonomy", "media", "translations", "git", "hugo"]:
w = QLabel(f"[{name}]")
w.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._stack.addWidget(w)
if not self.config.blog_repo:
return
self._articles = scan_articles(Path(self.config.blog_repo))
+ self._articles_view.set_articles(self._articles)
+ self._missing_view.set_articles(self._articles)
missing = [a for a in self._articles if not a.has_translation]
self._badge_no_trans.setText(str(len(missing)))
+ def _on_article_selected(self, article: Article):
+ self._detail_view.set_article(article)
+ self._stack.setCurrentIndex(self._page_detail)
+
+ def _do_open_typora(self, article: Article):
+ subprocess.Popen([self.config.typora_bin, str(article.path)])
+
+ def _do_open_frontmatter(self, article: Article):
+ from ui.frontmatter_editor import FrontmatterEditor
+ dlg = FrontmatterEditor(article, Path(self.config.blog_repo), self)
+ if dlg.exec():
+ self._detail_view.set_article(article)
+
+ def _do_translate(self, article: Article):
+ pass # implemented in Task 15
+
+ def _do_git_push(self, branch: str):
+ pass # implemented in Task 16
+
+ def _do_delete_article(self, article: Article):
+ pass # implemented in Task 16
+
def _setup_watcher(self):
if not self.config.blog_repo:
return