intrgrating suggestions after betta 1

This commit is contained in:
2025-09-08 18:06:00 +03:00
parent eb262451ad
commit 106045d72a
34 changed files with 1549 additions and 146 deletions

View File

@@ -58,6 +58,11 @@ class ArchiveInPage:
self.selected_delete_order = order
self.page.open(self.delete_dialog)
def download_order_file(self, order):
file = order['file']
url=f"{API_BASE_URL}/orders_in/files/{file}"
self.page.launch_url(url)
def load_orders(self):
try:
token = self.page.client_storage.get("token")
@@ -90,6 +95,11 @@ class ArchiveInPage:
self.orders_list.controls.clear()
for order in self.orders:
client = self.get_client(order['client_id'])
buttons = []
if order.get("file"):
buttons.append(ft.Button("Download", icon=ft.Icons.DOWNLOAD, on_click=lambda e, o=order: self.download_order_file(o)))
buttons.append(ft.Button("View", icon=ft.Icons.PICTURE_AS_PDF, on_click=lambda e, o=order: self.view_order(o)))
buttons.append(ft.Button("Delete", icon=ft.Icons.CANCEL, on_click=lambda e, o=order: self.cancel_order(o)))
self.orders_list.controls.append(
ft.Container(
content=ft.Row([
@@ -97,8 +107,7 @@ class ArchiveInPage:
ft.Text(f"{client}", size=16, weight=ft.FontWeight.BOLD),
ft.Text(f"Order Number: {order['order_number']}", size=14),
], expand=True),
ft.Button("View",icon=ft.Icons.PICTURE_AS_PDF, on_click=lambda e, o=order: self.view_order(o)),
ft.Button("Delete", icon=ft.Icons.CANCEL, on_click=lambda e, o=order: self.cancel_order(o))
*buttons
]),
padding=10,
border=ft.border.all(1, ft.Colors.GREY_300),
@@ -108,6 +117,18 @@ class ArchiveInPage:
)
self.page.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['orders_in'] == 1 else False
def build(self):
self.refresh()
return ft.Container(
@@ -115,7 +136,7 @@ class ArchiveInPage:
[
ft.Row(
[
ft.Text("Archive", size=24, weight=ft.FontWeight.BOLD),
ft.Text("Archive Orders In", size=24, weight=ft.FontWeight.BOLD),
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
@@ -123,4 +144,30 @@ class ArchiveInPage:
self.orders_list
]
)
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Archive Orders In", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -107,6 +107,18 @@ class ArchivePage:
self.dashboard.placeholder.content = self.order_page.build()
self.dashboard.placeholder.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['orders_out'] == 1 else False
def build(self):
self.refresh()
return ft.Container(
@@ -114,7 +126,7 @@ class ArchivePage:
[
ft.Row(
[
ft.Text("Archive", size=24, weight=ft.FontWeight.BOLD),
ft.Text("Archive Orders Out", size=24, weight=ft.FontWeight.BOLD),
ft.Button("Back", icon=ft.Icons.ARROW_BACK_IOS_NEW, on_click=self.on_go_back_btn_click)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
@@ -122,4 +134,30 @@ class ArchivePage:
self.orders_list
]
)
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Archive Orders Out", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -265,6 +265,18 @@ class ClientsPage:
except Exception as e:
print("Error loading subscription:", e)
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['clients'] == 1 else False
def build(self):
self.load_clients()
self.subscription = self.get_current_subscription_plan()
@@ -291,4 +303,30 @@ class ClientsPage:
expand=True
),
expand=True,
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Clients", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -208,6 +208,18 @@ class DestinationsPage:
except Exception as e:
print("Error loading subscription:", e)
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['destinations'] == 1 else False
def build(self):
self.refresh()
self.add_destination_btn = ft.ElevatedButton("Add Destination", icon=ft.Icons.ADD, on_click=lambda e: self.open_dialog())
@@ -230,4 +242,30 @@ class DestinationsPage:
self.destinations_column
],
expand=True,
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Adderess", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -1196,7 +1196,7 @@ class OrdersEditPage:
[
self.loading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1213,7 +1213,7 @@ class OrdersEditPage:
[
self.loading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1226,7 +1226,7 @@ class OrdersEditPage:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1289,7 +1289,7 @@ class OrdersEditPage:
[
self.unloading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1306,7 +1306,7 @@ class OrdersEditPage:
[
self.unloading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1319,7 +1319,7 @@ class OrdersEditPage:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(

View File

@@ -129,6 +129,7 @@ class OrdersInPage:
)
self.ldm_quantity = ft.TextField(
value='13.6',
expand=True,
keyboard_type=ft.KeyboardType.NUMBER,
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
@@ -159,7 +160,12 @@ class OrdersInPage:
)
self.received_price = ft.TextField(
label="Price Received - visible only to you!",
label="Price Received",
keyboard_type=ft.KeyboardType.NUMBER,
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
)
self.expenses = ft.TextField(
label="Expenses",
keyboard_type=ft.KeyboardType.NUMBER,
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
)
@@ -193,6 +199,130 @@ class OrdersInPage:
),
)
self.file_picker = ft.FilePicker(on_result=self.on_file_result)
self.page.overlay.append(self.file_picker)
# --- Persistent date & time pickers (must be on page.overlay) ---
self.loading_date_picker = ft.DatePicker(
first_date=datetime.datetime(year=2000, month=10, day=1),
last_date=datetime.datetime(year=2025, month=10, day=1),
on_change=self.on_loading_date_click,
)
self.unloading_date_picker = ft.DatePicker(
first_date=datetime.datetime(year=2000, month=10, day=1),
last_date=datetime.datetime(year=2025, month=10, day=1),
on_change=self.on_unloading_date_click,
)
self.loading_time_picker_start = ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_loading_hour_click,
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
)
self.loading_time_picker_end = ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_loading_hour_click,
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
)
self.unloading_time_picker_start = ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_unloading_hour_click,
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
)
self.unloading_time_picker_end = ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_unloading_hour_click,
time_picker_entry_mode=ft.TimePickerEntryMode.INPUT_ONLY,
)
# Attach to overlay
self.page.overlay.extend([
self.loading_date_picker,
self.unloading_date_picker,
self.loading_time_picker_start,
self.loading_time_picker_end,
self.unloading_time_picker_start,
self.unloading_time_picker_end,
])
#self.page.update()
self.upload_order_btn = ft.Button(
"Upload",
icon=ft.Icons.UPLOAD_FILE,
width=150,
on_click=self.on_upload_document_btn_click
)
self.filename = ft.Text()
def _open_date_picker(self, picker):
# Works on both newer and older Flet
if hasattr(picker, "pick_date"):
picker.pick_date()
else:
picker.open = True
self.page.update()
def _open_time_picker(self, picker):
# Works on both newer and older Flet
if hasattr(picker, "pick_time"):
picker.pick_time()
else:
picker.open = True
self.page.update()
def on_upload_document_btn_click(self, e):
# Ensure FilePicker is attached to the page (Portainer/Flet sometimes reuses page objects)
if self.file_picker not in getattr(self.page.overlay, "controls", []):
self.page.overlay.append(self.file_picker)
self.page.update()
self.file_picker.pick_files(
allow_multiple=False,
allowed_extensions=["png", "jpg", "jpeg", "pdf"]
)
def on_file_result(self, e: ft.FilePickerResultEvent):
if not e.files:
return
file = e.files[0]
# Build a safe filename: order_in_YYYYmmdd_HHMMSS_userid_originalname
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
user_id = self.page.session.get("user_id")
new_filename = f"order_in_{timestamp}_{user_id}_{file.name}"
upload_url = self.page.get_upload_url(new_filename, 1000)
self.file_picker.upload([ft.FilePickerUploadFile(file.name, upload_url=upload_url)])
self.filename.value = new_filename
self.filename.update()
#import os
#import shutil
#source_path = os.path.join("uploads", new_filename)
# destination_path = os.path.join("assets/images", new_filename)
# try:
# time.sleep(2)
# shutil.move(source_path, destination_path)
# self.logo.src = f"images/{new_filename}"
# self.logo.update()
# self.logo_field.value = new_filename
# self.page.update()
# self.page.client_storage.set("logo_filename", new_filename)
# self.dashboard.logo.src = f"images/{new_filename}"
# self.dashboard.logo.update()
# except Exception as err:
# self.message.value = f"Upload error: {err}"
# self.message.color = ft.Colors.RED
# self.page.update()
def on_location_btn_click(self, destination):
query = destination["address"].replace(", ", "+")
maps_url = f"https://www.google.com/maps/search/?api=1&query={query}"
@@ -266,7 +396,7 @@ class OrdersInPage:
#print(self.loading_date.value)
def on_loading_hour_click(self, e):
if len(self.loading_hour.value) != None and len(self.loading_hour.value)==0:
if not self.loading_hour.value:
self.loading_hour.value = str(e.control.value)
else:
self.loading_hour.value += f' - {e.control.value}'
@@ -322,10 +452,10 @@ class OrdersInPage:
self.loading_error_message.value = "Please select loading point!"
self.loading_error_message.update()
return
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
self.loading_error_message.value = "Add loading informations!"
self.loading_error_message.update()
return
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
# self.loading_error_message.value = "Add loading informations!"
# self.loading_error_message.update()
# return
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
self.loading_error_message.value = "Add loading date!"
self.loading_error_message.update()
@@ -391,7 +521,7 @@ class OrdersInPage:
self.unloading_date.update()
def on_unloading_hour_click(self, e):
if len(self.unloading_hour.value) != None and len(self.unloading_hour.value)==0:
if not self.unloading_hour.value:
self.unloading_hour.value = str(e.control.value)
else:
self.unloading_hour.value += f' - {e.control.value}'
@@ -440,10 +570,10 @@ class OrdersInPage:
self.unloading_error_message.value = "Please select unloading point!"
self.unloading_error_message.update()
return
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
self.unloading_error_message.value = "Add unloading informations!"
self.unloading_error_message.update()
return
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
# self.unloading_error_message.value = "Add unloading informations!"
# self.unloading_error_message.update()
# return
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
self.unloading_error_message.value = "Add unloading date!"
self.unloading_error_message.update()
@@ -684,7 +814,9 @@ class OrdersInPage:
'trailer_reg_number': self.trailer_reg_number.value,
'received_price': self.received_price.value,
'loading_addresses': loading_addresses,
'unloading_addresses': unloading_addresses
'unloading_addresses': unloading_addresses,
'file':self.filename.value,
'expenses': self.expenses.value,
}
#print(saved_data)
if self.order_number.value == None or len(self.order_number.value)==0:
@@ -762,6 +894,18 @@ class OrdersInPage:
except Exception as ex:
self.error_message.value = f"Error: {str(ex)}"
self.error_message.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['orders_in'] == 1 else False
def build(self):
self.save_btn = ft.FilledButton(
@@ -785,7 +929,9 @@ class OrdersInPage:
ft.Row(
[
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
self.order_number
self.order_number,
self.upload_order_btn,
self.filename
]
)
],
@@ -882,11 +1028,12 @@ class OrdersInPage:
[
ft.Row(
[
ft.Text("Price", size=18, weight=ft.FontWeight.BOLD)
ft.Text("Price / Expenses", size=18, weight=ft.FontWeight.BOLD)
],
alignment=ft.MainAxisAlignment.START
),
self.received_price,
self.expenses
],
expand=2.5
)
@@ -926,15 +1073,9 @@ class OrdersInPage:
[
self.loading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
first_date=datetime.datetime(year=2000, month=10, day=1),
last_date=datetime.datetime(year=2025, month=10, day=1),
on_change=self.on_loading_date_click,
)
),
on_click=lambda e: self._open_date_picker(self.loading_date_picker),
)
],
expand=True
@@ -943,30 +1084,14 @@ class OrdersInPage:
[
self.loading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_loading_hour_click,
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
)
),
on_click=lambda e: self._open_time_picker(self.loading_time_picker_start),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_loading_hour_click,
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
)
),
on_click=lambda e: self._open_time_picker(self.loading_time_picker_end),
),
ft.ElevatedButton(
"Reset",
@@ -1019,15 +1144,9 @@ class OrdersInPage:
[
self.unloading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
first_date=datetime.datetime(year=2000, month=10, day=1),
last_date=datetime.datetime(year=2025, month=10, day=1),
on_change=self.on_unloading_date_click,
)
),
on_click=lambda e: self._open_date_picker(self.unloading_date_picker),
)
],
expand=True
@@ -1036,30 +1155,14 @@ class OrdersInPage:
[
self.unloading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_unloading_hour_click,
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
)
),
on_click=lambda e: self._open_time_picker(self.unloading_time_picker_start),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
confirm_text="Confirm",
error_invalid_text="Time out of range",
help_text="Pick your time slot",
on_change=self.on_unloading_hour_click,
time_picker_entry_mode = ft.TimePickerEntryMode.INPUT_ONLY
)
),
on_click=lambda e: self._open_time_picker(self.unloading_time_picker_end),
),
ft.ElevatedButton(
"Reset",
@@ -1101,5 +1204,31 @@ class OrdersInPage:
scroll=ft.ScrollMode.ADAPTIVE,
spacing=20
)
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Create Order In", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -156,7 +156,8 @@ class OrdersOutPage:
self.ldm_quantity = ft.TextField(
expand=True,
keyboard_type=ft.KeyboardType.NUMBER,
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string="")
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
value=13.6
)
self.kg_quantity = ft.TextField(
@@ -402,10 +403,10 @@ class OrdersOutPage:
self.loading_error_message.value = "Please select loading point!"
self.loading_error_message.update()
return
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
self.loading_error_message.value = "Add loading informations!"
self.loading_error_message.update()
return
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
# self.loading_error_message.value = "Add loading informations!"
# self.loading_error_message.update()
# return
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
self.loading_error_message.value = "Add loading date!"
self.loading_error_message.update()
@@ -520,10 +521,10 @@ class OrdersOutPage:
self.unloading_error_message.value = "Please select unloading point!"
self.unloading_error_message.update()
return
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
self.unloading_error_message.value = "Add unloading informations!"
self.unloading_error_message.update()
return
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
# self.unloading_error_message.value = "Add unloading informations!"
# self.unloading_error_message.update()
# return
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
self.unloading_error_message.value = "Add unloading date!"
self.unloading_error_message.update()
@@ -867,6 +868,18 @@ class OrdersOutPage:
except Exception as e:
print("Error loading subscription:", e)
return None
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['orders_out'] == 1 else False
def build(self):
self.save_btn = ft.FilledButton(
@@ -1050,7 +1063,7 @@ class OrdersOutPage:
[
self.loading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1067,7 +1080,7 @@ class OrdersOutPage:
[
self.loading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1080,7 +1093,7 @@ class OrdersOutPage:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1143,7 +1156,7 @@ class OrdersOutPage:
[
self.unloading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1160,7 +1173,7 @@ class OrdersOutPage:
[
self.unloading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1173,7 +1186,7 @@ class OrdersOutPage:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1225,5 +1238,31 @@ class OrdersOutPage:
scroll=ft.ScrollMode.ADAPTIVE,
spacing=20
)
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Create Order Out", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -1,6 +1,8 @@
import flet as ft
from pages.orders_in_page import OrdersInPage
from pages.orders_out_page import OrdersOutPage
from pages.archive_in_page import ArchiveInPage
from pages.archive_page import ArchivePage
class OrdersPage:
def __init__(self, page: ft.Page, dashboard):
@@ -17,6 +19,16 @@ class OrdersPage:
self.dashboard.placeholder.content = orders_out_page.build()
self.dashboard.placeholder.update()
def on_archive_order_out_btn_click(self, e):
archive = ArchivePage(self.page, self.dashboard, self)
self.dashboard.placeholder.content = archive.build()
self.dashboard.placeholder.update()
def on_archive_order_in_btn_click(self, e):
archive = ArchiveInPage(self.page, self.dashboard, self)
self.dashboard.placeholder.content = archive.build()
self.dashboard.placeholder.update()
def build(self):
return ft.Container(
content=ft.Column(
@@ -31,7 +43,7 @@ class OrdersPage:
ft.Container(
ft.Row(
[
ft.Text("Incoming orders", size=20)
ft.Text("Orders In", size=20)
],
alignment=ft.MainAxisAlignment.CENTER
),
@@ -39,11 +51,22 @@ class OrdersPage:
width=250,
height=80
),
ft.FilledButton(
"Orders In",
on_click=self.on_orders_in_btn_click,
width=150
ft.Row(
[
ft.FilledButton(
"Create",
on_click=self.on_orders_in_btn_click,
width=100
),
ft.FilledButton(
"Archive",
on_click=self.on_archive_order_in_btn_click,
width=100
)
],
alignment=ft.MainAxisAlignment.SPACE_EVENLY
)
],
alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER
@@ -62,7 +85,7 @@ class OrdersPage:
ft.Container(
ft.Row(
[
ft.Text("Outcoming orders", size=20)
ft.Text("Orders Out", size=20)
],
alignment=ft.MainAxisAlignment.CENTER
),
@@ -70,10 +93,20 @@ class OrdersPage:
width=250,
height=80
),
ft.FilledButton(
"Orders Out",
on_click=self.on_orders_out_btn_click,
width=150
ft.Row(
[
ft.FilledButton(
"Create",
on_click=self.on_orders_out_btn_click,
width=100
),
ft.FilledButton(
"Archive",
on_click=self.on_archive_order_out_btn_click,
width=100
)
],
alignment=ft.MainAxisAlignment.SPACE_EVENLY
)
],
alignment=ft.MainAxisAlignment.CENTER,

View File

@@ -2,6 +2,7 @@ import flet as ft
import requests
import time
from config import API_BASE_URL
from pages.users_page import Users
class ProfilePage:
def __init__(self, page: ft.Page, dashboard):
@@ -138,9 +139,9 @@ class ProfilePage:
),
)
self.created_at_text = ft.Text(value="Created At: TBD") # Set dynamically later
self.edit_button = ft.ElevatedButton(text="Edit Profile", on_click=self.on_edit_click)
self.save_button = ft.ElevatedButton(text="Save Changes", visible=False, on_click=self.on_save_click)
self.cancel_button = ft.TextButton(text="Cancel", visible=False, on_click=self.on_cancel_click)
self.edit_button = ft.ElevatedButton(text="Edit Profile", on_click=self.on_edit_click, width=100)
self.save_button = ft.ElevatedButton(text="Save Changes", visible=False, on_click=self.on_save_click, width=130)
self.cancel_button = ft.ElevatedButton(text="Cancel", visible=False, on_click=self.on_cancel_click, width=100)
self.upload_logo_btn = ft.ElevatedButton("Upload Company Logo", icon=ft.Icons.UPLOAD, on_click=self.on_upload_click, disabled=True)
self.message = ft.Text()
self.logo = ft.Image(src="images/image_placeholder.png", width=250)
@@ -177,6 +178,18 @@ class ProfilePage:
),
)
self.add_users = ft.Button(
"Add Users",
icon=ft.Icons.MANAGE_ACCOUNTS_ROUNDED,
width=250,
on_click=self.on_add_users_btn_click
)
def on_add_users_btn_click(self, e):
users = Users(self.page, self.dashboard)
self.dashboard.placeholder.content = users.build()
self.dashboard.placeholder.update()
def _auth_headers(self):
"""Build Authorization header from client storage robustly (web/desktop)."""
t = self.page.client_storage.get("token")
@@ -408,12 +421,22 @@ class ProfilePage:
self.message.color = ft.Colors.RED
self.page.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
return False
def build(self):
self.populate_user_data()
return ft.Container(
content=ft.Column(
[
ft.Text("User Profile", size=24, weight=ft.FontWeight.BOLD),
ft.Text("Company Profile", size=24, weight=ft.FontWeight.BOLD),
ft.Row(
[
ft.Column(
@@ -425,7 +448,10 @@ class ProfilePage:
self.smtp_user,
self.smtp_host,
self.smtp_port,
ft.Row([self.edit_button, self.save_button, self.cancel_button])
ft.Text(),
ft.Row([self.edit_button, self.save_button, self.cancel_button]),
ft.Divider(),
self.add_users,ft.Text()
],
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
width=250
@@ -455,4 +481,30 @@ class ProfilePage:
],
scroll=ft.ScrollMode.ADAPTIVE
),
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Company Profile", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -326,6 +326,18 @@ class ReportPage:
self.total.value = f"Total: {total}"
self.total.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['report'] == 1 else False
def build(self):
return ft.Container(
content=ft.Column(
@@ -386,4 +398,30 @@ class ReportPage:
alignment=ft.MainAxisAlignment.START,
),
expand=True
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Reports", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -93,6 +93,16 @@ class Subscription:
self.current_subscription_status.value = self.status[status]
self.current_subscription_status.update()
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
return False
def build(self):
return ft.Container(
content=ft.Column(
@@ -244,4 +254,30 @@ class Subscription:
spacing=50
),
expand=True
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Subscription", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -0,0 +1,60 @@
import flet as ft
import requests
import time
from config import API_BASE_URL
import re
from pages.dashboard_page import DashboardPage
class TemporaryPassword:
def __init__(self, page: ft.Page):
self.page = page
self.password = ft.TextField(label="Password", password=True, can_reveal_password=True, width=300)
self.confirm_password = ft.TextField(label="Confirm Password", password=True, can_reveal_password=True, width=300)
self.message = ft.Text("", color=ft.Colors.RED, text_align=ft.TextAlign.CENTER)
def on_save_btn_click(self, e):
# Password strength validation
if len(self.password.value) < 8 or not re.search(r"[A-Z]", self.password.value) or not re.search(r"[0-9]", self.password.value):
self.message.value = "Password must be at least 8 characters long and include a number and a capital letter"
self.message.update()
return
# Placeholder for register logic
if self.password.value != self.confirm_password.value:
self.message.value = "Passwords do not match"
self.message.update()
return
try:
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.post(f"{API_BASE_URL}/auth/temporary_password", headers=headers, json={
"password": self.password.value
})
if response.status_code == 200:
self.message.color = ft.Colors.GREEN
self.message.value = "Password updated!\nYou will be redirected to Dashboard"
self.message.update()
time.sleep(3)
# Proceed to Dashboard
self.page.go("/dashboard")
else:
self.message.color = ft.Colors.RED
self.message.value = response.json().get("message", "Password update fail!")
self.message.update()
except Exception as e:
print(e)
def build(self):
return ft.Container(
content=ft.Column(
[
ft.Text("This is your first login,\nplease change your password!", weight=ft.FontWeight.BOLD, size=20, text_align=ft.TextAlign.CENTER),
self.password,
self.confirm_password,
self.message,
ft.Button("Save", width=150, on_click=self.on_save_btn_click)
],
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
alignment=ft.MainAxisAlignment.CENTER,
spacing=20,
)
)

View File

@@ -245,6 +245,18 @@ class TransportersPage:
except Exception as e:
print("Error loading subscription:", e)
def get_client_access(self):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
user = response.json()
if user['user_role'] == 'user':
return True
else:
id = self.page.session.get("user_id")
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return True if response.json()['transporters'] == 1 else False
def build(self):
self.transporter_list = ft.Column(spacing=10, expand=True, scroll=ft.ScrollMode.ADAPTIVE,)
self.refresh()
@@ -270,4 +282,30 @@ class TransportersPage:
],
alignment=ft.MainAxisAlignment.START,
) if self.get_client_access() else ft.Container(
content=ft.Column(
[
ft.Row(
[
ft.Text("Transporters", size=24, weight=ft.FontWeight.BOLD),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
ft.Row(
[
ft.Text(
"You do not have access to this page content",
size=24,
weight=ft.FontWeight.BOLD,
color=ft.Colors.RED
)
],
alignment=ft.MainAxisAlignment.CENTER
),
ft.Text("")
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
expand=True
),
expand=True
)

View File

@@ -1,6 +1,7 @@
import flet as ft
import requests
from config import API_BASE_URL
from pages.temporary_password_page import TemporaryPassword
class TwoFactorAuth:
def __init__(self, page: ft.Page, email: str, login, auth):
@@ -35,10 +36,30 @@ class TwoFactorAuth:
print('Admin Logged In')
self.page.go("/admin")
else:
self.success_text.value = "Verification successful. You are now logged in."
self.error_text.value = ""
self.page.update()
self.page.go("/dashboard") # Change this to your main page
ui = requests.get(
f"{API_BASE_URL}/auth/me",
headers={"Authorization": f"Bearer {token}"},
timeout=10,
)
if ui.status_code == 200:
uj = ui.json()
try:
logo_filename = uj.get("logo_filename", "")
if logo_filename:
self.page.client_storage.set("custom_logo", logo_filename)
except Exception:
pass
if uj['temporary_password'] == 1:
self.auth.placeholder.content.clean()
temp_passwd_page = TemporaryPassword(self.page)
self.auth.placeholder.content = temp_passwd_page.build()
self.auth.placeholder.update()
else:
self.success_text.value = "Verification successful. You are now logged in."
self.error_text.value = ""
self.page.update()
self.page.go("/dashboard")
else:
self.error_text.value = "Invalid or expired code."
self.success_text.value = ""

View File

@@ -0,0 +1,274 @@
import flet as ft
import requests
from config import API_BASE_URL
class Users:
def __init__(self, page: ft.Page, dashboard):
self.page = page
self.dashboard = dashboard
self.users = self.get_users()
self.selected_user = None
self.users_list = ft.ListView(
controls=self.create_users_list(self.users, self.on_edit_btn_click, self.on_delete_btn_click),
spacing=10,
expand=True
)
self.add_user_dialog = ft.AlertDialog(
title="Add Company User",
content=ft.Column(
[
ft.TextField(label="Name"),
ft.TextField(label="Email"),
ft.TextField(label="Temporary Password", password=True, can_reveal_password=True),
ft.Text("Give user access to add and modify:"),
ft.Checkbox(label="Orders In"),
ft.Checkbox(label="Order Out"),
ft.Checkbox(label="Addresses"),
ft.Checkbox(label="Clients"),
ft.Checkbox(label="Transporters"),
ft.Checkbox(label="Report"),
ft.Text("", color=ft.Colors.RED)
],
width=600,
height=500
),
actions=[
ft.TextButton(
"Cancel",
width=100,
on_click=self.on_cancel_btn_click
),
ft.ElevatedButton(
"Save",
width=100,
on_click=self.on_save_btn_click
)
]
)
self.delete_dialog = ft.AlertDialog(
title=ft.Text("Delete User?"),
actions=[
ft.TextButton("Cancel", on_click=self.on_cancel_delete_btn_click, width=100),
ft.Button("Yes", on_click=self.on_confirm_delete_btn_click, width=100),
]
)
self.edit_user_dialog = ft.AlertDialog(
title=ft.Text(f"Edit User"),
content=ft.Column(
[
ft.Checkbox(label="Orders In"),
ft.Checkbox(label="Order Out"),
ft.Checkbox(label="Addresses"),
ft.Checkbox(label="Clients"),
ft.Checkbox(label="Transporters"),
ft.Checkbox(label="Report"),
],
width=400,
height=350
),
actions=[
ft.TextButton(
"Cancel",
width=100,
on_click=self.on_edit_cancel_btn_click
),
ft.ElevatedButton(
"Save",
width=100,
on_click=self.on_edit_save_btn_click
)
]
)
def on_edit_cancel_btn_click(self, e):
self.page.close(self.edit_user_dialog)
def on_edit_save_btn_click(self, e):
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
access_payload = {
'company_user_id':self.selected_user['id'],
'orders_in':1 if self.edit_user_dialog.content.controls[0].value else 0,
'orders_out':1 if self.edit_user_dialog.content.controls[1].value else 0,
'addresses':1 if self.edit_user_dialog.content.controls[2].value else 0,
'clients':1 if self.edit_user_dialog.content.controls[3].value else 0,
'transporters':1 if self.edit_user_dialog.content.controls[4].value else 0,
'report':1 if self.edit_user_dialog.content.controls[5].value else 0
}
response = requests.put(f"{API_BASE_URL}/company_user/update_access", headers=headers, json=access_payload)
#set to default
self.selected_user = None
self.page.close(self.edit_user_dialog)
self.edit_user_dialog.content.controls[0].value = False
self.edit_user_dialog.content.controls[1].value = False
self.edit_user_dialog.content.controls[2].value = False
self.edit_user_dialog.content.controls[3].value = False
self.edit_user_dialog.content.controls[4].value = False
self.edit_user_dialog.content.controls[5].value = False
self.edit_user_dialog.update()
def on_confirm_delete_btn_click(self, e):
self.page.close(self.delete_dialog)
try:
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
delete_payload = {
'user_id':self.selected_user['id']
}
response = requests.post(f"{API_BASE_URL}/admin/users/deactivate", headers=headers, json=delete_payload)
except Exception as e:
print("Error deleting company users:", e)
users = self.get_users()
self.users_list.controls.clear()
self.users_list.controls = self.create_users_list(users, self.on_edit_btn_click, self.on_delete_btn_click)
self.users_list.update()
self.selected_user = None
def on_cancel_delete_btn_click(self, e):
self.page.close(self.delete_dialog)
def add_new_user(self, e):
self.page.open(self.add_user_dialog)
def on_edit_btn_click(self, item):
self.selected_user = item
access = self.get_user_access(item['id'])
self.edit_user_dialog.content.controls[0].value = True if access['orders_in']==1 else False
self.edit_user_dialog.content.controls[1].value = True if access['orders_out']==1 else False
self.edit_user_dialog.content.controls[2].value = True if access['destinations']==1 else False
self.edit_user_dialog.content.controls[3].value = True if access['clients']==1 else False
self.edit_user_dialog.content.controls[4].value = True if access['transporters']==1 else False
self.edit_user_dialog.content.controls[5].value = True if access['report']==1 else False
self.page.open(self.edit_user_dialog)
def get_user_access(self, id):
try:
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/company_user/access/{id}", headers=headers)
return response.json()
except Exception as e:
print("Error loading company user access:", e)
def on_delete_btn_click(self, item):
self.selected_user = item
self.page.open(self.delete_dialog)
def on_save_btn_click(self, e):
self.page.close(self.add_user_dialog)
try:
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
create_payload = {
'name':self.add_user_dialog.content.controls[0].value,
'email':self.add_user_dialog.content.controls[1].value,
'company_id':self.page.session.get("user_id"),
'password':self.add_user_dialog.content.controls[2].value
}
response = requests.post(f"{API_BASE_URL}/company_user/register_company_user", headers=headers, json=create_payload)
company_user_id = response.json()['company_user_id'] if response.status_code == 201 else self.show_error_mesage
if company_user_id:
access_payload = {
'company_user_id':company_user_id,
'orders_in':1 if self.add_user_dialog.content.controls[4].value else 0,
'orders_out':1 if self.add_user_dialog.content.controls[5].value else 0,
'addresses':1 if self.add_user_dialog.content.controls[6].value else 0,
'clients':1 if self.add_user_dialog.content.controls[7].value else 0,
'transporters':1 if self.add_user_dialog.content.controls[8].value else 0,
'report':1 if self.add_user_dialog.content.controls[9].value else 0
}
response = requests.post(f"{API_BASE_URL}/company_user/access", headers=headers, json=access_payload)
except Exception as e:
print("Error loading company users:", e)
users = self.get_users()
self.users_list.controls.clear()
self.users_list.controls = self.create_users_list(users, self.on_edit_btn_click, self.on_delete_btn_click)
self.users_list.update()
#set dialog to default valuse:
self.add_user_dialog.content.controls[0].value = ''
self.add_user_dialog.content.controls[1].value = ''
self.add_user_dialog.content.controls[2].value = ''
self.add_user_dialog.content.controls[4].value = False
self.add_user_dialog.content.controls[5].value = False
self.add_user_dialog.content.controls[6].value = False
self.add_user_dialog.content.controls[7].value = False
self.add_user_dialog.content.controls[8].value = False
self.add_user_dialog.content.controls[9].value = False
self.add_user_dialog.content.controls[10].value = ""
self.add_user_dialog.update()
def show_error_mesage(self):
self.add_user_dialog.content.controls[10].value = "A user with this email has been already created, to reactivate it send a request at office@ordergo.eu"
self.add_user_dialog.update()
def on_cancel_btn_click(self, e):
self.page.close(self.add_user_dialog)
def get_users(self):
try:
user_id = self.page.session.get("user_id")
users = []
token = self.page.client_storage.get("token")
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/admin/users/company_users", headers=headers)
if response.status_code == 200:
all_users=response.json()
for user in all_users:
if user['company_id'] == user_id and user['active'] == 1:
users.append(user)
return users
else:
return []
except Exception as e:
print("Error loading company users:", e)
def create_users_list(self, items, on_click_handler, on_click_handler2):
return [
ft.Container(
content=ft.Row(
[
ft.Column(
[
ft.Text(item['name'], expand=True, weight=ft.FontWeight.BOLD),
ft.Text(item['email'], size=12)
]
),
ft.Row(
[
ft.Button("Edit", on_click=lambda e, id=item: on_click_handler(id), width=100),
ft.FilledButton("Delete", on_click=lambda e, id=item: on_click_handler2(id), width=100, bgcolor=ft.Colors.RED)
]
)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
width=300,
bgcolor=ft.Colors.BLUE_50,
padding=10,
border_radius=8,
border=ft.border.all(1, ft.Colors.GREY_300),
)
for item in items
]
def build(self):
return ft.Container(
content= ft.Column(
[
ft.Row(
[
ft.Text("Users", size=24, weight=ft.FontWeight.BOLD),
ft.Button("Add User", icon=ft.Icons.ADD, on_click=self.add_new_user)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN
),
self.users_list
],
expand=True
),
expand=True
)

View File

@@ -188,6 +188,12 @@ class ViewOrdersIn:
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
value=self.order['received_price']
)
self.expenses = ft.TextField(
label="Expenses",
keyboard_type=ft.KeyboardType.NUMBER,
input_filter=ft.InputFilter(allow=True, regex_string=r"^[0-9]*\.?[0-9]*$", replacement_string=""),
value=self.order['expenses']
)
self.error_message = ft.Text(color = ft.Colors.RED)
@@ -280,6 +286,18 @@ class ViewOrdersIn:
self.unloading_query = addresses
self.unloading.controls = self.create_unloading_list(addresses, self.on_delete_unloading_address_btn_click)
self.file_picker = ft.FilePicker(on_result=self.on_file_result)
self.page.overlay.append(self.file_picker)
self.upload_order_btn = ft.Button(
"Upload",
icon=ft.Icons.UPLOAD_FILE,
width=150,
on_click=self.on_upload_document_btn_click
)
self.filename = ft.Text(value=self.order['file'])
def on_go_back_btn_click(self, e):
self.dashboard.placeholder.content = self.archive.build()
self.dashboard.placeholder.update()
@@ -447,10 +465,10 @@ class ViewOrdersIn:
self.loading_error_message.value = "Please select loading point!"
self.loading_error_message.update()
return
if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
self.loading_error_message.value = "Add loading informations!"
self.loading_error_message.update()
return
# if self.loading_informations.value == None or len(self.loading_informations.value) == 0:
# self.loading_error_message.value = "Add loading informations!"
# self.loading_error_message.update()
# return
if self.loading_date.value == None or len(str(self.loading_date.value)) == 0:
self.loading_error_message.value = "Add loading date!"
self.loading_error_message.update()
@@ -480,6 +498,29 @@ class ViewOrdersIn:
self.loading_error_message.value = "All fields of the loading address are required."
self.loading_error_message.update()
def on_upload_document_btn_click(self, e):
# Ensure FilePicker is attached to the page (Portainer/Flet sometimes reuses page objects)
if self.file_picker not in getattr(self.page.overlay, "controls", []):
self.page.overlay.append(self.file_picker)
self.page.update()
self.file_picker.pick_files(
allow_multiple=False,
allowed_extensions=["png", "jpg", "jpeg", "pdf"]
)
def on_file_result(self, e: ft.FilePickerResultEvent):
if not e.files:
return
file = e.files[0]
# Build a safe filename: order_in_YYYYmmdd_HHMMSS_userid_originalname
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
user_id = self.page.session.get("user_id")
new_filename = f"order_in_{timestamp}_{user_id}_{file.name}"
upload_url = self.page.get_upload_url(new_filename, 1000)
self.file_picker.upload([ft.FilePickerUploadFile(file.name, upload_url=upload_url)])
self.filename.value = new_filename
self.filename.update()
def on_searching_unloading_address(self, e):
query = e.control.value.lower()
self.filtered_addresses_ul = [a for a in self.all_addresses if query in a["name"].lower()]
@@ -565,10 +606,10 @@ class ViewOrdersIn:
self.unloading_error_message.value = "Please select unloading point!"
self.unloading_error_message.update()
return
if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
self.unloading_error_message.value = "Add unloading informations!"
self.unloading_error_message.update()
return
# if self.unloading_informations.value == None or len(self.unloading_informations.value) == 0:
# self.unloading_error_message.value = "Add unloading informations!"
# self.unloading_error_message.update()
# return
if self.unloading_date.value == None or len(str(self.unloading_date.value)) == 0:
self.unloading_error_message.value = "Add unloading date!"
self.unloading_error_message.update()
@@ -782,7 +823,9 @@ class ViewOrdersIn:
'trailer_reg_number': self.trailer_reg_number.value,
'received_price': self.received_price.value,
'loading_addresses': loading_addresses,
'unloading_addresses': unloading_addresses
'unloading_addresses': unloading_addresses,
'file':self.filename.value,
'expenses': self.expenses.value,
}
#print(saved_data)
if self.order_number.value == None or len(self.order_number.value)==0:
@@ -904,7 +947,9 @@ class ViewOrdersIn:
ft.Row(
[
ft.Text("Number", size=18, weight=ft.FontWeight.BOLD),
self.order_number
self.order_number,
self.upload_order_btn,
self.filename
]
),
ft.Row(
@@ -998,6 +1043,7 @@ class ViewOrdersIn:
alignment=ft.MainAxisAlignment.START
),
self.received_price,
self.expenses,
],
expand=2.5
)
@@ -1037,7 +1083,7 @@ class ViewOrdersIn:
[
self.loading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1054,7 +1100,7 @@ class ViewOrdersIn:
[
self.loading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1067,7 +1113,7 @@ class ViewOrdersIn:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1130,7 +1176,7 @@ class ViewOrdersIn:
[
self.unloading_date,
ft.ElevatedButton(
"Pick date",
"Select date",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.DatePicker(
@@ -1147,7 +1193,7 @@ class ViewOrdersIn:
[
self.unloading_hour,
ft.ElevatedButton(
"Pick hour - Start",
"Select hour - Start",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(
@@ -1160,7 +1206,7 @@ class ViewOrdersIn:
),
),
ft.ElevatedButton(
"Pick hour - End",
"Select hour - End",
icon=ft.Icons.CALENDAR_MONTH,
on_click=lambda e: self.page.open(
ft.TimePicker(