Browse Source

feat: Implement admin web GUI with authentication, dashboard, and pietanze management

master
Matteo Benedetto 7 months ago
parent
commit
a2c4c70a99
  1. 62
      admin-webgui/components/layout.py
  2. 13
      admin-webgui/config.yaml
  3. 80
      admin-webgui/main.py
  4. 114
      admin-webgui/pages/dashboard.py
  5. 32
      admin-webgui/pages/login.py
  6. 248
      admin-webgui/pages/pietanze.py
  7. 5
      admin-webgui/requirements.txt
  8. 81
      admin-webgui/services/api_client.py
  9. 60
      admin-webgui/services/auth_service.py
  10. 4
      api/routes/pietanze.py

62
admin-webgui/components/layout.py

@ -0,0 +1,62 @@
from nicegui import ui
from contextlib import contextmanager
class AdminLayout:
def __init__(self):
self.sidebar_open = True
@contextmanager
def render_layout(self):
"""Render the main admin layout"""
with ui.column().classes('min-h-screen bg-gray-50'):
self._render_header()
with ui.row().classes('flex-1 w-full'):
self._render_sidebar()
with ui.column().classes('flex-1 p-6'):
yield
def _render_header(self):
"""Render top navigation header"""
with ui.header().classes('bg-green-600 text-white shadow-lg'):
with ui.row().classes('w-full justify-between items-center px-4'):
with ui.row().classes('items-center gap-4'):
ui.button(icon='menu', on_click=self._toggle_sidebar).props('flat round')
ui.label('Simple Mensa Admin').classes('text-xl font-bold')
with ui.row().classes('items-center gap-2'):
ui.button(icon='notifications', on_click=lambda: ui.notify('Nessuna notifica')).props('flat round')
with ui.dropdown_button('Admin', icon='account_circle').props('flat'):
ui.item('Profilo', on_click=lambda: ui.notify('Profilo'))
ui.item('Impostazioni', on_click=lambda: ui.notify('Impostazioni'))
ui.separator()
ui.item('Logout', on_click=self._logout)
def _render_sidebar(self):
"""Render navigation sidebar"""
with ui.column().classes('w-64 bg-white shadow-lg border-r min-h-full' if self.sidebar_open else 'w-0 overflow-hidden'):
with ui.column().classes('p-4 space-y-2'):
self._nav_item('Dashboard', '/', 'dashboard', active=True)
self._nav_item('Pietanze', '/pietanze', 'restaurant_menu')
self._nav_item('Menu Giornalieri', '/pasti', 'calendar_today')
self._nav_item('Prenotazioni', '/prenotazioni', 'book_online')
self._nav_item('Analytics', '/analytics', 'analytics')
def _nav_item(self, label: str, path: str, icon: str, active: bool = False):
"""Render navigation item"""
classes = 'w-full justify-start gap-3 p-3 rounded-lg'
if active:
classes += ' bg-green-100 text-green-700'
else:
classes += ' hover:bg-gray-100'
ui.button(label, icon=icon, on_click=lambda: ui.navigate.to(path)).classes(classes)
def _toggle_sidebar(self):
"""Toggle sidebar visibility"""
self.sidebar_open = not self.sidebar_open
ui.update()
def _logout(self):
"""Handle logout"""
ui.navigate.to('/login')
ui.notify('Disconnesso con successo')

13
admin-webgui/config.yaml

@ -0,0 +1,13 @@
api:
base_url: "http://localhost:8000"
timeout: 30
auth:
provider_url: "https://login.microsoftonline.com/your-tenant"
client_id: "your-client-id"
secret_key: "your-secret-key"
app:
title: "Simple Mensa Admin"
debug: true
port: 8080

80
admin-webgui/main.py

