This commit is contained in:
2026-04-06 15:40:06 +05:00
parent 10fb240dcd
commit 7435992e15
21 changed files with 993 additions and 150 deletions

View File

@@ -2,6 +2,7 @@ import asyncio
import flet as ft
import designer as ds
import api_client
import theme_manager as tm
class ProfilePage(ft.Column):
@@ -31,21 +32,53 @@ class ProfilePage(ft.Column):
hint_text="Загрузка...",
)
# ── Выбор темы ─────────────────────────────────────────────────
self._theme_dropdown = ds.CastomDropdown(
label="Тема интерфейса",
hint_text="Выберите тему...",
options=[
ft.DropdownOption(key=t["id"], text=t["name"])
for t in tm.theme_list()
],
)
self._theme_dropdown.on_select = self._on_theme_preview
# ── Слайдер масштаба ───────────────────────────────────────────
self._scale_value = ds.SCALE
self._scale_label = ft.Text(
value=f"Масштаб: {ds.SCALE:.1f}×",
size=ds.s(13),
color=ds.colors.text_secondary,
)
self._scale_slider = ft.Slider(
min=tm.SCALE_MIN,
max=tm.SCALE_MAX,
divisions=round((tm.SCALE_MAX - tm.SCALE_MIN) / tm.SCALE_STEP),
value=ds.SCALE,
label="{value}×",
active_color=ds.colors.primary,
inactive_color=ds.colors.surface_alt,
thumb_color=ds.colors.primary,
on_change=self._on_scale_preview,
on_change_end=self._on_scale_apply,
)
self._loading = ft.ProgressRing(visible=False, width=ds.s(24), height=ds.s(24))
self._status = ft.Text(value="", size=ds.s(14))
super().__init__(
spacing=ds.s(16),
controls=[
ds.CastomText(
text="Редактировать профиль",
size=18,
weight=ft.FontWeight.BOLD,
),
ds.CastomText("Редактировать профиль", size=18, weight=ft.FontWeight.BOLD),
self._first_name,
self._last_name,
self._org_dropdown,
self._group_dropdown,
ft.Divider(height=ds.s(8), color=ds.colors.border),
ds.CastomText("Внешний вид", size=16, weight=ft.FontWeight.BOLD),
self._theme_dropdown,
self._scale_label,
self._scale_slider,
ft.Row(
spacing=ds.s(12),
controls=[
@@ -57,6 +90,8 @@ class ProfilePage(ft.Column):
],
)
# ── загрузка данных ─────────────────────────────────────────────────────
async def load(self):
token = self._page.session.store.get("auth_token") or ""
me, orgs, groups = await asyncio.gather(
@@ -66,7 +101,7 @@ class ProfilePage(ft.Column):
)
self._orgs = orgs or []
_opt_style = ft.ButtonStyle(color=ds.clolors.ferst)
_opt_style = ft.ButtonStyle(color=ds.colors.text_primary)
self._org_dropdown.options = [
ft.DropdownOption(key=o["id"], text=o["name_organization"], style=_opt_style)
@@ -80,6 +115,13 @@ class ProfilePage(ft.Column):
]
self._group_dropdown.hint_text = "Выберите группу..."
# устанавливаем текущие настройки внешнего вида
self._theme_dropdown.value = tm.load_theme_id(self._page)
saved_scale = tm.load_scale(self._page)
self._scale_slider.value = saved_scale
self._scale_value = saved_scale
self._scale_label.value = f"Масштаб: {saved_scale:.1f}×"
if me:
self._first_name.value = me.get("first_name", "")
self._last_name.value = me.get("last_name", "")
@@ -88,6 +130,8 @@ class ProfilePage(ft.Column):
self.update()
# ── обработчики ─────────────────────────────────────────────────────────
def _on_org_change(self, e):
selected_id = e.control.value
for o in self._orgs:
@@ -96,6 +140,33 @@ class ProfilePage(ft.Column):
break
self.update()
def _on_theme_preview(self, e):
"""Применить тему предварительно (только bgcolor страницы)."""
theme_id = e.control.value
if theme_id:
tm.apply_theme(self._page, theme_id)
self._page.update()
def _on_scale_preview(self, e):
"""Обновить подпись слайдера во время перетаскивания."""
v = round(e.control.value, 1)
self._scale_value = v
self._scale_label.value = f"Масштаб: {v:.1f}×"
self._scale_label.update()
def _on_scale_apply(self, e):
"""Применить масштаб сразу при отпускании слайдера."""
scale = round(e.control.value, 1)
self._scale_value = scale
theme_id = self._theme_dropdown.value or tm.load_theme_id(self._page)
tm.save_preferences(self._page, theme_id, scale)
tm.apply_scale(self._page, scale)
tm.apply_theme(self._page, theme_id)
url_key = self._page.session.store.get("url_key") or ""
self._page.go(f"/profile/{url_key}")
# ── сохранение ──────────────────────────────────────────────────────────
async def _save(self, e):
self._loading.visible = True
self._status.value = ""
@@ -104,8 +175,8 @@ class ProfilePage(ft.Column):
token = self._page.session.store.get("auth_token") or ""
user_id = self._page.session.store.get("user_id") or ""
fn = self._first_name.value.strip() if self._first_name.value else None
ln = self._last_name.value.strip() if self._last_name.value else None
fn = (self._first_name.value or "").strip() or None
ln = (self._last_name.value or "").strip() or None
data, err = await api_client.update_user(
user_id=user_id,
@@ -117,15 +188,26 @@ class ProfilePage(ft.Column):
)
self._loading.visible = False
if err:
self._status.value = err
self._status.color = "red"
else:
self._status.value = "Данные успешно сохранены!"
self._status.color = ds.clolors.ferst
# Обновляем сессию для корректного сохранения при прохождении тестов
if data:
self.update()
return
# всё успешно — сохраняем тему и масштаб в localStorage
theme_id = self._theme_dropdown.value or "default"
scale = self._scale_value
tm.save_preferences(self._page, theme_id, scale)
tm.apply_scale(self._page, scale)
tm.apply_theme(self._page, theme_id)
if data:
self._page.session.store.set("group_id", data.get("group_id") or "")
self._page.session.store.set("org_id", data.get("organization_id") or "")
self.update()
# перезагружаем текущую страницу — пересоздаёт все виджеты
# с новым SCALE и новыми цветами
url_key = self._page.session.store.get("url_key") or ""
self._page.go(f"/profile/{url_key}")