81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
|
|
"""Scheduler page — list tasks + remove."""
|
||
|
|
|
||
|
|
import datetime
|
||
|
|
import flet as ft
|
||
|
|
|
||
|
|
from gui import services as svc
|
||
|
|
|
||
|
|
|
||
|
|
def _fmt_ts(ts) -> str:
|
||
|
|
if not ts:
|
||
|
|
return "—"
|
||
|
|
try:
|
||
|
|
return datetime.datetime.fromtimestamp(int(ts)).strftime("%Y-%m-%d %H:%M:%S")
|
||
|
|
except (TypeError, ValueError, OSError):
|
||
|
|
return "—"
|
||
|
|
|
||
|
|
|
||
|
|
def build(page: ft.Page) -> ft.Control:
|
||
|
|
list_view = ft.Column(spacing=8, expand=True, scroll=ft.ScrollMode.AUTO)
|
||
|
|
|
||
|
|
def render():
|
||
|
|
list_view.controls.clear()
|
||
|
|
tasks = svc.scheduler_all()
|
||
|
|
if not tasks:
|
||
|
|
list_view.controls.append(
|
||
|
|
ft.Text("Запланированных задач нет. Создавай голосом: «Каждые 30 минут напомни попить воды».",
|
||
|
|
color="#8aa0a8"))
|
||
|
|
for t in sorted(tasks, key=lambda x: x.get("created_at", 0)):
|
||
|
|
list_view.controls.append(_task_card(t, on_delete=lambda tid=t["id"]: do_remove(tid)))
|
||
|
|
page.update()
|
||
|
|
|
||
|
|
def do_remove(task_id: str):
|
||
|
|
if svc.scheduler_remove(task_id):
|
||
|
|
page.open(ft.SnackBar(content=ft.Text("Задача удалена", color="white"), bgcolor="#33d3a4"))
|
||
|
|
render()
|
||
|
|
|
||
|
|
render()
|
||
|
|
|
||
|
|
return ft.Column(
|
||
|
|
[
|
||
|
|
ft.Row([
|
||
|
|
ft.Text("Расписание", size=24, weight=ft.FontWeight.W_700),
|
||
|
|
ft.FilledButton(text="Обновить", icon=ft.Icons.REFRESH, on_click=lambda e: render()),
|
||
|
|
], alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
|
||
|
|
list_view,
|
||
|
|
],
|
||
|
|
spacing=10,
|
||
|
|
expand=True,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _task_card(t: dict, on_delete) -> ft.Control:
|
||
|
|
name = t.get("name", "?")
|
||
|
|
sched_h = t.get("schedule_human") or t.get("schedule_kind") or "?"
|
||
|
|
action = t.get("action") or {}
|
||
|
|
action_text = action.get("text") or action.get("speak") or action.get("phrase") or "(нет текста)"
|
||
|
|
return ft.Container(
|
||
|
|
content=ft.Column([
|
||
|
|
ft.Row([
|
||
|
|
ft.Text(name, weight=ft.FontWeight.W_600, size=14),
|
||
|
|
ft.Container(
|
||
|
|
content=ft.Text(sched_h, size=10),
|
||
|
|
bgcolor="#1e3a44",
|
||
|
|
padding=ft.padding.symmetric(horizontal=8, vertical=2),
|
||
|
|
border_radius=10,
|
||
|
|
),
|
||
|
|
], alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
|
||
|
|
ft.Text(f"Действие: {action_text}", size=11, color="#a8b8c0"),
|
||
|
|
ft.Row([
|
||
|
|
ft.Text(f"создана: {_fmt_ts(t.get('created_at'))}", size=10, color="#5a6a72"),
|
||
|
|
ft.Text(f"последний запуск: {_fmt_ts(t.get('last_fired'))}", size=10, color="#5a6a72"),
|
||
|
|
]),
|
||
|
|
ft.Row([
|
||
|
|
ft.OutlinedButton(text="Удалить", icon=ft.Icons.DELETE, on_click=lambda e: on_delete()),
|
||
|
|
]),
|
||
|
|
], spacing=6),
|
||
|
|
bgcolor="#15252b",
|
||
|
|
border_radius=8,
|
||
|
|
padding=12,
|
||
|
|
)
|