init commit
This commit is contained in:
458
transportmanager/client/pages/profile_page.py
Normal file
458
transportmanager/client/pages/profile_page.py
Normal file
@@ -0,0 +1,458 @@
|
||||
import flet as ft
|
||||
import requests
|
||||
import time
|
||||
from config import API_BASE_URL
|
||||
|
||||
class ProfilePage:
|
||||
def __init__(self, page: ft.Page, dashboard):
|
||||
self.page = page
|
||||
self.dashboard = dashboard
|
||||
self.name_field = ft.TextField(
|
||||
label="Company Name",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.contact_name_field = ft.TextField(
|
||||
label="Contact Name",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.email_field = ft.TextField(
|
||||
label="Email",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.phone_field = ft.TextField(
|
||||
label="Phone",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.register_number_field = ft.TextField(
|
||||
label="Register Number",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.vat = ft.TextField(
|
||||
label="VAT",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.address = None
|
||||
self.street_and_number = ft.TextField(
|
||||
label="Street and number",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.postal_code = ft.TextField(
|
||||
label="Postal code",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.city = ft.TextField(
|
||||
label="City",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.region_county = ft.TextField(
|
||||
label="Region / County",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.country = ft.TextField(
|
||||
label="Country",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.logo_field = ft.TextField(
|
||||
label="Logo Filename",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.terms_field = ft.TextField(
|
||||
label="Terms",
|
||||
multiline=True,
|
||||
min_lines=5,
|
||||
max_lines=10,
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.first_order_number_field = ft.TextField(
|
||||
label="First Order Number",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
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.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)
|
||||
self.file_picker = ft.FilePicker(on_result=self.on_file_result)
|
||||
self.page.overlay.append(self.file_picker)
|
||||
|
||||
#email credentials
|
||||
self.email_credentials = None
|
||||
self.smtp_host = ft.TextField(
|
||||
label="SMTP HOST",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.smtp_port = ft.TextField(
|
||||
label="SMTP PORT",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
self.smtp_user = ft.TextField(
|
||||
label="Email (SMTP USER)",
|
||||
disabled=True,
|
||||
input_filter=ft.InputFilter(
|
||||
allow=True,
|
||||
regex_string=r"^[\x20-\x7E]*$",
|
||||
replacement_string=""
|
||||
),
|
||||
)
|
||||
|
||||
def _auth_headers(self):
|
||||
"""Build Authorization header from client storage robustly (web/desktop)."""
|
||||
t = self.page.client_storage.get("token")
|
||||
if not t:
|
||||
return {}
|
||||
# Token may be stored as dict or quoted string depending on login flow/runtime
|
||||
if isinstance(t, dict):
|
||||
t = t.get("access_token") or t.get("token") or ""
|
||||
if not isinstance(t, str):
|
||||
t = str(t)
|
||||
t = t.strip().strip('"')
|
||||
return {"Authorization": f"Bearer {t}"} if t else {}
|
||||
|
||||
def on_edit_click(self, e):
|
||||
self.name_field.disabled = False
|
||||
self.contact_name_field.disabled = False
|
||||
self.email_field.disabled = False
|
||||
self.phone_field.disabled = False
|
||||
self.register_number_field.disabled = False
|
||||
self.vat.disabled = False
|
||||
self.street_and_number.disabled = False
|
||||
self.postal_code.disabled = False
|
||||
self.city.disabled = False
|
||||
self.region_county.disabled = False
|
||||
self.country.disabled = False
|
||||
self.logo_field.disabled = False
|
||||
self.terms_field.disabled = False
|
||||
self.first_order_number_field.disabled = False
|
||||
self.upload_logo_btn.disabled = False
|
||||
self.smtp_host.disabled = False
|
||||
self.smtp_port.disabled = False
|
||||
self.smtp_user.disabled = False
|
||||
self.edit_button.visible = False
|
||||
self.save_button.visible = True
|
||||
self.cancel_button.visible = True
|
||||
self.page.update()
|
||||
|
||||
def on_save_click(self, e):
|
||||
# Save logic would be implemented here
|
||||
headers = self._auth_headers()
|
||||
if not headers:
|
||||
self.message.value = "Unauthorized: No token"
|
||||
self.message.color = ft.Colors.RED
|
||||
self.page.update()
|
||||
return
|
||||
address = f'{self.street_and_number.value} %{self.postal_code.value} %{self.city.value} %{self.region_county.value} %{self.country.value}'
|
||||
data = {
|
||||
"name": self.name_field.value,
|
||||
"contact_name": self.contact_name_field.value,
|
||||
"email": self.email_field.value,
|
||||
"phone": self.phone_field.value,
|
||||
"register_number": self.register_number_field.value,
|
||||
"vat": self.vat.value,
|
||||
"address": address,
|
||||
"logo_filename": self.logo_field.value,
|
||||
"terms": self.terms_field.value,
|
||||
"first_order_number": self.first_order_number_field.value
|
||||
}
|
||||
try:
|
||||
response = requests.put(f"{API_BASE_URL}/profile/", json=data, headers=headers)
|
||||
if response.status_code == 200:
|
||||
self.message.value = "Profile updated successfully!"
|
||||
self.message.color = ft.Colors.GREEN
|
||||
# Save logo filename to client storage
|
||||
self.page.client_storage.set("logo_filename", self.logo_field.value)
|
||||
self.page.session.set("first_order_number", self.first_order_number_field.value)
|
||||
else:
|
||||
self.message.value = f"Failed to update profile: {response.status_code}"
|
||||
self.message.color = ft.Colors.RED
|
||||
except Exception as e:
|
||||
self.message.value = f"Error updating profile: {e}"
|
||||
self.message.color = ft.Colors.RED
|
||||
|
||||
#email credentials
|
||||
data = {
|
||||
'smtp_host' : self.smtp_host.value,
|
||||
'smtp_port' : self.smtp_port.value,
|
||||
'smtp_user' : self.smtp_user.value
|
||||
}
|
||||
try:
|
||||
if not self.email_credentials:
|
||||
response = requests.post(f"{API_BASE_URL}/profile/email", json=data, headers=headers)
|
||||
else:
|
||||
response = requests.put(f"{API_BASE_URL}/profile/email", json=data, headers=headers)
|
||||
if response.status_code == 200:
|
||||
self.message.value = "Profile updated successfully!"
|
||||
self.message.color = ft.Colors.GREEN
|
||||
else:
|
||||
self.message.value = f"Failed to create / update email credentials: {response.status_code}"
|
||||
self.message.color = ft.Colors.RED
|
||||
except Exception as e:
|
||||
self.message.value = f"Error creating / updating email credentials: {e}"
|
||||
self.message.color = ft.Colors.RED
|
||||
|
||||
self.name_field.disabled = True
|
||||
self.contact_name_field.disabled = True
|
||||
self.email_field.disabled = True
|
||||
self.phone_field.disabled = True
|
||||
self.register_number_field.disabled = True
|
||||
self.vat.disabled = True
|
||||
self.street_and_number.disabled = True
|
||||
self.postal_code.disabled = True
|
||||
self.city.disabled = True
|
||||
self.region_county.disabled = True
|
||||
self.country.disabled = True
|
||||
self.logo_field.disabled = True
|
||||
self.terms_field.disabled = True
|
||||
self.first_order_number_field.disabled = True
|
||||
self.upload_logo_btn.disabled = True
|
||||
self.smtp_host.disabled = True
|
||||
self.smtp_port.disabled = True
|
||||
self.smtp_user.disabled = True
|
||||
self.edit_button.visible = True
|
||||
self.save_button.visible = False
|
||||
self.cancel_button.visible = False
|
||||
self.page.update()
|
||||
|
||||
def on_cancel_click(self, e):
|
||||
# Reset fields or fetch previous values here
|
||||
self.name_field.disabled = True
|
||||
self.contact_name_field.disabled = True
|
||||
self.email_field.disabled = True
|
||||
self.phone_field.disabled = True
|
||||
self.register_number_field.disabled = True
|
||||
self.vat.disabled = True
|
||||
self.street_and_number.disabled = True
|
||||
self.postal_code.disabled = True
|
||||
self.city.disabled = True
|
||||
self.region_county.disabled = True
|
||||
self.country.disabled = True
|
||||
self.logo_field.disabled = True
|
||||
self.terms_field.disabled = True
|
||||
self.first_order_number_field.disabled = True
|
||||
self.upload_logo_btn.disabled = True
|
||||
self.smtp_host.disabled = True
|
||||
self.smtp_port.disabled = True
|
||||
self.smtp_user.disabled = True
|
||||
self.edit_button.visible = True
|
||||
self.save_button.visible = False
|
||||
self.cancel_button.visible = False
|
||||
self.page.update()
|
||||
|
||||
def populate_user_data(self):
|
||||
user_id = self.page.session.get("user_id")
|
||||
if not user_id:
|
||||
self.message.value = "User not authenticated."
|
||||
self.message.color = ft.Colors.RED
|
||||
return
|
||||
headers = self._auth_headers()
|
||||
if not headers:
|
||||
self.message.value = "Unauthorized: No token"
|
||||
self.message.color = ft.Colors.RED
|
||||
return
|
||||
response = requests.get(f"{API_BASE_URL}/profile/", headers=headers, timeout=10)
|
||||
if response.status_code == 200:
|
||||
user_data = response.json()
|
||||
#print(user_data)
|
||||
self.name_field.value = user_data.get("name", "")
|
||||
self.contact_name_field.value = user_data.get("contact_name", "")
|
||||
self.email_field.value = user_data.get("email", "")
|
||||
self.phone_field.value = user_data.get("phone", "")
|
||||
self.register_number_field.value = user_data.get("register_number", "")
|
||||
self.vat.value = user_data.get("vat", "")
|
||||
self.street_and_number.value = user_data.get("address").split(" %")[0] if user_data.get("address") and len(user_data.get("address")) > 0 else ''
|
||||
self.postal_code.value = user_data.get("address").split(" %")[1] if user_data.get("address") and len(user_data.get("address")) > 0 else ''
|
||||
self.city.value = user_data.get("address").split(" %")[2] if user_data.get("address") and len(user_data.get("address")) > 0 else ''
|
||||
self.region_county.value = user_data.get("address").split(" %")[3] if user_data.get("address") and len(user_data.get("address")) > 0 else ''
|
||||
self.country.value = user_data.get("address").split(" %")[4] if user_data.get("address") and len(user_data.get("address")) > 0 else ''
|
||||
logo_filename = user_data.get("logo_filename", "")
|
||||
self.logo_field.value = logo_filename
|
||||
self.logo.src = f"images/{logo_filename}" if logo_filename else "images/image_placeholder.png"
|
||||
if logo_filename:
|
||||
self.page.client_storage.set("logo_filename", logo_filename)
|
||||
self.dashboard.logo.src = f"images/{logo_filename}"
|
||||
self.dashboard.logo.update()
|
||||
self.terms_field.value = user_data.get("terms", "")
|
||||
self.first_order_number_field.value = user_data.get("first_order_number", "")
|
||||
self.created_at_text.value = f"Created At: {user_data.get('created_at', '')}"
|
||||
if self.register_number_field.value == '':
|
||||
self.on_edit_click('')
|
||||
else:
|
||||
self.message.value = f"Failed to load user data: {response.text}"
|
||||
self.message.color = ft.Colors.RED
|
||||
|
||||
response = requests.get(f"{API_BASE_URL}/profile/email", headers=headers, timeout=10)
|
||||
if response.status_code == 200:
|
||||
self.email_credentials = response.json()
|
||||
self.smtp_host.value = self.email_credentials['smtp_host']
|
||||
self.smtp_port.value = self.email_credentials['smtp_port']
|
||||
self.smtp_user.value = self.email_credentials['smtp_user']
|
||||
else:
|
||||
print(f"Failed to load email credentials: {response.text}")
|
||||
|
||||
#except Exception as e:
|
||||
# self.message.value = f"Error fetching user data: {e}"
|
||||
# self.message.color = ft.Colors.RED
|
||||
|
||||
def on_upload_click(self, e):
|
||||
self.file_picker.pick_files(
|
||||
allow_multiple=False,
|
||||
allowed_extensions=["png", "jpg", "jpeg"]
|
||||
)
|
||||
|
||||
def on_file_result(self, e: ft.FilePickerResultEvent):
|
||||
if not e.files:
|
||||
return
|
||||
file = e.files[0]
|
||||
new_filename = f"user_logo_{self.page.session.get('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)])
|
||||
|
||||
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 build(self):
|
||||
self.populate_user_data()
|
||||
return ft.Container(
|
||||
content=ft.Column(
|
||||
[
|
||||
ft.Text("User Profile", size=24, weight=ft.FontWeight.BOLD),
|
||||
ft.Row(
|
||||
[
|
||||
ft.Column(
|
||||
[
|
||||
self.logo,
|
||||
self.upload_logo_btn,
|
||||
self.message,
|
||||
ft.Text(),
|
||||
self.smtp_user,
|
||||
self.smtp_host,
|
||||
self.smtp_port,
|
||||
ft.Row([self.edit_button, self.save_button, self.cancel_button])
|
||||
],
|
||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||
width=250
|
||||
),
|
||||
ft.Column(
|
||||
[
|
||||
self.name_field,
|
||||
self.contact_name_field,
|
||||
self.email_field,
|
||||
self.phone_field,
|
||||
self.register_number_field,
|
||||
self.vat,
|
||||
self.street_and_number,
|
||||
self.postal_code,
|
||||
self.city,
|
||||
self.region_county,
|
||||
self.country,
|
||||
self.first_order_number_field,
|
||||
self.terms_field,
|
||||
],
|
||||
spacing=20,
|
||||
expand=True
|
||||
)
|
||||
],
|
||||
vertical_alignment=ft.CrossAxisAlignment.START
|
||||
)
|
||||
],
|
||||
scroll=ft.ScrollMode.ADAPTIVE
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user