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, 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, 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 )