import flet as ft import requests from helpers.roles import Roles from dataclasses import dataclass, field from datetime import datetime @dataclass class State: file_picker: ft.FilePicker | None = None picked_files: list[ft.FilePickerFile] = field(default_factory=list) state = State() class DocumenteJuridice: def __init__(self, page: ft.Page): self.page = page self.editing_category_id = None self.category_id_to_delete = None self.document_id_to_delete = None self.current_category_id = None self.search_bar = ft.TextField( label="Cauta", on_submit=self.on_search_bar_submit, expand=True ) self.category_name = ft.TextField( label="Nume categorie", ) self.roles_checkboxes = [ ft.Checkbox(label=role.upper()) for role in [ Roles.USER, Roles.PROPRIETAR, Roles.CENZOR, Roles.ADMINISTRATOR, Roles.PRESEDINTE, Roles.EXPERT ] ] self.access_levels = ft.Column( controls=self.roles_checkboxes, height=200, scroll=ft.ScrollMode.AUTO ) self.add_categories_dialog = ft.AlertDialog( title=ft.Text("Adauga Categorie"), content=ft.Column( [ self.category_name, ft.Text("Nivele Acces:"), self.access_levels ], height=300, tight=True ), actions=[ ft.FilledButton( "Salveaza", on_click=self.on_save_category_btn_click ), ft.FilledButton( "Anuleaza", on_click=self.on_cancel_category_btn_click, bgcolor=ft.Colors.GREY, ), ], ) self.delete_confirmation_dialog = ft.AlertDialog( title=ft.Text("Doriti sa stergeti categoria?"), content=ft.Text("Stergerea categoriei implica si stergerea tuturor documentelor din aceasta categorie."), actions=[ ft.FilledButton( "Da", on_click=self.confirm_delete_category ), ft.FilledButton( "Nu", bgcolor=ft.Colors.GREY, on_click=self.close_delete_dialog ), ], ) self.delete_document_confirmation_dialog = ft.AlertDialog( title=ft.Text("Doriti sa stergeti documentul?"), content=ft.Text("Aceasta actiune este permanenta."), actions=[ ft.FilledButton( "Da", on_click=self.confirm_delete_document ), ft.FilledButton( "Nu", bgcolor=ft.Colors.GREY, on_click=self.close_delete_document_dialog ), ], ) self.all_categories = self.get_categories() self.category_list = ft.ListView( controls=self.create_category_list( self.all_categories, self.on_edit_category_btn_click, self.on_delete_category_btn_click ), spacing=10, expand=True ) self.add_document = ft.Button( "Adauga Document", icon=ft.Icons.ADD, on_click=self.add_new_document ) self.all_documents = [] self.documents_list = ft.ListView( spacing=10, expand=True ) self.category = ft.Dropdown( label="Selectează Categoria", options=[ ft.dropdown.Option(key=str(cat['id']), text=cat['name']) for cat in self.all_categories ] ) self.documenet_title = None self.documenet_displayed_title = ft.TextField(label="Document",read_only=True) self.add_new_document_dialog = ft.AlertDialog( title=ft.Text("Adauga document"), content=ft.Column( [ ft.Row( [ self.documenet_displayed_title, ft.Button( "Incarca", icon=ft.Icons.UPLOAD, on_click=self.handle_file_upload ), ] ), self.category ], height=160, ), actions=[ ft.FilledButton( "Salveaza", on_click=self.on_save_document_btn_click, ), ft.FilledButton( "Anuleaza", bgcolor=ft.Colors.GREY, on_click=self.on_cancel_save_dialog_btn_click, ), ], ) def on_add_btn_click(self, e): self.editing_category_id = None self.category_name.value = "" for cb in self.roles_checkboxes: cb.value = False self.add_categories_dialog.title = ft.Text("Adauga Categorie") self.page.show_dialog(self.add_categories_dialog) self.page.update() def on_save_category_btn_click(self, e): category = self.category_name.value access = ",".join([cb.label for cb in self.roles_checkboxes if cb.value]) base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') if self.editing_category_id: url = f'{base_url}/documents/categories/update/{self.editing_category_id}' method = requests.put else: url = f'{base_url}/documents/categories/add' method = requests.post response = method( url, json={ "name": category, "access": access }, headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } ) if response.status_code in [200, 201]: self.update_category_list() self.category_name.value = None self.editing_category_id = None self.page.pop_dialog() else: raise Exception("Operatiuna nu sa putut realiza, token-ul a expirat, va rugam sa va autentificati. Daca eroarea persita contactati echipa de IT!") def update_category_list(self): self.all_categories = self.get_categories() self.category_list.controls = self.create_category_list( self.all_categories, self.on_edit_category_btn_click, self.on_delete_category_btn_click ) self.category_list.update() # Actualizăm și opțiunile din dropdown-ul pentru documente self.category.options = [ ft.dropdown.Option(key=str(cat['id']), text=cat['name']) for cat in self.all_categories ] def on_cancel_category_btn_click(self, e): self.category_name.value = None self.page.pop_dialog() def on_search_bar_submit(self, e): # Luăm textul din search bar și îl convertim la litere mici pentru o căutare case-insensitive query = self.search_bar.value.strip().lower() # Filtrăm lista de documente stocată în self.all_documents if not query: filtered_docs = self.all_documents else: filtered_docs = [ doc for doc in self.all_documents if query in doc['name'].lower() ] # Actualizăm interfața cu lista filtrată self.documents_list.controls = self.create_documents_list( filtered_docs, lambda doc: self.page.run_task(self.on_download_document_btn_click, doc), self.on_delete_document_btn_click ) self.documents_list.update() def add_new_document(self, e): self.page.show_dialog(self.add_new_document_dialog) async def handle_file_upload(self, e: ft.Event[ft.Button]): print('File uploaded') try: state.file_picker = ft.FilePicker() files = await state.file_picker.pick_files(allow_multiple=False) print("Picked file:", files) state.picked_files = files uploaded_file_name = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{state.picked_files[0].name}" await state.file_picker.upload( files=[ ft.FilePickerUploadFile( name=file.name, upload_url=self.page.get_upload_url(uploaded_file_name, 60), ) for file in state.picked_files ] ) self.documenet_title = uploaded_file_name self.documenet_displayed_title.value = uploaded_file_name.split("_")[1] self.documenet_displayed_title.update() return f'{uploaded_file_name}' except Exception as e: print(e) def on_save_document_btn_click(self, e): if not self.category.value or not self.documenet_title: # Opțional: Poți adăuga un mesaj de eroare vizibil pentru utilizator return selected_cat_id = int(self.category.value) # Identificăm obiectul categoriei pentru a-i moșteni accesul și numele category = next((cat for cat in self.all_categories if cat['id'] == selected_cat_id), None) if not category: return base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') # Construim path-ul relativ (Categorie/NumeFisier) dest_path = f"{category['name']}/{self.documenet_title}" payload = { "category_id": selected_cat_id, "name": self.documenet_displayed_title.value, "path": dest_path, "access": category.get('access', '') # Moștenim accesul de la categorie } response = requests.post( f"{base_url}/documents/standards/add", json=payload, headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 201: self.page.pop_dialog() self.list_all_category_documents(selected_cat_id) # Resetăm câmpurile după salvarea cu succes self.documenet_title = None self.documenet_displayed_title.value = "" def on_cancel_save_dialog_btn_click(self, e): self.documenet_title = None self.documenet_displayed_title.value = "" self.page.pop_dialog() def create_category_list(self, items, on_click_handler, on_click_handler2): """Helper to create list items for a column.""" return [ ft.Container( content=ft.Row( [ ft.Row( [ ft.Icon(ft.Icons.ARROW_RIGHT, size=20), ft.Text(value=item['name']) ] ), ft.Row( [ ft.IconButton( icon=ft.Icons.EDIT, on_click=lambda e, id=item: on_click_handler(id), ), ft.IconButton( icon=ft.Icons.REFRESH, on_click=lambda e, id=item['id']: self.on_refresh_category_click(id), icon_color=ft.Colors.BLUE_400, ), ft.IconButton( icon=ft.Icons.DELETE, on_click=lambda e, id=item['id']: on_click_handler2(id), icon_color=ft.Colors.RED, ), ] ) ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, ), expand=True, bgcolor=ft.Colors.BLUE_50, border = ft.Border.all(1, ft.Colors.GREY), padding=10, border_radius=8, ink=True, on_click=lambda e, cid=item['id']: self.list_all_category_documents(cid) ) for item in items ] def on_edit_category_btn_click(self, category): self.editing_category_id = category['id'] self.category_name.value = category['name'] access_list = category.get('access', '').split(',') for cb in self.roles_checkboxes: cb.value = cb.label in access_list self.add_categories_dialog.title = ft.Text("Editeaza Categorie") self.page.show_dialog(self.add_categories_dialog) self.page.update() def on_refresh_category_click(self, category_id): base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') try: response = requests.post( f'{base_url}/documents/categories/refresh/{category_id}', headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 200: added = response.json().get("added", 0) # Daca suntem in categoria care s-a improspatat, reincarcam lista de documente if self.current_category_id == category_id: self.list_all_category_documents(category_id) # Notificare succes self.page.snack_bar = ft.SnackBar( content=ft.Text(f"Refresh complet. S-au adaugat {added} documente."), bgcolor=ft.Colors.GREEN_400 ) self.page.snack_bar.open = True self.page.update() except Exception as e: print(f"Error during category refresh: {e}") def on_delete_category_btn_click(self, category_id): self.category_id_to_delete = category_id self.page.show_dialog(self.delete_confirmation_dialog) self.page.update() def confirm_delete_category(self, e): if self.category_id_to_delete: base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') response = requests.delete( f'{base_url}/documents/categories/delete/{self.category_id_to_delete}', headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 200: # Dacă categoria ștearsă este cea ale cărei documente sunt afișate, golim lista if self.current_category_id == self.category_id_to_delete: self.documents_list.controls = [] self.documents_list.update() self.current_category_id = None self.update_category_list() self.category_id_to_delete = None self.page.pop_dialog() def close_delete_dialog(self, e): self.category_id_to_delete = None self.page.pop_dialog() def get_categories(self): base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') try: response = requests.get( f'{base_url}/documents/categories', headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 200: return response.json() except Exception as e: print(f"Error fetching categories: {e}") return [] def list_all_category_documents(self, category_id): self.current_category_id = category_id self.all_documents = self.get_all_documents(category_id) or [] self.documents_list.controls = self.create_documents_list( self.all_documents, lambda doc: self.page.run_task(self.on_download_document_btn_click, doc), self.on_delete_document_btn_click ) self.documents_list.update() async def on_download_document_btn_click(self, document): base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') # Deschidem link-ul de download în browser # path în document este de forma "NumeCategorie/fisier.ext" download_url = f"{base_url}/documents/download?path={document['path']}&token={token}" await self.page.launch_url(download_url) def on_delete_document_btn_click(self, doc_id): self.document_id_to_delete = doc_id self.page.show_dialog(self.delete_document_confirmation_dialog) self.page.update() def confirm_delete_document(self, e): if self.document_id_to_delete: base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') response = requests.delete( f'{base_url}/documents/standards/delete/{self.document_id_to_delete}', headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 200: if hasattr(self, 'current_category_id'): self.list_all_category_documents(self.current_category_id) self.document_id_to_delete = None self.page.pop_dialog() def close_delete_document_dialog(self, e): self.document_id_to_delete = None self.page.pop_dialog() def get_all_documents(self, category_id): base_url = self.page.session.store.get('api_base_url') token = self.page.session.store.get('token') try: response = requests.get( f'{base_url}/documents/standards/category/{category_id}', headers={'Authorization': f'Bearer {token}'} ) if response.status_code == 200: return response.json() except Exception as e: print(f"Error fetching documents: {e}") return [] def create_documents_list(self, items, on_click_handler, on_click_handler2): """Helper to create list items for a column.""" return [ ft.Container( content=ft.Row( [ ft.Row( [ ft.Icon(ft.Icons.ARROW_RIGHT, size=20), ft.Text(value=item['name']) ] ), ft.Row( [ ft.IconButton( icon=ft.Icons.DOWNLOAD, on_click=lambda e, id=item: on_click_handler(id), ), ft.IconButton( icon=ft.Icons.DELETE, on_click=lambda e, id=item['id']: on_click_handler2(id), icon_color=ft.Colors.RED, ), ] ) ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN, ), expand=True, bgcolor=ft.Colors.BLUE_50, border = ft.Border.all(1, ft.Colors.GREY), padding=10, border_radius=8 ) for item in items ] def build(self): return ft.Container( content=ft.Row( [ ft.Column( [ ft.Row( [ ft.Button( "Adauga Categori", icon=ft.Icons.ADD, on_click=self.on_add_btn_click, width=300 ), ], alignment=ft.MainAxisAlignment.CENTER ), self.category_list ], alignment=ft.MainAxisAlignment.START, width = 350, ), ft.VerticalDivider(width=1), ft.Column( [ ft.Row( [ self.add_document ], alignment=ft.MainAxisAlignment.END ), ft.Row( [ self.search_bar ] ), self.documents_list ], expand=True ) ], ), expand=True )