511 lines
20 KiB
Python
511 lines
20 KiB
Python
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):
|
|
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,
|
|
# allow ASCII printable + CR/LF + tab (optional)
|
|
regex_string=r"[\x09\x20-\x7E\r\n]",
|
|
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, 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)
|
|
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=""
|
|
),
|
|
)
|
|
|
|
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")
|
|
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 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("Company 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.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
|
|
),
|
|
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
|
|
),
|
|
) 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
|
|
) |