Initial Switch to V2. Completely Overhauled Backend, Frontend and General Structure.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from typing import Any, Dict, Optional
|
||||
from auth.models import StaffPermissions
|
||||
|
||||
|
||||
@@ -35,3 +35,7 @@ class StaffResponse(BaseModel):
|
||||
class StaffListResponse(BaseModel):
|
||||
staff: list[StaffResponse]
|
||||
total: int
|
||||
|
||||
|
||||
class PreferencesUpdate(BaseModel):
|
||||
prefs: Dict[str, Any]
|
||||
|
||||
23
backend/staff/orm.py
Normal file
23
backend/staff/orm.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from datetime import datetime, timezone
|
||||
from sqlalchemy import Boolean, Column, DateTime, String
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from database.postgres import Base
|
||||
|
||||
|
||||
def _now():
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class Staff(Base):
|
||||
__tablename__ = "staff"
|
||||
|
||||
id = Column(String(128), primary_key=True) # Firestore doc ID during transition
|
||||
firestore_id = Column(String(128), unique=True) # same as id during transition
|
||||
email = Column(String(256), unique=True, nullable=False)
|
||||
name = Column(String(255), nullable=False)
|
||||
role = Column(String(64), nullable=False, default="staff")
|
||||
permissions = Column(JSONB, nullable=False, default=dict)
|
||||
hashed_password = Column(String(256), nullable=False)
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
created_at = Column(DateTime(timezone=True), nullable=False, default=_now)
|
||||
updated_at = Column(DateTime(timezone=True), nullable=False, default=_now, onupdate=_now)
|
||||
@@ -1,10 +1,12 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from typing import Any
|
||||
from auth.dependencies import get_current_user, require_staff_management
|
||||
from auth.models import TokenPayload
|
||||
from staff import service
|
||||
from staff.models import (
|
||||
StaffCreate, StaffUpdate, StaffPasswordUpdate,
|
||||
StaffResponse, StaffListResponse,
|
||||
PreferencesUpdate,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api/staff", tags=["staff"])
|
||||
@@ -15,6 +17,22 @@ async def get_current_staff(current_user: TokenPayload = Depends(get_current_use
|
||||
return await service.get_staff_me(current_user.sub)
|
||||
|
||||
|
||||
@router.get("/me/preferences", response_model=dict)
|
||||
async def get_preferences(current_user: TokenPayload = Depends(get_current_user)):
|
||||
"""Return all UI preferences for the current staff member."""
|
||||
return await service.get_preferences(current_user.sub)
|
||||
|
||||
|
||||
@router.patch("/me/preferences/{page_key}", response_model=dict)
|
||||
async def update_preferences(
|
||||
page_key: str,
|
||||
body: PreferencesUpdate,
|
||||
current_user: TokenPayload = Depends(get_current_user),
|
||||
):
|
||||
"""Merge preference keys for a specific page into the staff member's stored prefs."""
|
||||
return await service.update_preferences(current_user.sub, page_key, body.prefs)
|
||||
|
||||
|
||||
@router.get("", response_model=StaffListResponse)
|
||||
async def list_staff(
|
||||
search: str = Query(None),
|
||||
|
||||
@@ -157,6 +157,33 @@ async def update_staff_password(staff_id: str, new_password: str, current_user_r
|
||||
return {"message": "Password updated successfully"}
|
||||
|
||||
|
||||
async def get_preferences(staff_id: str) -> dict:
|
||||
"""Return the ui_prefs map for a staff member, defaulting to {} if not set."""
|
||||
db = get_db()
|
||||
doc = db.collection("admin_users").document(staff_id).get()
|
||||
if not doc.exists:
|
||||
raise NotFoundError("Staff member not found")
|
||||
return doc.to_dict().get("ui_prefs", {})
|
||||
|
||||
|
||||
async def update_preferences(staff_id: str, page_key: str, prefs: dict) -> dict:
|
||||
"""Merge a page-level preferences dict into ui_prefs.<page_key> on the staff document.
|
||||
Only the supplied keys are overwritten — other keys in the same page block survive.
|
||||
"""
|
||||
db = get_db()
|
||||
doc_ref = db.collection("admin_users").document(staff_id)
|
||||
doc = doc_ref.get()
|
||||
if not doc.exists:
|
||||
raise NotFoundError("Staff member not found")
|
||||
|
||||
existing_prefs = doc.to_dict().get("ui_prefs", {})
|
||||
page_prefs = {**existing_prefs.get(page_key, {}), **prefs}
|
||||
existing_prefs[page_key] = page_prefs
|
||||
|
||||
doc_ref.update({"ui_prefs": existing_prefs})
|
||||
return existing_prefs
|
||||
|
||||
|
||||
async def delete_staff(staff_id: str, current_user_role: str, current_user_id: str) -> dict:
|
||||
db = get_db()
|
||||
doc_ref = db.collection("admin_users").document(staff_id)
|
||||
|
||||
Reference in New Issue
Block a user