diff --git a/app/main.py b/app/main.py index 1ab74b2..f63f67a 100644 --- a/app/main.py +++ b/app/main.py @@ -1337,6 +1337,8 @@ def create_user(payload: dict, request: Request, _: User = Depends(require_admin expires_at=expires_at, active=payload.get("active", True), is_admin=payload.get("is_admin", False), + first_name=payload.get("first_name", ""), + last_name=payload.get("last_name", ""), ) db.add(user) db.commit() @@ -1349,7 +1351,7 @@ def edit_user(user_id: int, payload: dict, request: Request, _: User = Depends(r user = db.get(User, user_id) if not user: raise HTTPException(status_code=404, detail="User not found") - for key in ["username", "active", "is_admin"]: + for key in ["username", "active", "is_admin", "first_name", "last_name"]: if key in payload: setattr(user, key, payload[key]) if "password" in payload and payload["password"]: diff --git a/app/models.py b/app/models.py index 95f729a..af7e332 100644 --- a/app/models.py +++ b/app/models.py @@ -32,6 +32,8 @@ class User(Base): expires_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), index=True) active: Mapped[bool] = mapped_column(Boolean, default=True, index=True) is_admin: Mapped[bool] = mapped_column(Boolean, default=False) + first_name: Mapped[str] = mapped_column(String(64), default="") + last_name: Mapped[str] = mapped_column(String(64), default="") created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=lambda: dt.datetime.now(dt.timezone.utc)) @@ -50,6 +52,8 @@ class Service(Base): icon_path: Mapped[str] = mapped_column(Text, default="") active: Mapped[bool] = mapped_column(Boolean, default=True) warm_pool_size: Mapped[int] = mapped_column(Integer, default=0) + first_name: Mapped[str] = mapped_column(String(64), default="") + last_name: Mapped[str] = mapped_column(String(64), default="") created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=lambda: dt.datetime.now(dt.timezone.utc)) @@ -59,6 +63,8 @@ class Category(Base): id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(128), unique=True, index=True) slug: Mapped[str] = mapped_column(String(64), unique=True, index=True) + first_name: Mapped[str] = mapped_column(String(64), default="") + last_name: Mapped[str] = mapped_column(String(64), default="") created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=lambda: dt.datetime.now(dt.timezone.utc)) @@ -69,6 +75,8 @@ class ServiceCategory(Base): id: Mapped[int] = mapped_column(Integer, primary_key=True) service_id: Mapped[int] = mapped_column(ForeignKey("services.id", ondelete="CASCADE"), index=True) category_id: Mapped[int] = mapped_column(ForeignKey("categories.id", ondelete="CASCADE"), index=True) + first_name: Mapped[str] = mapped_column(String(64), default="") + last_name: Mapped[str] = mapped_column(String(64), default="") created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=lambda: dt.datetime.now(dt.timezone.utc)) @@ -90,6 +98,8 @@ class RdpSlot(Base): rdp_username: Mapped[str] = mapped_column(String(128)) rdp_password: Mapped[str] = mapped_column(String(256), default="") container_name: Mapped[Optional[str]] = mapped_column(String(128), nullable=True) + first_name: Mapped[str] = mapped_column(String(64), default="") + last_name: Mapped[str] = mapped_column(String(64), default="") created_at: Mapped[dt.datetime] = mapped_column(DateTime(timezone=True), default=lambda: dt.datetime.now(dt.timezone.utc)) diff --git a/app/static/style.css b/app/static/style.css index aec24e7..a4fa7dc 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -107,6 +107,22 @@ button { } .header-left { display: flex; align-items: center; } .header-right { display: flex; align-items: center; gap: 0.75rem; } + +.user-avatar { + width: 34px; + height: 34px; + border-radius: 50%; + background: linear-gradient(135deg, #1e7dc8 0%, #1360a0 100%); + color: #fff; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.02em; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + box-shadow: 0 2px 8px rgba(14,80,160,0.4); +} .header-username { color: #ffffff; font-size: 1rem; diff --git a/app/templates/admin.html b/app/templates/admin.html index 7f98520..07490b0 100644 --- a/app/templates/admin.html +++ b/app/templates/admin.html @@ -53,8 +53,8 @@
{% for u in users %} - {% endfor %} @@ -66,6 +66,8 @@
+ + @@ -91,6 +93,8 @@
Добавить пользователя
+ + @@ -668,9 +672,11 @@ return r.json(); } - function selectUser(id, username, active, isAdmin, expiresIso) { + function selectUser(id, username, active, isAdmin, expiresIso, firstName, lastName) { document.getElementById('u_id').value = id; document.getElementById('u_name').value = username; + document.getElementById('u_first_name').value = firstName || ''; + document.getElementById('u_last_name').value = lastName || ''; document.getElementById('u_exp').value = dateFromIso(expiresIso); document.getElementById('u_pwd').value = ''; document.getElementById('u_active').value = String(active); @@ -684,6 +690,8 @@ if (!expDate) return alert('Выберите дату деактивации'); await api('/api/admin/users', 'POST', { username: document.getElementById('new_u_name').value, + first_name: document.getElementById('new_u_first_name').value, + last_name: document.getElementById('new_u_last_name').value, password: document.getElementById('new_u_pwd').value, expires_at: expiryToApi(expDate), active: document.getElementById('new_u_active').value === 'true', @@ -699,6 +707,8 @@ if (!expDate) return alert('Выберите дату деактивации'); const payload = { username: document.getElementById('u_name').value, + first_name: document.getElementById('u_first_name').value, + last_name: document.getElementById('u_last_name').value, expires_at: expiryToApi(expDate), active: document.getElementById('u_active').value === 'true', is_admin: document.getElementById('u_admin').value === 'true', diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 8ed9c08..d4d7a73 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -44,7 +44,8 @@
- {{ user.username }} +
{{ ((user.first_name[0] if user.first_name else user.username[0]) + (user.last_name[0] if user.last_name else ''))|upper }}
+ {{ (user.first_name + ' ' + user.last_name)|trim or user.username }}
{% if user.is_admin %}