Compare commits
5 Commits
49c6b54617
...
e461869d7c
Author | SHA1 | Date |
---|---|---|
DEsimas | e461869d7c | |
DEsimas | 6db6dba13d | |
DEsimas | e2c4bf6563 | |
DEsimas | 7bb4ac17df | |
DEsimas | b47278beb6 |
|
@ -0,0 +1,2 @@
|
||||||
|
flet==0.25.2
|
||||||
|
psycopg2==2.9.10
|
278
UI/src/main.py
278
UI/src/main.py
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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 "")
|
|
@ -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}"
|
|
@ -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,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
|
||||||
|
])
|
||||||
|
]))
|
|
@ -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])
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
|
|
@ -8,4 +8,3 @@ services:
|
||||||
- 6000:5432
|
- 6000:5432
|
||||||
volumes:
|
volumes:
|
||||||
- ./postgres-data:/var/lib/postgresql/data
|
- ./postgres-data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in New Issue