Add better grades details
This commit is contained in:
parent
2f6a8fb398
commit
6477ad1f9b
3 changed files with 299 additions and 182 deletions
100
src/main.py
100
src/main.py
|
@ -23,7 +23,7 @@ from sdk.src.interfaces.prometheus.interface import *
|
|||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
def sync(page):
|
||||
def sync(page: ft.Page):
|
||||
auth_context_raw = loadauth("Fuji", "Auth Context")
|
||||
auth_context = PrometheusAuthContext.model_validate_json(auth_context_raw)
|
||||
|
||||
|
@ -55,81 +55,33 @@ def sync(page):
|
|||
|
||||
create_grades_database(grades_list=grades)
|
||||
|
||||
# Get the current route to know which page to refresh
|
||||
current_route = page.route
|
||||
|
||||
# Use the built-in change_page function to rewrite the current page
|
||||
page.views.clear()
|
||||
|
||||
routes = {
|
||||
"/": HomePage(),
|
||||
"/grades": GradesPage(page),
|
||||
"/timetable": TimetablePage(),
|
||||
"/homework": HomeworkPage(),
|
||||
"/exams": ExamsPage(),
|
||||
"/attendance": AttendancePage(),
|
||||
"/behaviour": BehaviourPage(),
|
||||
"/settings": SettingsPage(page)
|
||||
}
|
||||
|
||||
# Access the navigation bar from the main function
|
||||
bar = get_navigation_bar(page)
|
||||
|
||||
view = ft.View(current_route, [
|
||||
ft.Row([
|
||||
bar,
|
||||
ft.Container(content=routes.get(current_route, HomePage()), expand=True)
|
||||
], expand=True)
|
||||
])
|
||||
|
||||
page.views.append(view)
|
||||
page.update()
|
||||
|
||||
# You'll need to make the navigation bar accessible
|
||||
def get_navigation_bar(page):
|
||||
return ft.NavigationRail(
|
||||
selected_index=get_index_from_route(page.route),
|
||||
label_type=ft.NavigationRailLabelType.ALL,
|
||||
extended=False,
|
||||
min_width=50,
|
||||
min_extended_width=400,
|
||||
group_alignment=0,
|
||||
destinations=[
|
||||
ft.NavigationRailDestination(icon=ft.Icons.HOME_OUTLINED, selected_icon=ft.Icons.HOME, label=(_("Home"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.LOOKS_6_OUTLINED, selected_icon=ft.Icons.LOOKS_6, label=(_("Grades"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.BACKPACK_OUTLINED, selected_icon=ft.Icons.BACKPACK, label=(_("Timetable"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.BOOK_OUTLINED, selected_icon=ft.Icons.BOOK, label=(_("Homework"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.CALENDAR_TODAY_OUTLINED, selected_icon=ft.Icons.CALENDAR_TODAY, label=(_("Exams"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.EVENT_NOTE_OUTLINED, selected_icon=ft.Icons.EVENT_NOTE, label=(_("Attendance"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.STICKY_NOTE_2_OUTLINED, selected_icon=ft.Icons.STICKY_NOTE_2, label=(_("Behaviour"))),
|
||||
ft.NavigationRailDestination(icon=ft.Icons.SETTINGS_OUTLINED, selected_icon=ft.Icons.SETTINGS_ROUNDED, label=(_("Settings"))),
|
||||
],
|
||||
on_change=lambda e: page.go([
|
||||
"/",
|
||||
"/grades",
|
||||
"/timetable",
|
||||
"/homework",
|
||||
"/exams",
|
||||
"/attendance",
|
||||
"/behaviour",
|
||||
"/settings"
|
||||
][e.control.selected_index])
|
||||
)
|
||||
|
||||
def get_index_from_route(route):
|
||||
route_to_index = {
|
||||
"/": 0,
|
||||
"/grades": 1,
|
||||
"/timetable": 2,
|
||||
"/homework": 3,
|
||||
"/exams": 4,
|
||||
"/attendance": 5,
|
||||
"/behaviour": 6,
|
||||
"/settings": 7
|
||||
}
|
||||
return route_to_index.get(route, 0)
|
||||
def change_page(route):
|
||||
routes = {
|
||||
"/": HomePage(),
|
||||
"/grades": GradesPage(page),
|
||||
"/timetable": TimetablePage(),
|
||||
"/homework": HomeworkPage(),
|
||||
"/exams": ExamsPage(),
|
||||
"/attendance": AttendancePage(),
|
||||
"/behaviour": BehaviourPage(),
|
||||
"/settings": SettingsPage(page)
|
||||
}
|
||||
|
||||
page.views.clear()
|
||||
view = ft.View(route, [
|
||||
ft.Row([
|
||||
bar,
|
||||
ft.Container(content=routes.get(route, HomePage()), expand=True)
|
||||
], expand=True)
|
||||
])
|
||||
page.views.append(view)
|
||||
page.update()
|
||||
|
||||
change_page(page.route)
|
||||
|
||||
def main(page: ft.Page):
|
||||
global bar
|
||||
|
||||
# Page settings
|
||||
page.title = "Fuji"
|
||||
page.theme = ft.Theme(
|
||||
|
|
|
@ -3,148 +3,183 @@ from sqlitehandlernr import fetch_all_grades
|
|||
from i18n import _
|
||||
|
||||
def parse_grade_value(value):
|
||||
"""
|
||||
Parse grade value with optional + or - suffix.
|
||||
Args:
|
||||
value (str): Grade value as a string
|
||||
|
||||
Returns:
|
||||
float: Parsed grade value, or None if unrecognized
|
||||
"""
|
||||
try:
|
||||
base_value = float(value.rstrip('+-')) # Remove +/- suffix and convert to float
|
||||
base_value = float(value.rstrip('+-'))
|
||||
if value.endswith('+'):
|
||||
return base_value + 0.25 # Add 0.25 for "+" suffix
|
||||
return base_value + 0.25
|
||||
elif value.endswith('-'):
|
||||
return base_value - 0.25 # Subtract 0.25 for "-" suffix
|
||||
return base_value - 0.25
|
||||
return base_value
|
||||
except ValueError:
|
||||
return None # Ignore unrecognized values
|
||||
return None
|
||||
|
||||
def format_date(dt):
|
||||
return dt.strftime("%Y-%m-%d")
|
||||
|
||||
def GradesPage(page):
|
||||
"""
|
||||
Generate grades page with subject-based panels and expandable content
|
||||
|
||||
Args:
|
||||
page (flet.Page): Flet page instance
|
||||
"""
|
||||
|
||||
# Retrieve all grades from SQLite database
|
||||
grades = fetch_all_grades()
|
||||
|
||||
# Create modal dialog for displaying grade details
|
||||
modal = ft.AlertDialog(
|
||||
modal=True,
|
||||
title=ft.Text(_("Test modal")), # Set modal title with translated text
|
||||
content=ft.Text(""), # Placeholder text that will be updated dynamically
|
||||
actions=[
|
||||
ft.TextButton("OK", on_click=lambda e: page.close(modal)), # Close modal when OK button clicked
|
||||
],
|
||||
actions_alignment=ft.MainAxisAlignment.END,
|
||||
)
|
||||
|
||||
# Initialize subject-based panels and data storage
|
||||
panels = []
|
||||
subjects = {}
|
||||
|
||||
# Group grades by subject
|
||||
|
||||
for grade in grades:
|
||||
subject = grade.subject
|
||||
if subject not in subjects: # Create new subject entry if not already present
|
||||
if subject not in subjects:
|
||||
subjects[subject] = []
|
||||
subjects[subject].append(grade) # Add grade to corresponding subject list
|
||||
subjects[subject].append(grade)
|
||||
|
||||
def format_date(dt):
|
||||
"""Format a datetime object to YYYY-MM-DD string"""
|
||||
return dt.strftime("%Y-%m-%d")
|
||||
modal = ft.BottomSheet(
|
||||
content=ft.Column([], spacing=5),
|
||||
enable_drag=True,
|
||||
open=False,
|
||||
)
|
||||
|
||||
def open_grade_modal(grade):
|
||||
"""
|
||||
Open modal dialog with detailed grade information
|
||||
|
||||
Args:
|
||||
grade (dict): Grade dictionary containing details
|
||||
"""
|
||||
modal.title = ft.Text(_("Grade Details")) # Update modal title
|
||||
parsed_value = parse_grade_value(grade.value) # Parse grade value for display
|
||||
parsed_value = parse_grade_value(grade.value)
|
||||
grade_display = f"{grade.value}" if parsed_value is not None else _("Grade not recognized")
|
||||
|
||||
# Fixed line - don't use replace with None
|
||||
grade_text = f"{_('Grade')}: {grade.value}"
|
||||
if parsed_value is None:
|
||||
grade_text = f"{_('Grade')}: {_('Grade not recognized')}"
|
||||
|
||||
modal.content = ft.Column([
|
||||
ft.Text(f"{_('Subject')}: {grade.subject}", size=14), # Display subject and translated text
|
||||
ft.Text(grade_text, size=14),
|
||||
ft.Text(f"{_('Date')}: {format_date(grade.created_at)}", size=14), # Format datetime object
|
||||
ft.Text(f"{_('Weight')}: {getattr(grade, 'weight', 1.0)}", size=14),
|
||||
ft.Text(f"{_('Description')}: {grade.name}", size=14),
|
||||
ft.Text(f"{_('Creator')}: {grade.creator}", size=14)
|
||||
], expand=False)
|
||||
page.open(modal) # Open modal dialog
|
||||
|
||||
# Generate subject-based panels with expandable content
|
||||
ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Column([
|
||||
ft.Text(grade.subject, size=30, weight=ft.FontWeight.BOLD),
|
||||
ft.Text(grade.name, size=18, color=ft.Colors.WHITE70),
|
||||
], spacing=3, tight=True),
|
||||
padding=ft.padding.only(left=20, top=-20, bottom=5, right=20),
|
||||
expand=True,
|
||||
),
|
||||
ft.Container(
|
||||
content=ft.Column([
|
||||
ft.Container(
|
||||
content=ft.Text(grade_display, size=48, weight=ft.FontWeight.BOLD),
|
||||
width=80,
|
||||
height=80,
|
||||
bgcolor=ft.Colors.GREEN_700,
|
||||
border_radius=ft.border_radius.only(top_left=8, top_right=8),
|
||||
alignment=ft.alignment.center,
|
||||
margin=ft.margin.only(bottom=5, top=40)
|
||||
),
|
||||
ft.Container(
|
||||
content=ft.Row([
|
||||
ft.Icon(name=ft.Icons.SCALE, size=16, color=ft.Colors.WHITE70),
|
||||
ft.Text(f"{getattr(grade, 'weight', 1.0):.2f}", size=16),
|
||||
], spacing=5, alignment=ft.MainAxisAlignment.CENTER),
|
||||
width=80,
|
||||
height=30,
|
||||
bgcolor=ft.Colors.GREEN_700,
|
||||
border_radius=ft.border_radius.only(bottom_left=8, bottom_right=8),
|
||||
alignment=ft.alignment.center,
|
||||
),
|
||||
], spacing=0, alignment=ft.MainAxisAlignment.CENTER),
|
||||
padding=ft.padding.only(right=20, top=-5, bottom=10),
|
||||
),
|
||||
], alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.LOOKS_6_ROUNDED, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Grade", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text(grade_display, size=20),
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True)
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
),
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.SCALE, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Weight", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text(f"{getattr(grade, 'weight', 1.0):.2f}", size=20),
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True)
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
),
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.CALENDAR_TODAY, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Date", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text(format_date(grade.created_at), size=20),
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True)
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
),
|
||||
], spacing=5)
|
||||
modal.open = True
|
||||
page.update()
|
||||
|
||||
for subject, grades_list in subjects.items():
|
||||
header = ft.Container(
|
||||
content=ft.Column(
|
||||
[
|
||||
ft.Text(subject, size=15), # Display subject name
|
||||
ft.Row([
|
||||
ft.Text(f"{len(grades_list)} "+_("grades"), size=14), # Display number of grades and translated text
|
||||
ft.Text((_("Average"))+": {:.2f}".format(
|
||||
sum(parse_grade_value(g.value) for g in grades_list if parse_grade_value(g.value) is not None) / max(len([g for g in grades_list if parse_grade_value(g.value) is not None]), 1)
|
||||
), size=14), # Calculate and display average grade
|
||||
])
|
||||
],
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
horizontal_alignment=ft.CrossAxisAlignment.START,
|
||||
expand=True,
|
||||
),
|
||||
content=ft.Column([
|
||||
ft.Text(subject, size=15),
|
||||
ft.Row([
|
||||
ft.Text(f"{len(grades_list)} " + _("grades"), size=14),
|
||||
ft.Text((_("Average")) + ": {:.2f}".format(
|
||||
sum(parse_grade_value(g.value) for g in grades_list if parse_grade_value(g.value) is not None) / max(len([g for g in grades_list if parse_grade_value(g.value) is not None]), 1)
|
||||
), size=14),
|
||||
])
|
||||
], alignment=ft.MainAxisAlignment.CENTER, horizontal_alignment=ft.CrossAxisAlignment.START, expand=True),
|
||||
padding=ft.padding.all(10),
|
||||
expand=True,
|
||||
)
|
||||
|
||||
|
||||
grade_rows = []
|
||||
for grade in grades_list:
|
||||
grade_rows.append(
|
||||
ft.Container(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Text(grade.value, text_align=ft.TextAlign.CENTER), # Display grade value
|
||||
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
padding=ft.padding.symmetric(horizontal=12, vertical=8),
|
||||
border_radius=5,
|
||||
margin=ft.margin.all(5),
|
||||
width=None, # Allow the container to size based on content
|
||||
on_click=lambda e, grade=grade: open_grade_modal(grade), # Open modal dialog when row clicked
|
||||
),
|
||||
ft.Column(
|
||||
[
|
||||
ft.Text(grade.name), # Display grade name
|
||||
ft.Row([
|
||||
ft.Text(format_date(grade.created_at)), # Format datetime object
|
||||
ft.Text((_("Weight"))+": {:.1f}".format(getattr(grade, 'weight', 1.0)))
|
||||
])
|
||||
],
|
||||
spacing=1
|
||||
),
|
||||
], expand=True), # Allow Row to expand and avoid tight squeezing
|
||||
on_click=lambda e, grade=grade: open_grade_modal(grade),
|
||||
)
|
||||
ft.Container(
|
||||
content=ft.Text(grade.value, text_align=ft.TextAlign.CENTER),
|
||||
bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
padding=ft.padding.symmetric(horizontal=12, vertical=8),
|
||||
border_radius=5,
|
||||
margin=ft.margin.all(5),
|
||||
on_click=lambda e, grade=grade: open_grade_modal(grade),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text(grade.name),
|
||||
ft.Row([
|
||||
ft.Text(format_date(grade.created_at)),
|
||||
ft.Text((_("Weight")) + ": {:.1f}".format(getattr(grade, 'weight', 1.0)))
|
||||
])
|
||||
], spacing=1)
|
||||
], expand=True),
|
||||
on_click=lambda e, grade=grade: open_grade_modal(grade),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
panel = ft.ExpansionPanel(
|
||||
header=header,
|
||||
content=ft.Column(grade_rows),
|
||||
expand=False,
|
||||
#bgcolor=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
#can_tap_header=True,
|
||||
)
|
||||
panels.append(panel)
|
||||
|
||||
|
||||
return ft.Column([
|
||||
ft.Text((_("Grades")), size=30, weight="bold"),
|
||||
ft.ExpansionPanelList(panels) # Display subject-based panels with expandable content
|
||||
],
|
||||
scroll=True
|
||||
)
|
||||
ft.ExpansionPanelList(panels),
|
||||
modal
|
||||
], scroll=True)
|
||||
|
|
130
src/test.py
Normal file
130
src/test.py
Normal file
|
@ -0,0 +1,130 @@
|
|||
import flet as ft
|
||||
|
||||
def main(page: ft.Page):
|
||||
page.theme = ft.Theme(
|
||||
color_scheme_seed=ft.Colors.PINK,
|
||||
font_family="Roboto",
|
||||
page_transitions=ft.PageTransitionsTheme(
|
||||
macos=ft.PageTransitionTheme.NONE,
|
||||
linux=ft.PageTransitionTheme.NONE,
|
||||
windows=ft.PageTransitionTheme.NONE
|
||||
),
|
||||
)
|
||||
page.theme_mode = ft.ThemeMode.DARK
|
||||
|
||||
modal = ft.BottomSheet(
|
||||
content=ft.Column([
|
||||
ft.Row([
|
||||
# Subject details
|
||||
ft.Container(
|
||||
content=ft.Column([
|
||||
ft.Text("Wychowanie fizyczne", size=30, weight=ft.FontWeight.BOLD),
|
||||
ft.Text("rozgrzewka", size=18, color=ft.Colors.WHITE70),
|
||||
], spacing=3, tight=True),
|
||||
padding=ft.padding.only(left=20, top=-20, bottom=5, right=20),
|
||||
expand=True,
|
||||
),
|
||||
|
||||
# Grade display
|
||||
ft.Container(
|
||||
content=ft.Column([
|
||||
ft.Container(
|
||||
content=ft.Text("5", size=48, weight=ft.FontWeight.BOLD),
|
||||
width=80,
|
||||
height=80,
|
||||
bgcolor=ft.Colors.GREEN_700,
|
||||
border_radius=ft.border_radius.only(top_left=8, top_right=8),
|
||||
alignment=ft.alignment.center,
|
||||
margin=ft.margin.only(bottom=5, top=40)
|
||||
),
|
||||
ft.Container(
|
||||
content=ft.Row([
|
||||
ft.Icon(name=ft.Icons.SCALE, size=16),
|
||||
ft.Text("1.00", size=16),
|
||||
], spacing=5, alignment=ft.MainAxisAlignment.CENTER),
|
||||
width=80,
|
||||
height=30,
|
||||
bgcolor=ft.Colors.GREEN_700,
|
||||
border_radius=ft.border_radius.only(bottom_left=8, bottom_right=8),
|
||||
alignment=ft.alignment.center,
|
||||
),
|
||||
], spacing=0, alignment=ft.MainAxisAlignment.CENTER),
|
||||
padding=ft.padding.only(right=20, top=-5, bottom=10),
|
||||
),
|
||||
], alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
|
||||
|
||||
|
||||
# Grade details
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.SCALE, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Grade", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text("5", size=20)
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True)
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
margin=ft.margin.only(left=15, right=15, bottom=5),
|
||||
),
|
||||
|
||||
|
||||
# Weight details
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.SCALE, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Weight", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text("1.00", size=20),
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True), # Align and expand
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
margin=ft.margin.only(left=15, right=15, top=5, bottom=5),
|
||||
),
|
||||
|
||||
# Date details
|
||||
ft.Card(
|
||||
content=ft.Row([
|
||||
ft.Container(
|
||||
content=ft.Icon(name=ft.Icons.CALENDAR_TODAY, size=20),
|
||||
width=40,
|
||||
height=40,
|
||||
border_radius=8,
|
||||
alignment=ft.alignment.center,
|
||||
padding=ft.padding.only(left=10),
|
||||
),
|
||||
ft.Column([
|
||||
ft.Text("Date", size=14, color=ft.Colors.WHITE70),
|
||||
ft.Text("2/10/25", size=20),
|
||||
], spacing=2, alignment=ft.MainAxisAlignment.CENTER, expand=True), # Align and expand
|
||||
], spacing=15, alignment=ft.MainAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||
height=75,
|
||||
color=ft.Colors.SURFACE_CONTAINER_HIGHEST,
|
||||
margin=ft.margin.only(left=15, right=15, bottom=5, top=5),
|
||||
),
|
||||
|
||||
|
||||
], spacing=5),
|
||||
enable_drag=True,
|
||||
open=True,
|
||||
)
|
||||
|
||||
# Add the modal directly to the page
|
||||
page.add(ft.ElevatedButton("Open Grade Details", on_click=lambda _: page.open(modal)))
|
||||
|
||||
ft.app(target=main)
|
Loading…
Add table
Reference in a new issue