prerelease/0.6.0 #1
@@ -4,7 +4,7 @@ from bson import ObjectId
|
||||
from rio import Component, Rectangle, Column, Text, Row, PointerEventListener, TextInput
|
||||
from rio.event import on_populate
|
||||
|
||||
from elm.types import UserSession, Ticket, Seat
|
||||
from elm.types import UserSession, Ticket, Seat, User
|
||||
from elm.components import ElmButton
|
||||
from elm.services import UserService
|
||||
|
||||
@@ -18,9 +18,11 @@ class AccountInfoBox(Component):
|
||||
password_change_in_progress: bool = False
|
||||
ticket: Optional[Ticket] = None
|
||||
seat: Optional[Seat] = None
|
||||
fixed_user: Optional[User] = None
|
||||
|
||||
@on_populate
|
||||
async def on_populate(self) -> None:
|
||||
if self.fixed_user is None:
|
||||
try:
|
||||
user = await self.session[UserService].get_user(self.session[UserSession].user_name)
|
||||
if user:
|
||||
@@ -31,6 +33,10 @@ class AccountInfoBox(Component):
|
||||
self.session.navigate_to("./login")
|
||||
except KeyError:
|
||||
self.session.navigate_to("./login")
|
||||
else:
|
||||
self.mail = self.fixed_user.user_mail
|
||||
self.ticket = await Ticket.find_one({"owner.$id": self.fixed_user.id})
|
||||
self.seat = await Seat.find_one({"user.$id": ObjectId(self.fixed_user.id)})
|
||||
|
||||
async def set_new_password(self) -> None:
|
||||
self.password_change_in_progress = True
|
||||
@@ -86,7 +92,7 @@ class AccountInfoBox(Component):
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
),
|
||||
Column(
|
||||
TextInput(text=self.session[UserSession].user_name, label="Nutzername", is_sensitive=False),
|
||||
TextInput(text=self.fixed_user.user_name if self.fixed_user is not None else self.session[UserSession].user_name, label="Nutzername", is_sensitive=False),
|
||||
TextInput(text=self.mail, label="E-Mail Adresse", is_sensitive=False),
|
||||
row_col(
|
||||
PointerEventListener(
|
||||
|
||||
@@ -87,15 +87,33 @@ class UserNavigation(Component):
|
||||
async def on_populate(self) -> None:
|
||||
self.session.create_task(self.update_balance())
|
||||
|
||||
def show_admin_navigation(self) -> bool:
|
||||
try:
|
||||
user_session = self.session[UserSession]
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
return user_session.is_team_member
|
||||
|
||||
def build(self) -> Component:
|
||||
return Rectangle(
|
||||
content=Column(
|
||||
base_nav = [
|
||||
UserNavigationButton(f"Guthaben: {self.session[AccountingService].make_euro_string_from_decimal(self.balance)}", "/balance", self.close_navigation),
|
||||
UserNavigationButton("Meine Bestellungen", "/my-orders", self.close_navigation),
|
||||
UserNavigationButton("Mein Profil", "/my-profile", self.close_navigation),
|
||||
# UserNavigationButton("Mein Clan", "/my-clans", self.close_navigation), ToDo: Implement
|
||||
UserNavigationButton("Mein Profil", "/my-profile", self.close_navigation)
|
||||
]
|
||||
|
||||
if self.show_admin_navigation():
|
||||
base_nav.extend([
|
||||
UserNavigationButton("Admin: Benutzer", "/user-admin", self.close_navigation),
|
||||
UserNavigationButton("Admin: Catering", "/catering-admin", self.close_navigation)
|
||||
])
|
||||
|
||||
base_nav.append(
|
||||
UserNavigationButton("Ausloggen", "/logout", self.close_navigation)
|
||||
),
|
||||
)
|
||||
|
||||
return Rectangle(
|
||||
content=Column(*base_nav),
|
||||
min_width=3.5,
|
||||
min_height=3.5,
|
||||
fill=self.session.theme.background_color
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import partial
|
||||
from typing import Optional
|
||||
from decimal import Decimal
|
||||
|
||||
from rio import Component, Column, Row, Text, Spacer, page, Rectangle, TextInput, GuardEvent, Button, TextInputChangeEvent, NumberInput
|
||||
from rio.event import on_populate
|
||||
|
||||
from elm.types import UserSession, User, Transaction
|
||||
from elm.services import AccountingService, MailingService
|
||||
from elm.components import AccountInfoBox
|
||||
|
||||
logger = logging.getLogger(__name__.split(".")[-1])
|
||||
|
||||
def user_admin_page_guard(event: GuardEvent) -> Optional[str]:
|
||||
try:
|
||||
if event.session[UserSession].is_team_member:
|
||||
return None
|
||||
return "/"
|
||||
except KeyError:
|
||||
return "/"
|
||||
|
||||
@page(name="Benutzerverwaltung", url_segment="user-admin", guard=user_admin_page_guard)
|
||||
class UserAdminPage(Component):
|
||||
all_users: list[User] = list()
|
||||
user_list: list[User] = list()
|
||||
search_bar_text: str = ""
|
||||
active_user: Optional[User] = None
|
||||
transaction_value: float = 0.0
|
||||
transaction_reason: str = ""
|
||||
active_user_balance: str = "0.00 €"
|
||||
|
||||
@on_populate
|
||||
async def on_populate(self) -> None:
|
||||
user_list = await User.find_all().to_list()
|
||||
self.all_users = sorted(user_list, key=lambda u: u.user_name)
|
||||
self.user_list = sorted(user_list, key=lambda u: u.user_name)
|
||||
|
||||
async def on_search_bar_text_changed(self, e: TextInputChangeEvent) -> None:
|
||||
self.user_list = list(filter(lambda user: (e.text.lower() in user.user_name.lower()), self.all_users))
|
||||
|
||||
async def on_user_clicked(self, user: User) -> None:
|
||||
self.active_user = user
|
||||
self.active_user_balance = self.session[AccountingService].make_euro_string_from_decimal(
|
||||
await self.session[AccountingService].get_balance(user.user_name)
|
||||
)
|
||||
|
||||
async def create_debit_transaction(self) -> None:
|
||||
if not self.active_user:
|
||||
return
|
||||
logger.info(f"Trying to remove {self.transaction_value} to {self.active_user.user_name} ({self.transaction_reason})")
|
||||
new_transaction = Transaction(
|
||||
user_name=self.active_user.user_name,
|
||||
value=Decimal(str(self.transaction_value)),
|
||||
is_debit=True,
|
||||
title=self.transaction_reason
|
||||
)
|
||||
try:
|
||||
await new_transaction.save()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
self.transaction_value = 0.0
|
||||
self.transaction_reason = ""
|
||||
self.active_user_balance = self.session[AccountingService].make_euro_string_from_decimal(
|
||||
await self.session[AccountingService].get_balance(self.active_user.user_name)
|
||||
)
|
||||
|
||||
async def create_credit_transaction(self) -> None:
|
||||
if not self.active_user:
|
||||
return
|
||||
logger.info(f"Trying to add {self.transaction_value} to {self.active_user.user_name} ({self.transaction_reason})")
|
||||
value = Decimal(str(self.transaction_value))
|
||||
new_transaction = Transaction(
|
||||
user_name=self.active_user.user_name,
|
||||
value=value,
|
||||
is_debit=False,
|
||||
title=self.transaction_reason
|
||||
)
|
||||
try:
|
||||
await new_transaction.save()
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
self.transaction_value = 0.0
|
||||
self.transaction_reason = ""
|
||||
total_balance = await self.session[AccountingService].get_balance(self.active_user.user_name)
|
||||
self.active_user_balance = self.session[AccountingService].make_euro_string_from_decimal(total_balance)
|
||||
|
||||
self.session.create_task(self.session[MailingService].send_email(
|
||||
subject="Dein Guthaben wurde aufgeladen!",
|
||||
body=self.session[MailingService].generate_account_balance_added_mail_body(user=self.active_user, added_balance=value, total_balance=total_balance),
|
||||
receiver=self.active_user.user_mail
|
||||
))
|
||||
|
||||
def build(self) -> Component:
|
||||
right_panel_contents = []
|
||||
if not self.active_user:
|
||||
right_panel_contents.append(Spacer())
|
||||
else:
|
||||
right_panel_contents.extend([
|
||||
AccountInfoBox(fixed_user=self.active_user),
|
||||
Rectangle(
|
||||
content=Column(
|
||||
Rectangle(
|
||||
content=Rectangle(
|
||||
content=Text(f"LAN Konto - Kontostand: {self.active_user_balance}", margin=0.5, selectable=False, overflow="wrap"),
|
||||
fill=self.session.theme.header_box_background_color,
|
||||
margin=0.4
|
||||
),
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
),
|
||||
Column(
|
||||
NumberInput(
|
||||
value=self.bind().transaction_value,
|
||||
label="Betrag",
|
||||
suffix_text="€",
|
||||
decimals=2,
|
||||
margin=1,
|
||||
margin_bottom=0
|
||||
),
|
||||
TextInput(
|
||||
text=self.bind().transaction_reason,
|
||||
label="Beschreibung",
|
||||
margin=1,
|
||||
margin_bottom=0
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
content="Entfernen",
|
||||
shape="rectangle",
|
||||
color="danger",
|
||||
margin=1,
|
||||
on_press=self.create_debit_transaction
|
||||
),
|
||||
Button(
|
||||
content="Hinzufügen",
|
||||
shape="rectangle",
|
||||
color="success",
|
||||
margin=1,
|
||||
on_press=self.create_credit_transaction
|
||||
)
|
||||
),
|
||||
margin=1,
|
||||
spacing=1
|
||||
),
|
||||
Spacer()
|
||||
),
|
||||
fill=self.session.theme.box_color,
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color
|
||||
),
|
||||
])
|
||||
|
||||
|
||||
|
||||
return Row(
|
||||
Rectangle(
|
||||
content=Column(
|
||||
Rectangle(
|
||||
content=Rectangle(
|
||||
content=Text("Nutzerliste", margin=0.5, selectable=False, overflow="wrap"),
|
||||
fill=self.session.theme.header_box_background_color,
|
||||
margin=0.4
|
||||
),
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
),
|
||||
Column(
|
||||
TextInput(label="Nutzername", text=self.bind().search_bar_text, on_change=self.on_search_bar_text_changed, margin_bottom=1),
|
||||
*[Button(content=user.user_name, shape="rectangle", style="plain-text", on_press=partial(self.on_user_clicked, user)) for user in self.user_list],
|
||||
margin=1
|
||||
),
|
||||
Spacer()
|
||||
),
|
||||
fill=self.session.theme.box_color,
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
min_width=25
|
||||
),
|
||||
Rectangle(
|
||||
content=Column(
|
||||
Rectangle(
|
||||
content=Rectangle(
|
||||
content=Text(f"Nutzer bearbeiten{': ' if self.active_user else ''}{self.active_user.user_name if self.active_user else ''}", margin=0.5, selectable=False, overflow="wrap"),
|
||||
fill=self.session.theme.header_box_background_color,
|
||||
margin=0.4
|
||||
),
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
),
|
||||
Column(
|
||||
*right_panel_contents,
|
||||
margin=1,
|
||||
spacing=1
|
||||
),
|
||||
Spacer()
|
||||
),
|
||||
fill=self.session.theme.box_color,
|
||||
stroke_width=0.1,
|
||||
stroke_color=self.session.theme.box_border_color,
|
||||
grow_x=True
|
||||
),
|
||||
spacing=1,
|
||||
margin=1
|
||||
)
|
||||
@@ -69,6 +69,8 @@ class UserService:
|
||||
|
||||
async def is_login_valid(self, user_name: str, password_clear_text: str) -> bool:
|
||||
user = await self.get_user(user_name)
|
||||
if not user:
|
||||
user = await self.get_user(user_name.lower()) # Migrated users had all lowercase names
|
||||
user_password_hash = sha256(password_clear_text.encode(encoding="utf-8")).hexdigest()
|
||||
if not user:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user