initial commit

This commit is contained in:
2025-01-19 00:04:08 +01:00
parent 2298827ff3
commit e37de4a7a4
21 changed files with 2926 additions and 0 deletions

Binary file not shown.

35
frontend/main.py Normal file
View File

@@ -0,0 +1,35 @@
import flet as ft
from urllib.parse import urlparse, parse_qs
from pages.main_page import main_page # Import main page function
from pages.login_page import login_page # Import login page function
from pages.register_teacher_page import register_teacher_page # Import register page function
from pages.register_student_page import register_student_page # Import search page function
from pages.create_page import create_page # Import create page function7
from pages.join_page import join_page
# Define your routes
ROUTES = {
"/": main_page,
"/login": login_page,
"/register_teacher": register_teacher_page,
"/register_student": register_student_page,
"/create": create_page,
"/join": join_page
}
def main(page: ft.Page):
def route_change(e: ft.RouteChangeEvent):
page.clean()
parsed_url = urlparse(e.route) # Parse the route
query_params = parse_qs(parsed_url.query) # Extract query parameters
render_function = ROUTES.get(parsed_url.path, main_page)
render_function(page)
page.on_route_change = route_change # Set up route change handling
page.go(page.route) # Navigate to the current route
if __name__ == "__main__":
ft.app(target=main, view=ft.AppView.WEB_BROWSER, port=9000)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,199 @@
import flet as ft
import requests
import threading
import asyncio
import websockets
from websockets import connect # Ensure this is imported
import json
from datetime import datetime
def student_main_page(page: ft.Page):
page.clean()
page.theme_mode = ft.ThemeMode.LIGHT # Set the theme to light
page.title = "Room Information"
page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top
page.scroll = "adaptive"
page.padding = 20
# Container for lessons
lessons_container = ft.Column(alignment=ft.MainAxisAlignment.CENTER, spacing=20)
# Buttons
test_button = ft.TextButton(
text="Auch Räume an anderen Tagen anzeigen",
on_click=lambda e: page.go("/all_rooms"),
style=ft.ButtonStyle(padding=20),
visible=False, # Initially hidden
)
join_more_rooms_button = ft.TextButton(
text="Tritt noch mehr Räume bei",
on_click=lambda e: page.go("/join"),
style=ft.ButtonStyle(padding=25),
)
def leave_current_room(e=None, unique_id=None):
if not unique_id:
page.snack_bar = ft.SnackBar(ft.Text("No room selected to leave."))
page.snack_bar.open = True
page.update()
return
url = f"http://awesom-o.org:8000/student/leave_room/?unique_id={unique_id}"
headers = {"accept": "application/json", "Content-Type": "application/json"}
data = {"session_id": page.session.get("access_token")}
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
if e:
page.snack_bar = ft.SnackBar(ft.Text(f"Left Room {unique_id}"))
page.snack_bar.open = True
page.update()
except requests.RequestException as error:
# Handle all bad responses
error_detail = "Something went wrong."
if hasattr(error, "response") and error.response:
try:
error_detail = error.response.json().get("detail", "Something went wrong.")
except json.JSONDecodeError:
error_detail = error.response.text or "Something went wrong."
page.snack_bar = ft.SnackBar(ft.Text(f"Error: {error_detail}"))
page.snack_bar.open = True
page.update()
async def get_rooms():
uri = "ws://localhost:8000/ws/student/my_room"
headers = {"session-id": page.session.get("access_token")}
try:
async with websockets.connect(uri, extra_headers=headers) as websocket:
while True:
message = await websocket.recv()
data = json.loads(message)
# Clear previous lessons
lessons_container.controls.clear()
if "error" in data:
# Show "No Rooms for Today" message
lessons_container.controls.append(
ft.Text(
"Für heute hast du noch keine Räume ausgewählt!",
size=50,
weight="bold",
text_align="center",
)
)
else:
rooms = data.get("rooms", [])
# Filter and sort lessons for today
today = datetime.now().date()
today_lessons = [room for room in rooms if room["lesson_date"] == str(today)]
today_lessons.sort(key=lambda x: x["lesson_time"])
# Check if there are rooms on other dates
other_date_rooms = [room for room in rooms if room["lesson_date"] != str(today)]
test_button.visible = bool(other_date_rooms) # Show button if there are rooms on other dates
if today_lessons:
for lesson in today_lessons:
# Create lesson card
room_card = ft.Card(
content=ft.Container(
content=ft.Row(
[
# Room details
ft.Column(
[
# Top line: room_number, info, location
ft.Row(
[
ft.Text(
f"{lesson['room_number']} {lesson['info']}",
size=20,
weight="bold",
),
ft.Text(
f"| {lesson['location']}",
size=20,
weight="bold",
),
],
spacing=5,
),
# Second line: first_name and last_name
ft.Text(
f"Teacher: {lesson['first_name']} {lesson['last_name']}",
size=16,
),
# Third line: lesson_time
ft.Text(
f"Time: {lesson['lesson_time']}",
size=16,
),
],
expand=True,
alignment=ft.MainAxisAlignment.CENTER,
),
# IconButton on the right
ft.IconButton(
icon=ft.icons.EXIT_TO_APP,
on_click=lambda e, lesson_id=lesson['unique_id']: leave_current_room(e, lesson_id),
tooltip="View Room Details",
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
padding=20, # Add padding inside the card
),
margin=10,
elevation=5, # Add shadow for better visual appeal
)
lessons_container.controls.append(room_card)
else:
# Show "No Rooms for Today" message
lessons_container.controls.append(
ft.Text(
"No Rooms for Today",
size=50,
weight="bold",
text_align="center",
)
)
# Update page
page.update()
except Exception as e:
lessons_container.controls.clear()
lessons_container.controls.append(ft.Text(f"Error: {e}", size=50, weight="bold", text_align="center"))
page.update()
def start_get_rooms():
# Ensure a new event loop in the thread
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(get_rooms())
# Start the WebSocket client in a thread
import threading
threading.Thread(target=start_get_rooms, daemon=True).start()
# Add components to page
page.add(
ft.Column(
[
# Lessons container (centered)
ft.Column(
[lessons_container],
alignment=ft.MainAxisAlignment.CENTER,
expand=True,
),
# Buttons in one row at the bottom
ft.Row(
[test_button, join_more_rooms_button],
alignment=ft.MainAxisAlignment.CENTER,
spacing=20, # Add spacing between buttons
),
],
expand=True,
)
)

View File

@@ -0,0 +1,288 @@
import flet as ft
import requests
import datetime
def create_page(page: ft.Page):
page.clean()
page.theme_mode = ft.ThemeMode.LIGHT # Set the theme to light
def is_student():
url = 'http://127.0.0.1:8000/check_role'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
session_id = page.session.get("access_token")
if not session_id:
print("Session ID is missing.")
return None
data = {'session_id': session_id}
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
role = response.json()
if 'role' in role and 'role' in role['role']:
if role['role']['role'] == "student":
role = "s"
page.go("/login")
return role
elif role['role']['role'] == "teacher":
role = "t"
return role
print("Unexpected response structure:", role)
return None
except requests.exceptions.RequestException as error:
print("Error during role check:", error)
return None
page.title = "Create Room Page"
if is_student == "s":
page.gp("/")
session = page.session.get("access_token")
if not session:
page.go("/login")
# Initialize selected room state (use a simple variable instead of ft.State)
selected_room = ""
selected_date = ""
selected_time = ""
SEARCH_RESULT = ""
date_text = ft.Text(
"", # Initially empty
size=18, # Font size
weight=ft.FontWeight.BOLD, # Make the text bold
color="black", # Text colo
visible=False # Initially hidden
)
# Function to fetch room data
def fetch_rooms():
try:
response = requests.get("http://awesom-o.org:8000/teacher/all_room_information")
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print("Error fetching room data:", e)
return []
# Fetch rooms initially (don't display them immediately)
room_data = fetch_rooms()
# Function to filter room data
def search_rooms(query):
query = query.lower()
return [
room for room in room_data
if query in room["room_number"].lower()
or query in room["info"].lower()
or query in room["location"].lower()
]
# Search results container
search_results = ft.Column(
visible=False, # Initially hidden
scroll=ft.ScrollMode.AUTO,
expand=True
)
# Update search results
def update_results(query):
if query.strip() == "": # When the search field is empty, show all rooms
search_results.controls.clear()
for room in room_data:
search_results.controls.append(
ft.ListTile(
title=ft.Text(f"Room {room['room_number']}: {room['info']}"),
subtitle=ft.Text(room['location']),
on_click=lambda e, r=room: select_room(r),
)
)
search_results.visible = True
else:
search_results.controls.clear()
filtered_rooms = search_rooms(query)
for room in filtered_rooms:
search_results.controls.append(
ft.ListTile(
title=ft.Text(f"Room {room['room_number']}: {room['info']}"),
subtitle=ft.Text(room['location']),
on_click=lambda e, r=room: select_room(r),
)
)
search_results.visible = True
close_button.visible = search_results.visible # Show close button when results are visible
page.update()
# Select a room
def select_room(room):
nonlocal selected_room
nonlocal SEARCH_RESULT
selected_room = f"Room {room['room_number']}: {room['info']} - {room['location']}"
search_bar.value = selected_room
SEARCH_RESULT = room['number']
search_results.visible = False
close_button.visible = False
page.update()
# Close button to hide search results
def close_search_results(e):
search_results.visible = False
close_button.visible = False
page.update()
def handle_change_date_picker(e):
# Once a date is selected, update the Text and make it visible
nonlocal selected_date
selected_date = e.control.value.strftime('%Y-%m-%d')
if selected_time != "":
date_text.value = f"Your Room will be started on: {selected_date} at {selected_time}"
date_text.visible = True # Make the Text visible after date selection
page.update()
else:
date_text.value = f"Your Room will be started on: {selected_date}"
date_text.visible = True # Make the Text visible after date selection
page.update() # Update the page to reflect the changes
def handle_change_dropdown(e):
nonlocal selected_time
selected_time1 = e.control.value
selected_time = selected_time1.replace("Start time: ", "")
if selected_date != "":
date_text.value = f"Your Room will be started on: {selected_date} at {selected_time}"
date_text.visible = True # Make the Text visible after date selection
page.update()
else:
date_text.value = f"Your Room will be started at {selected_time}"
date_text.visible = True # Make the Text visible after date selection
page.update() # Update the page to reflect the changes
def go_to_main(e):
page.go("/") # Replace with the appropriate route for your register page
def create(e):
page.update()
search_results = SEARCH_RESULT.strip()
max_students = max_students_field.value.strip()
selected_date
selected_time
# Validate input
if not search_results or not max_students or not selected_date or not selected_time:
info_label.value = "Please enter Room Number, Max Students, Lesson Date and Lesson Time."
info_label.color = "red"
page.update()
return
try:
max_students = int(max_students)
except ValueError:
info_label.value = "Max Students must be a number."
info_label.color = "red"
page.update()
return
if max_students < 1:
info_label.value = "Please don't enter a negative or zero value for Max Students."
info_label.color = "red"
page.update()
return
try:
url = 'http://127.0.0.1:8000/teacher/create_room'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
data = {
'room_number': search_results,
'max_students': max_students,
'session_id': page.session.get("access_token"),
'lesson_time': selected_time,
'lesson_date': selected_date
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
success_message = "The Room was created Successfully!"
info_label.value = success_message
info_label.color = "green"
else:
error_message = "Something went wrong..."
info_label.value = error_message
info_label.color = "red"
except requests.exceptions.RequestException as e:
info_label.value = f"An error occurred: {str(e)}"
info_label.color = "red"
page.update()
# Search bar
search_bar = ft.TextField(
label="Search for a room",
on_focus=lambda e: update_results(""), # Show all rooms when search bar is focused
on_change=lambda e: update_results(e.control.value),
expand=True # Make the search bar take up available space
)
# Close button
close_button = ft.IconButton(
icon=ft.icons.CLOSE,
on_click=close_search_results,
visible=False # Initially hidden
)
max_students_field = ft.TextField(label="Max Students")
info_label = ft.Text("", theme_style=ft.TextThemeStyle.TITLE_SMALL)
# Add components to the page
page.add(
ft.Row([search_bar, close_button]), # Add search bar and close button in a row
search_results,
max_students_field,
ft.Row([
ft.OutlinedButton(
"Pick the date when your lesson begins",
icon=ft.icons.CALENDAR_MONTH,
height=40,
on_click=lambda e: page.open(
ft.DatePicker(
first_date=datetime.datetime(year=2024, month=11, day=20),
on_change=handle_change_date_picker,
)
),
),
date_text, # Add the styled Text below the button
], spacing=20),
ft.Dropdown(
hint_text="Choose the Start of your Lesson",
options=[
ft.dropdown.Option("Start time: 8:00"),
ft.dropdown.Option("Start time: 9:20"),
ft.dropdown.Option("Start time: 10:40"),
ft.dropdown.Option("Start time: 11:50"),
ft.dropdown.Option("Start time: 12:50"),
ft.dropdown.Option("Start time: 13:55"),
ft.dropdown.Option("Start time: 15:00"),
],on_change=handle_change_dropdown,
label_style=ft.TextStyle(size=16), # Correct way to set font size for Dropdown
),
ft.ElevatedButton("Submit", on_click=create),
info_label,
ft.Row([
ft.TextButton("Enough? Click here to go to the Main Page", on_click=go_to_main),
]),
)
# Example usage
if __name__ == "__main__":
ft.app(target=create_page, view=ft.AppView.WEB_BROWSER)

425
frontend/pages/join_page.py Normal file
View File

@@ -0,0 +1,425 @@
import flet as ft
import asyncio
import websockets
import json
import requests
import threading
from datetime import datetime
def get_initials(user_name: str):
if user_name:
return user_name[0].capitalize()
else:
return "E"
def get_avatar_color(user_name: str):
colors_lookup = [
ft.colors.AMBER,
ft.colors.BLUE,
ft.colors.BROWN,
ft.colors.CYAN,
ft.colors.GREEN,
ft.colors.INDIGO,
ft.colors.LIME,
ft.colors.ORANGE,
ft.colors.PINK,
ft.colors.PURPLE,
ft.colors.RED,
ft.colors.TEAL,
]
return colors_lookup[hash(user_name) % len(colors_lookup)]
def join_page(page: ft.Page):
page.clean()
page.theme_mode = ft.ThemeMode.LIGHT # Set the theme to light
page.title = "Join Rooms"
page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top
page.scroll = "adaptive"
page.padding = 20
# Check if the user is logged in
session = page.session.get("access_token")
if not session:
page.go("/login") # Redirect to login if no session
def test(e):
print(e)
page.go("/test")
print("test")
# Search field
search_field = ft.TextField(label="Lehrkraft / Raumnummer ", expand=True)
# Dropdown for time filter
time_filter = ft.Dropdown(
label="Stunde wählen",
options=[
ft.dropdown.Option("8:00", "8:00"),
ft.dropdown.Option("9:20", "9:20"),
ft.dropdown.Option("10:40", "10:40"),
ft.dropdown.Option("11:50", "11:50"),
ft.dropdown.Option("12:50", "12:50"),
ft.dropdown.Option("13:55", "13:55"),
ft.dropdown.Option("15:00", "15:00"),
],
width=170, # Shorter width for time filter
)
# Cross icon button to clear time filter
clear_time_filter = ft.IconButton(
icon=ft.icons.CLOSE,
on_click=lambda e: clear_filter("time"),
tooltip="Clear Time Filter",
)
# Container for the room list
room_container = ft.Column(scroll=ft.ScrollMode.AUTO, expand=True)
def ausloggen(e = None):
page.go("/login")
print(page.session.set("access_token", None))
# Function to clear filters
def clear_filter(filter_type):
if filter_type == "time":
time_filter.value = None
if filter_type == "lesson":
lesson_filter.value = None
update_room_list(json.loads(page.session.get("rooms_data", "[]")))
def update_room_list(rooms):
# Ensure rooms is a list
if not isinstance(rooms, list):
rooms = []
# Apply search filter
search_query = search_field.value.lower()
filtered_rooms = [
room
for room in rooms
if (search_query in str(room.get("room_name", "")).lower() # Search by room_name
or search_query in room.get("info", "").lower() # Search by room_info
or search_query in room.get("location", "").lower() # Search by location
or search_query in room.get("subjects", "").lower() # Search by subjects
or search_query in room.get("first_name", "").lower() # Search by teacher_first_name
or search_query in room.get("last_name", "").lower()) # Search by teacher_last_name
]
if time_filter.value: # Only apply the filter if a time is selected
filtered_rooms = [
room
for room in filtered_rooms
if room.get("lesson_time", "") == time_filter.value
]
if lesson_filter.value: # Only apply the filter if a time is selected
filtered_rooms = [
room
for room in filtered_rooms
if lesson_filter.value.lower() in room.get("subjects", "").lower().split(", ")
]
if show_full_rooms.value == True:
filtered_rooms = [
room
for room in filtered_rooms
if room.get("is_open", "") == True
]
# Apply date filter (only if a date is selected)
room_container.controls.clear()
def page_dialog_click(e, unique_id):
dialog_join = ft.AlertDialog(
modal=True,
title=ft.Text("Bitte bestätige"),
content=ft.Text("Möchtest du diesem Raum wirklich beitreten?"),
actions=[
ft.TextButton("Ja", on_click=lambda e: (page.close(dialog_join), room_clicked(e, unique_id))),
ft.TextButton("Nein", on_click=lambda e: page.close(dialog_join)),
],
actions_alignment=ft.MainAxisAlignment.END,
)
page.dialog = dialog_join
dialog_join.open = True
page.update()
def room_clicked(e, unique_id):
try:
url = f"http://awesom-o.org:8000/student/join_room/?unique_id={unique_id}"
headers = {"accept": "application/json", "Content-Type": "application/json"}
data = {"session_id": page.session.get("access_token")}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
try:
page.snack_bar = ft.SnackBar(ft.Text(f"Raum erfolgreich beigetreten: {unique_id}"))
page.snack_bar.open = True
page.update()
page.go("/")
except:
page.snack_bar = ft.SnackBar(ft.Text(f"Ein fehler ist aufgetreten, bitte melde dich bei der Administration"))
page.snack_bar.open = True
page.update()
else:
dialog_join = ft.AlertDialog(
modal=True,
title=ft.Text("Fehler"),
content=ft.Text(response.json().get("detail")),
actions=[
ft.TextButton("Schließen", on_click=lambda e: page.close(dialog_join)),
],
actions_alignment=ft.MainAxisAlignment.END,
)
page.dialog = dialog_join
dialog_join.open = True
page.update()
except requests.exceptions.RequestException as e:
dialog_join = ft.AlertDialog(
modal=True,
title=ft.Text("Fehler"),
content=ft.Text("Ein Fehler ist aufgetreten"),
actions=[
ft.TextButton("Schließen", on_click=lambda e: page.close(dialog_join)),
],
actions_alignment=ft.MainAxisAlignment.END,
)
page.dialog = dialog_join
dialog_join.open = True
page.update()
for room in filtered_rooms:
room_name = room.get("room_name", "Unknown Room")
room_info = room.get("info", "")
location = room.get("location", "")
first_name = room.get("first_name", "")
last_name = room.get("last_name", "")
subjects = room.get("subjects", "")
lesson_time = room.get("lesson_time", "")
lesson_date = room.get("lesson_date", "")
max_students = room.get("max_students", 0)
current_students = room.get("current_students", 0)
unique_id = room.get("unique_id", "")
is_open = room.get("is_open", True)
joined = room.get("joined", False)
if current_students == max_students:
BACKROUND_COLOR = "#f10d0c"
TEXT_COLOR = ft.colors.WHITE
user_interface_button_text = ft.Text("Raum bereits voll")
else:
BACKROUND_COLOR = "#729fcf"
TEXT_COLOR = ft.colors.BLACK
if joined == True:
user_interface_button_text = ft.Text("Raum bereits gebucht")
else:
user_interface_button_text = ft.TextButton(
"Jetzt Platz reservieren",
on_click=lambda e, unique_id=unique_id: page_dialog_click(e, unique_id),
style=ft.ButtonStyle(
bgcolor=ft.colors.WHITE,
color=ft.colors.BLACK,
shape=ft.RoundedRectangleBorder(radius=5),
padding=20,
))
room_card = ft.Card(
content=ft.Container(
content=ft.Row(
[
# Room details
ft.Column(
[
ft.Text(f"Heute, {lesson_time} Uhr", size=16, color=TEXT_COLOR),
ft.Text(f"Lehrkraft: {first_name} {last_name} - {subjects}", size=16, color=TEXT_COLOR),
ft.Text(f"Raum {room_name} {room_info} | {location}", size=16, color=TEXT_COLOR),
ft.Text(f"Belegte Plätze: {current_students}/{max_students}", size=16, color=TEXT_COLOR),
],
expand=True,
alignment=ft.MainAxisAlignment.CENTER,
),
# IconButton on the right
user_interface_button_text,
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
padding=20, # Add padding inside the card
),
color=BACKROUND_COLOR,
margin=10,
)
room_container.controls.append(room_card)
if not filtered_rooms:
room_container.controls.append(ft.Text("Keine offenen Räume verfügbar für Heute.", size=18))
page.update()
# WebSocket listener for real-time updates
async def listen_for_updates():
uri = "ws://localhost:8000/ws/open_rooms"
headers = {"session-id": page.session.get("access_token")}
async with websockets.connect(uri, extra_headers=headers) as websocket:
print("Connected to WebSocket server")
try:
while True:
# Receive data from the server
data = await websocket.recv()
rooms = json.loads(data)
# Ensure rooms is a list
if isinstance(rooms, dict):
rooms = rooms.get("rooms", [])
elif not isinstance(rooms, list):
rooms = []
# Store rooms data in session for filtering
page.session.set("rooms_data", json.dumps(rooms))
# Extract unique dates and sort them from nearest to longest
unique_dates = list(set(room.get("lesson_date", "") for room in rooms))
unique_dates.sort(key=lambda x: datetime.strptime(x, "%Y-%m-%d")) # Sort dates
# Update the room list
update_room_list(rooms)
except websockets.ConnectionClosed:
print("WebSocket connection closed")
# Start the WebSocket listener in a separate thread
def start_websocket_listener():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(listen_for_updates())
threading.Thread(target=start_websocket_listener, daemon=True).start()
top_bar = ft.Row(
[
search_field,
ft.Row([time_filter, clear_time_filter], spacing=5),
],
alignment=ft.MainAxisAlignment.START,
height=50,
)
lesson_filter = ft.Dropdown(
label="Fach wählen",
options=[
ft.dropdown.Option("De", "De"),
ft.dropdown.Option("Ma", "Ma"),
ft.dropdown.Option("NW", "NW"),
ft.dropdown.Option("En", "En"),
ft.dropdown.Option("Gs", "Gs"),
ft.dropdown.Option("Re", "Re"),
ft.dropdown.Option("WN", "WN"),
ft.dropdown.Option("Fr", "Fr"),
ft.dropdown.Option("Sp", "Sp"),
ft.dropdown.Option("Sn", "Sn"),
],
width=170,
)
clear_lesson_filter = ft.IconButton(
icon=ft.icons.CLOSE,
on_click=lambda e: clear_filter("lesson"),
tooltip="Clear Lesson Filter",
)
show_full_rooms = ft.Checkbox(label="Volle Räume ausblenden ", value=False, label_style=ft.TextStyle(size=20))
middle_bar = ft.Row(
[
show_full_rooms,
ft.Row(
[
ft.Column(
[
ft.Text(
"Nur Räume anzeigen, in denen\nfolgendes Fach angeboten wird:",
size=20,
),
],
),
lesson_filter,
clear_lesson_filter,
],
spacing=5,
alignment=ft.MainAxisAlignment.END,
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
height=50,
)
go_home_button = ft.Container(
content=ft.Text("Home", size=24, color=ft.colors.BLUE_900),
on_click=lambda e: page.go("/"),
alignment=ft.alignment.center,
width=200,
height=60,
bgcolor='transparent',
ink=True,
)
navigation_bar = ft.Row(
[go_home_button, ft.Text("Räume suchen", size=23, weight=ft.FontWeight.BOLD, width=170, no_wrap=True)],
)
page.add(
ft.Column(
[
ft.Row(
[
ft.Text(
"Daltonraum-Buchungssystem der IGS Garbsen", size=30),
ft.Column([
ft.PopupMenuButton(
items=[
ft.PopupMenuItem(content=ft.Text(f"Eingeloggt als: {page.session.get("username")}", weight=ft.FontWeight.BOLD)),
ft.PopupMenuItem(text="Profil anzeigen", on_click=test),
ft.PopupMenuItem(text="Ausloggen", on_click=ausloggen),
],
content=ft.CircleAvatar(
content=ft.Text(get_initials(
page.session.get("username"))),
color=ft.colors.WHITE,
bgcolor=get_avatar_color(
page.session.get("username")),
),
menu_position=ft.PopupMenuPosition.UNDER,
tooltip="",
)
],
# Align column content to the end (optional)
alignment=ft.MainAxisAlignment.END,
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN, # Space out the two elements
vertical_alignment=ft.CrossAxisAlignment.CENTER, # Align items vertically
),
navigation_bar,
top_bar,
middle_bar,
ft.Column(
[
room_container,
],
alignment=ft.MainAxisAlignment.CENTER,
expand=True,
),
],
expand=True,
alignment=ft.MainAxisAlignment.CENTER,
)
)

View File

@@ -0,0 +1,79 @@
import flet as ft
import requests
def login_page(page: ft.Page):
page.clean()
page.title = "Login Page"
page.theme_mode = ft.ThemeMode.LIGHT # Set the theme to light
# UI components
username_field = ft.TextField(label="Accountname")
password_field = ft.TextField(label="Passwort", password=True, can_reveal_password=True)
# Create an info text label
info_label = ft.Text("", theme_style=ft.TextThemeStyle.TITLE_SMALL)
# Function to handle login
def login(e):
username = username_field.value.strip() # Strip whitespace
password = password_field.value.strip()
# Validate input
if not username or not password:
info_label.value = "Bitte Benutzername und Passwort eingeben"
info_label.color = "red"
page.update() # Update the page to reflect the changes
return
# Send request over HTTP
try:
url = 'http://awesom-o.org:8000/login'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
data = {
'username': username,
'password': password
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
token_data = response.json()
page.session.set("username", token_data['username'])
page.session.set("access_token", token_data['session_id'])
page.go("/")
else:
error_message = response.json().get("detail")
info_label.value = error_message
info_label.color = "red"
except requests.exceptions.RequestException as e:
info_label.value = f"An error occurred: {str(e)}"
info_label.color = "red"
# Update the page to reflect the changes
page.update()
# Function to navigate to the register page
def go_to_register(e):
page.go("/register_student") # Replace with the appropriate route for your register page
# Add components to the page
page.add(
ft.Text("Daltonraum-Buchungssystem der IGS Garbsen", size=30),
ft.Text("Login", size=23, weight=ft.FontWeight.BOLD),
username_field,
password_field,
ft.ElevatedButton("Anmelden", on_click=login),
info_label, # Add the info label to the page
ft.Row([
ft.TextButton("Noch nicht registriert? Registriere dich hier!", on_click=go_to_register),
])
)
# You can run the login page separately or as part of your main app.
if __name__ == "__main__":
ft.app(target=login_page, view=ft.AppView.WEB_BROWSER)

272
frontend/pages/main_page.py Normal file
View File

@@ -0,0 +1,272 @@
import flet as ft
import requests
import threading
import asyncio
import websockets
from websockets import connect # Ensure this is imported
import json
from datetime import datetime
from pages.student_main_page import student_main_page
def main_page(page: ft.Page):
page.clean()
page.theme_mode = ft.ThemeMode.LIGHT
session = page.session.get("access_token")
if not session:
page.go("/login") # Redirect to login if no session
def is_student():
url = 'http://awesom-o.org:8000/check_role'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
session_id = page.session.get("access_token")
if not session_id:
return None
data = {'session_id': session_id}
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
role = response.json()
if role and 'role' in role and 'role' in role['role']:
if role['role']['role'] == "student":
role = "s"
return role
elif role['role']['role'] == "teacher":
role = "t"
return role
print("Unexpected response structure:", role)
return None
except requests.exceptions.RequestExceptin as error:
print("Error during role check:", error)
return None
page.title = "Room Information"
page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top
if is_student() == "s":
student_main_page(page)
elif is_student() == "t":
async def fetch_room_data(session_id: str, update_room_list):
uri = "ws://localhost:8000/ws/teacher/open_rooms"
headers = {"session-id": session_id} # Add session ID to headers
async with websockets.connect(uri, extra_headers=headers) as websocket:
print("WebSocket connected")
try:
while True:
# Receive data from the WebSocket
data = await websocket.recv()
room_data = json.loads(data)
open_rooms = room_data.get("open_rooms", [])
# Update the room list
update_room_list(open_rooms)
except websockets.ConnectionClosed:
print("WebSocket connection closed")
except Exception as e:
print(f"WebSocket error: {e}")
# Function to fetch students in a room
def fetch_students_in_room(unique_id: str, session_id: str):
url = f"http://awesom-o.org:8000/teacher/room_students/?unique_id={unique_id}"
headers = {"accept": "application/json", "Content-Type": "application/json"}
data = {"session_id": session_id}
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"Error fetching students: {e}")
return None
# Function to delete a room
def delete_room(unique_id: str, session_id: str):
url = f"http://awesom-o.org:8000/teacher/delete_room/?unique_id={unique_id}"
headers = {"accept": "application/json", "Content-Type": "application/json"}
data = {"session_id": session_id}
try:
response = requests.delete(url, json=data, headers=headers)
response.raise_for_status()
return True
except requests.RequestException as e:
print(f"Error deleting room: {e}")
return False
# Main Flet app
page.title = "Room Management"
page.vertical_alignment = ft.MainAxisAlignment.START # Align content to the top
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER
page.padding = 20
page.scroll = "adaptive"
# Check if the user is logged in
session_id = page.session.get("access_token")
if not session_id:
page.go("/login") # Redirect to login if no session
# Search field
search_field = ft.TextField(label="Search", expand=True)
# Icon button to navigate to "/create" page
create_button = ft.IconButton(
icon=ft.icons.ADD,
on_click=lambda _: page.go("/create"),
)
# Container for the room list
room_container = ft.Column(scroll=ft.ScrollMode.AUTO, expand=True)
# Function to update the room list
def update_room_list(rooms):
room_container.controls.clear()
for room in rooms:
room_name = room.get("room_name", "Unknown Room")
room_info = room.get("info", "")
location = room.get("location", "")
teacher_name = room.get("teacher_name", "")
max_students = room.get("max_students", 0)
current_students = room.get("current_students", 0)
unique_id = room.get("unique_id", "")
# Create a clickable room card
room_card = ft.GestureDetector(
content=ft.Card(
content=ft.Container(
content=ft.Column(
[
# Top line: room_name, room_info, location
ft.Row(
[
ft.Text(f"{room_name}", size=20),
ft.Text(f"{room_info}", size=16),
ft.Text(f"| {location}", size=16),
],
spacing=5,
),
# Teacher line
ft.Text(f"Teacher: {teacher_name}", size=16),
# Capacity line
ft.Text(f"Capacity: {current_students}/{max_students}", size=16),
],
spacing=10,
),
padding=20,
),
margin=10,
elevation=5,
),
on_tap=lambda e, unique_id=unique_id: open_room_dialog(unique_id),
)
room_container.controls.append(room_card)
if not rooms:
room_container.controls.append(ft.Text("No open rooms available.", size=18))
page.update()
# Function to open the room dialog
def open_room_dialog(unique_id: str):
# Fetch students in the room
students_data = fetch_students_in_room(unique_id, session_id)
if not students_data:
page.snack_bar = ft.SnackBar(ft.Text("Failed to fetch students."))
page.snack_bar.open = True
page.update()
return
students = students_data.get("students", [])
if students != None:
students_list = ft.Column(
[ft.Text(f"Student: {student}") for student in students],
scroll=ft.ScrollMode.AUTO,
expand=True,
)
else:
students_list = ft.Column(ft.Text(f"There are no students who joined your Room"),
scroll=ft.ScrollMode.AUTO,
expand=True,
)
# Confirmation dialog for deleting the room
def open_delete_confirmation_dialog():
page.close(dialog)
def confirm_delete(e):
if delete_room(unique_id, session_id):
page.snack_bar = ft.SnackBar(ft.Text("Room deleted successfully."))
page.snack_bar.open = True
page.update()
page.close(confirm_dialog)
page.update()
else:
page.snack_bar = ft.SnackBar(ft.Text("Failed to delete room."))
page.snack_bar.open = True
page.update()
confirm_dialog = ft.AlertDialog(
title=ft.Text("Are you sure you want to delete this room?"),
actions=[
ft.TextButton("No", on_click=lambda e: page.close(confirm_dialog)),
ft.TextButton("Yes", on_click=confirm_delete),
],
)
page.dialog = confirm_dialog
confirm_dialog.open = True
page.update()
# Room dialog
dialog = ft.AlertDialog(
title=ft.Text("Room Details"),
content=students_list,
actions=[
ft.TextButton("Close", on_click=lambda e: page.close(dialog)),
ft.TextButton("Delete Room", on_click=lambda e: open_delete_confirmation_dialog()),
],
)
page.dialog = dialog
dialog.open = True
page.update()
# WebSocket listener for real-time updates
async def listen_for_updates():
while True:
try:
await fetch_room_data(session_id, update_room_list)
except Exception as e:
print(f"WebSocket connection error: {e}. Reconnecting in 5 seconds...")
await asyncio.sleep(5) # Wait before reconnecting
# Top bar layout
top_bar = ft.Row(
[
search_field,
create_button,
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
vertical_alignment=ft.CrossAxisAlignment.CENTER,
)
# Add components to the page
page.add(
top_bar,
room_container,
)
# Start the WebSocket listener
page.run_task(listen_for_updates)
else:
page.go("/login")

View File

@@ -0,0 +1,94 @@
import flet as ft
import requests
import json
def register_student_page(page: ft.Page):
page.clean()
page.title = "Register Page"
page.theme_mode = ft.ThemeMode.LIGHT
# UI components
username_field = ft.TextField(label="Accountname (darfst du dir ausdenken)")
first_name_field = ft.TextField(label="Vorname")
last_name_field = ft.TextField(label="Nachname")
user_class_field = ft.TextField(label="Klasse")
password_field0 = ft.TextField(label="Passwort", password=True, can_reveal_password=True)
password_field1 = ft.TextField(label="Passwort bestätigen", password=True, can_reveal_password=True)
# Create an info text label
info_label = ft.Text("", theme_style=ft.TextThemeStyle.TITLE_SMALL)
# Function to handle registration
def register(e):
username = username_field.value.strip()
password0 = password_field0.value.strip()
first_name = first_name_field.value.strip()
last_name = last_name_field.value.strip()
user_class = user_class_field.value.strip()
password1 = password_field1.value.strip()
# Validate input
if not username or not password0 or not password1 or not first_name or not last_name or not user_class :
info_label.value = "Bitte fülle alle lücken aus"
info_label.color = "red"
page.update()
return
if not password0 == password1:
info_label.value = "Passwörter stimmen nicht überein"
info_label.color = "red"
page.update()
return
# Send request over HTTP
try:
url = 'http://awesom-o.org:8000/student/register'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
data = {
'username': username,
'first_name': first_name,
'last_name': last_name,
'user_class': user_class,
'password': password0,
}
response = requests.post(url, headers=headers, data=json.dumps(data))
if response.status_code == 200:
session_id = response.json().get("session_id")
username = response.json().get("username")
page.session.set("access_token", session_id)
page.session.set("username", username)
page.go("/")
else:
error_message = response.json().get("detail", "Registrierung fehlgeschlagen")
info_label.value = error_message
info_label.color = "red"
except requests.exceptions.RequestException as e:
info_label.value = f"An error occurred: {str(e)}"
info_label.color = "red"
page.update()
page.add(
ft.Text("Daltonraum-Buchungssystem der IGS Garbsen", size=30),
ft.Text("Registrieren", size=23, weight=ft.FontWeight.BOLD),
username_field,
first_name_field,
last_name_field,
user_class_field,
password_field0,
password_field1,
ft.ElevatedButton("Registrieren", on_click=register),
info_label, # Add the info label to the page
ft.TextButton("Bereits registriert? Zurück zur Login-Seite.", on_click=lambda _: page.go("/login")) # Link to login
)
# You can run the registration page separately or as part of your main app.
if __name__ == "__main__":
ft.app(target=register_student_page, view=ft.AppView.WEB_BROWSER)

View File

@@ -0,0 +1,180 @@
import flet as ft
import requests
def register_teacher_page(page: ft.Page):
page.clean()
page.title = "Register Page"
page.theme_mode = ft.ThemeMode.LIGHT
subjects_list = []
row = ft.Row()
# UI components
username_field = ft.TextField(label="Benutzername")
first_name_field = ft.TextField(label="Vorname")
last_name_field = ft.TextField(label="Nachname")
password_field0 = ft.TextField(label="Passwort", password=True, can_reveal_password=True)
password_field1 = ft.TextField(label="Passwort bestätigen", password=True, can_reveal_password=True)
secret_teacher_password_field = ft.TextField(label="Secret Teacher Password", password=True, can_reveal_password=True)
info_label = ft.Text("", theme_style=ft.TextThemeStyle.TITLE_SMALL)
def delete_subject(dropdown, delete_button):
if dropdown.value in subjects_list:
subjects_list.remove(dropdown.value)
row.controls.remove(dropdown)
row.controls.remove(delete_button)
page.update()
refresh_dropdowns()
def append_subject(e=None, dropdown=None, plus_button=None):
if not dropdown.value or dropdown.value in subjects_list or dropdown.value == "Wähle deine Fächer aus":
dropdown.value = "" # Reset if empty or duplicate
page.update()
return
subjects_list.append(dropdown.value)
# Disable current dropdown and button after selection
dropdown.disabled = True
plus_button.visible = False
# Add new dropdown to the same row
add_new_dropdown()
page.update()
def on_dropdown_change(e, dropdown, plus_button):
if dropdown.value and dropdown.value not in subjects_list and dropdown.value != "Wähle deine Fächer aus":
subjects_list.append(dropdown.value)
dropdown.disabled = True
plus_button.visible = False
add_new_dropdown()
page.update()
def refresh_dropdowns():
for control in row.controls:
if isinstance(control, ft.Dropdown) and not control.disabled:
available_subjects = get_available_subjects()
control.options = [
ft.dropdown.Option("Wähle deine Fächer aus", disabled=True)
] + [
ft.dropdown.Option(subj) for subj in available_subjects
]
page.update()
def get_available_subjects():
available_subjects = [
"De", "En", "Fr", "Gs", "Ma", "NW", "Re", "Sn", "Sp", "Wn"
]
return [subj for subj in available_subjects if subj not in subjects_list]
def add_new_dropdown():
options = [
ft.dropdown.Option("Wähle deine Fächer aus", disabled=True)
] + [
ft.dropdown.Option(subj) for subj in get_available_subjects()
]
dropdown = ft.Dropdown(
width=250,
options=options,
value="Wähle deine Fächer aus",
on_change=lambda e: on_dropdown_change(e, dropdown, plus_button)
)
plus_button = ft.IconButton(
icon=ft.icons.ADD,
visible=False # Initially hidden
)
delete_button = ft.IconButton(
icon=ft.icons.CLOSE_OUTLINED,
on_click=lambda e: delete_subject(dropdown, delete_button)
)
plus_button.on_click = lambda e: append_subject(e, dropdown, plus_button)
# Add dropdown and buttons to the row
row.controls.append(dropdown)
row.controls.append(plus_button)
row.controls.append(delete_button)
page.update()
def register(e):
print(subjects_list)
username = username_field.value.strip()
password0 = password_field0.value.strip()
password1 = password_field1.value.strip()
secret_teacher_password = secret_teacher_password_field.value.strip()
last_name = last_name_field.value.strip()
first_name = first_name_field.value.strip()
subjects_string = ", ".join(subjects_list)
if not username or not password0 or not secret_teacher_password or not password1 or not last_name or not first_name or not subjects_string:
info_label.value = "Bitte fülle alle Felder aus"
info_label.color = "red"
page.update()
#page.clean()
#register_teacher_page(page=page)
return
if not password0 == password1:
info_label.value = "Passwörter stimmen nicht überein"
info_label.color = "red"
page.update()
return
try:
url = 'http://awesom-o.org:8000/teacher/register'
headers = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
data = {
'username': username,
'password': password0,
'teacher_secret_password': secret_teacher_password,
'first_name': first_name,
'last_name': last_name,
'subjects': subjects_string
}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
session_id = response.json().get("session_id")
username = response.json().get("username")
page.session.set("access_token", session_id)
page.session.set("username", username)
page.go("/")
else:
error_message = response.json().get("detail", "Registrierung fehlgeschlagen")
info_label.value = error_message
info_label.color = "red"
except requests.exceptions.RequestException as e:
info_label.value = f"An error occurred: {str(e)}"
info_label.color = "red"
page.update()
page.add(
ft.Text("Daltonraum-Buchungssystem der IGS Garbsen", size=30),
ft.Text("Registrieren", size=23, weight=ft.FontWeight.BOLD),
username_field,
ft.Row([
first_name_field,
last_name_field
]),
row,
password_field0,
password_field1,
secret_teacher_password_field,
ft.ElevatedButton("Registrieren", on_click=register),
info_label,
ft.TextButton("Kein Lehrer? Klicke hier!", on_click=lambda _: page.go("/register_student"))
)
add_new_dropdown()

View File

@@ -0,0 +1,343 @@
import flet as ft
import requests
import threading
import asyncio
import websockets
from websockets import connect # Ensure this is imported
import json
from datetime import datetime
def get_initials(user_name: str):
if user_name:
return user_name[0].capitalize()
else:
return "E" # or any default value you prefer
def get_avatar_color(user_name: str):
colors_lookup = [
ft.colors.AMBER,
ft.colors.BLUE,
ft.colors.BROWN,
ft.colors.CYAN,
ft.colors.GREEN,
ft.colors.INDIGO,
ft.colors.LIME,
ft.colors.ORANGE,
ft.colors.PINK,
ft.colors.PURPLE,
ft.colors.RED,
ft.colors.TEAL,
]
return colors_lookup[hash(user_name) % len(colors_lookup)]
def student_main_page(page: ft.Page):
page.clean()
page.title = "Room Information"
page.theme_mode = ft.ThemeMode.LIGHT
page.vertical_alignment = ft.MainAxisAlignment.START # Align all content to the top
page.scroll = "adaptive"
page.padding = 20
# Container for lessons
lessons_container = ft.Column(
alignment=ft.MainAxisAlignment.CENTER, spacing=20)
def test(e):
print(e)
page.go("/test")
print("test")
def ausloggen(e=None):
page.go("/login")
print(page.session.get("access_token"))
def page_dialog_click(e, lesson_id):
dialog_leave = ft.AlertDialog(
modal=True,
title=ft.Text("Bitte bestätige"),
content=ft.Text("Möchtest du diesem Raum wirklich verlassen?"),
actions=[
ft.TextButton("Ja", on_click=lambda e: (page.close(
dialog_leave), leave_current_room(e, lesson_id))),
ft.TextButton(
"Nein", on_click=lambda e: page.close(dialog_leave)),
],
actions_alignment=ft.MainAxisAlignment.END,
)
page.dialog = dialog_leave
dialog_leave.open = True
page.update()
def leave_current_room(e=None, unique_id=None):
if not unique_id:
page.snack_bar = ft.SnackBar(
ft.Text("Kein Raum zum verlassen ausgewählt"))
page.snack_bar.open = True
page.update()
return
url = f"http://awesom-o.org:8000/student/leave_room/?unique_id={
unique_id}"
headers = {"accept": "application/json",
"Content-Type": "application/json"}
data = {"session_id": page.session.get("access_token")}
try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
if response:
get_rooms()
page.snack_bar = ft.SnackBar(
ft.Text("Raum erfolgreich verlassen"))
page.snack_bar.open = True
page.update()
get_rooms()
except requests.RequestException as error:
# Handle all bad responses
error_detail = "Something went wrong."
if hasattr(error, "response") and error.response:
try:
error_detail = error.response.json().get("detail", "Something went wrong.")
except json.JSONDecodeError:
error_detail = error.response.text or "Etwas ist schiefgelaufen"
page.snack_bar = ft.SnackBar(ft.Text(f"Error: {error_detail}"))
page.snack_bar.open = True
page.update()
async def get_rooms():
uri = "ws://localhost:8000/ws/student/my_room"
headers = {"session-id": page.session.get("access_token")}
try:
async with websockets.connect(uri, extra_headers=headers) as websocket:
while True:
message = await websocket.recv()
data = json.loads(message)
# Clear previous lessons
lessons_container.controls.clear()
if "error" in data:
# Show "No Rooms for Today" message
lessons_container.controls.append(
ft.Container(
content=ft.Text(
"Für heute wurden noch keine Räume ausgewählt.",
size=25,
text_align="center",
),
border=ft.Border(
top=ft.BorderSide(1, ft.colors.BLACK),
bottom=ft.BorderSide(1, ft.colors.BLACK),
left=ft.BorderSide(1, ft.colors.BLACK),
right=ft.BorderSide(1, ft.colors.BLACK)
),
border_radius=20,
padding=10,
height=150,
bgcolor=ft.colors.WHITE,
alignment=ft.alignment.center,
)
)
else:
rooms = data.get("rooms", [])
# Filter and sort lessons for today
today = datetime.now().date()
today_lessons = [
room for room in rooms if room["lesson_date"] == str(today)]
today_lessons.sort(key=lambda x: x["lesson_time"])
# Check if there are rooms on other dates
other_date_rooms = [
room for room in rooms if room["lesson_date"] != str(today)]
if today_lessons:
for lesson in today_lessons:
if lesson['current_students'] == lesson['max_students']:
BACKROUND_COLOR = "#f10d0c"
TEXT_COLOR = ft.colors.WHITE
else:
BACKROUND_COLOR = "#729fcf"
TEXT_COLOR = ft.colors.BLACK
# Create lesson card
room_card = ft.Card(
content=ft.Container(
content=ft.Row(
[
# Room details
ft.Column(
[
# Top line:
# room_number, info, location
ft.Text(
f"Heute, {
lesson['lesson_time']} Uhr",
size=16,
color=TEXT_COLOR
),
# Second line: first_name and last_name
ft.Text(
f"Lehrkraft: {lesson['first_name']} {
lesson['last_name']} - {lesson['subjects']}",
size=16,
color=TEXT_COLOR
),
# Third line: lesson_time
ft.Text(
f"Raum {lesson['room_number']} {
lesson['info']} | {lesson['location']}",
size=16,
color=TEXT_COLOR
),
ft.Text(
f"Belegte Plätze: {
lesson['current_students']}/{lesson['max_students']}",
size=16,
color=TEXT_COLOR
),
],
expand=True,
alignment=ft.MainAxisAlignment.CENTER,
),
# IconButton on the right
ft.Column(
[
ft.TextButton("Aus Raum abmelden", on_click=lambda e, lesson_id=lesson['unique_id']: page_dialog_click(e, lesson_id),
style=ft.ButtonStyle(
bgcolor=ft.colors.WHITE,
color=ft.colors.BLACK,
shape=ft.RoundedRectangleBorder(
radius=5),
padding=20,
)
),
ft.Text("ACHTUNG: Hierdurch wird", size=12), ft.Text(
"dein reservierter Platz für", size=12), ft.Text("jemand anderes freigegeben.", size=12),
]
)
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
padding=20, # Add padding inside the card
),
color=BACKROUND_COLOR,
margin=10,
)
lessons_container.controls.append(room_card)
else:
# Show "No Rooms for Today" message
lessons_container.controls.append(
ft.Container(
content=ft.Text(
"Für heute wurden noch keine Räume ausgewählt.",
size=25,
text_align="center",
),
border=ft.Border(
top=ft.BorderSide(1, ft.colors.BLACK),
bottom=ft.BorderSide(
1, ft.colors.BLACK),
left=ft.BorderSide(1, ft.colors.BLACK),
right=ft.BorderSide(1, ft.colors.BLACK)
),
border_radius=20,
padding=10,
height=150,
bgcolor=ft.colors.WHITE,
alignment=ft.alignment.center,
)
)
# Update page
page.update()
except Exception as e:
lessons_container.controls.clear()
lessons_container.controls.append(
ft.Text(f"Error: {e}", size=50, weight="bold", text_align="center"))
page.update()
def start_get_rooms():
# Ensure a new event loop in the thread
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(get_rooms())
# Start the WebSocket client in a thread
import threading
threading.Thread(target=start_get_rooms, daemon=True).start()
join_more_rooms_button = ft.Container(
content=ft.Text("Räume suchen", size=24, color=ft.colors.BLUE_900),
on_click=lambda e: page.go("/join"),
alignment=ft.alignment.center,
width=200,
height=60,
bgcolor='transparent',
ink=True,
)
navigation_bar = ft.Row(
[ft.Text("Home", size=23, weight=ft.FontWeight.BOLD,
width=100), join_more_rooms_button],
)
# Add components to page
page.add(
ft.Column(
[
ft.Row(
[
ft.Text(
"Daltonraum-Buchungssystem der IGS Garbsen", size=30),
ft.Column([
ft.PopupMenuButton(
items=[
ft.PopupMenuItem(content=ft.Text(f"Eingeloggt als: {page.session.get("username")}", weight=ft.FontWeight.BOLD)),
ft.PopupMenuItem(text="Profil anzeigen", on_click=test),
ft.PopupMenuItem(text="Ausloggen", on_click=ausloggen),
],
content=ft.CircleAvatar(
content=ft.Text(get_initials(
page.session.get("username"))),
color=ft.colors.WHITE,
bgcolor=get_avatar_color(
page.session.get("username")),
),
menu_position=ft.PopupMenuPosition.UNDER,
tooltip="",
)
],
# Align column content to the end (optional)
alignment=ft.MainAxisAlignment.END,
),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN, # Space out the two elements
vertical_alignment=ft.CrossAxisAlignment.CENTER, # Align items vertically
),
navigation_bar,
ft.Row( # Wrap the Text in a Row
[
ft.Text(
"Du hast dich heute in folgende Daltonräume eingetragen:", size=25)
],
alignment=ft.MainAxisAlignment.CENTER # Center the Text horizontally
),
ft.Column(
[
lessons_container,
],
alignment=ft.MainAxisAlignment.CENTER,
expand=True, # Make it take available space vertically
),
],
expand=True, # This makes the outer column take available space as well
alignment=ft.MainAxisAlignment.CENTER, # Center the whole column vertically
)
)

Binary file not shown.

15
frontend/pages/test.py Normal file
View File

@@ -0,0 +1,15 @@
import flet as ft
def main(page: ft.Page):
# Button click handler to launch the download URL
def on_download_click(e):
# Launch the URL to trigger file download
page.launch_url("http://awesom-o.org:8000/download/tedt.pdf")
# Create an ElevatedButton that will trigger the download
download_button = ft.ElevatedButton("Download myfile", on_click=on_download_click)
# Add the button to the page
page.add(download_button)
ft.app(target=main)