Compare commits

...

5 Commits

Author SHA1 Message Date
DEsimas e461869d7c chore: add requirements 2024-12-19 23:23:45 +03:00
DEsimas 6db6dba13d feat: UI profile 2024-12-19 23:12:11 +03:00
DEsimas e2c4bf6563 feat: UI registration 2024-12-19 21:14:45 +03:00
DEsimas 7bb4ac17df feat: UI login 2024-12-19 20:04:33 +03:00
DEsimas b47278beb6 feat: UI games listing 2024-12-19 19:09:40 +03:00
14 changed files with 494 additions and 15 deletions

2
UI/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
flet==0.25.2
psycopg2==2.9.10

View File

@ -1,23 +1,273 @@
import flet as ft import re
from requests.board_games import get_sections import flet as ft
from flet.core.page import RouteChangeEvent
from pages.profile import profile
from requests.board_games import get_sections, get_games
from requests.auth import login as login_req, registration
def root(page: ft.Page):
def update_games(section):
current_section.value = section
games = get_games(None if section == "Все" else section)
grid.controls.clear()
for game in games:
grid.controls.append(
ft.Column([
ft.Text(str(game)),
ft.Row([
ft.IconButton(icon=ft.icons.SHOPPING_CART),
ft.IconButton(icon=ft.icons.FAVORITE),
]),
])
)
page.update()
sections = [*get_sections(), "Все"]
sections_buttons = [ft.Button(section, on_click=lambda e, s=section: update_games(s)) for section in sections]
games = get_games()
current_section = ft.Ref[str]()
grid = ft.GridView(
expand=1,
runs_count=5,
max_extent=200,
child_aspect_ratio=1.0,
spacing=5,
run_spacing=5,
)
for game in games:
grid.controls.append(
ft.Column([
ft.Text(str(game)),
ft.Row([
ft.IconButton(
icon=ft.Icons.SHOPPING_CART
),
ft.IconButton(
icon=ft.Icons.FAVORITE
)
])
])
)
def go_to_login():
page.route = "/login"
page.update()
def go_to_register():
page.route = "/register"
page.update()
def go_to_profile():
page.route = "/profile"
page.update()
def logout():
page.client_storage.remove("user_id")
page.clean()
root(page)
page.update()
user_id = page.client_storage.get("user_id")
page.add(
ft.Row(
[
ft.Text(
"Весёлые кубики",
color="#59799f",
size=24,
weight=ft.FontWeight.BOLD,
),
ft.Row([
ft.Button(
text="Вход",
on_click=lambda e: go_to_login(),
),
ft.Button(
text="Регистрация",
on_click=lambda e: go_to_register(),
),
]) if user_id is None else ft.Row([
ft.Button(
"Профиль",
on_click=lambda e: go_to_profile()
),
ft.Button(
text="Выйти",
on_click=lambda e: logout(),
)
])
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN, # Adjust alignment if needed
),
ft.Column(
[ft.Row(sections_buttons, alignment=ft.MainAxisAlignment.CENTER)],
alignment=ft.MainAxisAlignment.START
),
grid
)
def login(page):
def close_banner(e):
page.close(banner)
action_button_style = ft.ButtonStyle(color=ft.Colors.BLUE)
banner = ft.Banner(
bgcolor=ft.Colors.AMBER_100,
leading=ft.Icon(ft.Icons.WARNING_AMBER_ROUNDED, color=ft.Colors.AMBER, size=40),
content=ft.Text(
value="Ведены неверные данные пользователя",
color=ft.Colors.BLACK,
),
actions=[
ft.TextButton(text="Ок", style=action_button_style, on_click=close_banner),
],
)
# Login button handler
def login_clicked(e):
email = email_input.value
password = password_input.value
user_id = login_req(email, password)
if user_id is None:
page.open(banner)
else:
page.client_storage.set("user_id", user_id)
back_clicked(None)
# Back button handler
def back_clicked(e):
page.go("/")
page.update()
# Email input field
email_input = ft.TextField(
label="Email",
hint_text="Enter your email",
width=300,
)
# Password input field
password_input = ft.TextField(
label="Password",
hint_text="Enter your password",
password=True,
can_reveal_password=True,
width=300,
)
# Login and Back buttons
login_button = ft.ElevatedButton("Войти", on_click=login_clicked)
back_button = ft.TextButton("Назад", on_click=back_clicked)
page.horizontal_alignment=ft.CrossAxisAlignment.CENTER
# Add components to the page
page.add(
ft.Column(
[
ft.Text("Вход", size=24, weight=ft.FontWeight.BOLD),
email_input,
password_input,
login_button,
back_button
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
),
)
def register(page):
page.horizontal_alignment=ft.CrossAxisAlignment.CENTER
def back_clicked(e):
page.go("/")
page.update()
# Поля для ввода данных
first_name_input = ft.TextField(label="Имя", hint_text="Введите ваше имя")
last_name_input = ft.TextField(label="Фамилия", hint_text="Введите вашу фамилию")
middle_name_input = ft.TextField(label="Отчество", hint_text="Введите ваше отчество (опционально)")
age_input = ft.TextField(label="Возраст", hint_text="Введите ваш возраст", keyboard_type=ft.KeyboardType.NUMBER)
phone_input = ft.TextField(label="Телефон", hint_text="Введите ваш телефон", keyboard_type=ft.KeyboardType.PHONE)
email_input = ft.TextField(label="Электронная почта", hint_text="Введите вашу электронную почту",
keyboard_type=ft.KeyboardType.EMAIL)
password_input = ft.TextField(label="Пароль", hint_text="Введите ваш пароль", password=True,
can_reveal_password=True)
# Обработчик кнопки регистрации
def register_clicked(e):
first_name = first_name_input.value.strip()
last_name = last_name_input.value.strip()
middle_name = middle_name_input.value.strip()
age = int(age_input.value.strip())
phone = phone_input.value.strip()
email = email_input.value.strip()
password = password_input.value
user_id = registration(first_name, last_name, middle_name, age, phone, email, password)
page.client_storage.set("user_id", user_id)
page.go("/")
# Кнопка для регистрации
register_button = ft.ElevatedButton("Зарегистрироваться", on_click=register_clicked)
# Добавление элементов на страницу
back_button = ft.TextButton("Назад", on_click=back_clicked)
page.add(
ft.Column(
[
ft.Text("Регистрация", size=24, weight=ft.FontWeight.BOLD),
first_name_input,
last_name_input,
middle_name_input,
age_input,
phone_input,
email_input,
password_input,
register_button,
back_button
],
alignment=ft.MainAxisAlignment.START,
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
spacing=10,
width=600
)
)
def main(page: ft.Page): def main(page: ft.Page):
print("Rerender!") def route_change(e):
route = e.route
page.clean()
if route == "/login":
login(page)
elif route == "/register":
register(page)
elif route == "/profile":
profile(page)
else:
root(page)
page.update()
sections = get_sections() page.title = "Весёлые кубики"
page.on_route_change = route_change
sections_buttons = [ft.Button(section) for section in sections] route_change(RouteChangeEvent(page.route))
page.add(
ft.SafeArea(
ft.Column (
[ft.Row(sections_buttons, alignment=ft.MainAxisAlignment.CENTER)],
alignment=ft.MainAxisAlignment.START
)
)
)
ft.app(main) ft.app(main)

View File

11
UI/src/models/game.py Normal file
View File

@ -0,0 +1,11 @@
class Game:
def __init__(self, board_game_id, name, genre, publisher, price, count = None):
self.board_game_id = board_game_id
self.name = name
self.genre = genre
self.publisher = publisher
self.price = price
self.count = count
def __str__(self):
return f"{self.name}\nЖанр: {self.genre}\nИздатель: {self.publisher}\n{self.price}" + (f"\n{self.count} шт." if self.count is not None else "")

10
UI/src/models/order.py Normal file
View File

@ -0,0 +1,10 @@
class Order:
def __init__(self, id, pick_up, date, cost, games = []):
self.id = id
self.pick_up = pick_up
self.date = str(date)[:-7]
self.cost = cost
self.games = games
def __str__(self):
return f"{self.pick_up}\n{self.date}\n{self.cost}"

9
UI/src/models/user.py Normal file
View File

@ -0,0 +1,9 @@
class User:
def __init__(self, id, surname, forename, patronymic, age, phone_number, email):
self.id = id
self.surname = surname
self.forename = forename
self.patronymic = patronymic
self.age = age
self.phone_number = phone_number
self.email = email

0
UI/src/pages/__init__.py Normal file
View File

61
UI/src/pages/profile.py Normal file
View File

@ -0,0 +1,61 @@
import flet as ft
from alembic.command import history
from requests.auth import get_user
from requests.clients import get_wishlist, get_cart, get_history
def profile(page: ft.Page):
def back_clicked(e):
page.go("/")
page.update()
def order_clicked(e):
pass
user = get_user(page.client_storage.get("user_id"))
wishlist = get_wishlist(user.id)
cart = get_cart(user.id)
history = get_history(user.id)
page.add(ft.Column([
ft.TextButton("Назад", on_click=back_clicked),
ft.Text(f"Здравствуйте, {user.forename} {user.patronymic}!", size=24),
ft.Text(f"Список желаний", size=20),
ft.Row([
ft.Column([
ft.Text(str(game)),
ft.Row([
ft.IconButton(
icon=ft.Icons.SHOPPING_CART
),
ft.IconButton(
icon=ft.Icons.DELETE
)
])
]) for game in wishlist
]),
ft.Text(f"Корзина", size=20),
ft.Row([
ft.Column([
ft.Text(str(game)),
ft.Row([
ft.IconButton(
icon=ft.Icons.DELETE
)
])
]) for game in cart
]),
ft.Button("Заказать"),
ft.Text(f"История заказов", size=20),
ft.Row([
ft.Column([
ft.Text(f"Заказ №{order.id}", size=20),
ft.Text(str(order)),
ft.Text(f"Состав:", size=18),
ft.Row([
ft.Text(str(game)) for game in order.games
])
]) for order in history
])
]))

45
UI/src/requests/auth.py Normal file
View File

@ -0,0 +1,45 @@
from models.user import User
from requests.connection import get_cursor
def login(email, password):
cursor = get_cursor()[0]
cursor.execute(f"select * from clients join auth using(client_id) where email='{email}' and password='{password}'")
res = cursor.fetchone()
if res is None:
return None
else:
return res[0]
def registration(first_name, last_name, middle_name, age, phone, email, password):
(cursor, conn) = get_cursor()
cursor.execute(f"""
insert into clients (forename, surname, patronymic, age, phone_number, email)
values ('{first_name}', '{last_name}', '{middle_name}', {age}, '{phone}', '{email}');
""")
conn.commit()
cursor.execute(f"select (client_id) from clients where email='{email}'")
user_id = cursor.fetchone()[0]
cursor.execute(f"""
insert into auth (client_id, password)
values ('{user_id}', '{password}');
""")
cursor.execute(f"""
insert into bags (client_id)
values ('{user_id}');
""")
cursor.execute(f"""
insert into wishlists (client_id)
values ('{user_id}');
""")
conn.commit()
return user_id
def get_user(user_id):
(cursor, conn) = get_cursor()
cursor.execute(f"select * from clients where client_id={user_id}")
res = cursor.fetchone()
return User(res[0], res[1], res[2], res[3], res[4], res[5], res[6])

View File

@ -1,9 +1,34 @@
import psycopg2
from models.game import Game
from requests.connection import get_cursor from requests.connection import get_cursor
def get_sections(): def get_sections():
cursor = get_cursor() cursor = get_cursor()[0]
cursor.execute("select name from sections") cursor.execute("select name from sections")
return [elem[0] for elem in cursor.fetchall()] return [elem[0] for elem in cursor.fetchall()]
def get_games(section = None):
cursor = get_cursor()[0]
cursor.execute("SET lc_monetary TO 'ru_RU.UTF-8'")
if section is None:
cursor.execute("""
select *
from board_games join sections_board_games using(board_game_id) join sections using(section_id)
""")
else:
cursor.execute(f"""
select *
from board_games join sections_board_games using(board_game_id) join sections using(section_id)
where sections.name='{section}'
""")
res = [Game(game[1], game[2], game[3], game[4], game[5]) for game in cursor.fetchall()]
return res

View File

@ -0,0 +1,67 @@
from models.game import Game
from models.order import Order
from requests.connection import get_cursor
def get_wishlist(user_id):
(cursor, conn) = get_cursor()
cursor.execute("SET lc_monetary TO 'ru_RU.UTF-8'")
cursor.execute(f"""
select *
from wishlists_games join wishlists using(wishlist_id) join board_games using(board_game_id)
where client_id = {user_id}
""")
res = cursor.fetchall()
parsed = []
for game in res:
parsed.append(Game(game[0], game[3], game[4], game[5], game[6]))
return parsed
def get_cart(user_id):
(cursor, conn) = get_cursor()
cursor.execute("SET lc_monetary TO 'ru_RU.UTF-8'")
cursor.execute(f"""
select *
from bags_board_games join bags using(bag_id) join board_games using(board_game_id)
where client_id = {user_id}
""")
res = cursor.fetchall()
parsed = []
for game in res:
parsed.append(Game(game[0], game[4], game[5], game[6], game[7], game[2]))
return parsed
def get_history(user_id):
(cursor, conn) = get_cursor()
cursor.execute("SET lc_monetary TO 'ru_RU.UTF-8'")
cursor.execute(f"select * from orders where client_id = {user_id}")
res = [Order(o[0], o[2], o[3], o[4]) for o in cursor.fetchall()]
for order in res:
cursor.execute("SET lc_monetary TO 'ru_RU.UTF-8'")
cursor.execute(f"""
select *
from orders_games join board_games using(board_game_id)
where order_id = {order.id}
""")
games = [Game(g[0], g[4], g[5], g[6], g[7], g[3]) for g in cursor.fetchall()]
order.games = games
return res

View File

@ -8,4 +8,4 @@ def get_cursor():
if connection is None: if connection is None:
connection = psycopg2.connect(database="postgres", user="postgres", password="not_admin", host="serafimdev.com", port=6000) connection = psycopg2.connect(database="postgres", user="postgres", password="not_admin", host="serafimdev.com", port=6000)
return connection.cursor() return (connection.cursor(), connection)

View File

@ -8,4 +8,3 @@ services:
- 6000:5432 - 6000:5432
volumes: volumes:
- ./postgres-data:/var/lib/postgresql/data - ./postgres-data:/var/lib/postgresql/data

View File

@ -105,8 +105,8 @@ INSERT INTO bags_board_games (bag_id, board_game_id, count) VALUES
INSERT INTO wishlists (client_id) VALUES INSERT INTO wishlists (client_id) VALUES
(1), (1),
(1), (2),
(1), (3),
(4), (4),
(5), (5),
(6), (6),