feat(frontend+backend): add theming to the blazor frontend

This commit is contained in:
2025-09-04 22:16:04 -04:00
parent 133efdddea
commit 0768e4816d
30 changed files with 1544 additions and 686 deletions

View File

@@ -85,6 +85,7 @@ class Member(BaseModel):
email: Optional[str] = Field(default=None, alias="Email")
phoneNumber: Optional[str] = Field(default=None, alias="PhoneNumber")
classificationId: Optional[int] = Field(default=None, alias="ClassificationId")
classificationName: Optional[str] = Field(default=None, alias="ClassificationName")
notes: Optional[str] = Field(default=None, alias="Notes")
isActive: int = Field(default=1, alias="IsActive")
lastScheduledAt: Optional[datetime] = Field(default=None, alias="LastScheduledAt")
@@ -121,6 +122,7 @@ class Service(BaseModel):
serviceId: int = Field(alias="ServiceId")
serviceTypeId: int = Field(alias="ServiceTypeId")
serviceDate: date = Field(alias="ServiceDate")
serviceTypeName: Optional[str] = Field(default=None, alias="ServiceTypeName")
class Config:
populate_by_name = True
@@ -152,6 +154,8 @@ class Schedule(BaseModel):
declinedAt: Optional[datetime] = Field(default=None, alias="DeclinedAt")
expiresAt: Optional[datetime] = Field(default=None, alias="ExpiresAt")
declineReason: Optional[str] = Field(default=None, alias="DeclineReason")
member: Optional["Member"] = Field(default=None, alias="Member")
service: Optional["Service"] = Field(default=None, alias="Service")
class Config:
populate_by_name = True
@@ -206,7 +210,7 @@ def get_scheduling_service(repos: dict = Depends(get_repositories)):
# Helper functions to convert between DB and API models
def db_member_to_api(db_member: DbMember) -> Member:
def db_member_to_api(db_member: DbMember, classification_name: str = None) -> Member:
return Member(
MemberId=db_member.MemberId,
FirstName=db_member.FirstName,
@@ -214,6 +218,7 @@ def db_member_to_api(db_member: DbMember) -> Member:
Email=db_member.Email,
PhoneNumber=db_member.PhoneNumber,
ClassificationId=db_member.ClassificationId,
ClassificationName=classification_name,
Notes=db_member.Notes,
IsActive=db_member.IsActive,
LastScheduledAt=db_member.LastScheduledAt,
@@ -247,12 +252,18 @@ def db_classification_to_api(db_classification: DbClassification) -> Classificat
)
def db_service_to_api(db_service: DbService) -> Service:
return Service(
ServiceId=db_service.ServiceId,
ServiceTypeId=db_service.ServiceTypeId,
ServiceDate=db_service.ServiceDate,
)
def db_service_to_api(db_service: DbService, service_type: DbServiceType = None) -> Service:
service_dict = {
"ServiceId": db_service.ServiceId,
"ServiceTypeId": db_service.ServiceTypeId,
"ServiceDate": db_service.ServiceDate,
}
# Add service type name if provided
if service_type:
service_dict["ServiceTypeName"] = service_type.TypeName
return Service(**service_dict)
def db_service_type_to_api(db_service_type: DbServiceType) -> ServiceType:
@@ -262,18 +273,28 @@ def db_service_type_to_api(db_service_type: DbServiceType) -> ServiceType:
)
def db_schedule_to_api(db_schedule: DbSchedule) -> Schedule:
return Schedule(
ScheduleId=db_schedule.ScheduleId,
ServiceId=db_schedule.ServiceId,
MemberId=db_schedule.MemberId,
Status=db_schedule.Status,
ScheduledAt=db_schedule.ScheduledAt,
AcceptedAt=db_schedule.AcceptedAt,
DeclinedAt=db_schedule.DeclinedAt,
ExpiresAt=db_schedule.ExpiresAt,
DeclineReason=db_schedule.DeclineReason,
)
def db_schedule_to_api(db_schedule: DbSchedule, member: DbMember = None, service: DbService = None) -> Schedule:
schedule_dict = {
"ScheduleId": db_schedule.ScheduleId,
"ServiceId": db_schedule.ServiceId,
"MemberId": db_schedule.MemberId,
"Status": db_schedule.Status,
"ScheduledAt": db_schedule.ScheduledAt,
"AcceptedAt": db_schedule.AcceptedAt,
"DeclinedAt": db_schedule.DeclinedAt,
"ExpiresAt": db_schedule.ExpiresAt,
"DeclineReason": db_schedule.DeclineReason,
}
# Add nested member data if provided
if member:
schedule_dict["Member"] = db_member_to_api(member)
# Add nested service data if provided
if service:
schedule_dict["Service"] = db_service_to_api(service)
return Schedule(**schedule_dict)
# API Endpoints
@@ -282,7 +303,19 @@ def db_schedule_to_api(db_schedule: DbSchedule) -> Schedule:
@app.get("/api/members", response_model=List[Member])
async def get_members(repos: dict = Depends(get_repositories)):
db_members = repos["member_repo"].list_all()
return [db_member_to_api(member) for member in db_members]
result = []
for member in db_members:
# Fetch classification name if classification ID exists
classification_name = None
if member.ClassificationId:
classification = repos["classification_repo"].get_by_id(member.ClassificationId)
if classification:
classification_name = classification.ClassificationName
result.append(db_member_to_api(member, classification_name))
return result
@app.get("/api/members/{member_id}", response_model=Member)
@@ -394,7 +427,40 @@ async def get_service_types(repos: dict = Depends(get_repositories)):
@app.get("/api/schedules", response_model=List[Schedule])
async def get_schedules(repos: dict = Depends(get_repositories)):
db_schedules = repos["schedule_repo"].list_all()
return [db_schedule_to_api(schedule) for schedule in db_schedules]
result = []
for schedule in db_schedules:
# Fetch related member and service data
member = repos["member_repo"].get_by_id(schedule.MemberId)
service = repos["service_repo"].get_by_id(schedule.ServiceId)
# Fetch service type if service exists
service_with_type = None
if service:
service_type = repos["service_type_repo"].get_by_id(service.ServiceTypeId)
service_with_type = db_service_to_api(service, service_type)
# Convert to API format with nested data
schedule_dict = {
"ScheduleId": schedule.ScheduleId,
"ServiceId": schedule.ServiceId,
"MemberId": schedule.MemberId,
"Status": schedule.Status,
"ScheduledAt": schedule.ScheduledAt,
"AcceptedAt": schedule.AcceptedAt,
"DeclinedAt": schedule.DeclinedAt,
"ExpiresAt": schedule.ExpiresAt,
"DeclineReason": schedule.DeclineReason,
}
if member:
schedule_dict["Member"] = db_member_to_api(member)
if service_with_type:
schedule_dict["Service"] = service_with_type
result.append(Schedule(**schedule_dict))
return result
@app.get("/api/schedules/{schedule_id}", response_model=Schedule)
@@ -402,7 +468,36 @@ async def get_schedule(schedule_id: int, repos: dict = Depends(get_repositories)
db_schedule = repos["schedule_repo"].get_by_id(schedule_id)
if not db_schedule:
raise HTTPException(status_code=404, detail="Schedule not found")
return db_schedule_to_api(db_schedule)
# Fetch related member and service data
member = repos["member_repo"].get_by_id(db_schedule.MemberId)
service = repos["service_repo"].get_by_id(db_schedule.ServiceId)
# Fetch service type if service exists
service_with_type = None
if service:
service_type = repos["service_type_repo"].get_by_id(service.ServiceTypeId)
service_with_type = db_service_to_api(service, service_type)
# Convert to API format with nested data
schedule_dict = {
"ScheduleId": db_schedule.ScheduleId,
"ServiceId": db_schedule.ServiceId,
"MemberId": db_schedule.MemberId,
"Status": db_schedule.Status,
"ScheduledAt": db_schedule.ScheduledAt,
"AcceptedAt": db_schedule.AcceptedAt,
"DeclinedAt": db_schedule.DeclinedAt,
"ExpiresAt": db_schedule.ExpiresAt,
"DeclineReason": db_schedule.DeclineReason,
}
if member:
schedule_dict["Member"] = db_member_to_api(member)
if service_with_type:
schedule_dict["Service"] = service_with_type
return Schedule(**schedule_dict)
@app.post("/api/schedules/{schedule_id}/accept", response_model=Schedule)