add V2
BIN
UI_V2/.DS_Store
vendored
Normal file
3
UI_V2/.env
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
SUPERUSER_EMAIL=macamete.robert@gmail.com
|
||||||
|
SUPERUSER_PASSWORD=Inteligent1_eu
|
||||||
|
SUPERUSER_ROLE=admin
|
||||||
31
UI_V2/Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Slim Python image
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# System deps for pip & curl (for healthcheck)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Ensure persistent folders exist
|
||||||
|
RUN mkdir -p /app/instance /app/assets
|
||||||
|
|
||||||
|
# Default port
|
||||||
|
ENV FLET_PORT=8080
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -fsS http://127.0.0.1:8080/ || exit 1
|
||||||
|
|
||||||
|
# Run entrypoint to create superuser and start the app
|
||||||
|
ENTRYPOINT ["sh", "-c", "python create_super_user.py && python main.py"]
|
||||||
BIN
UI_V2/admin/__pycache__/banner.cpython-313.pyc
Normal file
BIN
UI_V2/admin/__pycache__/category.cpython-313.pyc
Normal file
BIN
UI_V2/admin/__pycache__/clients.cpython-313.pyc
Normal file
BIN
UI_V2/admin/__pycache__/dashboard.cpython-313.pyc
Normal file
BIN
UI_V2/admin/__pycache__/orders.cpython-313.pyc
Normal file
BIN
UI_V2/admin/__pycache__/products.cpython-313.pyc
Normal file
88
UI_V2/admin/banner.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import flet as ft
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
class Banner:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
|
||||||
|
self.banner_image = ft.Image(
|
||||||
|
src='images/banner_placeholder.png',
|
||||||
|
height=350,
|
||||||
|
width=1000,
|
||||||
|
fit=ft.ImageFit.COVER
|
||||||
|
)
|
||||||
|
|
||||||
|
self.file_dialog = ft.FilePicker(
|
||||||
|
on_result=self.on_file_picker_result,
|
||||||
|
on_upload=self.on_upload_progress
|
||||||
|
)
|
||||||
|
self.page.overlay.append(self.file_dialog)
|
||||||
|
self.page.update() # Required to register the FilePicker control
|
||||||
|
|
||||||
|
self.uploaded_files = []
|
||||||
|
|
||||||
|
def open_file_picker(self, e=None):
|
||||||
|
self.file_dialog.pick_files(
|
||||||
|
allow_multiple=False,
|
||||||
|
allowed_extensions=["png", "jpg", "jpeg"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_file_picker_result(self, e: ft.FilePickerResultEvent):
|
||||||
|
if e.files:
|
||||||
|
file = e.files[0]
|
||||||
|
file_name = file.name
|
||||||
|
upload_url = self.page.get_upload_url(file_name, 600)
|
||||||
|
|
||||||
|
print(f"Uploading {file_name} to {upload_url}")
|
||||||
|
|
||||||
|
upload_task = ft.FilePickerUploadFile(
|
||||||
|
name=file.name,
|
||||||
|
upload_url=upload_url
|
||||||
|
)
|
||||||
|
self.file_dialog.upload([upload_task])
|
||||||
|
|
||||||
|
def on_upload_progress(self, e: ft.FilePickerUploadEvent):
|
||||||
|
if e.progress == 1:
|
||||||
|
print(f"Upload complete: {e.file_name}")
|
||||||
|
|
||||||
|
base_path = os.getcwd() # <-- The correct root path
|
||||||
|
uploads_path = os.path.join(base_path, "uploads")
|
||||||
|
assets_path = os.path.join(base_path, "assets", "images")
|
||||||
|
os.makedirs(assets_path, exist_ok=True)
|
||||||
|
|
||||||
|
source_file = os.path.join(uploads_path, e.file_name)
|
||||||
|
destination_file2 = os.path.join(assets_path, e.file_name)
|
||||||
|
destination_file = os.path.join(assets_path, 'banner.png')
|
||||||
|
|
||||||
|
if not os.path.exists(source_file):
|
||||||
|
print(f"❌ File not found: {source_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.copy(source_file, destination_file2)
|
||||||
|
shutil.move(source_file, destination_file)
|
||||||
|
print(f"✅ File moved: {source_file} → {destination_file}")
|
||||||
|
self.banner_image.src = f'images/{e.file_name}'
|
||||||
|
self.banner_image.update()
|
||||||
|
self.page.update()
|
||||||
|
self.foto = e.file_name
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"❌ Error moving file: {ex}")
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.banner_image,
|
||||||
|
ft.Button(
|
||||||
|
"Adauga imagine",
|
||||||
|
on_click=self.open_file_picker,
|
||||||
|
icon=ft.Icons.UPLOAD
|
||||||
|
)
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
242
UI_V2/admin/category.py
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.categories import Categories
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
class Category:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.categories_manager = Categories()
|
||||||
|
self.all_cateogies = self.categories_manager.get_categories()
|
||||||
|
self.list_of_categories = ft.ListView(
|
||||||
|
controls=self.create_list(self.all_cateogies, self.on_edit_btn_click, self.on_delete_btn_click),
|
||||||
|
spacing=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
self.foto = None
|
||||||
|
self.edit_id = None
|
||||||
|
|
||||||
|
self.file_dialog = ft.FilePicker(
|
||||||
|
on_result=self.on_file_picker_result,
|
||||||
|
on_upload=self.on_upload_progress
|
||||||
|
)
|
||||||
|
self.page.overlay.append(self.file_dialog)
|
||||||
|
self.page.update() # Required to register the FilePicker control
|
||||||
|
|
||||||
|
self.uploaded_files = []
|
||||||
|
|
||||||
|
self.category_image = ft.Image(
|
||||||
|
width=150,
|
||||||
|
height=150,
|
||||||
|
src='images/placeholder.png',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.category_name = ft.TextField(label="Denumire")
|
||||||
|
|
||||||
|
self.add_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Categorie"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.category_image,
|
||||||
|
ft.Button("Incarca", icon=ft.Icons.UPLOAD, on_click=self.open_file_picker)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
self.category_name
|
||||||
|
],
|
||||||
|
height=200,
|
||||||
|
width=400
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Salveaza",
|
||||||
|
on_click=self.on_save_btn_click,
|
||||||
|
icon=ft.Icons.SAVE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Anuleaza",
|
||||||
|
on_click=self.on_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.delete_item_id = None
|
||||||
|
|
||||||
|
self.confirm_delete_alert = ft.AlertDialog(
|
||||||
|
title=ft.Text("Confirmati?"),
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Da",
|
||||||
|
on_click=self.on_delete_product_click,
|
||||||
|
icon=ft.Icons.DELETE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Nu",
|
||||||
|
on_click=self.on_delete_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_file_picker_result(self, e: ft.FilePickerResultEvent):
|
||||||
|
if e.files:
|
||||||
|
file = e.files[0]
|
||||||
|
file_name = file.name
|
||||||
|
upload_url = self.page.get_upload_url(file_name, 600)
|
||||||
|
|
||||||
|
print(f"Uploading {file_name} to {upload_url}")
|
||||||
|
|
||||||
|
upload_task = ft.FilePickerUploadFile(
|
||||||
|
name=file.name,
|
||||||
|
upload_url=upload_url
|
||||||
|
)
|
||||||
|
self.file_dialog.upload([upload_task])
|
||||||
|
|
||||||
|
def open_file_picker(self, e=None):
|
||||||
|
self.file_dialog.pick_files(
|
||||||
|
allow_multiple=False,
|
||||||
|
allowed_extensions=["png", "jpg", "jpeg"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_upload_progress(self, e: ft.FilePickerUploadEvent):
|
||||||
|
if e.progress == 1:
|
||||||
|
print(f"Upload complete: {e.file_name}")
|
||||||
|
|
||||||
|
# Resolve paths relative to the UI folder (two levels up from this file)
|
||||||
|
ui_root = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
uploads_path = os.path.join(ui_root, "uploads")
|
||||||
|
assets_path = os.path.join(ui_root, "assets", "images")
|
||||||
|
os.makedirs(assets_path, exist_ok=True)
|
||||||
|
|
||||||
|
source_file = os.path.join(uploads_path, e.file_name)
|
||||||
|
destination_file = os.path.join(assets_path, e.file_name)
|
||||||
|
|
||||||
|
if not os.path.exists(source_file):
|
||||||
|
print(f"❌ File not found: {source_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.move(source_file, destination_file)
|
||||||
|
print(f"✅ File moved: {source_file} → {destination_file}")
|
||||||
|
self.category_image.src = f'images/{e.file_name}'
|
||||||
|
self.category_image.update()
|
||||||
|
self.page.update()
|
||||||
|
self.foto = e.file_name
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"❌ Error moving file: {ex}")
|
||||||
|
|
||||||
|
def on_add_btn_click(self, e):
|
||||||
|
self.page.open(self.add_dialog)
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
if self.category_image.src == 'images/placeholder.png' or self.category_name.value == '':
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.edit_id is None:
|
||||||
|
self.categories_manager.add(self.category_name.value, self.category_image.src)
|
||||||
|
else:
|
||||||
|
self.categories_manager.update(self.category_name.value, self.category_image.src, self.edit_id )
|
||||||
|
self.edit_id = None
|
||||||
|
|
||||||
|
self.page.close(self.add_dialog)
|
||||||
|
self.category_image.src = 'images/placeholder.png'
|
||||||
|
self.category_image.update()
|
||||||
|
self.category_name.value = ''
|
||||||
|
self.category_name.update()
|
||||||
|
self.update_list()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.add_dialog)
|
||||||
|
self.category_image.src = 'images/placeholder.png'
|
||||||
|
self.category_image.update()
|
||||||
|
self.category_name.value = ''
|
||||||
|
self.category_name.update()
|
||||||
|
|
||||||
|
def create_list(self, items, on_click_handler, on_click_handler2):
|
||||||
|
"""Helper to create list items for a column."""
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.ARROW_RIGHT, size=20),
|
||||||
|
ft.Text(value=item['name'])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.EDIT,
|
||||||
|
on_click=lambda e, id=item: on_click_handler(id),
|
||||||
|
),
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.DELETE,
|
||||||
|
on_click=lambda e, id=item['id']: on_click_handler2(id),
|
||||||
|
icon_color=ft.Colors.RED,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def on_edit_btn_click(self, item):
|
||||||
|
self.edit_id = item['id']
|
||||||
|
self.category_name.value = item['name']
|
||||||
|
self.category_image.src = item['image']
|
||||||
|
self.page.open(self.add_dialog)
|
||||||
|
|
||||||
|
def on_delete_btn_click(self, id):
|
||||||
|
self.delete_item_id = id
|
||||||
|
self.page.open(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def on_delete_product_click(self, e):
|
||||||
|
self.categories_manager.delete(self.delete_item_id )
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
print('Update list')
|
||||||
|
self.update_list()
|
||||||
|
|
||||||
|
def on_delete_cancel_btn_click(self, e):
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def update_list(self):
|
||||||
|
self.all_cateogies = self.categories_manager.get_categories()
|
||||||
|
self.list_of_categories.controls.clear()
|
||||||
|
self.list_of_categories.controls = self.create_list(self.all_cateogies, self.on_edit_btn_click, self.on_delete_btn_click)
|
||||||
|
self.list_of_categories.update()
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Categori", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_btn_click),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
self.list_of_categories
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
352
UI_V2/admin/clients.py
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.users import Users
|
||||||
|
from dbActions.company import Company
|
||||||
|
|
||||||
|
class Clients:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.user_manager = Users()
|
||||||
|
self.company_manager = Company()
|
||||||
|
|
||||||
|
self.user_name = ft.TextField(label="Nume si Prenume")
|
||||||
|
self.email = ft.TextField(label="E-mail")
|
||||||
|
self.phone = ft.TextField(label="Telefon")
|
||||||
|
self.address = ft.TextField(label="Adresa", multiline=True, min_lines=3, max_lines=5)
|
||||||
|
self.company_name = ft.TextField(label="Denumire firma")
|
||||||
|
self.vat = ft.TextField(label="CUI")
|
||||||
|
self.register_number = ft.TextField(label="Numar registru comert")
|
||||||
|
self.company_address = ft.TextField(label="Sediu", multiline=True, min_lines=3, max_lines=5)
|
||||||
|
self.company_placeholder = ft.Column()
|
||||||
|
self.second_address_placeholder = ft.Column()
|
||||||
|
self.second_address = ft.TextField(
|
||||||
|
label="Adresa de livrare",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client_column = ft.Column(
|
||||||
|
[
|
||||||
|
self.user_name,
|
||||||
|
self.email,
|
||||||
|
self.phone,
|
||||||
|
self.address,
|
||||||
|
ft.Button("Persoana Juridica?", on_click=self.on_is_comopany_btn_click),
|
||||||
|
self.company_placeholder,
|
||||||
|
ft.Button("Adresa de livrare difera de adresa de domiciliu?", on_click=self.on_second_address),
|
||||||
|
self.second_address_placeholder
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
width=400,
|
||||||
|
auto_scroll=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_user_dialog = ft.AlertDialog(
|
||||||
|
title="Client",
|
||||||
|
content=self.client_column,
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Salveaza",
|
||||||
|
on_click=self.on_save_btn_click,
|
||||||
|
icon=ft.Icons.SAVE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Anuleaza",
|
||||||
|
on_click=self.on_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.is_company = False
|
||||||
|
self.is_second_address = False
|
||||||
|
|
||||||
|
self.all_clients = self.user_manager.get_all()
|
||||||
|
self.list_of_clients = ft.ListView(
|
||||||
|
controls=self.create_list(self.all_clients, self.on_edit_btn_click, self.on_delete_btn_click, self.on_view_btn_click),
|
||||||
|
spacing=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.view_name = ft.Text()
|
||||||
|
self.view_email = ft.Text()
|
||||||
|
self.view_phone = ft.Text()
|
||||||
|
self.view_address = ft.Text()
|
||||||
|
self.view_company_placeholder = ft.Column()
|
||||||
|
self.view_second_address_placeholder = ft.Column()
|
||||||
|
|
||||||
|
self.view_company_name = ft.Text()
|
||||||
|
self.view_company_vat = ft.Text()
|
||||||
|
self.view_company_rn = ft.Text()
|
||||||
|
self.view_company_address = ft.Text()
|
||||||
|
self.view_second_address = ft.Text()
|
||||||
|
|
||||||
|
self.view_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Vizualizeaza"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Utilizator:", weight=ft.FontWeight.BOLD),
|
||||||
|
self.view_name,
|
||||||
|
self.view_email,
|
||||||
|
self.view_phone,
|
||||||
|
self.view_address,
|
||||||
|
self.view_company_placeholder,
|
||||||
|
self.view_second_address_placeholder
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
width=400
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.Button("Ok", on_click=self.on_ok_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.delete_item_id = None
|
||||||
|
|
||||||
|
self.confirm_delete_alert = ft.AlertDialog(
|
||||||
|
title=ft.Text("Confirmati?"),
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Da",
|
||||||
|
on_click=self.on_delete_client_click,
|
||||||
|
icon=ft.Icons.DELETE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Nu",
|
||||||
|
on_click=self.on_delete_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.edit_id = None
|
||||||
|
|
||||||
|
def on_ok_btn_click(self, e):
|
||||||
|
self.page.close(self.view_dialog)
|
||||||
|
self.view_name.value = ''
|
||||||
|
self.view_email.value = ''
|
||||||
|
self.view_phone.value = ''
|
||||||
|
self.view_address.value = ''
|
||||||
|
self.view_company_placeholder.controls.clear()
|
||||||
|
self.view_second_address_placeholder.controls.clear()
|
||||||
|
|
||||||
|
def on_edit_btn_click(self, item):
|
||||||
|
self.edit_id = item['id']
|
||||||
|
self.user_name.value = item['name']
|
||||||
|
self.email.value = item['email']
|
||||||
|
self.email.disabled = True
|
||||||
|
self.phone.value = item['phone']
|
||||||
|
self.address.value = item['address'].split("~")[0]
|
||||||
|
company = self.company_manager.get_company(item['id'])
|
||||||
|
if company:
|
||||||
|
self.company_name.value = company['name']
|
||||||
|
self.company_placeholder.controls.append(self.company_name)
|
||||||
|
self.vat.value = company['vat']
|
||||||
|
self.company_placeholder.controls.append(self.vat)
|
||||||
|
self.register_number.value = company['register_number']
|
||||||
|
self.company_placeholder.controls.append(self.register_number)
|
||||||
|
self.company_address.value = company['address']
|
||||||
|
self.company_placeholder.controls.append(self.company_address)
|
||||||
|
if len(item['address'].split("~")) > 1:
|
||||||
|
self.second_address.value = item['address'].split("~")[1]
|
||||||
|
self.second_address_placeholder.controls.append(self.second_address)
|
||||||
|
self.page.open(self.add_user_dialog)
|
||||||
|
|
||||||
|
def on_delete_btn_click(self, id):
|
||||||
|
self.delete_item_id = id
|
||||||
|
self.page.open(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def on_delete_client_click(self, e):
|
||||||
|
self.user_manager.delete(self.delete_item_id)
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
self.all_clients = self.user_manager.get_all()
|
||||||
|
self.list_of_clients.controls = self.create_list(self.all_clients, self.on_edit_btn_click, self.on_delete_btn_click, self.on_view_btn_click)
|
||||||
|
self.list_of_clients.update()
|
||||||
|
|
||||||
|
def on_delete_cancel_btn_click(self, e):
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def on_view_btn_click(self, item):
|
||||||
|
self.view_name.value = f"Nume: {item['name']}"
|
||||||
|
self.view_email.value = f"E-mail: {item['email']}"
|
||||||
|
self.view_phone.value = f"Telefon: {item['phone']}"
|
||||||
|
self.view_address.value = f"Adresa: {item['address'].split("~")[0]}"
|
||||||
|
company = self.company_manager.get_company(item['id'])
|
||||||
|
if company:
|
||||||
|
self.view_company_placeholder.controls.append(ft.Text(""))
|
||||||
|
self.view_company_placeholder.controls.append(ft.Text("Date Companie:", weight=ft.FontWeight.BOLD))
|
||||||
|
self.view_company_name.value = f"Denumire: {company['name']}"
|
||||||
|
self.view_company_placeholder.controls.append(self.view_company_name)
|
||||||
|
self.view_company_vat.value = f"CUI: {company['vat']}"
|
||||||
|
self.view_company_placeholder.controls.append(self.view_company_vat)
|
||||||
|
self.view_company_rn.value = f"Registru comertului: {company['register_number']}"
|
||||||
|
self.view_company_placeholder.controls.append(self.view_company_rn)
|
||||||
|
self.view_company_address.value = f"Sediu: {company['address']}"
|
||||||
|
self.view_company_placeholder.controls.append(self.view_company_address)
|
||||||
|
if len(item['address'].split("~")) > 1:
|
||||||
|
self.view_second_address_placeholder.controls.append(ft.Text(""))
|
||||||
|
self.view_second_address_placeholder.controls.append(ft.Text("Adresa Livrare:", weight=ft.FontWeight.BOLD))
|
||||||
|
self.view_second_address.value = f"Adresa: {item['address'].split("~")[1]}"
|
||||||
|
self.view_second_address_placeholder.controls.append(self.view_second_address)
|
||||||
|
self.page.open(self.view_dialog)
|
||||||
|
|
||||||
|
def create_list(self, items, on_click_handler, on_click_handler2, on_click_handler3):
|
||||||
|
"""Helper to create list items for a column."""
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(value=item['name'], weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(value=item['email'], size=12)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.PREVIEW,
|
||||||
|
on_click=lambda e, id=item: on_click_handler3(id),
|
||||||
|
),
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.EDIT,
|
||||||
|
on_click=lambda e, id=item: on_click_handler(id),
|
||||||
|
),
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.DELETE,
|
||||||
|
on_click=lambda e, id=item['id']: on_click_handler2(id),
|
||||||
|
icon_color=ft.Colors.RED,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
user_name = self.user_name.value
|
||||||
|
user_email = self.email.value
|
||||||
|
user_phone = self.phone.value
|
||||||
|
user_address = self.address.value
|
||||||
|
company = {}
|
||||||
|
|
||||||
|
company['name'] = self.company_name.value
|
||||||
|
company['vat'] = self.vat.value
|
||||||
|
company['register_number'] = self.register_number.value
|
||||||
|
company['address'] = self.company_address.value
|
||||||
|
delivery_address = self.second_address.value
|
||||||
|
address = f'{user_address} ~ {delivery_address}'
|
||||||
|
if self.edit_id:
|
||||||
|
if len(delivery_address) == 0:
|
||||||
|
self.user_manager.update_user_data(user_name, user_phone, user_address, self.edit_id)
|
||||||
|
else:
|
||||||
|
self.user_manager.update_user_data(user_name, user_phone, address, self.edit_id)
|
||||||
|
exising_company = self.company_manager.get_company(self.edit_id)
|
||||||
|
if exising_company:
|
||||||
|
company['user_id'] = self.edit_id
|
||||||
|
company['id'] = exising_company['id']
|
||||||
|
self.company_manager.update_company(company)
|
||||||
|
else:
|
||||||
|
|
||||||
|
user_id = self.user_manager.invite_user(user_email, user_name, user_phone, user_address)
|
||||||
|
print(user_id)
|
||||||
|
if user_id:
|
||||||
|
company['user_id'] = user_id
|
||||||
|
if self.is_company:
|
||||||
|
self.company_manager.add_company(company)
|
||||||
|
if self.is_second_address:
|
||||||
|
self.user_manager.update_user_data(user_name, user_phone, address, user_id)
|
||||||
|
self.clear_fileds()
|
||||||
|
self.page.close(self.add_user_dialog)
|
||||||
|
self.all_clients = self.user_manager.get_all()
|
||||||
|
self.list_of_clients.controls = self.create_list(self.all_clients, self.on_edit_btn_click, self.on_delete_btn_click, self.on_view_btn_click)
|
||||||
|
self.list_of_clients.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.add_user_dialog)
|
||||||
|
self.clear_fileds()
|
||||||
|
|
||||||
|
def clear_fileds(self):
|
||||||
|
self.user_name.value = ''
|
||||||
|
self.user_name.update()
|
||||||
|
self.email.value = ''
|
||||||
|
self.email.update()
|
||||||
|
self.phone.value = ''
|
||||||
|
self.phone.update()
|
||||||
|
self.address.value = ''
|
||||||
|
self.address.update()
|
||||||
|
try:
|
||||||
|
self.company_name.value = ''
|
||||||
|
self.company_name.update()
|
||||||
|
self.vat.value = ''
|
||||||
|
self.vat.update()
|
||||||
|
self.register_number.value = ''
|
||||||
|
self.register_number.update()
|
||||||
|
self.company_address.value = ''
|
||||||
|
self.company_address.update()
|
||||||
|
except Exception as e:
|
||||||
|
print(f'No company, Error: {e}')
|
||||||
|
try:
|
||||||
|
self.second_address.value = ''
|
||||||
|
self.second_address.update()
|
||||||
|
except Exception as e:
|
||||||
|
print(f'No second address, Error: {e}')
|
||||||
|
self.company_placeholder.controls.clear()
|
||||||
|
self.company_placeholder.update()
|
||||||
|
self.second_address_placeholder.controls.clear()
|
||||||
|
self.second_address_placeholder.update()
|
||||||
|
|
||||||
|
def on_is_comopany_btn_click(self, e):
|
||||||
|
self.is_company = True
|
||||||
|
self.company_placeholder.controls.append(ft.Text("Date firma"))
|
||||||
|
self.company_placeholder.controls.append(self.company_name)
|
||||||
|
self.company_placeholder.controls.append(self.vat)
|
||||||
|
self.company_placeholder.controls.append(self.register_number)
|
||||||
|
self.company_placeholder.controls.append(self.company_address)
|
||||||
|
self.company_placeholder.update()
|
||||||
|
|
||||||
|
def on_second_address(self, e):
|
||||||
|
self.is_second_address = True
|
||||||
|
self.second_address_placeholder.controls.append(ft.Text("Adresa de livrare"))
|
||||||
|
self.second_address_placeholder.controls.append(
|
||||||
|
self.second_address
|
||||||
|
)
|
||||||
|
self.second_address_placeholder.update()
|
||||||
|
|
||||||
|
def on_add_btn_click(self, e):
|
||||||
|
self.page.open(self.add_user_dialog)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Clienti",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=18
|
||||||
|
),
|
||||||
|
ft.Button(
|
||||||
|
icon=ft.Icons.ADD,
|
||||||
|
text = "Invita",
|
||||||
|
on_click= self.on_add_btn_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
self.list_of_clients
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
128
UI_V2/admin/dashboard.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import flet as ft
|
||||||
|
from admin.category import Category
|
||||||
|
from admin.products import ProductsPage
|
||||||
|
from admin.banner import Banner
|
||||||
|
from admin.orders import OrdersPage
|
||||||
|
from admin.clients import Clients
|
||||||
|
|
||||||
|
class Dashboard:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.category = Category(self.page)
|
||||||
|
self.placeholder = ft.Container(
|
||||||
|
content=self.category.build(),
|
||||||
|
padding=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
self.rail = ft.NavigationRail(
|
||||||
|
selected_index=0,
|
||||||
|
label_type=ft.NavigationRailLabelType.ALL,
|
||||||
|
min_width=100,
|
||||||
|
#min_extended_width=400,
|
||||||
|
leading=ft.Image('images/tainagustului.png', width=80),
|
||||||
|
group_alignment=-0.9,
|
||||||
|
destinations=[
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.CATEGORY_OUTLINED,
|
||||||
|
selected_icon=ft.Icons.CATEGORY_ROUNDED,
|
||||||
|
label="Categori",
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.ADD_BOX_OUTLINED,
|
||||||
|
selected_icon=ft.Icons.ADD_BOX_ROUNDED,
|
||||||
|
label="Produse",
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icon(ft.Icons.MANAGE_ACCOUNTS_OUTLINED),
|
||||||
|
selected_icon=ft.Icon(ft.Icons.MANAGE_ACCOUNTS_ROUNDED),
|
||||||
|
label="Clienti",
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.SHOPPING_CART_OUTLINED,
|
||||||
|
selected_icon=ft.Icon(ft.Icons.SHOPPING_CART_ROUNDED),
|
||||||
|
label_content=ft.Text("Comenzi"),
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.SHOPIFY_OUTLINED,
|
||||||
|
selected_icon=ft.Icon(ft.Icons.SHOPIFY_ROUNDED),
|
||||||
|
label_content=ft.Text("Magazin"),
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.IMAGE_OUTLINED,
|
||||||
|
selected_icon=ft.Icon(ft.Icons.IMAGE_ROUNDED),
|
||||||
|
label_content=ft.Text("Banner"),
|
||||||
|
),
|
||||||
|
ft.NavigationRailDestination(
|
||||||
|
icon=ft.Icons.LOGOUT_OUTLINED,
|
||||||
|
selected_icon=ft.Icon(ft.Icons.LOGOUT_ROUNDED),
|
||||||
|
label_content=ft.Text("Logout"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
on_change=lambda e: self.navigate_to(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
def navigate_to(self, e):
|
||||||
|
index = e.control.selected_index
|
||||||
|
match index:
|
||||||
|
case 0:
|
||||||
|
self.category = Category(self.page)
|
||||||
|
self.placeholder.content = self.category.build()
|
||||||
|
self.placeholder.update()
|
||||||
|
case 1:
|
||||||
|
self.products = ProductsPage(self.page)
|
||||||
|
self.placeholder.content = self.products.build()
|
||||||
|
self.placeholder.update()
|
||||||
|
case 2:
|
||||||
|
self.clients = Clients(self.page)
|
||||||
|
self.placeholder.content = self.clients.build()
|
||||||
|
self.placeholder.update()
|
||||||
|
case 3:
|
||||||
|
self.orders = OrdersPage(self.page)
|
||||||
|
self.placeholder.content = self.orders.build()
|
||||||
|
self.placeholder.update()
|
||||||
|
case 4:
|
||||||
|
self.page.launch_url('http://0.0.0.0:5555/')
|
||||||
|
case 5:
|
||||||
|
self.banner = Banner(self.page)
|
||||||
|
self.placeholder.content = self.banner.build()
|
||||||
|
self.placeholder.update()
|
||||||
|
case 6:
|
||||||
|
self.page.client_storage.clear()
|
||||||
|
self.page.session.clear()
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
if self.page.session.get("user") is None or self.page.session.get("user")['role'] != 'admin':
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"Nu aveti drepturi sa accesati pagina de admin!",
|
||||||
|
size=20,
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
text_align=ft.TextAlign.CENTER
|
||||||
|
),
|
||||||
|
ft.Text("Va rugam sa va autentificati cu un rol de admin",text_align=ft.TextAlign.CENTER)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.rail,
|
||||||
|
ft.VerticalDivider(width=1),
|
||||||
|
self.placeholder
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
#self.shop_page
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
374
UI_V2/admin/orders.py
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.orders import Orders
|
||||||
|
from dbActions.users import Users
|
||||||
|
from dbActions.products import Products
|
||||||
|
from helpers.default_user import DefaultUser
|
||||||
|
|
||||||
|
class OrdersPage:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.orders = Orders()
|
||||||
|
self.users = Users()
|
||||||
|
self.products = Products()
|
||||||
|
self.selected_user = None
|
||||||
|
self.selected_order = None
|
||||||
|
self.selected_order_products = None
|
||||||
|
self.original_status = None
|
||||||
|
self.customer_email = None
|
||||||
|
self.default_user = DefaultUser(self.page)
|
||||||
|
self.header = ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"Comenzi",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=18
|
||||||
|
),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START
|
||||||
|
)
|
||||||
|
self.all_orders = self.orders.get_orders()
|
||||||
|
self.all_orders = self.all_orders[::-1]
|
||||||
|
self.oll_orders_list = ft.ListView(
|
||||||
|
controls=self.create_list(self.all_orders, self.on_order_click),
|
||||||
|
spacing=10,
|
||||||
|
expand=3,
|
||||||
|
height=700
|
||||||
|
)
|
||||||
|
self.fileters = ft.RadioGroup(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Radio(value="on_hold", label="In asteptare"),
|
||||||
|
ft.Radio(value="new", label="Noua"),
|
||||||
|
ft.Radio(value="in_progress", label="In lucru"),
|
||||||
|
ft.Radio(value="completed", label="Complete"),
|
||||||
|
ft.Radio(value="all", label="Toate")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
on_change=self.on_filter_change
|
||||||
|
)
|
||||||
|
self.name = ft.Text()
|
||||||
|
self.email = ft.Text()
|
||||||
|
self.phone = ft.Text()
|
||||||
|
self.address = ft.Text()
|
||||||
|
self.total_pay = ft.Text(weight=ft.FontWeight.BOLD)
|
||||||
|
self.products_column = ft.Column([])
|
||||||
|
self.status = ft.Text()
|
||||||
|
self.buttons_state = ft.RadioGroup(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Radio(value="on_hold", label="In asteptare"),
|
||||||
|
ft.Radio(value="new", label="Noua"),
|
||||||
|
ft.Radio(value="in_progress", label="In lucru"),
|
||||||
|
ft.Radio(value="completed", label="Complete"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
on_change=self.on_radio_value_change
|
||||||
|
)
|
||||||
|
self.total_row = ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Total platit",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.total_pay
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
)
|
||||||
|
|
||||||
|
self.order_details = ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Detalii utilizator",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=18
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Nume si prenume",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.name
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="E-mail",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.email
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Telefon",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.phone
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Adresa",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.address
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value="Status",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
),
|
||||||
|
self.status,
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
self.total_row,
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Text(
|
||||||
|
value="Produse",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=18
|
||||||
|
),
|
||||||
|
self.products_column,
|
||||||
|
ft.Divider(),
|
||||||
|
self.buttons_state
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.order_details_placeholder = ft.Container(
|
||||||
|
expand=7,
|
||||||
|
padding=10,
|
||||||
|
border_radius=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.message_field = ft.TextField(
|
||||||
|
label="Scrie un mesaj clientului",
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
multiline=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.change_state_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Modifica statusul"),
|
||||||
|
content=self.message_field,
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Da",
|
||||||
|
on_click=self.on_change_state_btn_click
|
||||||
|
),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Nu",
|
||||||
|
on_click=self.on_cancel_state_btn_click,
|
||||||
|
bgcolor=ft.Colors.GREY
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_radio_value_change(self, e):
|
||||||
|
self.page.open(self.change_state_dialog)
|
||||||
|
|
||||||
|
def on_change_state_btn_click(self, e):
|
||||||
|
self.page.close(self.change_state_dialog)
|
||||||
|
status = self.buttons_state.value
|
||||||
|
self.orders.update_order_status(status, self.selected_order['id'])
|
||||||
|
print(status)
|
||||||
|
self.all_orders = self.orders.get_orders()
|
||||||
|
self.all_orders = self.all_orders[::-1]
|
||||||
|
self.oll_orders_list.controls.clear()
|
||||||
|
self.oll_orders_list.controls = self.create_list(self.all_orders, self.on_order_click)
|
||||||
|
self.oll_orders_list.update()
|
||||||
|
self.order_details_placeholder.content = None
|
||||||
|
self.order_details_placeholder.update()
|
||||||
|
#-------------------------------------------> TBI Send information email to customer
|
||||||
|
message = self.message_field.value
|
||||||
|
self.message_field.value = ''
|
||||||
|
self.message_field.update()
|
||||||
|
print(message)
|
||||||
|
email = self.customer_email
|
||||||
|
|
||||||
|
def on_cancel_state_btn_click(self, e):
|
||||||
|
self.buttons_state.value = self.original_status
|
||||||
|
self.buttons_state.update()
|
||||||
|
self.page.close(self.change_state_dialog)
|
||||||
|
|
||||||
|
def get_total_pay(self):
|
||||||
|
sume = 0
|
||||||
|
print (self.selected_order_products)
|
||||||
|
if self.selected_order_products:
|
||||||
|
for product in self.selected_order_products:
|
||||||
|
sume += (product['price'] - product['price']*product['discount']/100)
|
||||||
|
print(sume)
|
||||||
|
return sume
|
||||||
|
|
||||||
|
def on_order_click(self, item):
|
||||||
|
self.products_column.controls.clear()
|
||||||
|
self.order_details_placeholder.content = None
|
||||||
|
self.order_details_placeholder.content = self.order_details
|
||||||
|
self.order_details_placeholder.bgcolor=ft.Colors.BROWN_50
|
||||||
|
try:
|
||||||
|
self.order_details_placeholder.update()
|
||||||
|
except:
|
||||||
|
print('ERROR Unable to update the Order Details Placeholder')
|
||||||
|
print("order item", item)
|
||||||
|
self.selected_user = self.users.get(item['user_id'])
|
||||||
|
if self.selected_user == None:
|
||||||
|
self.selected_user = self.default_user.default_user
|
||||||
|
self.selected_order = item
|
||||||
|
products = self.orders.get_order_products(item['id'])
|
||||||
|
self.selected_order_products = []
|
||||||
|
if products:
|
||||||
|
for product in products:
|
||||||
|
prod = self.products.get(product['prdouct_id'])
|
||||||
|
print(prod)
|
||||||
|
if prod:
|
||||||
|
self.selected_order_products.append(prod)
|
||||||
|
|
||||||
|
self.name.value = self.selected_user['name'] if '@default.com' not in self.selected_user['email'] else 'Anonim user'
|
||||||
|
self.name.update()
|
||||||
|
self.email.value = self.selected_user['email']
|
||||||
|
self.email.update()
|
||||||
|
self.customer_email = self.selected_user['email']
|
||||||
|
self.address.value = self.selected_user['address'] if '@default.com' not in self.selected_user['email'] else 'Anonim user'
|
||||||
|
self.address.update()
|
||||||
|
self.phone.value = self.selected_user['phone'] if '@default.com' not in self.selected_user['email'] else 'Anonim user'
|
||||||
|
self.phone.update()
|
||||||
|
self.status.value = "Status"
|
||||||
|
self.status.update()
|
||||||
|
for product in self.selected_order_products:
|
||||||
|
name_label = ft.Text(
|
||||||
|
"Denumire produs",
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
name = ft.Text(product['name'])
|
||||||
|
name_row = ft.Row(
|
||||||
|
[
|
||||||
|
name_label,
|
||||||
|
name
|
||||||
|
]
|
||||||
|
)
|
||||||
|
image = ft.Image(
|
||||||
|
src=product['image'],
|
||||||
|
width=200,
|
||||||
|
height=200,
|
||||||
|
fit=ft.ImageFit.CONTAIN
|
||||||
|
)
|
||||||
|
quantity_label = ft.Text("Cantitate")
|
||||||
|
quantity = ft.Text(product['quantity'])
|
||||||
|
quantity_row = ft.Row(
|
||||||
|
[
|
||||||
|
quantity_label,
|
||||||
|
quantity
|
||||||
|
]
|
||||||
|
)
|
||||||
|
product_row = ft.Row(
|
||||||
|
[
|
||||||
|
image,
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
name_row,
|
||||||
|
quantity_row
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.products_column.controls.append(product_row)
|
||||||
|
print(self.products_column.controls)
|
||||||
|
self.total_pay.value = f"{self.get_total_pay()} Lei"
|
||||||
|
self.total_pay.update()
|
||||||
|
self.products_column.update()
|
||||||
|
|
||||||
|
self.buttons_state.value = item['status']
|
||||||
|
self.buttons_state.update()
|
||||||
|
self.original_status = item['status']
|
||||||
|
|
||||||
|
def on_filter_change(self, e):
|
||||||
|
print(e.data)
|
||||||
|
buffer = []
|
||||||
|
if e.data == 'all':
|
||||||
|
self.oll_orders_list.controls = self.create_list(self.all_orders, self.on_order_click)
|
||||||
|
self.oll_orders_list.update()
|
||||||
|
else:
|
||||||
|
for order in self.all_orders:
|
||||||
|
if order['status'] == e.data:
|
||||||
|
buffer.append(order)
|
||||||
|
self.oll_orders_list.controls = self.create_list(buffer, self.on_order_click)
|
||||||
|
self.oll_orders_list.update()
|
||||||
|
|
||||||
|
def get_status(self, status):
|
||||||
|
STATUS = {
|
||||||
|
'on_hold': "In asteptare",
|
||||||
|
'new': "Noua",
|
||||||
|
'in_progress': "In lucru",
|
||||||
|
"completed": "Completa"
|
||||||
|
}
|
||||||
|
return STATUS[status]
|
||||||
|
|
||||||
|
def create_list(self, items, on_click_handler):
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.ARROW_RIGHT, size=20),
|
||||||
|
ft.Text(value=item['id'])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value=self.get_status(item['status'])
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
ink=True,
|
||||||
|
on_click=lambda e, i=item: on_click_handler(i),
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.header,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(value="Filtreaza dupa"),
|
||||||
|
self.fileters
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.oll_orders_list,
|
||||||
|
self.order_details_placeholder
|
||||||
|
],
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
expand=True,
|
||||||
|
#scroll=ft.ScrollMode.ADAPTIVE
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
308
UI_V2/admin/products.py
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.categories import Categories
|
||||||
|
from dbActions.products import Products
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
class ProductsPage:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.category_manager = Categories()
|
||||||
|
self.product_manager = Products()
|
||||||
|
|
||||||
|
self.name = ft.TextField(label="Denumire")
|
||||||
|
self.description = ft.TextField(label="Descriere", multiline=True, min_lines=3, max_lines=5)
|
||||||
|
self.details = ft.TextField(label="Detalii", multiline=True, min_lines=3, max_lines=5)
|
||||||
|
self.price = ft.TextField(label="Pret")
|
||||||
|
self.discount = ft.TextField(label="Reducere (%)")
|
||||||
|
self.quantity = ft.TextField(label="Cantitate")
|
||||||
|
self.product_image = ft.Image(
|
||||||
|
width=150,
|
||||||
|
height=150,
|
||||||
|
src='images/placeholder.png',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.aviability = ft.Dropdown(
|
||||||
|
label='Disonibilitate',
|
||||||
|
options=[
|
||||||
|
ft.dropdown.Option(key='in_stock', text="In stoc"),
|
||||||
|
ft.dropdown.Option(key='in_provider_stock', text="In stoc la furnizor"),
|
||||||
|
ft.dropdown.Option(key='not_available', text="Indisponibil"),
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.category = ft.Dropdown(
|
||||||
|
label="Categorie",
|
||||||
|
options=self.get_categories(),
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
self.add_product_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Produs"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.product_image,
|
||||||
|
ft.Button("Incarca", icon=ft.Icons.UPLOAD, on_click=self.open_file_picker)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
self.name,
|
||||||
|
self.category,
|
||||||
|
self.description,
|
||||||
|
self.details,
|
||||||
|
self.price,
|
||||||
|
self.discount,
|
||||||
|
self.quantity,
|
||||||
|
self.aviability,
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
width=400
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Salveaza",
|
||||||
|
on_click=self.on_save_btn_click,
|
||||||
|
icon=ft.Icons.SAVE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Anuleaza",
|
||||||
|
on_click=self.on_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.foto = None
|
||||||
|
self.file_dialog = ft.FilePicker(
|
||||||
|
on_result=self.on_file_picker_result,
|
||||||
|
on_upload=self.on_upload_progress
|
||||||
|
)
|
||||||
|
self.page.overlay.append(self.file_dialog)
|
||||||
|
self.page.update() # Required to register the FilePicker control
|
||||||
|
|
||||||
|
self.uploaded_files = []
|
||||||
|
|
||||||
|
self._all_products = self.product_manager.get_all()
|
||||||
|
self.products_list = ft.ListView(
|
||||||
|
controls=self.create_list(self._all_products, self.edit_product, self.delete_product),
|
||||||
|
spacing=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.edit_item_id = None
|
||||||
|
self.delete_item_id = None
|
||||||
|
|
||||||
|
self.confirm_delete_alert = ft.AlertDialog(
|
||||||
|
title=ft.Text("Confirmati?"),
|
||||||
|
actions=[
|
||||||
|
ft.Button(
|
||||||
|
"Da",
|
||||||
|
on_click=self.on_delete_product_click,
|
||||||
|
icon=ft.Icons.DELETE,
|
||||||
|
),
|
||||||
|
ft.TextButton(
|
||||||
|
"Nu",
|
||||||
|
on_click=self.on_delete_cancel_btn_click,
|
||||||
|
icon=ft.Icons.CANCEL,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_file_picker_result(self, e: ft.FilePickerResultEvent):
|
||||||
|
if e.files:
|
||||||
|
file = e.files[0]
|
||||||
|
file_name = file.name
|
||||||
|
upload_url = self.page.get_upload_url(file_name, 600)
|
||||||
|
|
||||||
|
print(f"Uploading {file_name} to {upload_url}")
|
||||||
|
|
||||||
|
upload_task = ft.FilePickerUploadFile(
|
||||||
|
name=file.name,
|
||||||
|
upload_url=upload_url
|
||||||
|
)
|
||||||
|
self.file_dialog.upload([upload_task])
|
||||||
|
|
||||||
|
def open_file_picker(self, e=None):
|
||||||
|
self.file_dialog.pick_files(
|
||||||
|
allow_multiple=False,
|
||||||
|
allowed_extensions=["png", "jpg", "jpeg"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_upload_progress(self, e: ft.FilePickerUploadEvent):
|
||||||
|
if e.progress == 1:
|
||||||
|
print(f"Upload complete: {e.file_name}")
|
||||||
|
|
||||||
|
# Resolve paths relative to the UI folder (two levels up from this file)
|
||||||
|
ui_root = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
uploads_path = os.path.join(ui_root, "uploads")
|
||||||
|
assets_path = os.path.join(ui_root, "assets", "images")
|
||||||
|
os.makedirs(assets_path, exist_ok=True)
|
||||||
|
|
||||||
|
source_file = os.path.join(uploads_path, e.file_name)
|
||||||
|
destination_file = os.path.join(assets_path, e.file_name)
|
||||||
|
|
||||||
|
if not os.path.exists(source_file):
|
||||||
|
print(f"❌ File not found: {source_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.move(source_file, destination_file)
|
||||||
|
print(f"✅ File moved: {source_file} → {destination_file}")
|
||||||
|
self.product_image.src = f'images/{e.file_name}'
|
||||||
|
self.product_image.update()
|
||||||
|
self.page.update()
|
||||||
|
self.foto = e.file_name
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"❌ Error moving file: {ex}")
|
||||||
|
|
||||||
|
def on_add_btn_click(self, e):
|
||||||
|
self.page.open(self.add_product_dialog)
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
product = {
|
||||||
|
'name': self.name.value,
|
||||||
|
'description': self.description.value,
|
||||||
|
'details': self.details.value,
|
||||||
|
'price' : self.price.value,
|
||||||
|
'discount' : self.discount.value,
|
||||||
|
'quantity' : self.quantity.value,
|
||||||
|
'image' : self.product_image.src,
|
||||||
|
'aviability' : self.aviability.value,
|
||||||
|
'category_id' : self.category.value
|
||||||
|
}
|
||||||
|
print(product)
|
||||||
|
if self.edit_item_id == None:
|
||||||
|
self.product_manager.add(product)
|
||||||
|
else:
|
||||||
|
self.product_manager.update(product, self.edit_item_id)
|
||||||
|
self.edit_item_id = None
|
||||||
|
|
||||||
|
self.page.close(self.add_product_dialog)
|
||||||
|
self.set_popup_to_default()
|
||||||
|
|
||||||
|
print('Update list')
|
||||||
|
self._all_products = self.product_manager.get_all()
|
||||||
|
self.products_list.controls=self.create_list(self._all_products, self.edit_product, self.delete_product)
|
||||||
|
self.products_list.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.add_product_dialog)
|
||||||
|
self.set_popup_to_default()
|
||||||
|
if self.edit_item_id != None:
|
||||||
|
self.edit_item_id = None
|
||||||
|
|
||||||
|
def set_popup_to_default(self):
|
||||||
|
self.name.value = ''
|
||||||
|
self.name.update()
|
||||||
|
self.description.value = ''
|
||||||
|
self.description.update()
|
||||||
|
self.details.value = ''
|
||||||
|
self.details.update()
|
||||||
|
self.price.value = ''
|
||||||
|
self.price.update()
|
||||||
|
self.discount.value = ''
|
||||||
|
self.discount.update()
|
||||||
|
self.quantity.value = ''
|
||||||
|
self.quantity.update()
|
||||||
|
self.product_image.src = 'images/placeholder.png'
|
||||||
|
self.product_image.update()
|
||||||
|
self.aviability.value = None
|
||||||
|
self.aviability.update()
|
||||||
|
self.category.value = None
|
||||||
|
self.category.update()
|
||||||
|
|
||||||
|
|
||||||
|
def get_categories(self):
|
||||||
|
categories = self.category_manager.get_categories()
|
||||||
|
return [
|
||||||
|
ft.dropdown.Option(key=cat['id'], text=cat['name'])
|
||||||
|
for cat in categories
|
||||||
|
]
|
||||||
|
|
||||||
|
def create_list(self, items, on_click_handler, on_click_handler2):
|
||||||
|
"""Helper to create list items for a column."""
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(value=item['name'], weight=ft.FontWeight.BOLD, size=15),
|
||||||
|
ft.Text(value=f"Pret: {item['price']}", size=12)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.EDIT,
|
||||||
|
on_click=lambda e, id=item: on_click_handler(id),
|
||||||
|
),
|
||||||
|
ft.IconButton(
|
||||||
|
icon = ft.Icons.DELETE,
|
||||||
|
on_click=lambda e, id=item['id']: on_click_handler2(id),
|
||||||
|
icon_color=ft.Colors.RED
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def edit_product(self, item):
|
||||||
|
self.edit_item_id = item['id']
|
||||||
|
self.name.value = item['name']
|
||||||
|
self.description.value = item['description']
|
||||||
|
self.details.value = item['details']
|
||||||
|
self.price.value = item['price']
|
||||||
|
self.discount.value = item['discount']
|
||||||
|
self.quantity.value = item['quantity']
|
||||||
|
self.product_image.src = item['image']
|
||||||
|
self.aviability.value = item['aviability']
|
||||||
|
self.category.value = item['category_id']
|
||||||
|
self.page.open(self.add_product_dialog)
|
||||||
|
|
||||||
|
def delete_product(self, id):
|
||||||
|
self.delete_item_id = id
|
||||||
|
self.page.open(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def on_delete_product_click(self, e):
|
||||||
|
self.product_manager.delete(self.delete_item_id )
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
print('Update list')
|
||||||
|
self._all_products = self.product_manager.get_all()
|
||||||
|
self.products_list.controls=self.create_list(self._all_products, self.edit_product, self.delete_product)
|
||||||
|
self.products_list.update()
|
||||||
|
|
||||||
|
def on_delete_cancel_btn_click(self, e):
|
||||||
|
self.delete_item_id = None
|
||||||
|
self.page.close(self.confirm_delete_alert)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Produse", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_btn_click),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
self.products_list
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
expand=True
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
BIN
UI_V2/assets/.DS_Store
vendored
Normal file
BIN
UI_V2/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
UI_V2/assets/icons/loading-animation.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
UI_V2/assets/images/banner.jpg
Normal file
|
After Width: | Height: | Size: 582 KiB |
BIN
UI_V2/assets/images/banner.png
Normal file
|
After Width: | Height: | Size: 582 KiB |
BIN
UI_V2/assets/images/banner_placeholder.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
UI_V2/assets/images/placeholder.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
UI_V2/assets/images/tainagustului.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
UI_V2/assets/images/tainagustului_white.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
31
UI_V2/create_super_user.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from dbActions.users import Users
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
def create_super_user():
|
||||||
|
users_manager = Users()
|
||||||
|
|
||||||
|
email = os.getenv('SUPERUSER_EMAIL', '').strip()
|
||||||
|
password = os.getenv('SUPERUSER_PASSWORD', '').strip()
|
||||||
|
role = 'admin'
|
||||||
|
|
||||||
|
if not email:
|
||||||
|
raise ValueError("SUPERUSER_EMAIL is not set or empty")
|
||||||
|
if not password:
|
||||||
|
raise ValueError("SUPERUSER_PASSWORD is not set or empty")
|
||||||
|
|
||||||
|
# check if user already exists
|
||||||
|
existing_user = users_manager.get_user(email)
|
||||||
|
if existing_user:
|
||||||
|
print(f"Super user '{email}' already exists. Skipping creation.")
|
||||||
|
return
|
||||||
|
|
||||||
|
passwd_hash = hashlib.md5(password.encode('utf-8')).hexdigest()
|
||||||
|
users_manager.add_user(email, passwd_hash, role)
|
||||||
|
print(f"Super user created: {email} (role={role})")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
create_super_user()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to create super user: {e}")
|
||||||
BIN
UI_V2/dbActions/__pycache__/categories.cpython-313.pyc
Normal file
BIN
UI_V2/dbActions/__pycache__/company.cpython-313.pyc
Normal file
BIN
UI_V2/dbActions/__pycache__/orders.cpython-313.pyc
Normal file
BIN
UI_V2/dbActions/__pycache__/products.cpython-313.pyc
Normal file
BIN
UI_V2/dbActions/__pycache__/users.cpython-313.pyc
Normal file
94
UI_V2/dbActions/categories.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class Categories:
|
||||||
|
def __init__(self, db_path="instance/app_database.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_categories_table()
|
||||||
|
|
||||||
|
def _create_categories_table(self):
|
||||||
|
"""Create the users table if it doesn't already exist."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS categories (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT,
|
||||||
|
image TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active'
|
||||||
|
);
|
||||||
|
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add(self, name, image: str) -> bool:
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO categories (name, image)
|
||||||
|
VALUES (?, ?)
|
||||||
|
""", (name, image))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM categories
|
||||||
|
WHERE id = ?
|
||||||
|
""", (id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"image": row[2],
|
||||||
|
"created_at": row[3],
|
||||||
|
"status":row[4],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_categories(self):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM categories;
|
||||||
|
""")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
buffer = []
|
||||||
|
for row in rows:
|
||||||
|
r = {
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"image": row[2],
|
||||||
|
"created_at": row[3],
|
||||||
|
"status":row[4],
|
||||||
|
}
|
||||||
|
buffer.append(r)
|
||||||
|
return buffer
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update(self, name, image, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE categories SET name = ?, image = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (name, image, id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM categories WHERE id=?;
|
||||||
|
''', (id,))
|
||||||
|
conn.commit()
|
||||||
97
UI_V2/dbActions/company.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class Company:
|
||||||
|
def __init__(self, db_path="instance/app_database.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_company_table()
|
||||||
|
|
||||||
|
def _create_company_table(self):
|
||||||
|
"""Create the company table if it doesn't already exist."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS company (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER,
|
||||||
|
name TEXT,
|
||||||
|
vat TEXT,
|
||||||
|
register_number TEXT,
|
||||||
|
address TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active'
|
||||||
|
);
|
||||||
|
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add_company(self, company):
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO company (user_id, name, vat, register_number, address)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", (
|
||||||
|
company['user_id'],
|
||||||
|
company['name'],
|
||||||
|
company['vat'],
|
||||||
|
company['register_number'],
|
||||||
|
company['address']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_company(self, user_id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM company
|
||||||
|
WHERE user_id = ?
|
||||||
|
""", (user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
'id': row[0],
|
||||||
|
'user_id': row[1],
|
||||||
|
'name': row[2],
|
||||||
|
'vat': row[3],
|
||||||
|
'register_number': row[4],
|
||||||
|
'address': row[5],
|
||||||
|
'created_at': row[6],
|
||||||
|
'status': row[7],
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_company(self, company):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE company SET name = ?, vat = ?, register_number = ?, address = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (company['name'],
|
||||||
|
company['vat'],
|
||||||
|
company['register_number'],
|
||||||
|
company['address'],
|
||||||
|
company['id']))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM company WHERE id=?;
|
||||||
|
''', (id,))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def deactivate(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE company SET status = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', ('inactive', id))
|
||||||
|
conn.commit()
|
||||||
163
UI_V2/dbActions/orders.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class Orders:
|
||||||
|
def __init__(self, db_path="instance/app_database.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_orders_table()
|
||||||
|
self._create_orders_map_table()
|
||||||
|
|
||||||
|
def _create_orders_table(self):
|
||||||
|
"""Create the orders table if it doesn't already exist."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS orders (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'on_hold'
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def _create_orders_map_table(self):
|
||||||
|
"""Create the orders table if it doesn't already exist."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS orders_products (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
product_id TEXT,
|
||||||
|
orders_id TEXT,
|
||||||
|
quantity INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add_order(self, user_id):
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO orders (user_id)
|
||||||
|
VALUES (?)
|
||||||
|
""", (user_id,))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add_product_to_order(self, product_id, orders_id, quantity):
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO orders_products (product_id, orders_id, quantity)
|
||||||
|
VALUES (?,?,?)
|
||||||
|
""", (product_id, orders_id, quantity))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_on_hold_order(self, user_id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM orders
|
||||||
|
WHERE user_id = ? and status = 'on_hold'
|
||||||
|
""", (user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"user_id": row[1],
|
||||||
|
"status": row[2],
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_order_status(self, status, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE orders SET status = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (status, id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_order_products(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM orders_products
|
||||||
|
WHERE orders_id = ?
|
||||||
|
""", (id,))
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
result = []
|
||||||
|
if rows:
|
||||||
|
for row in rows:
|
||||||
|
buffer = {
|
||||||
|
"id": row[0],
|
||||||
|
"prdouct_id": row[1],
|
||||||
|
"orders_id": row[2],
|
||||||
|
"quantity": row[3]
|
||||||
|
}
|
||||||
|
result.append(buffer)
|
||||||
|
return result
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update_order_map_quantity(self, id, quantity):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE orders_products SET quantity = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (quantity, id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_orders(self):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM orders
|
||||||
|
""")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
buffer = []
|
||||||
|
for row in rows:
|
||||||
|
r = {
|
||||||
|
"id": row[0],
|
||||||
|
"user_id": row[1],
|
||||||
|
"status": row[2],
|
||||||
|
}
|
||||||
|
buffer.append(r)
|
||||||
|
return buffer
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_orders_for_user(self, user_id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM orders WHERE user_id = ?
|
||||||
|
""", (user_id,))
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
buffer = []
|
||||||
|
for row in rows:
|
||||||
|
r = {
|
||||||
|
"id": row[0],
|
||||||
|
"user_id": row[1],
|
||||||
|
"status": row[2],
|
||||||
|
}
|
||||||
|
buffer.append(r)
|
||||||
|
return buffer
|
||||||
|
return []
|
||||||
|
|
||||||
|
def remove_product_from_order(self, order_id, product_id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM orders_products WHERE orders_id=? and product_id=?;
|
||||||
|
''', (order_id, product_id))
|
||||||
|
conn.commit()
|
||||||
140
UI_V2/dbActions/products.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class Products:
|
||||||
|
def __init__(self, db_path="instance/app_database.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_products_table()
|
||||||
|
|
||||||
|
def _create_products_table(self):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS products (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
details TEXT NOT NULL,
|
||||||
|
price REAL,
|
||||||
|
discount REAL,
|
||||||
|
quantity REAL,
|
||||||
|
aviability TEXT NOT NULL,
|
||||||
|
category_id INTEGER,
|
||||||
|
image TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add(self, product) -> bool:
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO products (name, description, details, price, discount, quantity, aviability, category_id, image)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""", (product['name'], product['description'],product['details'],product['price'], product['discount'], product['quantity'], product['aviability'], product['category_id'], product['image']))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM products
|
||||||
|
WHERE id = ?
|
||||||
|
""", (id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"description": row[2],
|
||||||
|
"details":row[3],
|
||||||
|
"price": row[4],
|
||||||
|
"discount": row[5],
|
||||||
|
"quantity": row[6],
|
||||||
|
"aviability": row[7],
|
||||||
|
"category_id": row[8],
|
||||||
|
"image":row[9],
|
||||||
|
"created_at": row[10]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM products
|
||||||
|
""")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
products = []
|
||||||
|
for row in rows:
|
||||||
|
product={
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"description": row[2],
|
||||||
|
"details":row[3],
|
||||||
|
"price": row[4],
|
||||||
|
"discount": row[5],
|
||||||
|
"quantity": row[6],
|
||||||
|
"aviability": row[7],
|
||||||
|
"category_id": row[8],
|
||||||
|
"image":row[9],
|
||||||
|
"created_at": row[10]
|
||||||
|
}
|
||||||
|
products.append(product)
|
||||||
|
return products
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_all_by_category(self, category_id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM products
|
||||||
|
WHERE category_id = ?
|
||||||
|
""", (category_id,))
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
products = []
|
||||||
|
for row in rows:
|
||||||
|
product={
|
||||||
|
"id": row[0],
|
||||||
|
"name": row[1],
|
||||||
|
"description": row[2],
|
||||||
|
"details":row[3],
|
||||||
|
"price": row[4],
|
||||||
|
"discount": row[5],
|
||||||
|
"quantity": row[6],
|
||||||
|
"aviability": row[7],
|
||||||
|
"category_id": row[8],
|
||||||
|
"image": row[9],
|
||||||
|
"created_at": row[10],
|
||||||
|
}
|
||||||
|
products.append(product)
|
||||||
|
return products
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update(self, product, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE products SET name = ?, description =?, details=?, price=?, discount=?, quantity=?, aviability=?, category_id=?, image=?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (product['name'], product['description'], product['details'], product['price'], product['discount'], product['quantity'], product['aviability'], product['category_id'], product['image'] , id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM products WHERE id=?;
|
||||||
|
''', (id,))
|
||||||
|
conn.commit()
|
||||||
206
UI_V2/dbActions/users.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
import sqlite3
|
||||||
|
from typing import Optional
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
class Users:
|
||||||
|
def __init__(self, db_path="instance/app_database.db"):
|
||||||
|
self.db_path = db_path
|
||||||
|
self._create_users_table()
|
||||||
|
|
||||||
|
def _create_users_table(self):
|
||||||
|
"""Create the users table if it doesn't already exist."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
password TEXT,
|
||||||
|
token TEXT,
|
||||||
|
name TEXT,
|
||||||
|
phone TEXT,
|
||||||
|
address TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
|
role TEXT DEFAULT 'client'
|
||||||
|
);
|
||||||
|
|
||||||
|
""")
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def hash_password(self, password: str) -> bytes:
|
||||||
|
return hashlib.md5(password.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
def add_user(self, email, passwd, role):
|
||||||
|
"""Register a new user."""
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO users (email, password, role)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""", (email, passwd, role))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False # Username already exist
|
||||||
|
|
||||||
|
def invite_user(self, email, name, phone, address, role='invited'):
|
||||||
|
#try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO users (email, name, phone, address, role)
|
||||||
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", (email, name, phone, address, role))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.lastrowid
|
||||||
|
#except sqlite3.IntegrityError:
|
||||||
|
# return None
|
||||||
|
|
||||||
|
def register_user(self, email: str, password: str) -> bool:
|
||||||
|
"""Register a new user."""
|
||||||
|
try:
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO users (email, password)
|
||||||
|
VALUES (?, ?)
|
||||||
|
""", (email, password))
|
||||||
|
conn.commit()
|
||||||
|
return True
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return False # Username already exist
|
||||||
|
|
||||||
|
def authenticate_user(self, email: str, password: str) -> bool:
|
||||||
|
"""Authenticate a user."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM users
|
||||||
|
WHERE email = ? AND password = ?
|
||||||
|
""", (email, password))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"email": row[1],
|
||||||
|
"token": row[3],
|
||||||
|
"name":row[4],
|
||||||
|
"phone": row[5],
|
||||||
|
"address": row[6],
|
||||||
|
"created_at": row[7],
|
||||||
|
"status": row[8],
|
||||||
|
"role":row[9]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_user(self, email: str) -> Optional[dict]:
|
||||||
|
"""Retrieve user details by username."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM users
|
||||||
|
WHERE email = ?
|
||||||
|
""", (email,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"email": row[1],
|
||||||
|
"token": row[3],
|
||||||
|
"name":row[4],
|
||||||
|
"phone": row[5],
|
||||||
|
"address": row[6],
|
||||||
|
"created_at": row[7],
|
||||||
|
"status": row[8],
|
||||||
|
"role":row[9]
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get(self, id: int) -> Optional[dict]:
|
||||||
|
"""Retrieve user details by username."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM users
|
||||||
|
WHERE id = ?
|
||||||
|
""", (id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row:
|
||||||
|
return {
|
||||||
|
"id": row[0],
|
||||||
|
"email": row[1],
|
||||||
|
"token": row[3],
|
||||||
|
"name":row[4],
|
||||||
|
"phone": row[5],
|
||||||
|
"address": row[6],
|
||||||
|
"created_at": row[7],
|
||||||
|
"status": row[8],
|
||||||
|
"role":row[9]
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_all(self) -> Optional[dict]:
|
||||||
|
"""Retrieve user details by username."""
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT * FROM users
|
||||||
|
""")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
if rows:
|
||||||
|
buffer = []
|
||||||
|
for row in rows:
|
||||||
|
buffer.append({
|
||||||
|
"id": row[0],
|
||||||
|
"email": row[1],
|
||||||
|
"token": row[3],
|
||||||
|
"name":row[4],
|
||||||
|
"phone": row[5],
|
||||||
|
"address": row[6],
|
||||||
|
"created_at": row[7],
|
||||||
|
"status": row[8],
|
||||||
|
"role":row[9]
|
||||||
|
})
|
||||||
|
return buffer
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update_password(self, email, passwd):
|
||||||
|
'''Update user password'''
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE users SET password = ?
|
||||||
|
WHERE email = ?
|
||||||
|
''', (passwd, email))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def update_token(self, id, token):
|
||||||
|
'''Update user token'''
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE users SET token = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (token, id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def update_user_data(self, name, phone, address, id):
|
||||||
|
'''Update user data'''
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
UPDATE users SET name = ?, phone = ?, address = ?
|
||||||
|
WHERE id = ?
|
||||||
|
''', (name, phone, address, id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def delete(self, id):
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
DELETE FROM users WHERE id=?;
|
||||||
|
''', (id,))
|
||||||
|
conn.commit()
|
||||||
32
UI_V2/docker_compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
reverse-proxy:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
tg_instance:
|
||||||
|
tg_assets:
|
||||||
|
|
||||||
|
services:
|
||||||
|
tainagustului:
|
||||||
|
build: .
|
||||||
|
container_name: tainagustului
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
TZ: "Europe/Bucharest"
|
||||||
|
FLET_PORT: "8080"
|
||||||
|
VIRTUAL_HOST: "tainagustului.ro,www.tainagustului.ro"
|
||||||
|
VIRTUAL_PORT: "8080"
|
||||||
|
LETSENCRYPT_HOST: "tainagustului.ro,www.tainagustului.ro"
|
||||||
|
LETSENCRYPT_EMAIL: "macamete.robert@gmail.com"
|
||||||
|
# If your app reads DB path from env, uncomment one of these:
|
||||||
|
# DATABASE_URL: "sqlite:////app/instance/shop.db" # SQLAlchemy style
|
||||||
|
# SHOP_DB_PATH: "/app/instance/shop.db" # custom var
|
||||||
|
expose:
|
||||||
|
- "8080"
|
||||||
|
volumes:
|
||||||
|
- tg_instance:/app/instance
|
||||||
|
- tg_assets:/app/assets
|
||||||
|
networks:
|
||||||
|
- reverse-proxy
|
||||||
BIN
UI_V2/helpers/__pycache__/default_user.cpython-313.pyc
Normal file
BIN
UI_V2/helpers/__pycache__/emails.cpython-313.pyc
Normal file
16
UI_V2/helpers/default_user.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import flet as ft
|
||||||
|
|
||||||
|
class DefaultUser:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.default_user = {
|
||||||
|
'id':self.page.session_id,
|
||||||
|
'email':f'user_{self.page.session_id}@default.com',
|
||||||
|
'name': 'Default User',
|
||||||
|
'phone': None,
|
||||||
|
'address': None,
|
||||||
|
'created_at': "None",
|
||||||
|
'status':'active',
|
||||||
|
'role': 'default_user'
|
||||||
|
}
|
||||||
|
|
||||||
139
UI_V2/helpers/emails.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import smtplib
|
||||||
|
from email.message import EmailMessage
|
||||||
|
import os
|
||||||
|
|
||||||
|
def send_email(to_email, subject, body):
|
||||||
|
smtp_host = os.environ.get("SMTP_HOST")
|
||||||
|
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||||
|
smtp_user = os.environ.get("SMTP_USER")
|
||||||
|
smtp_pass = os.environ.get("SMTP_PASS")
|
||||||
|
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email with attachment
|
||||||
|
def send_email_with_attachment(to_email, subject, body, attachment_path):
|
||||||
|
smtp_host = os.environ.get("SMTP_HOST")
|
||||||
|
smtp_port = int(os.environ.get("SMTP_PORT", 587))
|
||||||
|
smtp_user = os.environ.get("SMTP_USER")
|
||||||
|
smtp_pass = os.environ.get("SMTP_PASS")
|
||||||
|
sender_email = os.environ.get("SMTP_FROM", smtp_user)
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email using Gmail directly
|
||||||
|
def send_gmail(to_email, subject, body):
|
||||||
|
smtp_host = "smtp.gmail.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = 'macamete.robert@gmail.com'
|
||||||
|
smtp_pass = 'advx yqlv jkaa czvr'
|
||||||
|
sender_email = 'macamete.robert@gmail.com'
|
||||||
|
|
||||||
|
if not all([smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# Send email with attachment using Gmail directly
|
||||||
|
def send_gmail_with_attachment(to_email, subject, body, attachment_path):
|
||||||
|
smtp_host = "smtp.gmail.com"
|
||||||
|
smtp_port = 587
|
||||||
|
smtp_user = 'macamete.robert@gmail.com'
|
||||||
|
smtp_pass = 'advx yqlv jkaa czvr'
|
||||||
|
sender_email = 'macamete.robert@gmail.com'
|
||||||
|
|
||||||
|
if not all([smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("GMAIL_USER and GMAIL_PASS must be set in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
# Send email with attachment
|
||||||
|
def send_custom_email_with_attachment(to_email, subject, body, attachment_path, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS):
|
||||||
|
smtp_host = SMTP_HOST
|
||||||
|
smtp_port = int(SMTP_PORT)
|
||||||
|
smtp_user = SMTP_USER
|
||||||
|
smtp_pass = SMTP_PASS
|
||||||
|
sender_email = smtp_user
|
||||||
|
|
||||||
|
if not all([smtp_host, smtp_port, smtp_user, smtp_pass]):
|
||||||
|
raise ValueError("SMTP config incomplete in environment variables.")
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = sender_email
|
||||||
|
msg["To"] = to_email
|
||||||
|
msg.set_content(body)
|
||||||
|
|
||||||
|
if attachment_path and os.path.isfile(attachment_path):
|
||||||
|
with open(attachment_path, "rb") as f:
|
||||||
|
file_data = f.read()
|
||||||
|
file_name = os.path.basename(attachment_path)
|
||||||
|
msg.add_attachment(file_data, maintype="application", subtype="pdf", filename=file_name)
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f"Attachment file not found: {attachment_path}")
|
||||||
|
|
||||||
|
with smtplib.SMTP(smtp_host, smtp_port) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_pass)
|
||||||
|
server.send_message(msg)
|
||||||
BIN
UI_V2/instance/app_database.db
Normal file
90
UI_V2/main.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import flet as ft
|
||||||
|
from pages.auth.auth import Auth
|
||||||
|
from pages.home.home import Home
|
||||||
|
from admin.dashboard import Dashboard
|
||||||
|
from pages.categories.category import Category
|
||||||
|
from pages.products.product import ProductPage
|
||||||
|
from pages.profile.profilepage import ProfilePage
|
||||||
|
from pages.shopping_cart.cart import Cart
|
||||||
|
from pages.shopping_cart.peload_card import PreloadCard
|
||||||
|
|
||||||
|
import os
|
||||||
|
os.environ["FLET_SECRET_KEY"] = os.urandom(12).hex()
|
||||||
|
|
||||||
|
def main(page: ft.Page):
|
||||||
|
page.title = "Taina Gustului"
|
||||||
|
page.theme_mode = ft.ThemeMode.LIGHT
|
||||||
|
page.theme = ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.BROWN))
|
||||||
|
page.vertical_alignment = ft.MainAxisAlignment.CENTER
|
||||||
|
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
|
||||||
|
page.padding = 0
|
||||||
|
|
||||||
|
def route_change(route):
|
||||||
|
page.controls.clear()
|
||||||
|
|
||||||
|
if route == "/auth":
|
||||||
|
login = Auth(page)
|
||||||
|
page.add(login.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if route in ("/home", "/", None):
|
||||||
|
home = Home(page)
|
||||||
|
page.add(home.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if route == "/admin":
|
||||||
|
if not page.client_storage.get("is_authenticated"):
|
||||||
|
page.go("/auth")
|
||||||
|
return
|
||||||
|
dashbaord = Dashboard(page)
|
||||||
|
page.add(dashbaord.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'categorie' in route:
|
||||||
|
category = Category(page)
|
||||||
|
page.add(category.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if 'produs' in route:
|
||||||
|
produs = ProductPage(page)
|
||||||
|
page.add(produs.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if route == "/profil":
|
||||||
|
profile = ProfilePage(page)
|
||||||
|
page.add(profile.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if route == "/cos":
|
||||||
|
cart = Cart(page)
|
||||||
|
page.add(cart.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
if route == "/pre_load_cos":
|
||||||
|
preload = PreloadCard(page)
|
||||||
|
page.add(preload.build())
|
||||||
|
page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 5) Fallback 404
|
||||||
|
page.add(ft.Text("404: Page not found"))
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
page.on_route_change = lambda _: route_change(page.route)
|
||||||
|
page.go("/")
|
||||||
|
|
||||||
|
ft.app(
|
||||||
|
target=main,
|
||||||
|
assets_dir="assets",
|
||||||
|
upload_dir="uploads",
|
||||||
|
view=ft.WEB_BROWSER,
|
||||||
|
port=8080,
|
||||||
|
host="0.0.0.0"
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/auth/__pycache__/auth.cpython-313.pyc
Normal file
BIN
UI_V2/pages/auth/__pycache__/forgot_password.cpython-313.pyc
Normal file
BIN
UI_V2/pages/auth/__pycache__/login.cpython-313.pyc
Normal file
BIN
UI_V2/pages/auth/__pycache__/register.cpython-313.pyc
Normal file
34
UI_V2/pages/auth/auth.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import flet as ft
|
||||||
|
from pages.auth.login import Login
|
||||||
|
from pages.auth.register import Register
|
||||||
|
|
||||||
|
class Auth:
|
||||||
|
def __init__(self, page: ft.Page, go_to = 'login'):
|
||||||
|
self.page = page
|
||||||
|
self.go_to = go_to
|
||||||
|
self.logo = ft.Image("images/tainagustului.png", width=200)
|
||||||
|
self.login = Login(self.page, self)
|
||||||
|
self.register = Register(self.page, self, self.login)
|
||||||
|
|
||||||
|
go_to = 'login' if self.page.session.get('go_to') == None or 'login' else self.page.session.get('go_to')
|
||||||
|
if go_to == 'login':
|
||||||
|
self.placeholder = ft.Container(
|
||||||
|
content=self.login.build(),
|
||||||
|
width=350
|
||||||
|
)
|
||||||
|
if go_to == 'register':
|
||||||
|
self.placeholder = ft.Container(
|
||||||
|
content=self.register.build(),
|
||||||
|
width=350
|
||||||
|
)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.logo,
|
||||||
|
self.placeholder,
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
)
|
||||||
159
UI_V2/pages/auth/forgot_password.py
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.users import Users
|
||||||
|
from helpers.emails import send_gmail
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
class ForgotPassword:
|
||||||
|
def __init__(self, page: ft.Page, auth, login):
|
||||||
|
self.page = page
|
||||||
|
self.auth = auth
|
||||||
|
self.login = login
|
||||||
|
self.email = ft.TextField(label="E-mail")
|
||||||
|
self.password = ft.TextField(label="Parola", password=True, can_reveal_password=True)
|
||||||
|
self.repeat_password = ft.TextField(label="Repeta Parola", password=True, can_reveal_password=True)
|
||||||
|
self.inserted_code = ft.TextField(label="Inserati codul primit prin e-mail")
|
||||||
|
self.error_message = ft.Text("", color=ft.Colors.RED)
|
||||||
|
self.placeholder = ft.Column([], horizontal_alignment=ft.CrossAxisAlignment.CENTER)
|
||||||
|
self.otp_code = self._generate_numeric_code()
|
||||||
|
self.user_manager = Users()
|
||||||
|
self.trimite_code_placeholder = ft.Column(
|
||||||
|
[
|
||||||
|
self.email,
|
||||||
|
ft.Button("Trimite cod", on_click=self.send_code_on_email, width=150),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
self.code_placeholder = ft.Column([], horizontal_alignment=ft.CrossAxisAlignment.CENTER)
|
||||||
|
|
||||||
|
def _generate_numeric_code(self) -> str:
|
||||||
|
digits = string.digits
|
||||||
|
return ''.join(secrets.choice(digits) for _ in range(6))
|
||||||
|
|
||||||
|
def send_code_on_email(self, e):
|
||||||
|
if self._is_valid_email(self.email.value):
|
||||||
|
self.trimite_code_placeholder.controls = []
|
||||||
|
self.trimite_code_placeholder.update()
|
||||||
|
self.code_placeholder.controls.clear()
|
||||||
|
self.code_placeholder.controls.append(self.inserted_code)
|
||||||
|
self.code_placeholder.controls.append(ft.Button("Verifica", width=150, on_click=self.verfy_code))
|
||||||
|
self.code_placeholder.update()
|
||||||
|
print(self.otp_code)
|
||||||
|
# send_gmail(
|
||||||
|
# to_email=self.email.value,
|
||||||
|
# subject="Codul de verificare",
|
||||||
|
# body=f"Codul de verificare este: {self.otp_code}"
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
def _are_all_fields_inserted(self, password=None, repeat_password=None):
|
||||||
|
valid = True
|
||||||
|
self.error_message.value = ''
|
||||||
|
if not password:
|
||||||
|
valid = False
|
||||||
|
if not repeat_password:
|
||||||
|
valid = False
|
||||||
|
if not valid:
|
||||||
|
self.error_message.value = "All fields are required!"
|
||||||
|
self.error_message.update()
|
||||||
|
return valid
|
||||||
|
|
||||||
|
def _check_repeat_password(self, password, confirm_password):
|
||||||
|
if password == confirm_password:
|
||||||
|
self.error_message.value = ""
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.error_message.value = "The passwords don't match!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_password_strong(self, password):
|
||||||
|
self.error_message.value = ""
|
||||||
|
if len(password) < 8:
|
||||||
|
self.error_message.value = "Password must be at least 8 characters long!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[A-Z]", password):
|
||||||
|
self.error_message.value = "Password must contain at least one uppercase letter!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[a-z]", password):
|
||||||
|
self.error_message.value = "Password must contain at least one lowercase letter!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[0-9]", password):
|
||||||
|
self.error_message.value = "Password must contain at least one digit!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[^a-zA-Z0-9]", password):
|
||||||
|
self.error_message.value = "Password must contain at least one special character!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
password = self.password.value
|
||||||
|
repeat_password = self.repeat_password.value
|
||||||
|
if self._are_all_fields_inserted(password, repeat_password):
|
||||||
|
if self._is_password_strong(password):
|
||||||
|
if self._check_repeat_password(password, repeat_password):
|
||||||
|
print(self.email.value)
|
||||||
|
print(password)
|
||||||
|
if self.user_manager.update_password(self.email.value, password):
|
||||||
|
self.error_message.value = "Parola a fost salvata cu success."
|
||||||
|
self.error_message.color = ft.Colors.GREEN
|
||||||
|
self.error_message.update()
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Nu am gasit un cont valid cu aceasta adresa de email."
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
def verfy_code(self, e):
|
||||||
|
self.code_placeholder.controls = []
|
||||||
|
self.code_placeholder.update()
|
||||||
|
inserted_code = self.inserted_code.value
|
||||||
|
if inserted_code == self.otp_code:
|
||||||
|
self.placeholder.controls.clear()
|
||||||
|
self.placeholder.controls.append(self.password)
|
||||||
|
self.placeholder.controls.append(self.repeat_password)
|
||||||
|
self.placeholder.controls.append(
|
||||||
|
ft.Button("Salveaza", width=150, on_click=self.on_save_btn_click)
|
||||||
|
)
|
||||||
|
self.placeholder.update()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(inserted_code)
|
||||||
|
print(self.otp_code)
|
||||||
|
|
||||||
|
def on_back_btn_click(self, e):
|
||||||
|
self.auth.placeholder.content.clean()
|
||||||
|
self.auth.placeholder.content = self.login.build()
|
||||||
|
self.auth.placeholder.update()
|
||||||
|
|
||||||
|
def _is_valid_email(self, email: str) -> bool:
|
||||||
|
email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||||
|
if re.fullmatch(email_regex, email) is not None:
|
||||||
|
self.error_message.value = ""
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Va rugam inserati o adresa de e-mail valida!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
self.trimite_code_placeholder,
|
||||||
|
self.code_placeholder,
|
||||||
|
self.placeholder,
|
||||||
|
self.error_message,
|
||||||
|
ft.TextButton("Inapoi la Autentificare.", on_click=self.on_back_btn_click)
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
)
|
||||||
68
UI_V2/pages/auth/login.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import flet as ft
|
||||||
|
from pages.auth.register import Register
|
||||||
|
from dbActions.users import Users
|
||||||
|
from pages.auth.forgot_password import ForgotPassword
|
||||||
|
|
||||||
|
class Login:
|
||||||
|
def __init__(self, page: ft.Page, auth):
|
||||||
|
self.page = page
|
||||||
|
self.auth = auth
|
||||||
|
self.user_manager = Users()
|
||||||
|
self.email = ft.TextField(label="E-mail")
|
||||||
|
self.password = ft.TextField(label="Parola", password=True, can_reveal_password=True)
|
||||||
|
self.error_message = ft.Text("", color=ft.Colors.RED)
|
||||||
|
self.register = Register(self.page, self.auth, self)
|
||||||
|
|
||||||
|
def on_login_btn_click(self, e):
|
||||||
|
email = self.email.value
|
||||||
|
password = self.password.value
|
||||||
|
password_hash = self.user_manager.hash_password(password)
|
||||||
|
user = self.user_manager.authenticate_user(email, password_hash)
|
||||||
|
if user:
|
||||||
|
self.page.client_storage.set("is_authenticated", True)
|
||||||
|
self.page.session.set("user", user)
|
||||||
|
self.error_message.value = ''
|
||||||
|
self.error_message.update()
|
||||||
|
if user['role'] == 'admin':
|
||||||
|
self.page.go('/admin')
|
||||||
|
else:
|
||||||
|
if user['name'] is None or len(user['name'])<=1:
|
||||||
|
self.page.go("/profil")
|
||||||
|
else:
|
||||||
|
self.page.go('/')
|
||||||
|
else:
|
||||||
|
self.error_message.value = 'E-mail sau parola sunt gresite!'
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
def on_register_btn_click(self, e):
|
||||||
|
self.auth.placeholder.content = self.register.build()
|
||||||
|
self.auth.placeholder.update()
|
||||||
|
|
||||||
|
def on_forgot_password_btn_click(self, e):
|
||||||
|
forgot_password = ForgotPassword(self.page, self.auth, self)
|
||||||
|
self.auth.placeholder.content.clean()
|
||||||
|
self.auth.placeholder.content = forgot_password.build()
|
||||||
|
self.auth.placeholder.update()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Column(
|
||||||
|
[
|
||||||
|
self.email,
|
||||||
|
self.password,
|
||||||
|
self.error_message,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Button("Autentificare", width=200, on_click=self.on_login_btn_click)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Text(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.TextButton("Creaza cont", on_click=self.on_register_btn_click),
|
||||||
|
ft.TextButton("Ai uitat parola?", on_click=self.on_forgot_password_btn_click)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
123
UI_V2/pages/auth/register.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.users import Users
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Register:
|
||||||
|
def __init__(self, page: ft.Page, auth, login):
|
||||||
|
self.page = page
|
||||||
|
self.auth = auth
|
||||||
|
self.login = login
|
||||||
|
self.users_manager = Users()
|
||||||
|
self.email = ft.TextField(label="E-mail")
|
||||||
|
self.password = ft.TextField(label="Parola", password=True, can_reveal_password=True)
|
||||||
|
self.repeat_password = ft.TextField(label="Repeta parola", password=True, can_reveal_password=True)
|
||||||
|
self.error_message = ft.Text("", color=ft.Colors.RED)
|
||||||
|
|
||||||
|
def on_login_btn_click(self, e):
|
||||||
|
self.auth.placeholder.content = self.login.build()
|
||||||
|
self.auth.placeholder.update()
|
||||||
|
|
||||||
|
def _is_valid_email(self, email: str) -> bool:
|
||||||
|
email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||||
|
if re.fullmatch(email_regex, email) is not None:
|
||||||
|
self.error_message.value = ""
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Va rugam inserati un e-mail valid!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _are_all_fields_inserted(self, email=None, password=None, repeat_password=None):
|
||||||
|
valid = True
|
||||||
|
self.error_message.value = ''
|
||||||
|
if not email:
|
||||||
|
valid = False
|
||||||
|
if not password:
|
||||||
|
valid = False
|
||||||
|
if not repeat_password:
|
||||||
|
valid = False
|
||||||
|
if not valid:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.update()
|
||||||
|
return valid
|
||||||
|
|
||||||
|
def _check_repeat_password(self, password, confirm_password):
|
||||||
|
if password == confirm_password:
|
||||||
|
self.error_message.value = ""
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Parolele nu se potrivesc!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_password_strong(self, password):
|
||||||
|
self.error_message.value = ""
|
||||||
|
if len(password) < 8:
|
||||||
|
self.error_message.value = "Parola trebuie sa fie cel putin 8 caractere!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[A-Z]", password):
|
||||||
|
self.error_message.value = "Parola trebuie sa contina cel putin o litera mare!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[a-z]", password):
|
||||||
|
self.error_message.value = "Parola trebuie sa contina cel putin o litera mica!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[0-9]", password):
|
||||||
|
self.error_message.value = "Parola trebuie sa contina cel putin un numar!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
if not re.search(r"[^a-zA-Z0-9]", password):
|
||||||
|
self.error_message.value = "Parola trebuie sa contina cel putin un caracter special (exp: !@#$%^&*)!"
|
||||||
|
self.error_message.update()
|
||||||
|
return False
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def on_register_btn_click(self, e):
|
||||||
|
email = self.email.value
|
||||||
|
password = self.password.value
|
||||||
|
repeat_password = self.repeat_password.value
|
||||||
|
if self._are_all_fields_inserted(email, password, repeat_password):
|
||||||
|
print("All fileds are inserted")
|
||||||
|
if self._is_valid_email(email):
|
||||||
|
print("Email is valid")
|
||||||
|
if self._is_password_strong(password):
|
||||||
|
print('Password is string')
|
||||||
|
if self._check_repeat_password(password, repeat_password):
|
||||||
|
print("Password is valid!")
|
||||||
|
password_hash = self.users_manager.hash_password(password)
|
||||||
|
self.users_manager.register_user(email, password_hash)
|
||||||
|
self.error_message.value = "Inregistrarea a avut loc cu succes, va puteti autentifica!"
|
||||||
|
self.error_message.color = ft.Colors.GREEN
|
||||||
|
self.error_message.update()
|
||||||
|
time.sleep(3)
|
||||||
|
self.on_login_btn_click('')
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Column(
|
||||||
|
[
|
||||||
|
self.email,
|
||||||
|
self.password,
|
||||||
|
self.repeat_password,
|
||||||
|
self.error_message,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Button("Creaza cont", width=200, on_click=self.on_register_btn_click)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.Text(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.TextButton("Inapoi la Autentificare", on_click=self.on_login_btn_click),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/categories/__pycache__/category.cpython-313.pyc
Normal file
156
UI_V2/pages/categories/category.py
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.products import Products
|
||||||
|
|
||||||
|
class Category:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.products_manager = Products()
|
||||||
|
self.category = self.page.session.get("category")
|
||||||
|
|
||||||
|
self.products_group = ft.GridView(
|
||||||
|
spacing=10,
|
||||||
|
runs_count=5,
|
||||||
|
max_extent=200,
|
||||||
|
child_aspect_ratio=1.0,
|
||||||
|
expand=True,
|
||||||
|
width=1000
|
||||||
|
)
|
||||||
|
self.products = self.products_manager.get_all_by_category(self.category['id'])
|
||||||
|
self.add_products(self.products)
|
||||||
|
self.searchbar = ft.TextField(
|
||||||
|
label="Cauta produsul aceasta categorie",
|
||||||
|
expand=True,
|
||||||
|
on_submit=self.on_search_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
self.profile_placeholder = ft.Column()
|
||||||
|
self.profile_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.ACCOUNT_CIRCLE_OUTLINED,
|
||||||
|
on_click=self.on_profile_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
self.login_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.LOGIN,
|
||||||
|
on_click=self.on_login_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
if self.page.session.get("user") is not None and '@default.com' not in self.page.session.get("user")['email']:
|
||||||
|
self.profile_placeholder.controls.append(self.profile_btn)
|
||||||
|
else:
|
||||||
|
self.profile_placeholder.controls.append(self.login_btn)
|
||||||
|
|
||||||
|
def on_login_btn_click(self, e):
|
||||||
|
self.page.go('/auth')
|
||||||
|
|
||||||
|
def add_products(self, products):
|
||||||
|
for product in products:
|
||||||
|
self.new_price = ft.Text(
|
||||||
|
value=f"{float(product['price']) - float(product['price'])*float(product['discount'])/100}",
|
||||||
|
size=14 if product['discount'] != 0 else 12,
|
||||||
|
color=ft.Colors.RED if product['discount'] != 0 else ft.Colors.BLACK,
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
print(type(product['discount']))
|
||||||
|
self.old_price = ft.Text(
|
||||||
|
value=f"{product['price']}" if product['discount'] != 0 else '',
|
||||||
|
size=12,
|
||||||
|
color=ft.Colors.GREY,
|
||||||
|
style=ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH),
|
||||||
|
)
|
||||||
|
card = ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Image(src=product['image'], width=170, height=100, border_radius=10, fit=ft.ImageFit.COVER),
|
||||||
|
ft.Text(product['name'], weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(f"Pret:", size=12),
|
||||||
|
self.old_price,
|
||||||
|
self.new_price,
|
||||||
|
ft.Text(f"lei/{product['quantity']}g", size=12),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
spacing=4
|
||||||
|
)
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ink=True,
|
||||||
|
on_click=lambda e, title=product: self.on_product_click(title),
|
||||||
|
padding=5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.products_group.controls.append(card)
|
||||||
|
|
||||||
|
def on_search_btn_click(self, e):
|
||||||
|
search = self.searchbar.value
|
||||||
|
buffer = []
|
||||||
|
for product in self.products:
|
||||||
|
if search.lower() in product['name'].lower():
|
||||||
|
buffer.append(product)
|
||||||
|
self.products_group.controls.clear()
|
||||||
|
self.add_products(buffer)
|
||||||
|
self.products_group.update()
|
||||||
|
|
||||||
|
def on_profile_btn_click(self, e):
|
||||||
|
self.page.go('/profil')
|
||||||
|
|
||||||
|
def on_home_btn_click(self, e):
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def on_product_click(self, product):
|
||||||
|
self.page.session.set("product", product)
|
||||||
|
name = product['name'].replace(" ", "-").lower()
|
||||||
|
self.page.go(f'/produs/{name}')
|
||||||
|
|
||||||
|
def on_cart_btn_click(self, e):
|
||||||
|
self.page.go("/pre_load_cos")
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.searchbar,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SEARCH,
|
||||||
|
on_click=self.on_search_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
),
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.profile_placeholder,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SHOPPING_CART_OUTLINED,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE,
|
||||||
|
on_click=self.on_cart_btn_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(icon=ft.Icons.HOME, on_click=self.on_home_btn_click),
|
||||||
|
ft.Text(
|
||||||
|
value=self.category['name'],
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=18
|
||||||
|
),
|
||||||
|
],
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
self.products_group
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
expand=True,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
padding=10,
|
||||||
|
bgcolor=ft.Colors.WHITE
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/home/__pycache__/home.cpython-313.pyc
Normal file
216
UI_V2/pages/home/home.py
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.categories import Categories
|
||||||
|
from dbActions.products import Products
|
||||||
|
|
||||||
|
class Home:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.categories_manager = Categories()
|
||||||
|
self.header = ft.Row(
|
||||||
|
[
|
||||||
|
#ft.Button("Acasa", icon=ft.Icons.HOME, on_click=self.on_acasa_btn_click)
|
||||||
|
ft.Text("Categori", weight=ft.FontWeight.BOLD, size=18)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
width=1000
|
||||||
|
)
|
||||||
|
self.banner = ft.Image("images/banner.png", width=1000, height=350, fit=ft.ImageFit.COVER)
|
||||||
|
self.categories_group = ft.Row([], scroll=ft.ScrollMode.ADAPTIVE, expand=True)
|
||||||
|
self.add_categories()
|
||||||
|
self.products_manager = Products()
|
||||||
|
self.products_group = ft.GridView(
|
||||||
|
spacing=10,
|
||||||
|
runs_count=5,
|
||||||
|
max_extent=200,
|
||||||
|
child_aspect_ratio=1.0,
|
||||||
|
expand=True,
|
||||||
|
width=1000
|
||||||
|
)
|
||||||
|
self.products = self.products_manager.get_all()
|
||||||
|
self.add_products(self.products)
|
||||||
|
|
||||||
|
self.searchbar = ft.TextField(
|
||||||
|
label="Cauta produsul in toate categoriile",
|
||||||
|
expand=True,
|
||||||
|
on_submit=self.on_search_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
self.profile_placeholder = ft.Column()
|
||||||
|
self.profile_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.ACCOUNT_CIRCLE_OUTLINED,
|
||||||
|
on_click=self.on_profile_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
self.login_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.LOGIN,
|
||||||
|
on_click=self.on_login_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.page.session.get("user") is not None and '@default.com' not in self.page.session.get("user")['email']:
|
||||||
|
self.profile_placeholder.controls.append(self.profile_btn)
|
||||||
|
else:
|
||||||
|
self.profile_placeholder.controls.append(self.login_btn)
|
||||||
|
self.search_for = self.page.session.get("search_for")
|
||||||
|
if self.search_for:
|
||||||
|
self.searchbar.value = self.page.session.get("search_for")
|
||||||
|
search = self.searchbar.value
|
||||||
|
buffer = []
|
||||||
|
for product in self.products:
|
||||||
|
if search.lower() in product['name'].lower():
|
||||||
|
buffer.append(product)
|
||||||
|
self.products_group.controls.clear()
|
||||||
|
self.add_products(buffer)
|
||||||
|
self.page.session.set("search_for", None)
|
||||||
|
|
||||||
|
def on_acasa_btn_click(self, e):
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def on_login_btn_click(self, e):
|
||||||
|
self.page.go('/auth')
|
||||||
|
|
||||||
|
def add_products(self, products):
|
||||||
|
for product in products:
|
||||||
|
self.new_price = ft.Text(
|
||||||
|
value=f"{round(float(product['price']) - float(product['price'])*float(product['discount'])/100,2)}",
|
||||||
|
size=14 if product['discount'] != 0 else 12,
|
||||||
|
color=ft.Colors.RED if product['discount'] != 0 else ft.Colors.BLACK,
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
print(type(product['discount']))
|
||||||
|
self.old_price = ft.Text(
|
||||||
|
value=f"{product['price']}" if product['discount'] != 0 else '',
|
||||||
|
size=12,
|
||||||
|
color=ft.Colors.GREY,
|
||||||
|
style=ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH),
|
||||||
|
)
|
||||||
|
card = ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Image(src=product['image'], width=170, height=100, border_radius=10, fit=ft.ImageFit.COVER),
|
||||||
|
ft.Text(product['name'], weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(f"Pret:", size=12),
|
||||||
|
self.old_price,
|
||||||
|
self.new_price,
|
||||||
|
ft.Text(f"lei/{product['quantity']}g", size=12),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
spacing=4
|
||||||
|
)
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ink=True,
|
||||||
|
on_click=lambda e, title=product: self.on_product_click(title),
|
||||||
|
padding=5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.products_group.controls.append(card)
|
||||||
|
|
||||||
|
def add_categories(self):
|
||||||
|
categories = self.categories_manager.get_categories()
|
||||||
|
for category in categories:
|
||||||
|
card = ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Image(
|
||||||
|
src=category['image'],
|
||||||
|
width=100,
|
||||||
|
height = 80,
|
||||||
|
border_radius=10,
|
||||||
|
fit=ft.ImageFit.COVER),
|
||||||
|
ft.Text(category['name'])
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ink=True,
|
||||||
|
on_click=lambda e, title=category: self.on_category_click(title),
|
||||||
|
padding=5,
|
||||||
|
width=120,
|
||||||
|
height=120
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.categories_group.controls.append(card)
|
||||||
|
|
||||||
|
def on_category_click(self, cat):
|
||||||
|
name = cat['name'].replace(" ","-").lower()
|
||||||
|
self.page.session.set("category", cat)
|
||||||
|
self.page.go(f"/categorie/{name}")
|
||||||
|
|
||||||
|
def on_product_click(self, product):
|
||||||
|
self.page.session.set("product", product)
|
||||||
|
name = product['name'].replace(" ", "-").lower()
|
||||||
|
self.page.go(f'/produs/{name}')
|
||||||
|
|
||||||
|
def on_profile_btn_click(self, e):
|
||||||
|
self.page.go('/profil')
|
||||||
|
|
||||||
|
def on_cart_btn_click(self, e):
|
||||||
|
self.page.go("/pre_load_cos")
|
||||||
|
|
||||||
|
def on_search_btn_click(self, e):
|
||||||
|
search = self.searchbar.value
|
||||||
|
buffer = []
|
||||||
|
for product in self.products:
|
||||||
|
if search.lower() in product['name'].lower():
|
||||||
|
buffer.append(product)
|
||||||
|
self.products_group.controls.clear()
|
||||||
|
self.add_products(buffer)
|
||||||
|
self.products_group.update()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.searchbar,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SEARCH,
|
||||||
|
on_click=self.on_search_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
),
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.profile_placeholder,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SHOPPING_CART_OUTLINED,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE,
|
||||||
|
on_click = self.on_cart_btn_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
self.banner,
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
self.header,
|
||||||
|
self.categories_group,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Produse Populare", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
self.products_group
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
padding=10,
|
||||||
|
bgcolor=ft.Colors.WHITE
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/products/__pycache__/product.cpython-313.pyc
Normal file
464
UI_V2/pages/products/product.py
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.orders import Orders
|
||||||
|
from dbActions.products import Products
|
||||||
|
from helpers.default_user import DefaultUser
|
||||||
|
|
||||||
|
class ProductPage:
|
||||||
|
def __init__(self, page: ft.Page, shop=''):
|
||||||
|
self.page = page
|
||||||
|
self.shop = shop
|
||||||
|
self.orders = Orders()
|
||||||
|
self.product = self.page.session.get('product')
|
||||||
|
print(self.product)
|
||||||
|
|
||||||
|
self.product_main_image = ft.Image(
|
||||||
|
src = self.product['image'],
|
||||||
|
width=300,
|
||||||
|
height=250,
|
||||||
|
border_radius=10,
|
||||||
|
fit=ft.ImageFit.COVER
|
||||||
|
)
|
||||||
|
|
||||||
|
self.product_name = ft.Text(
|
||||||
|
value = self.product['name'],
|
||||||
|
size=20,
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
self.old_price = ft.Text(
|
||||||
|
value=f"{self.product['price']} Lei" if self.product['discount'] > 0 else '',
|
||||||
|
size=12,
|
||||||
|
color=ft.Colors.GREY,
|
||||||
|
style=ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH)
|
||||||
|
)
|
||||||
|
self.price = ft.Text(
|
||||||
|
value=f"{round(self.product['price'] - self.product['price']*self.product['discount']/100, 2)} Lei",
|
||||||
|
size=17 if self.product['discount'] > 0 else None,
|
||||||
|
color=ft.Colors.RED if self.product['discount'] > 0 else None,
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
|
||||||
|
self.description = ft.Text(
|
||||||
|
value=self.product['description'],
|
||||||
|
width=600
|
||||||
|
)
|
||||||
|
|
||||||
|
self.details = ft.Text(
|
||||||
|
value=self.product['details'],
|
||||||
|
width=600
|
||||||
|
)
|
||||||
|
|
||||||
|
self.quantity = ft.TextField(label="", value="1", width=60)
|
||||||
|
self.quantify_group = ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(ft.Icons.ARROW_CIRCLE_LEFT, on_click=self.remove_quantity),
|
||||||
|
self.quantity,
|
||||||
|
ft.IconButton(ft.Icons.ARROW_CIRCLE_RIGHT, on_click=self.add_quantity),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
aviab = {
|
||||||
|
'in_stock': "In stoc",
|
||||||
|
'in_provider_stock': "In stoc la furnizor",
|
||||||
|
'not_available': "Indisponibil"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.availability = ft.Text(aviab[self.product['aviability']])
|
||||||
|
self.stock_quantity = ft.Text(self.product['quantity'])
|
||||||
|
|
||||||
|
self.similar_products = ft.Row(width=1000, scroll=ft.ScrollMode.ADAPTIVE)
|
||||||
|
for sp in self.get_similar_porducts():
|
||||||
|
product = ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Stack(
|
||||||
|
[
|
||||||
|
ft.Image(
|
||||||
|
src=f"images/{sp['image']}",
|
||||||
|
fit=ft.ImageFit.COVER,
|
||||||
|
repeat=ft.ImageRepeat.NO_REPEAT,
|
||||||
|
border_radius=ft.border_radius.all(5),
|
||||||
|
width=220,
|
||||||
|
height=220
|
||||||
|
),
|
||||||
|
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(f"{sp['name']}", size=12),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
f"{sp['price']} Lei/{sp['quantity']}" if sp['discount']>0 else '',
|
||||||
|
size=12,
|
||||||
|
color=ft.Colors.GREY,
|
||||||
|
style=ft.TextStyle(decoration=ft.TextDecoration.LINE_THROUGH)
|
||||||
|
),
|
||||||
|
ft.Text(
|
||||||
|
f"{sp['price'] - sp['price']*sp['discount']/100} Lei/{sp['quantity']}",
|
||||||
|
size=14,
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
ft.IconButton(ft.Icons.ADD_SHOPPING_CART, on_click=lambda e, i=sp: self.on_sp_add_to_cart_click(i))
|
||||||
|
],
|
||||||
|
width=200,
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
bgcolor=ft.Colors.WHITE,
|
||||||
|
bottom=0,
|
||||||
|
border_radius=ft.border_radius.only(bottom_left=5, bottom_right=5),
|
||||||
|
padding=10
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
|
||||||
|
),
|
||||||
|
padding=10,
|
||||||
|
ink=True,
|
||||||
|
on_click=lambda e, i=sp: self.on_sp_product_click(i)
|
||||||
|
),
|
||||||
|
width=250,
|
||||||
|
height=250,
|
||||||
|
)
|
||||||
|
self.similar_products.controls.append(product)
|
||||||
|
|
||||||
|
self.product_content_image = ft.Column(
|
||||||
|
[
|
||||||
|
ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
self.product_main_image,
|
||||||
|
padding=10
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.product_content_details = ft.Column(
|
||||||
|
[
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Pret:", weight=ft.FontWeight.BOLD),
|
||||||
|
self.old_price,
|
||||||
|
self.price
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Text("Descriere", weight=ft.FontWeight.BOLD),
|
||||||
|
self.description,
|
||||||
|
ft.Text("Detalii", weight=ft.FontWeight.BOLD),
|
||||||
|
self.details,
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Disponibilitate", weight=ft.FontWeight.BOLD),
|
||||||
|
self.availability
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text('Cantitate (g)', weight=ft.FontWeight.BOLD),
|
||||||
|
self.stock_quantity
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.quantify_group,
|
||||||
|
ft.FilledButton(
|
||||||
|
"Adauga in cos",
|
||||||
|
icon=ft.Icons.SHOPPING_CART,
|
||||||
|
width=150,
|
||||||
|
on_click= lambda e, i=self.product: self.on_add_to_cart_btn_click(i)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START
|
||||||
|
)
|
||||||
|
|
||||||
|
self.desktop_container = ft.Row(alignment=ft.MainAxisAlignment.CENTER)
|
||||||
|
self.mobile_container = ft.Column()
|
||||||
|
|
||||||
|
if self.page.width < 600:
|
||||||
|
self.mobile_container.controls.append(self.product_content_image)
|
||||||
|
self.mobile_container.controls.append(self.product_content_details)
|
||||||
|
else:
|
||||||
|
self.desktop_container.controls.append(self.product_content_image)
|
||||||
|
self.desktop_container.controls.append(self.product_content_details)
|
||||||
|
|
||||||
|
self.confirm_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text('Adauga in cos?'),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.SHOPPING_CART, size=100)
|
||||||
|
],
|
||||||
|
height=100,
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Anuleaza",
|
||||||
|
bgcolor=ft.Colors.GREY,
|
||||||
|
on_click=self.on_cancel_btn_click),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Confirma",
|
||||||
|
on_click=self.on_confirm_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.page.add(self.confirm_dialog)
|
||||||
|
|
||||||
|
self.searchbar = ft.TextField(
|
||||||
|
label="Cauta produsul in toate categoriile",
|
||||||
|
expand=True,
|
||||||
|
on_submit=self.on_search_btn_click
|
||||||
|
)
|
||||||
|
|
||||||
|
self.ask_for_login_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Va rugam sa va autentificati!"),
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Daca nu aveti un cont activ, puteti crea unul."),
|
||||||
|
ft.Text("Dupa confirmare, selectati din nou produsul.")
|
||||||
|
],
|
||||||
|
height=50
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Continua fara cont",
|
||||||
|
bgcolor=ft.Colors.GREY,
|
||||||
|
on_click=self.on_cancel_go_to_login_btn_click),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Autentificare",
|
||||||
|
on_click=self.on_confirm_go_to_login_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.selected_item = None
|
||||||
|
|
||||||
|
self.profile_placeholder = ft.Column()
|
||||||
|
self.profile_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.ACCOUNT_CIRCLE_OUTLINED,
|
||||||
|
on_click=self.on_profile_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
self.login_btn = ft.IconButton(
|
||||||
|
icon=ft.Icons.LOGIN,
|
||||||
|
on_click=self.on_login_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
)
|
||||||
|
if self.page.session.get("user") is not None and '@default.com' not in self.page.session.get("user")['email']:
|
||||||
|
self.profile_placeholder.controls.append(self.profile_btn)
|
||||||
|
else:
|
||||||
|
self.profile_placeholder.controls.append(self.login_btn)
|
||||||
|
|
||||||
|
def on_login_btn_click(self, e):
|
||||||
|
self.page.go('/auth')
|
||||||
|
|
||||||
|
def on_cancel_go_to_login_btn_click(self, e):
|
||||||
|
self.page.close(self.ask_for_login_dialog)
|
||||||
|
self.user = DefaultUser(self.page)
|
||||||
|
#print(self.user.default_user)
|
||||||
|
self.page.session.set("user", self.user.default_user)
|
||||||
|
# print(self.page.session.get("user"))
|
||||||
|
# try:
|
||||||
|
# quantity = int(self.quantity.value)
|
||||||
|
# if quantity > 0:
|
||||||
|
# self.page.open(self.confirm_dialog)
|
||||||
|
# self._product = self.selected_item
|
||||||
|
# except Exception as e:
|
||||||
|
# print(e)
|
||||||
|
|
||||||
|
def on_confirm_go_to_login_btn_click(self, e):
|
||||||
|
self.page.close(self.ask_for_login_dialog)
|
||||||
|
self.page.go('/auth')
|
||||||
|
|
||||||
|
def on_search_btn_click(self, e):
|
||||||
|
self.page.session.set("search_for", self.searchbar.value)
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def on_profile_btn_click(self, e):
|
||||||
|
self.page.go('/profil')
|
||||||
|
|
||||||
|
def on_cart_btn_click(self, e):
|
||||||
|
if self.page.session.get('user') == None:
|
||||||
|
self.page.go("/pre_load_cos")
|
||||||
|
else:
|
||||||
|
self.page.go("/cos")
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.confirm_dialog)
|
||||||
|
|
||||||
|
def on_confirm_btn_click(self, e):
|
||||||
|
item = self.product
|
||||||
|
self.page.close(self.confirm_dialog)
|
||||||
|
user = self.page.session.get('user')
|
||||||
|
print("user:", user)
|
||||||
|
if user:
|
||||||
|
_cart = self.orders.get_on_hold_order(user['id'])
|
||||||
|
if not _cart:
|
||||||
|
self.orders.add_order(user['id'])
|
||||||
|
_cart = self.orders.get_on_hold_order(user['id'])
|
||||||
|
self.add_product_if_not_exists(item, _cart)
|
||||||
|
|
||||||
|
def add_product_if_not_exists(self, product, cart):
|
||||||
|
user = self.page.session.get('user')
|
||||||
|
self.on_hold_orders = self.orders.get_on_hold_order(user['id'])
|
||||||
|
self.order_products = self.orders.get_order_products(self.on_hold_orders['id'])
|
||||||
|
found = False
|
||||||
|
for order_product in self.order_products:
|
||||||
|
if str(product['id']) == str(order_product['prdouct_id']):
|
||||||
|
quantity = order_product['quantity'] + int(self.quantity.value)
|
||||||
|
self.orders.update_order_map_quantity(order_product['id'], quantity)
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
self.orders.add_product_to_order(product['id'], cart['id'], int(self.quantity.value))
|
||||||
|
|
||||||
|
def get_similar_porducts(self):
|
||||||
|
productsDB = Products()
|
||||||
|
similar_products = productsDB.get_all_by_category(self.product['category_id'])
|
||||||
|
products = []
|
||||||
|
|
||||||
|
for product in similar_products:
|
||||||
|
if product['id'] != self.product['id']:
|
||||||
|
products.append(product)
|
||||||
|
return products
|
||||||
|
|
||||||
|
def remove_quantity(self, e):
|
||||||
|
buffer = int(self.quantity.value)
|
||||||
|
if buffer > 0:
|
||||||
|
self.quantity.value = str(buffer-1)
|
||||||
|
self.quantity.update()
|
||||||
|
self.product['quantity'] -=1
|
||||||
|
|
||||||
|
def add_quantity(self, e):
|
||||||
|
buffer = int(self.quantity.value)
|
||||||
|
self.quantity.value = str(buffer+1)
|
||||||
|
self.quantity.update()
|
||||||
|
self.product['quantity'] +=1
|
||||||
|
|
||||||
|
def on_chanage_image_click(self, item):
|
||||||
|
self.product_main_image.src = 'images/'+item
|
||||||
|
self.product_main_image.update()
|
||||||
|
print(self.product_main_image.src)
|
||||||
|
|
||||||
|
def on_home_btn_click(self, e):
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def on_sp_product_click(self, product):
|
||||||
|
self.page.session.set('product', product)
|
||||||
|
|
||||||
|
def ask_for_create_user(self):
|
||||||
|
self.page.open(self.ask_for_login_dialog)
|
||||||
|
|
||||||
|
def on_add_to_cart_btn_click(self, item):
|
||||||
|
self.selected_item = item
|
||||||
|
user = self.page.session.get('user')
|
||||||
|
print(user)
|
||||||
|
if not user:
|
||||||
|
self.ask_for_create_user()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
quantity = int(self.quantity.value)
|
||||||
|
if quantity > 0:
|
||||||
|
self.page.open(self.confirm_dialog)
|
||||||
|
self._product = self.selected_item
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def on_sp_add_to_cart_click(self, item):
|
||||||
|
user = self.page.session.get('user')
|
||||||
|
if not user:
|
||||||
|
self.page.go('/login')
|
||||||
|
self.page.client_storage.remove("remembered_token")
|
||||||
|
self.page.client_storage.remove("remembered_user")
|
||||||
|
self.page.session.remove('user')
|
||||||
|
_cart = self.orders.get_on_hold_order(user['id'])
|
||||||
|
if not _cart:
|
||||||
|
self.orders.add_order(user['id'])
|
||||||
|
_cart = self.orders.get_on_hold_order(user['id'])
|
||||||
|
self.orders.add_product_to_order(item['id'], _cart['id'], 1)
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
self.searchbar,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SEARCH,
|
||||||
|
on_click=self.on_search_btn_click,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE
|
||||||
|
),
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
self.profile_placeholder,
|
||||||
|
ft.IconButton(
|
||||||
|
icon=ft.Icons.SHOPPING_CART_OUTLINED,
|
||||||
|
bgcolor=ft.Colors.BROWN,
|
||||||
|
icon_color=ft.Colors.WHITE,
|
||||||
|
on_click=self.on_cart_btn_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.IconButton(
|
||||||
|
on_click=self.on_home_btn_click,
|
||||||
|
icon=ft.Icons.HOME
|
||||||
|
),
|
||||||
|
self.product_name,
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
expand=True,
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
ft.Container(
|
||||||
|
content= ft.Column(
|
||||||
|
[
|
||||||
|
self.desktop_container,
|
||||||
|
self.mobile_container,
|
||||||
|
],
|
||||||
|
alignment=ft.CrossAxisAlignment.START,
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
padding=10
|
||||||
|
),
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Produse similare", size=17, weight=ft.FontWeight.BOLD),
|
||||||
|
self.similar_products,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
padding=10
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text()
|
||||||
|
],
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
],
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
width=1000
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
bgcolor=ft.Colors.WHITE,
|
||||||
|
padding=ft.padding.only(left=10, right=10)
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/profile/__pycache__/profilepage.cpython-313.pyc
Normal file
208
UI_V2/pages/profile/profilepage.py
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.users import Users
|
||||||
|
from dbActions.company import Company
|
||||||
|
|
||||||
|
class ProfilePage:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.user_manager = Users()
|
||||||
|
self.company_manager = Company()
|
||||||
|
self.user = self.page.session.get("user")
|
||||||
|
self.company = self.company_manager.get_company(self.user['id'])
|
||||||
|
self.user_name = ft.TextField(label="Nume si Prenume", value=self.user['name'])
|
||||||
|
self.email = ft.TextField(label="E-mail", value=self.user['email'], read_only=True)
|
||||||
|
self.phone = ft.TextField(label="Telefon", value=self.user['phone'])
|
||||||
|
self.address = ft.TextField(
|
||||||
|
label="Adresa",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value = self.user['address'].split("~")[0] if self.user['address'] else ''
|
||||||
|
)
|
||||||
|
self.company_name = ft.TextField(
|
||||||
|
label="Denumire firma",
|
||||||
|
value=self.company['name'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.vat = ft.TextField(
|
||||||
|
label="CUI",
|
||||||
|
value=self.company['vat'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.register_number = ft.TextField(
|
||||||
|
label="Numar registru comert",
|
||||||
|
value=self.company['register_number'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.company_address = ft.TextField(
|
||||||
|
label="Sediu",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value=self.company['address'] if self.company else '')
|
||||||
|
self.second_address_placeholder = ft.Column()
|
||||||
|
self.second_address = ft.TextField(
|
||||||
|
label="Adresa de livrare",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value=self.user['address'].split("~")[1] if self.user['address'] and len(self.user['address'].split("~"))>1 else ''
|
||||||
|
)
|
||||||
|
self.second_address_placeholder =ft.Column()
|
||||||
|
self.order_placeholder =ft.Column()
|
||||||
|
self.error_message = ft.Text("", text_align=ft.TextAlign.CENTER)
|
||||||
|
|
||||||
|
if self.user['address'] and len(self.user['address'].split("~"))>1 :
|
||||||
|
self.is_second_address = True
|
||||||
|
self.second_address_placeholder.controls.append(self.second_address)
|
||||||
|
if self.company:
|
||||||
|
self.is_company = True
|
||||||
|
self.order_placeholder.controls.append(self.company_name)
|
||||||
|
self.order_placeholder.controls.append(self.vat)
|
||||||
|
self.order_placeholder.controls.append(self.register_number)
|
||||||
|
self.order_placeholder.controls.append(self.company_address)
|
||||||
|
|
||||||
|
self.is_company = False
|
||||||
|
self.is_second_address = False
|
||||||
|
|
||||||
|
def on_second_address_btn_click(self, e):
|
||||||
|
self.is_second_address = True
|
||||||
|
self.second_address_placeholder.controls.append(self.second_address)
|
||||||
|
self.second_address_placeholder.update()
|
||||||
|
|
||||||
|
def on_order_btn_click(self, e):
|
||||||
|
self.is_company = True
|
||||||
|
self.order_placeholder.controls.append(self.company_name)
|
||||||
|
self.order_placeholder.controls.append(self.vat)
|
||||||
|
self.order_placeholder.controls.append(self.register_number)
|
||||||
|
self.order_placeholder.controls.append(self.company_address)
|
||||||
|
self.order_placeholder.update()
|
||||||
|
|
||||||
|
def check_inserted_user_data(self, username, phone, address):
|
||||||
|
found = False
|
||||||
|
if username is None or len(username)< 1:
|
||||||
|
found = True
|
||||||
|
if phone is None or len(phone)< 1:
|
||||||
|
found = True
|
||||||
|
if address is None or len(address)< 1:
|
||||||
|
found = True
|
||||||
|
if found:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
return found
|
||||||
|
|
||||||
|
def check_second_address_inserted(self, address):
|
||||||
|
if address is None or len(address)< 1:
|
||||||
|
self.error_message.value = "Va rugam inserati adresa de livrare!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_company_data_inserted(self, company_name, vat, register_number, company_address):
|
||||||
|
found = False
|
||||||
|
if company_name is None or len(company_name)< 1:
|
||||||
|
found = True
|
||||||
|
if vat is None or len(vat)< 1:
|
||||||
|
found = True
|
||||||
|
if register_number is None or len(register_number)< 1:
|
||||||
|
found = True
|
||||||
|
if company_address is None or len(company_address)< 1:
|
||||||
|
found = True
|
||||||
|
if found:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
return found
|
||||||
|
|
||||||
|
def on_save_btn_click(self, e):
|
||||||
|
username = self.user_name.value
|
||||||
|
phone = self.phone.value
|
||||||
|
address = self.address.value
|
||||||
|
if self.is_second_address:
|
||||||
|
if self.check_second_address_inserted(self.second_address.value):
|
||||||
|
return
|
||||||
|
address = self.address.value + '~' + self.second_address.value
|
||||||
|
if self.check_inserted_user_data(username, phone, address):
|
||||||
|
return
|
||||||
|
self.user_manager.update_user_data(username, phone, address, self.user['id'])
|
||||||
|
|
||||||
|
if self.is_company:
|
||||||
|
if self.check_company_data_inserted(self.company_name.value, self.vat.value, self.register_number.value, self.company_address.value):
|
||||||
|
return
|
||||||
|
if self.company:
|
||||||
|
company['name'] = self.company_name.value
|
||||||
|
company['vat'] = self.vat.value
|
||||||
|
company['register_number'] = self.register_number.value
|
||||||
|
company['address'] = self.company_address.value
|
||||||
|
self.company_manager.update_company(company)
|
||||||
|
else:
|
||||||
|
company = {
|
||||||
|
'user_id' : self.user['id'],
|
||||||
|
'name': self.company_name.value,
|
||||||
|
'vat': self.vat.value,
|
||||||
|
'register_number': self.register_number.value,
|
||||||
|
'address': self.company_address.value
|
||||||
|
}
|
||||||
|
self.company_manager.add_company(company)
|
||||||
|
|
||||||
|
self.error_message.value = "Profilul a fost salvat!"
|
||||||
|
self.error_message.color = ft.Colors.GREEN
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
def on_back_btn_click(self, e):
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def on_logout_btn_click(self, e):
|
||||||
|
self.page.session.clear()
|
||||||
|
self.page.go('/')
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Inapoi",
|
||||||
|
icon=ft.Icons.ARROW_BACK,
|
||||||
|
on_click = self.on_back_btn_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.END
|
||||||
|
),
|
||||||
|
ft.Icon(name=ft.Icons.ACCOUNT_CIRCLE, size=100),
|
||||||
|
self.user_name,
|
||||||
|
self.email,
|
||||||
|
self.phone,
|
||||||
|
self.address,
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER),
|
||||||
|
ft.Button("Adauga adresa livrare", width=400, on_click=self.on_second_address_btn_click),
|
||||||
|
self.second_address_placeholder,
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Button("Doriti Factura?", width=400, on_click=self.on_order_btn_click),
|
||||||
|
self.order_placeholder,
|
||||||
|
self.error_message,
|
||||||
|
ft.FilledButton(
|
||||||
|
"Salveaza",
|
||||||
|
icon=ft.Icons.SAVE,
|
||||||
|
on_click=self.on_save_btn_click,
|
||||||
|
width=400
|
||||||
|
),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Deconectare",
|
||||||
|
icon=ft.Icons.LOGOUT,
|
||||||
|
on_click=self.on_logout_btn_click,
|
||||||
|
width=400,
|
||||||
|
bgcolor=ft.Colors.GREY
|
||||||
|
),
|
||||||
|
ft.Text("")
|
||||||
|
],
|
||||||
|
width=400,
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE
|
||||||
|
),
|
||||||
|
padding=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
BIN
UI_V2/pages/shopping_cart/__pycache__/cart.cpython-313.pyc
Normal file
535
UI_V2/pages/shopping_cart/cart.py
Normal file
@@ -0,0 +1,535 @@
|
|||||||
|
import flet as ft
|
||||||
|
from dbActions.orders import Orders
|
||||||
|
from dbActions.products import Products
|
||||||
|
from dbActions.company import Company
|
||||||
|
from dbActions.users import Users
|
||||||
|
|
||||||
|
class Cart:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.user = self.page.session.get('user')
|
||||||
|
self.orders = Orders()
|
||||||
|
self.productsDB = Products()
|
||||||
|
self.company_manager = Company()
|
||||||
|
self.user_manager = Users()
|
||||||
|
self.products = []
|
||||||
|
self.is_second_address = None
|
||||||
|
self.is_company = None
|
||||||
|
|
||||||
|
self.delete_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Stergeti?"),
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton("Da", on_click=self.on_confirm_delete_btn_click),
|
||||||
|
ft.FilledButton("Nu", on_click=self.on_cancel_delete_btn_click, bgcolor=ft.Colors.GREY)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.on_hold_orders = self.orders.get_on_hold_order(self.user['id'])
|
||||||
|
if self.on_hold_orders:
|
||||||
|
self.order_products = self.orders.get_order_products(self.on_hold_orders['id'])
|
||||||
|
|
||||||
|
for product in self.order_products:
|
||||||
|
self.products.append(
|
||||||
|
{
|
||||||
|
'product':self.productsDB.get(product['prdouct_id']),
|
||||||
|
'quantity':product['quantity']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.product_list = ft.ListView(
|
||||||
|
controls=self.create_list(self.products, self.on_delete_product_click),
|
||||||
|
spacing=10,
|
||||||
|
expand=5
|
||||||
|
)
|
||||||
|
|
||||||
|
self.payment = ft.RadioGroup(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Radio(value="ramburs", label="Ramburs la curier"),
|
||||||
|
ft.Radio(value="plata_online_cu_cardul", label="Plata online cu cardul"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
on_change=self.on_payment_value_change
|
||||||
|
)
|
||||||
|
self.payment_message = ft.Text("")
|
||||||
|
self.error_message = ft.Text(
|
||||||
|
color=ft.Colors.RED
|
||||||
|
)
|
||||||
|
|
||||||
|
self.confirm_dialog = ft.AlertDialog(
|
||||||
|
title=ft.Text("Confirma"),
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Da",
|
||||||
|
on_click=self.on_confim_btn_click
|
||||||
|
),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Nu",
|
||||||
|
on_click=self.on_cancel_btn_click,
|
||||||
|
bgcolor=ft.Colors.GREY
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if '@default.com' not in self.user['email']:
|
||||||
|
self.all_orders = self.orders.get_orders_for_user(self.page.session.get('user')['id'])
|
||||||
|
self.all_orders = self.all_orders[::-1]
|
||||||
|
self.orders_list = ft.ListView(
|
||||||
|
controls=self.create_history_list(self.all_orders),
|
||||||
|
spacing=10,
|
||||||
|
expand=True
|
||||||
|
)
|
||||||
|
self.user = self.page.session.get("user")
|
||||||
|
self.company = self.company_manager.get_company(self.user['id'])
|
||||||
|
self.user_name = ft.TextField(
|
||||||
|
label="Nume si Prenume",
|
||||||
|
value=self.user['name'] if "@default.com" not in self.user['email'] else None
|
||||||
|
)
|
||||||
|
self.email = ft.TextField(
|
||||||
|
label="E-mail",
|
||||||
|
value=self.user['email'] if "@default.com" not in self.user['email'] else None,
|
||||||
|
read_only=True
|
||||||
|
)
|
||||||
|
self.phone = ft.TextField(
|
||||||
|
label="Telefon",
|
||||||
|
value=self.user['phone'] if "@default.com" not in self.user['email'] else None
|
||||||
|
)
|
||||||
|
self.address = ft.TextField(
|
||||||
|
label="Adresa",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value = self.user['address'].split("~")[0] if self.user['address'] else ''
|
||||||
|
)
|
||||||
|
self.company_name = ft.TextField(
|
||||||
|
label="Denumire firma",
|
||||||
|
value=self.company['name'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.vat = ft.TextField(
|
||||||
|
label="CUI",
|
||||||
|
value=self.company['vat'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.register_number = ft.TextField(
|
||||||
|
label="Numar registru comert",
|
||||||
|
value=self.company['register_number'] if self.company else ''
|
||||||
|
)
|
||||||
|
self.company_address = ft.TextField(
|
||||||
|
label="Sediu",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value=self.company['address'] if self.company else '')
|
||||||
|
self.second_address_placeholder = ft.Column()
|
||||||
|
self.second_address = ft.TextField(
|
||||||
|
label="Adresa de livrare",
|
||||||
|
multiline=True,
|
||||||
|
min_lines=3,
|
||||||
|
max_lines=5,
|
||||||
|
value=self.user['address'].split("~")[1] if self.user['address'] and len(self.user['address'].split("~"))>1 else ''
|
||||||
|
)
|
||||||
|
self.second_address_placeholder =ft.Column()
|
||||||
|
self.order_placeholder =ft.Column()
|
||||||
|
|
||||||
|
if self.user['address'] and len(self.user['address'].split("~"))>1 :
|
||||||
|
self.is_second_address = True
|
||||||
|
self.second_address_placeholder.controls.append(self.second_address)
|
||||||
|
if self.company:
|
||||||
|
self.is_company = True
|
||||||
|
self.order_placeholder.controls.append(self.company_name)
|
||||||
|
self.order_placeholder.controls.append(self.vat)
|
||||||
|
self.order_placeholder.controls.append(self.register_number)
|
||||||
|
self.order_placeholder.controls.append(self.company_address)
|
||||||
|
|
||||||
|
self.delivery_details = ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
"Detaili de livrare",
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
),
|
||||||
|
self.user_name,
|
||||||
|
self.email,
|
||||||
|
self.phone,
|
||||||
|
self.address,
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Text("Adresa de livrare difera de adresa de domiciliu?", text_align=ft.TextAlign.CENTER),
|
||||||
|
ft.Button("Adauga adresa livrare", width=500, on_click=self.on_second_address_btn_click),
|
||||||
|
self.second_address_placeholder,
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Button("Doriti Factura?", width=500, on_click=self.on_order_btn_click),
|
||||||
|
self.order_placeholder,
|
||||||
|
|
||||||
|
ft.Text(
|
||||||
|
"Metoda de plata",
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
),
|
||||||
|
self.payment,
|
||||||
|
self.payment_message,
|
||||||
|
ft.Row([self.error_message],alignment=ft.MainAxisAlignment.CENTER),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Comanda",
|
||||||
|
width=150,
|
||||||
|
on_click=self.open_confirm_dialog
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
],
|
||||||
|
expand=5,
|
||||||
|
alignment=ft.MainAxisAlignment.START
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.page.width < 600:
|
||||||
|
self.cart_placeholder = ft.Column(
|
||||||
|
[
|
||||||
|
self.product_list,
|
||||||
|
self.delivery_details
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.cart_placeholder = ft.Row(
|
||||||
|
[
|
||||||
|
self.product_list,
|
||||||
|
self.delivery_details
|
||||||
|
],
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START
|
||||||
|
)
|
||||||
|
self.order_list_placeholder = ft.Column()
|
||||||
|
if '@default.com' not in self.user['email']:
|
||||||
|
self.order_list_placeholder.controls.append(
|
||||||
|
ft.Text(
|
||||||
|
"Istoric comenzi",
|
||||||
|
weight=ft.FontWeight.BOLD
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.order_list_placeholder.controls.append(self.orders_list)
|
||||||
|
|
||||||
|
self.item_to_be_deleted = None
|
||||||
|
|
||||||
|
self.page.add(self.delete_dialog)
|
||||||
|
self.page.add(self.confirm_dialog)
|
||||||
|
|
||||||
|
def on_second_address_btn_click(self, e):
|
||||||
|
self.is_second_address = True
|
||||||
|
self.second_address_placeholder.controls.append(self.second_address)
|
||||||
|
self.second_address_placeholder.update()
|
||||||
|
|
||||||
|
def on_order_btn_click(self, e):
|
||||||
|
self.is_company = True
|
||||||
|
self.order_placeholder.controls.append(self.company_name)
|
||||||
|
self.order_placeholder.controls.append(self.vat)
|
||||||
|
self.order_placeholder.controls.append(self.register_number)
|
||||||
|
self.order_placeholder.controls.append(self.company_address)
|
||||||
|
self.order_placeholder.update()
|
||||||
|
|
||||||
|
def on_payment_value_change(self, e):
|
||||||
|
print(e.data)
|
||||||
|
if e.data == 'plata_online_cu_cardul':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def order_products(self, id):
|
||||||
|
products = self.orders.get_order_products(id)
|
||||||
|
all_products = []
|
||||||
|
for product in products:
|
||||||
|
name = self.productsDB.get(product['prdouct_id'])['name']
|
||||||
|
if name not in all_products:
|
||||||
|
all_products.append(name)
|
||||||
|
return ft.Text(
|
||||||
|
value=' '.join(all_products)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_go_back_button_click(self, e):
|
||||||
|
self.page.go("/")
|
||||||
|
|
||||||
|
def create_list(self, items, on_click_handler):
|
||||||
|
"""Helper to create list items for a column."""
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Row(
|
||||||
|
[
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.ARROW_RIGHT, size=20),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Image(
|
||||||
|
src=item['product']['image'],
|
||||||
|
width=100,
|
||||||
|
height=100,
|
||||||
|
fit=ft.ImageFit.COVER,
|
||||||
|
border_radius=10
|
||||||
|
),
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text(f"Denumire Produs: {item['product']['name']}", weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(f"Cantitate: {item['quantity']}"),
|
||||||
|
ft.Text(f"Descriere: {item['product']['description']}", size=12 , color=ft.Colors.GREY),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton("Sterge",on_click=lambda e, id=item: on_click_handler(id), bgcolor=ft.Colors.RED),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
def create_history_list(self, items):
|
||||||
|
return [
|
||||||
|
ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.icons.ARROW_RIGHT, size=20),
|
||||||
|
ft.Text(value="Numar comanda: "),
|
||||||
|
ft.Text(value=item['id'])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value='Status: '
|
||||||
|
),
|
||||||
|
ft.Text(
|
||||||
|
value=item['status']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text(
|
||||||
|
value='Produse: '
|
||||||
|
),
|
||||||
|
#self.order_products(item['id'])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.START,
|
||||||
|
),
|
||||||
|
width=300,
|
||||||
|
bgcolor=ft.Colors.BROWN_50,
|
||||||
|
padding=10,
|
||||||
|
border_radius=8,
|
||||||
|
border = ft.border.all(1, ft.Colors.GREY),
|
||||||
|
)
|
||||||
|
for item in items
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def on_delete_product_click(self, item):
|
||||||
|
print('Delte item', item)
|
||||||
|
|
||||||
|
self.page.open(self.delete_dialog)
|
||||||
|
self.page.update()
|
||||||
|
self.item_to_be_deleted = item['product']['id']
|
||||||
|
|
||||||
|
def on_confirm_delete_btn_click(self, e):
|
||||||
|
print("confirm delete item", self.item_to_be_deleted)
|
||||||
|
#remove item
|
||||||
|
self.orders.remove_product_from_order(self.on_hold_orders['id'], self.item_to_be_deleted)
|
||||||
|
#update list
|
||||||
|
self.order_products = self.orders.get_order_products(self.on_hold_orders['id'])
|
||||||
|
self.products = []
|
||||||
|
for product in self.order_products:
|
||||||
|
self.products.append(
|
||||||
|
{
|
||||||
|
'product':self.productsDB.get(product['prdouct_id']),
|
||||||
|
'quantity':product['quantity']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.product_list.controls.clear()
|
||||||
|
self.product_list.controls = self.create_list(self.products, self.on_delete_product_click)
|
||||||
|
self.product_list.update()
|
||||||
|
|
||||||
|
self.item_to_be_deleted = None
|
||||||
|
self.page.close(self.delete_dialog)
|
||||||
|
|
||||||
|
def on_cancel_delete_btn_click(self, e):
|
||||||
|
print("cancel item deletion: ", self.item_to_be_deleted)
|
||||||
|
self.item_to_be_deleted = None
|
||||||
|
self.page.close(self.delete_dialog)
|
||||||
|
|
||||||
|
|
||||||
|
def open_confirm_dialog(self, e):
|
||||||
|
print('open dialog')
|
||||||
|
print(self.on_hold_orders)
|
||||||
|
if self.on_hold_orders:
|
||||||
|
self.error_message.value = ''
|
||||||
|
self.error_message.update()
|
||||||
|
self.page.open(self.confirm_dialog)
|
||||||
|
else:
|
||||||
|
self.error_message.value = "Nu aveti nici un produs in cos!"
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
def on_cancel_btn_click(self, e):
|
||||||
|
self.page.close(self.confirm_dialog)
|
||||||
|
|
||||||
|
def check_second_address_inserted(self, address):
|
||||||
|
if address is None or len(address)< 1:
|
||||||
|
self.error_message.value = "Va rugam inserati adresa de livrare!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_company_data_inserted(self, company_name, vat, register_number, company_address):
|
||||||
|
found = False
|
||||||
|
if company_name is None or len(company_name)< 1:
|
||||||
|
found = True
|
||||||
|
if vat is None or len(vat)< 1:
|
||||||
|
found = True
|
||||||
|
if register_number is None or len(register_number)< 1:
|
||||||
|
found = True
|
||||||
|
if company_address is None or len(company_address)< 1:
|
||||||
|
found = True
|
||||||
|
if found:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
return found
|
||||||
|
|
||||||
|
def check_inserted_user_data(self, username, phone, address):
|
||||||
|
found = False
|
||||||
|
if username is None or len(username)< 1:
|
||||||
|
found = True
|
||||||
|
if phone is None or len(phone)< 1:
|
||||||
|
found = True
|
||||||
|
if address is None or len(address)< 1:
|
||||||
|
found = True
|
||||||
|
if found:
|
||||||
|
self.error_message.value = "Toate campurile sunt obligatori!"
|
||||||
|
self.error_message.color = ft.Colors.RED
|
||||||
|
self.error_message.update()
|
||||||
|
|
||||||
|
return found
|
||||||
|
|
||||||
|
def create_update_user_details(self):
|
||||||
|
username = self.user_name.value
|
||||||
|
phone = self.phone.value
|
||||||
|
address = self.address.value
|
||||||
|
|
||||||
|
if self.is_second_address:
|
||||||
|
if self.check_second_address_inserted(self.second_address.value):
|
||||||
|
return
|
||||||
|
address = self.address.value + '~' + self.second_address.value
|
||||||
|
if self.check_inserted_user_data(username, phone, address):
|
||||||
|
return
|
||||||
|
|
||||||
|
if '@default.com' in self.user['email']:
|
||||||
|
self.user = self.user_manager.invite_user()
|
||||||
|
self.page.session.set("user", self.user)
|
||||||
|
else:
|
||||||
|
self.user_manager.update_user_data(username, phone, address, self.user['id'])
|
||||||
|
|
||||||
|
if self.is_company:
|
||||||
|
if self.check_company_data_inserted(self.company_name.value, self.vat.value, self.register_number.value, self.company_address.value):
|
||||||
|
return
|
||||||
|
if self.company:
|
||||||
|
company['name'] = self.company_name.value
|
||||||
|
company['vat'] = self.vat.value
|
||||||
|
company['register_number'] = self.register_number.value
|
||||||
|
company['address'] = self.company_address.value
|
||||||
|
self.company_manager.update_company(company)
|
||||||
|
else:
|
||||||
|
company = {
|
||||||
|
'user_id' : self.user['id'],
|
||||||
|
'name': self.company_name.value,
|
||||||
|
'vat': self.vat.value,
|
||||||
|
'register_number': self.register_number.value,
|
||||||
|
'address': self.company_address.value
|
||||||
|
}
|
||||||
|
self.company_manager.add_company(company)
|
||||||
|
|
||||||
|
def on_confim_btn_click(self, e):
|
||||||
|
#create / update user details:
|
||||||
|
self.create_update_user_details()
|
||||||
|
|
||||||
|
print('confirm')
|
||||||
|
self.page.close(self.confirm_dialog)
|
||||||
|
self.orders.update_order_status("new", self.on_hold_orders['id'])
|
||||||
|
|
||||||
|
self.products = []
|
||||||
|
self.on_hold_orders = self.orders.get_on_hold_order(self.user['id'])
|
||||||
|
if self.on_hold_orders:
|
||||||
|
self.order_products = self.orders.get_order_products(self.on_hold_orders['id'])
|
||||||
|
|
||||||
|
for product in self.order_products:
|
||||||
|
self.products.append(
|
||||||
|
{
|
||||||
|
'product':self.productsDB.get(product['prdouct_id']),
|
||||||
|
'quantity':product['quantity']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.product_list.controls.clear()
|
||||||
|
self.product_list.controls = self.create_list(self.products, self.on_delete_product_click)
|
||||||
|
self.product_list.update()
|
||||||
|
if '@default.com' not in self.user['email']:
|
||||||
|
self.all_orders = self.orders.get_orders_for_user(self.page.session.get('user')['id'])
|
||||||
|
self.all_orders = self.all_orders[::-1]
|
||||||
|
self.orders_list.controls.clear()
|
||||||
|
self.orders_list.controls = self.create_history_list(self.all_orders)
|
||||||
|
self.orders_list.update()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(
|
||||||
|
ft.Icons.SHOPPING_CART_CHECKOUT,
|
||||||
|
size=40,
|
||||||
|
),
|
||||||
|
ft.Text(
|
||||||
|
"Produse adugate:",
|
||||||
|
weight=ft.FontWeight.BOLD,
|
||||||
|
size=15
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
ft.FilledButton(
|
||||||
|
text="Inapoi",
|
||||||
|
icon=ft.Icons.ARROW_BACK,
|
||||||
|
on_click=self.on_go_back_button_click
|
||||||
|
)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
|
||||||
|
),
|
||||||
|
ft.Divider(),
|
||||||
|
self.cart_placeholder,
|
||||||
|
self.order_list_placeholder,
|
||||||
|
ft.Text()
|
||||||
|
],
|
||||||
|
expand=True,
|
||||||
|
scroll=ft.ScrollMode.ADAPTIVE
|
||||||
|
),
|
||||||
|
expand=True,
|
||||||
|
width=1000,
|
||||||
|
padding=10
|
||||||
|
)
|
||||||
60
UI_V2/pages/shopping_cart/peload_card.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import flet as ft
|
||||||
|
from helpers.default_user import DefaultUser
|
||||||
|
|
||||||
|
class PreloadCard:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
self.ask_for_login_dialog = ft.AlertDialog(
|
||||||
|
title="Va rugam sa va autentificati",
|
||||||
|
content=ft.Text("Daca nu aveti un cont activ, puteti crea unul."),
|
||||||
|
actions=[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Continua fara cont",
|
||||||
|
bgcolor=ft.Colors.GREY,
|
||||||
|
on_click=self.on_cancel_go_to_login_btn_click),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Autentificare",
|
||||||
|
on_click=self.on_confirm_go_to_login_btn_click)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_cancel_go_to_login_btn_click(self, e):
|
||||||
|
#self.page.close(self.ask_for_login_dialog)
|
||||||
|
self.user = DefaultUser(self.page)
|
||||||
|
self.page.session.set("user", self.user.default_user)
|
||||||
|
self.page.go('/cos')
|
||||||
|
|
||||||
|
def on_confirm_go_to_login_btn_click(self, e):
|
||||||
|
#self.page.close(self.ask_for_login_dialog)
|
||||||
|
self.page.go('/auth')
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
if not self.page.session.get("user"):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Va rugam sa va autentificati", size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text("Daca nu aveti un cont activ, puteti crea unul."),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.FilledButton(
|
||||||
|
"Continua fara cont",
|
||||||
|
bgcolor=ft.Colors.GREY,
|
||||||
|
on_click=self.on_cancel_go_to_login_btn_click),
|
||||||
|
ft.FilledButton(
|
||||||
|
"Autentificare",
|
||||||
|
on_click=self.on_confirm_go_to_login_btn_click)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
#self.page.open(self.ask_for_login_dialog)
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER,
|
||||||
|
horizontal_alignment=ft.CrossAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ft.Container(
|
||||||
|
content=self.page.go('/cos')
|
||||||
|
)
|
||||||
|
|
||||||
1
UI_V2/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
flet==0.28.3
|
||||||