Files
news_all_spo/web/views/articles_list.py
2026-05-13 19:05:07 +05:00

208 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import flet as ft
import designer as d
import api_client as api
from views.dashboard import _admin_appbar, _sidebar
class ArticlesListView:
def __init__(self, page: ft.Page):
self.page = page
self._table_col = ft.Column(spacing=0, expand=True)
self._filter = "all"
def build_page_view(self) -> ft.View:
filter_tabs = ft.Tabs(
selected_index=0,
on_change=self._on_filter,
tabs=[
ft.Tab(text="Все"),
ft.Tab(text="Опубликовано"),
ft.Tab(text="Черновики"),
],
)
return ft.View(
route="/admin/articles",
bgcolor=d.BACKGROUND,
appbar=_admin_appbar(self.page, "Статьи"),
controls=[
ft.Row(
[
_sidebar(self.page, active="/admin/articles"),
ft.Container(
content=ft.Column(
[
ft.Row(
[
d.headline("Статьи", size=22),
ft.Row(
[
d.btn_primary(
"Новая статья",
icon=ft.icons.ADD,
on_click=lambda e: self.page.go("/admin/articles/new"),
)
]
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
filter_tabs,
self._table_col,
],
expand=True,
scroll=ft.ScrollMode.AUTO,
spacing=16,
),
expand=True,
padding=24,
),
],
expand=True,
spacing=0,
)
],
)
async def load(self):
await self._load_articles()
async def _on_filter(self, e):
idx = e.control.selected_index
self._filter = ["all", "published", "draft"][idx]
await self._load_articles()
async def _load_articles(self):
token = self.page.session.get("token")
status = None if self._filter == "all" else self._filter
data = await api.admin_list_articles(token, limit=200, status=status)
items = data.get("items", []) if data else []
self._render(items)
def _render(self, items: list):
if not items:
self._table_col.controls = [
ft.Container(
content=d.empty_state(ft.icons.ARTICLE_OUTLINED, "Статей пока нет"),
alignment=ft.alignment.center,
padding=40,
)
]
self.page.update()
return
header = ft.Container(
content=ft.Row(
[
ft.Text("Заголовок", size=12, color=d.TEXT_SECONDARY, expand=3),
ft.Text("Категория", size=12, color=d.TEXT_SECONDARY, expand=1),
ft.Text("Статус", size=12, color=d.TEXT_SECONDARY, width=110),
ft.Text("Дата", size=12, color=d.TEXT_SECONDARY, width=110),
ft.Text("Действия", size=12, color=d.TEXT_SECONDARY, width=130),
],
spacing=8,
),
bgcolor=d.BACKGROUND,
padding=ft.padding.symmetric(horizontal=16, vertical=10),
border_radius=ft.BorderRadius(8, 8, 0, 0),
border=ft.Border(bottom=ft.BorderSide(1, d.DIVIDER)),
)
rows = [header] + [self._build_row(a) for a in items]
self._table_col.controls = [
ft.Container(
content=ft.Column(rows, spacing=0),
bgcolor=d.SURFACE,
border_radius=8,
border=ft.border.all(1, d.DIVIDER),
)
]
self.page.update()
def _build_row(self, article: dict) -> ft.Container:
pub_date = ""
if article.get("published_at"):
pub_date = article["published_at"][:10]
elif article.get("created_at"):
pub_date = article["created_at"][:10]
cat_name = article.get("category", {})
cat_name = (cat_name or {}).get("name", "")
return ft.Container(
content=ft.Row(
[
ft.Text(
article["title"],
size=14,
expand=3,
max_lines=2,
overflow=ft.TextOverflow.ELLIPSIS,
),
ft.Text(cat_name, size=13, color=d.TEXT_SECONDARY, expand=1),
ft.Container(content=d.status_badge(article.get("status", "draft")), width=110),
ft.Text(pub_date, size=12, color=d.TEXT_SECONDARY, width=110),
ft.Row(
[
d.btn_icon(
ft.icons.EDIT_OUTLINED,
"Редактировать",
on_click=lambda e, aid=article["id"]: self.page.go(f"/admin/articles/{aid}"),
color=d.PRIMARY,
),
*([
d.btn_icon(
ft.icons.PUBLISH,
"Опубликовать",
on_click=lambda e, aid=article["id"]: self.page.run_task(self._publish, aid),
color=d.SUCCESS,
)
] if article.get("status") == "draft" else []),
d.btn_icon(
ft.icons.DELETE_OUTLINE,
"Удалить",
on_click=lambda e, a=article: self._confirm_delete(a),
color=d.ERROR,
),
],
spacing=0,
width=130,
),
],
spacing=8,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
),
padding=ft.padding.symmetric(horizontal=16, vertical=12),
border=ft.Border(bottom=ft.BorderSide(1, d.DIVIDER)),
ink=True,
)
def _confirm_delete(self, article: dict):
dlg = d.confirm_dialog(
self.page,
"Удалить статью?",
f'"{article["title"]}" будет удалена навсегда.',
lambda: self.page.run_task(self._delete, article["id"]),
)
self.page.dialog = dlg
dlg.open = True
self.page.update()
async def _delete(self, article_id: str):
token = self.page.session.get("token")
ok = await api.admin_delete_article(token, article_id)
if ok:
d.snack(self.page, "Статья удалена")
await self._load_articles()
else:
d.snack(self.page, "Ошибка удаления", error=True)
async def _publish(self, article_id: str):
token = self.page.session.get("token")
ok = await api.admin_publish_article(token, article_id)
if ok:
d.snack(self.page, "Статья опубликована")
await self._load_articles()
else:
d.snack(self.page, "Ошибка публикации", error=True)