diff --git a/lib/admin.py b/lib/admin.py index eb1e955..3291912 100644 --- a/lib/admin.py +++ b/lib/admin.py @@ -56,7 +56,7 @@ def page_admin(): update_userlist() - with (((ui.tab_panels(tabs, value=time_overview)))): + with ui.tab_panels(tabs, value=time_overview): with ui.tab_panel(time_overview): ui.markdown("##Übersichten") diff --git a/lib/definitions.py b/lib/definitions.py index 2c54691..ffa40f0 100644 --- a/lib/definitions.py +++ b/lib/definitions.py @@ -17,6 +17,7 @@ backupfolder = str(os.path.join(scriptpath, "backup")) usersettingsfilename = "settings.json" photofilename = "photo.jpg" +va_file = "vacation_application.json" # Status diff --git a/lib/homepage.py b/lib/homepage.py index a5f00a3..8a455c7 100644 --- a/lib/homepage.py +++ b/lib/homepage.py @@ -187,38 +187,89 @@ def homepage(): ui.separator() - with ui.grid(columns='1fr auto 1fr').classes('w-full justify-center'): + with ui.tabs().classes('w-full items-center') as tabs: + + overviews = ui.tab('Übersichten') + absence = ui.tab('Urlaubsantrag') + + with ui.grid(columns='1fr auto 1fr').classes('w-full items-center'): ui.space() + with ui.tab_panels(tabs, value=overviews): + with ui.tab_panel(overviews): - def activate_vacation(): - binder_vacation.value = True + def activate_vacation(): + binder_vacation.value = True - def activate_absence(): - binder_absence.value = True + def activate_absence(): + binder_absence.value = True - with ui.grid(columns='1fr 1fr'): + with ui.grid(columns='1fr 1fr'): - ui.markdown("**Monatsübersicht:**").classes('col-span-2') + ui.markdown("**Monatsübersicht:**").classes('col-span-2') - month_year_select = ui.select(list(reversed(available_years)), label="Jahr", on_change=update_month).bind_value_to(binder_available_years, 'value') - month_month_select = ui.select(available_months, label="Monat", on_change=enable_month) - month_month_select.disable() + month_year_select = ui.select(list(reversed(available_years)), label="Jahr", on_change=update_month).bind_value_to(binder_available_years, 'value') + month_month_select = ui.select(available_months, label="Monat", on_change=enable_month) + month_month_select.disable() - ui.space() - month_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/month/{current_user.username}/{month_year_select.value}-{month_month_select.value}", new_tab=True)).bind_enabled_from(binder_month_button, 'value') - ui.markdown("**Urlaubsanspruch**").classes('col-span-2') - vacation_select = ui.select(list(reversed(available_years)), on_change=activate_vacation) - vacation_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/vacation/{current_user.username}/{vacation_select.value}", new_tab=True)).bind_enabled_from(binder_vacation, 'value') - ui.markdown("**Fehlzeitenübersicht**").classes('col-span-2') - absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence) - absences_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"api/absence/{current_user.username}/{absences_select.value}", new_tab=True)).bind_enabled_from(binder_absence, 'value') - ui.separator().classes('col-span-2') + ui.space() + month_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/month/{current_user.username}/{month_year_select.value}-{month_month_select.value}", new_tab=True)).bind_enabled_from(binder_month_button, 'value') + ui.markdown("**Urlaubsanspruch**").classes('col-span-2') + vacation_select = ui.select(list(reversed(available_years)), on_change=activate_vacation) + vacation_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"/api/vacation/{current_user.username}/{vacation_select.value}", new_tab=True)).bind_enabled_from(binder_vacation, 'value') + ui.markdown("**Fehlzeitenübersicht**").classes('col-span-2') + absences_select = ui.select(list(reversed(available_years)), on_change=activate_absence) + absences_button = ui.button("Anzeigen", on_click=lambda: ui.navigate.to(f"api/absence/{current_user.username}/{absences_select.value}", new_tab=True)).bind_enabled_from(binder_absence, 'value') + ui.separator().classes('col-span-2') - def logout(): - app.storage.user.pop("active_user", None) - ui.navigate.to("/") + def logout(): + app.storage.user.pop("active_user", None) + ui.navigate.to("/") - ui.button("Logout", on_click=logout).classes('col-span-2') + ui.button("Logout", on_click=logout).classes('col-span-2') + with ui.tab_panel(absence): + ui.label("Urlaub für folgenden Zeitraum beantragen:") + vacation_date = ui.date().props('range today-btn') + def vacation_submission(): + try: + current_user.vacation_application(vacation_date.value["from"], vacation_date.value["to"]) + except TypeError: + current_user.vacation_application(vacation_date.value, vacation_date.value) + vacation_date.value = "" + with ui.dialog() as dialog, ui.card(): + ui.label("Urlaubsantrag wurde abgeschickt") + ui.button("OK", on_click=dialog.close) + open_vacation_applications.refresh() + dialog.open() + + ui.button("Einreichen", on_click=vacation_submission).classes('w-full items-center').tooltip("Hiermit reichen Sie einen Urlaubsantrag für den oben markierten Zeitraum ein.") + + @ui.refreshable + def open_vacation_applications(): + open_applications = current_user.get_open_vacation_applications() + if len(list(open_applications)) > 0: + ui.separator() + ui.label("Offene Urlaubsanträge:").classes('font-bold') + va_columns = [ {'label': 'Index', 'name': 'index', 'field': 'index', 'classes': 'hidden', 'headerClasses': 'hidden'}, + {'label': 'Start', 'name': 'start', 'field': 'start'}, + {'label': 'Ende', 'name': 'end', 'field': 'end'}] + va_rows = [ ] + date_string = '%d.%m.%Y' + for i, dates in open_applications.items(): + startdate_dt = datetime.datetime.strptime(dates[0], '%Y-%m-%d') + enddate_dt = datetime.datetime.strptime(dates[1], '%Y-%m-%d') + va_rows.append({'index': i, 'start': startdate_dt.strftime(date_string), 'end': enddate_dt.strftime(date_string)}) + + va_table = ui.table(columns=va_columns, rows=va_rows, selection="single", row_key="index").classes('w-full') + def retract_va(): + try: + current_user.revoke_vacation_application(va_table.selected[0]["index"]) + open_vacation_applications.refresh() + ui.notify("Urlaubsantrag zurückgezogen") + except IndexError: + ui.notify("Kein Urlaubsanstrag ausgewählt") + ui.button("Zurückziehen", on_click=retract_va).tooltip("Hiermit wird der oben gewählte Urlaubsantrag zurückgezogen.").classes('w-full') + + open_vacation_applications() ui.space() else: diff --git a/lib/users.py b/lib/users.py index a8a0f98..06b2bb3 100644 --- a/lib/users.py +++ b/lib/users.py @@ -11,7 +11,9 @@ import json import shutil import re -from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, standard_adminsettings, standard_usersettings +from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, \ + standard_adminsettings, standard_usersettings, va_file + # Benutzerklasse @@ -451,6 +453,39 @@ class user: return [total_time, in_time_stamp] + def vacation_application(self, startdate, enddate): + application_file = os.path.join(self.userfolder, va_file) + try: + with open(application_file, 'r') as json_file: + applications = json.load(json_file) + except FileNotFoundError: + applications = { } + applications[str(len(list(applications)))] = (startdate, enddate) + with open(application_file, 'w') as json_file: + json_file.write(json.dumps(applications, indent=4)) + + def get_open_vacation_applications(self): + application_file = os.path.join(self.userfolder, va_file) + try: + with open(application_file, 'r') as json_file: + applications = json.load(json_file) + except FileNotFoundError: + applications = { } + return applications + + def revoke_vacation_application(self, index): + application_file = os.path.join(self.userfolder, va_file) + with open(application_file, 'r') as json_file: + applications = json.load(json_file) + del(applications[index]) + new_applications = { } + new_index = 0 + for index, dates in applications.items(): + new_applications[new_index] = dates + new_index += 1 + with open(application_file, 'w') as json_file: + json_file.write(json.dumps(new_applications, indent=4)) + # Benutzer auflisten def list_users(): diff --git a/users/testuser1/vacation_application.json b/users/testuser1/vacation_application.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/users/testuser1/vacation_application.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/users/testuser10/settings.json b/users/testuser10/settings.json index 4630aa5..9734a44 100644 --- a/users/testuser10/settings.json +++ b/users/testuser10/settings.json @@ -1,8 +1,7 @@ { "username": "testuser10", "fullname": "Diego Dieci", - "password": "123456789", - "api_key": "807518cd5bd85c1e4855d340f9b77b23eac21b7f", + "password": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "workhours": { "2024-04-01": { "1": "1", @@ -14,5 +13,6 @@ "7": "7", "vacation": "30" } - } + }, + "api_key": "807518cd5bd85c1e4855d340f9b77b23eac21b7f" } \ No newline at end of file diff --git a/users/testuser10/vacation_application.json b/users/testuser10/vacation_application.json new file mode 100644 index 0000000..45adac7 --- /dev/null +++ b/users/testuser10/vacation_application.json @@ -0,0 +1,6 @@ +{ + "0": [ + "2025-06-09", + "2025-06-19" + ] +} \ No newline at end of file diff --git a/users/testuser2/settings.json b/users/testuser2/settings.json new file mode 100644 index 0000000..f852870 --- /dev/null +++ b/users/testuser2/settings.json @@ -0,0 +1,18 @@ +{ + "username": "testuser2", + "fullname": "testuser2", + "password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f", + "api_key": "84799b1cbb92514f047bc2186cb4b4aafb352d69", + "workhours": { + "2025-05-27": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "vacation": 0 + } + } +} \ No newline at end of file