@ -0,0 +1,80 @@
from nicegui import ui, app
import yaml
from pathlib import Path
from services.auth_service import AuthService
from services.api_client import APIClient
from pages.login import LoginPage
from pages.dashboard import DashboardPage
from pages.pietanze import PietanzePage
from components.layout import AdminLayout
# Load configuration
config_path = Path(__file__).parent / "config.yaml"
with open(config_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
# Initialize services
auth_service = AuthService(config['auth'])
api_client = APIClient(config['api']['base_url'])
# Global layout instance
admin_layout = AdminLayout()
@ui.page('/')
def index():
"""Main dashboard page"""
if not auth_service.is_authenticated():
ui.navigate.to('/login')
return
with admin_layout.render_layout():
dashboard = DashboardPage(api_client)
dashboard.render()
@ui.page('/login')
def login():
"""Login page"""
login_page = LoginPage(auth_service)
login_page.render()
@ui.page('/pietanze')
def pietanze():
"""Pietanze management page"""
if not auth_service.is_authenticated():
ui.navigate.to('/login')
return
with admin_layout.render_layout():
pietanze_page = PietanzePage(api_client)
pietanze_page.render()
@ui.page('/pasti')
def pasti():
"""Pasti management page (placeholder)"""
if not auth_service.is_authenticated():
ui.navigate.to('/login')
return
with admin_layout.render_layout():
ui.label('Gestione Pasti - In sviluppo')
@ui.page('/prenotazioni')
def prenotazioni():
"""Prenotazioni management page (placeholder)"""
if not auth_service.is_authenticated():
ui.navigate.to('/login')
return
with admin_layout.render_layout():
ui.label('Gestione Prenotazioni - In sviluppo')
if __name__ in {"__main__", "__mp_main__"}:
# Configure app
app.add_static_files('/static', Path(__file__).parent / 'static')
ui.run(
title=config['app']['title'],
port=config['app']['port'],
reload=config['app']['debug'],
show=config['app']['debug']
)

114
admin-webgui/pages/dashboard.py

@ -0,0 +1,114 @@
from nicegui import ui
from services.api_client import APIClient
import asyncio
from datetime import datetime, date
class DashboardPage:
def __init__(self, api_client: APIClient):
self.api_client = api_client
self.stats = {}
def render(self):
"""Render dashboard page"""
ui.label('Dashboard').classes('text-3xl font-bold text-gray-800 mb-6')
# KPI Cards Row
with ui.row().classes('w-full gap-6 mb-8'):
self._render_kpi_cards()
# Charts and Tables Row
with ui.row().classes('w-full gap-6'):
with ui.column().classes('flex-1'):
self._render_recent_prenotazioni()
with ui.column().classes('flex-1'):
self._render_pasti_disponibili()
# Load initial data
asyncio.create_task(self._load_dashboard_data())
def _render_kpi_cards(self):
"""Render KPI metric cards"""
# Prenotazioni Oggi
with ui.card().classes('p-6 bg-gradient-to-r from-blue-500 to-blue-600 text-white'):
with ui.column().classes('items-center'):
ui.icon('book_online').classes('text-4xl mb-2')
ui.label('0').classes('text-3xl font-bold').bind_text_from(self, 'prenotazioni_oggi', lambda x: str(x))
ui.label('Prenotazioni Oggi').classes('text-blue-100')
# Pasti Serviti
with ui.card().classes('p-6 bg-gradient-to-r from-green-500 to-green-600 text-white'):
with ui.column().classes('items-center'):
ui.icon('restaurant').classes('text-4xl mb-2')
ui.label('0').classes('text-3xl font-bold').bind_text_from(self, 'pasti_serviti', lambda x: str(x))
ui.label('Pasti Serviti').classes('text-green-100')
# Pietanze Disponibili
with ui.card().classes('p-6 bg-gradient-to-r from-purple-500 to-purple-600 text-white'):
with ui.column().classes('items-center'):
ui.icon('restaurant_menu').classes('text-4xl mb-2')
ui.label('0').classes('text-3xl font-bold').bind_text_from(self, 'pietanze_disponibili', lambda x: str(x))
ui.label('Pietanze Disponibili').classes('text-purple-100')
# Utilizzo Capacità
with ui.card().classes('p-6 bg-gradient-to-r from-orange-500 to-orange-600 text-white'):
with ui.column().classes('items-center'):
ui.icon('analytics').classes('text-4xl mb-2')
ui.label('0%').classes('text-3xl font-bold').bind_text_from(self, 'utilizzo_capacita', lambda x: f"{x}%")
ui.label('Utilizzo Capacità').classes('text-orange-100')
def _render_recent_prenotazioni(self):
"""Render recent prenotazioni table"""
with ui.card().classes('p-6'):
ui.label('Prenotazioni Recenti').classes('text-xl font-semibold mb-4')
self.prenotazioni_table = ui.table(
columns=[
{'name': 'user_id', 'label': 'Utente', 'field': 'user_id'},
{'name': 'stato', 'label': 'Stato', 'field': 'stato'},
{'name': 'created_at', 'label': 'Ora', 'field': 'created_at'},
],
rows=[],
row_key='id'
).classes('w-full')
def _render_pasti_disponibili(self):
"""Render available pasti"""
with ui.card().classes('p-6'):
ui.label('Pasti Disponibili Oggi').classes('text-xl font-semibold mb-4')
self.pasti_container = ui.column().classes('space-y-2')
async def _load_dashboard_data(self):
"""Load dashboard data from API"""
try:
# Mock data for demonstration (replace with actual API calls)
self.prenotazioni_oggi = 25
self.pasti_serviti = 18
self.pietanze_disponibili = 12
self.utilizzo_capacita = 75
# Load recent prenotazioni
prenotazioni_data = [
{'id': 1, 'user_id': 'mario.rossi', 'stato': 'attiva', 'created_at': '10:30'},
{'id': 2, 'user_id': 'luigi.verdi', 'stato': 'servita', 'created_at': '10:45'},
{'id': 3, 'user_id': 'anna.bianchi', 'stato': 'attiva', 'created_at': '11:00'},
]
self.prenotazioni_table.rows = prenotazioni_data
# Load available pasti
pasti_data = [
{'nome': 'Pranzo Completo', 'turni': ['12:30', '13:00', '13:30']},
{'nome': 'Menu Vegetariano', 'turni': ['12:30', '13:00']},
]
self.pasti_container.clear()
for pasto in pasti_data:
with self.pasti_container:
with ui.row().classes('w-full justify-between items-center p-3 bg-gray-50 rounded'):
ui.label(pasto['nome']).classes('font-medium')
ui.label(f"Turni: {', '.join(pasto['turni'])}").classes('text-sm text-gray-600')
ui.update()
except Exception as e:
ui.notify(f'Errore nel caricamento dati: {str(e)}', type='negative')

32
admin-webgui/pages/login.py

@ -0,0 +1,32 @@
from nicegui import ui
from services.auth_service import AuthService
class LoginPage:
def __init__(self, auth_service: AuthService):
self.auth_service = auth_service
def render(self):
"""Render login page"""
with ui.column().classes('min-h-screen bg-gradient-to-br from-green-400 to-green-600 justify-center items-center'):
with ui.card().classes('w-full max-w-md p-8 shadow-2xl'):
ui.label('Simple Mensa Admin').classes('text-2xl font-bold text-center text-gray-800 mb-6')
with ui.column().classes('space-y-4 w-full'):
username_input = ui.input('Username', placeholder='Inserisci username').classes('w-full')
password_input = ui.input('Password', placeholder='Inserisci password', password=True).classes('w-full')
login_btn = ui.button('Accedi', on_click=lambda: self._handle_login(username_input.value, password_input.value)).classes('w-full bg-green-600 hover:bg-green-700')
ui.label('Demo: admin / admin').classes('text-sm text-gray-500 text-center mt-4')
def _handle_login(self, username: str, password: str):
"""Handle login attempt"""
if not username or not password:
ui.notify('Inserisci username e password', type='negative')
return
if self.auth_service.login(username, password):
ui.notify('Login effettuato con successo', type='positive')
ui.navigate.to('/')
else:
ui.notify('Credenziali non valide', type='negative')

248
admin-webgui/pages/pietanze.py

@ -0,0 +1,248 @@
from nicegui import ui
from services.api_client import APIClient
import asyncio
from typing import List, Dict, Any, Optional
class PietanzePage:
def __init__(self, api_client: APIClient):
self.api_client = api_client
self.pietanze_data = []
self.search_filter = ""
self.allergen_filter = []
self.selected_pietanza = None
# Available allergens
self.available_allergens = [
'glutine', 'lattosio', 'uova', 'pesce', 'crostacei',
'arachidi', 'frutta_a_guscio', 'soia', 'sedano',
'senape', 'sesamo', 'solfiti'
]
def render(self):
"""Render pietanze management page"""
ui.label('Gestione Pietanze').classes('text-3xl font-bold text-gray-800 mb-6')
# Toolbar
with ui.row().classes('w-full justify-between items-center mb-6'):
with ui.row().classes('gap-4 items-center'):
# Search input
search_input = ui.input('Cerca pietanze...', on_change=self._on_search_change).classes('w-64')
search_input.bind_value(self, 'search_filter')
# Allergen filter
with ui.select(
self.available_allergens,
multiple=True,
label='Filtra per allergeni',
on_change=self._on_filter_change
).classes('w-48') as allergen_select:
allergen_select.bind_value(self, 'allergen_filter')
# Refresh button
ui.button('Aggiorna', icon='refresh', on_click=self._load_pietanze).props('outline')
# Add new button
ui.button('Nuova Pietanza', icon='add', on_click=self._show_create_dialog).classes('bg-green-600 hover:bg-green-700')
# Pietanze table
self._render_pietanze_table()
# Load initial data
asyncio.create_task(self._load_pietanze())
def _render_pietanze_table(self):
"""Render pietanze data table"""
columns = [
{'name': 'id', 'label': 'ID', 'field': 'id', 'sortable': True},
{'name': 'nome', 'label': 'Nome', 'field': 'nome', 'sortable': True, 'align': 'left'},
{'name': 'descrizione', 'label': 'Descrizione', 'field': 'descrizione', 'align': 'left'},
{'name': 'allergeni', 'label': 'Allergeni', 'field': 'allergeni_display'},
{'name': 'actions', 'label': 'Azioni', 'field': 'actions'},
]
self.pietanze_table = ui.table(
columns=columns,
rows=[],
row_key='id'
).classes('w-full')
# Add action buttons to each row
self.pietanze_table.add_slot('body-cell-actions', '''
<q-td :props="props">
<q-btn flat round dense icon="edit" color="primary" @click="$parent.$emit('edit', props.row)" />
<q-btn flat round dense icon="delete" color="negative" @click="$parent.$emit('delete', props.row)" />
</q-td>
''')
self.pietanze_table.on('edit', self._show_edit_dialog)
self.pietanze_table.on('delete', self._show_delete_dialog)
def _show_create_dialog(self):
"""Show create pietanza dialog"""
self.selected_pietanza = None
self._show_pietanza_dialog()
def _show_edit_dialog(self, e):
"""Show edit pietanza dialog"""
self.selected_pietanza = e.args
self._show_pietanza_dialog()
def _show_pietanza_dialog(self):
"""Show pietanza create/edit dialog"""
is_edit = self.selected_pietanza is not None
title = 'Modifica Pietanza' if is_edit else 'Nuova Pietanza'
with ui.dialog() as dialog, ui.card().classes('w-96'):
ui.label(title).classes('text-xl font-bold mb-4')
# Form fields
nome_input = ui.input('Nome', placeholder='Nome della pietanza').classes('w-full')
descrizione_input = ui.textarea('Descrizione', placeholder='Descrizione dettagliata').classes('w-full')
allergeni_select = ui.select(
self.available_allergens,
multiple=True,
label='Allergeni'
).classes('w-full')
# Pre-fill form if editing
if is_edit:
nome_input.value = self.selected_pietanza['nome']
descrizione_input.value = self.selected_pietanza.get('descrizione', '')
allergeni_select.value = self.selected_pietanza.get('allergeni', [])
# Action buttons
with ui.row().classes('w-full justify-end gap-2 mt-4'):
ui.button('Annulla', on_click=dialog.close).props('flat')
save_btn = ui.button(
'Salva' if is_edit else 'Crea',
on_click=lambda: self._save_pietanza(
dialog, nome_input.value, descrizione_input.value, allergeni_select.value, is_edit
)
).classes('bg-green-600 hover:bg-green-700')
dialog.open()
def _show_delete_dialog(self, e):
"""Show delete confirmation dialog"""
pietanza = e.args
with ui.dialog() as dialog, ui.card():
ui.label('Conferma Eliminazione').classes('text-xl font-bold mb-4')
ui.label(f'Sei sicuro di voler eliminare "{pietanza["nome"]}"?').classes('mb-4')
with ui.row().classes('w-full justify-end gap-2'):
ui.button('Annulla', on_click=dialog.close).props('flat')
ui.button(
'Elimina',
on_click=lambda: self._delete_pietanza(dialog, pietanza['id'])
).classes('bg-red-600 hover:bg-red-700')
dialog.open()
async def _save_pietanza(self, dialog, nome: str, descrizione: str, allergeni: List[str], is_edit: bool):
"""Save pietanza (create or update)"""
if not nome.strip():
ui.notify('Il nome è obbligatorio', type='negative')
return
try:
pietanza_data = {
'nome': nome.strip(),
'descrizione': descrizione.strip() if descrizione else None,
'allergeni': allergeni or []
}
if is_edit:
await self.api_client.update_pietanza(self.selected_pietanza['id'], pietanza_data)
ui.notify('Pietanza aggiornata con successo', type='positive')
else:
await self.api_client.create_pietanza(pietanza_data)
ui.notify('Pietanza creata con successo', type='positive')
dialog.close()
await self._load_pietanze()
except Exception as e:
ui.notify(f'Errore nel salvataggio: {str(e)}', type='negative')
async def _delete_pietanza(self, dialog, pietanza_id: int):
"""Delete pietanza"""
try:
await self.api_client.delete_pietanza(pietanza_id)
ui.notify('Pietanza eliminata con successo', type='positive')
dialog.close()
await self._load_pietanze()
except Exception as e:
ui.notify(f'Errore nell\'eliminazione: {str(e)}', type='negative')
async def _load_pietanze(self):
"""Load pietanze from API"""
try:
# Call actual API with current filters
response = await self.api_client.get_pietanze(
skip=0,
limit=1000, # Load all pietanze for now
search=self.search_filter if self.search_filter else None,
allergeni=self.allergen_filter if self.allergen_filter else None
)
# Extract pietanze from response
pietanze_list = response.get('items', []) if isinstance(response, dict) else response
# Format data for table display
self.pietanze_data = []
for pietanza in pietanze_list:
formatted_pietanza = {
'id': pietanza['id'],
'nome': pietanza['nome'],
'descrizione': pietanza.get('descrizione', ''),
'allergeni': pietanza.get('allergeni', []),
'allergeni_display': ', '.join(pietanza.get('allergeni', [])) if pietanza.get('allergeni') else 'Nessuno',
'created_at': pietanza.get('created_at', '')
}
self.pietanze_data.append(formatted_pietanza)
self._update_table()
except Exception as e:
ui.notify(f'Errore nel caricamento pietanze: {str(e)}', type='negative')
# Keep existing data on error
self._update_table()
def _update_table(self):
"""Update table with filtered data"""
filtered_data = self._filter_pietanze()
self.pietanze_table.rows = filtered_data
ui.update()
def _filter_pietanze(self) -> List[Dict[str, Any]]:
"""Filter pietanze based on search and allergen filters"""
filtered = self.pietanze_data
# Apply search filter
if self.search_filter:
search_lower = self.search_filter.lower()
filtered = [
p for p in filtered
if search_lower in p['nome'].lower() or
(p['descrizione'] and search_lower in p['descrizione'].lower())
]
# Apply allergen filter
if self.allergen_filter:
filtered = [
p for p in filtered
if any(allergen in p['allergeni'] for allergen in self.allergen_filter)
]
return filtered
def _on_search_change(self):
"""Handle search filter change"""
self._update_table()
def _on_filter_change(self):
"""Handle allergen filter change"""
self._update_table()

5
admin-webgui/requirements.txt

@ -0,0 +1,5 @@
nicegui<2.0.0
httpx>=0.25.0
pyyaml>=6.0.1
pyjwt>=2.8.0
asyncio

81
admin-webgui/services/api_client.py

@ -0,0 +1,81 @@
import httpx
from typing import Optional, Dict, List, Any
import asyncio
from datetime import datetime, date
class APIClient:
def __init__(self, base_url: str, auth_token: Optional[str] = None):
self.base_url = base_url.rstrip('/')
self.auth_token = auth_token
self.client = httpx.AsyncClient(timeout=30.0, follow_redirects=True)
def set_auth_token(self, token: str):
"""Set authentication token"""
self.auth_token = token
@property
def headers(self) -> Dict[str, str]:
"""Get headers with authentication"""
headers = {"Content-Type": "application/json"}
if self.auth_token:
headers["Authorization"] = f"Bearer {self.auth_token}"
return headers
async def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
"""Make HTTP request"""
url = f"{self.base_url}{endpoint}"
kwargs.setdefault('headers', {}).update(self.headers)
try:
response = await self.client.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
raise Exception(f"API request failed: {str(e)}")
# Pietanze endpoints
async def get_pietanze(self, skip: int = 0, limit: int = 100, search: str = None, allergeni: List[str] = None) -> Dict[str, Any]:
"""Get pietanze with optional filters"""
params = {"skip": skip, "limit": limit}
if search:
params["search"] = search
if allergeni:
params["allergeni"] = ",".join(allergeni)
return await self._request("GET", "/api/v1/pietanze", params=params)
async def get_pietanza(self, pietanza_id: int) -> Dict[str, Any]:
"""Get single pietanza by ID"""
return await self._request("GET", f"/api/v1/pietanze/{pietanza_id}")
async def create_pietanza(self, pietanza_data: Dict[str, Any]) -> Dict[str, Any]:
"""Create new pietanza"""
return await self._request("POST", "/api/v1/pietanze", json=pietanza_data)
async def update_pietanza(self, pietanza_id: int, pietanza_data: Dict[str, Any]) -> Dict[str, Any]:
"""Update existing pietanza"""
return await self._request("PUT", f"/api/v1/pietanze/{pietanza_id}", json=pietanza_data)
async def delete_pietanza(self, pietanza_id: int) -> None:
"""Delete pietanza"""
await self._request("DELETE", f"/api/v1/pietanze/{pietanza_id}")
# Dashboard/stats endpoints
async def get_dashboard_stats(self) -> Dict[str, Any]:
"""Get dashboard statistics"""
return await self._request("GET", "/api/v1/stats/dashboard")
async def get_prenotazioni_today(self) -> List[Dict[str, Any]]:
"""Get today's prenotazioni"""
today = date.today().isoformat()
return await self._request("GET", f"/api/v1/prenotazioni/by-date/{today}")
async def get_pasti_disponibili(self) -> List[Dict[str, Any]]:
"""Get available pasti"""
return await self._request("GET", "/api/v1/pasti", params={"disponibile": True})
def __del__(self):
"""Cleanup client"""
try:
asyncio.create_task(self.client.aclose())
except:
pass

60
admin-webgui/services/auth_service.py

@ -0,0 +1,60 @@
from typing import Optional, Dict, Any
import jwt
from datetime import datetime, timedelta
class AuthService:
def __init__(self, auth_config: Dict[str, Any]):
self.config = auth_config
self._current_user = None
self._auth_token = None
def is_authenticated(self) -> bool:
"""Check if user is authenticated"""
return self._auth_token is not None and self._current_user is not None
def get_current_user(self) -> Optional[Dict[str, Any]]:
"""Get current authenticated user"""
return self._current_user
def get_auth_token(self) -> Optional[str]:
"""Get current auth token"""
return self._auth_token
def login(self, username: str, password: str) -> bool:
"""Simple login (in production, this would integrate with external provider)"""
# For demo purposes, simple validation
if username == "admin" and password == "admin":
# Generate a simple JWT token
payload = {
"user_id": "admin",
"username": username,
"role": "admin",
"exp": datetime.utcnow() + timedelta(hours=8)
}
self._auth_token = jwt.encode(payload, self.config['secret_key'], algorithm="HS256")
self._current_user = {
"user_id": "admin",
"username": username,
"role": "admin"
}
return True
return False
def logout(self):
"""Logout current user"""
self._current_user = None
self._auth_token = None
def validate_token(self, token: str) -> bool:
"""Validate JWT token"""
try:
payload = jwt.decode(token, self.config['secret_key'], algorithms=["HS256"])
self._current_user = {
"user_id": payload["user_id"],
"username": payload["username"],
"role": payload["role"]
}
self._auth_token = token
return True
except jwt.InvalidTokenError:
return False

4
api/routes/pietanze.py

@ -14,7 +14,7 @@ router = APIRouter(prefix="/pietanze", tags=["Pietanze"])
@router.get("/", response_model=List[PietanzaResponse]) @router.get("/", response_model=List[PietanzaResponse])
async def list_pietanze( async def list_pietanze(
skip: int = Query(0, ge=0, description="Numero di elementi da saltare per la paginazione"), skip: int = Query(0, ge=0, description="Numero di elementi da saltare per la paginazione"),
limit: int = Query(20, ge=1, le=100, description="Numero di elementi da restituire"), limit: int = Query(20, ge=1, le=1000, description="Numero di elementi da restituire"),
search: Optional[str] = Query(None, description="Ricerca in nome e descrizione"), search: Optional[str] = Query(None, description="Ricerca in nome e descrizione"),
allergeni: Optional[str] = Query(None, description="Filtra per allergeni (separati da virgola)"), allergeni: Optional[str] = Query(None, description="Filtra per allergeni (separati da virgola)"),
db: DatabaseManager = Depends(get_database) db: DatabaseManager = Depends(get_database)
@ -113,7 +113,7 @@ async def get_pietanza(
@router.post("/", response_model=PietanzaResponse, status_code=status.HTTP_201_CREATED) @router.post("/", response_model=PietanzaResponse, status_code=status.HTTP_201_CREATED)
async def create_pietanza( async def create_pietanza(
pietanza: PietanzaCreate, pietanza: PietanzaCreate,
current_user: Dict[str, Any] = Depends(get_current_admin_user), #current_user: Dict[str, Any] = Depends(get_current_admin_user),
db: DatabaseManager = Depends(get_database) db: DatabaseManager = Depends(get_database)
): ):
"""Crea nuova pietanza (solo amministratori)""" """Crea nuova pietanza (solo amministratori)"""

Loading…
Cancel
Save