Add whatsapp feature
This commit is contained in:
@@ -40,17 +40,25 @@ router = APIRouter(prefix="/api/v1/tenants/{tenant_id}/dashboard", tags=["dashbo
|
||||
# Response Models
|
||||
# ============================================================
|
||||
|
||||
class HeadlineData(BaseModel):
|
||||
"""i18n-ready headline data"""
|
||||
key: str = Field(..., description="i18n translation key")
|
||||
params: Dict[str, Any] = Field(default_factory=dict, description="Parameters for translation")
|
||||
|
||||
|
||||
class HealthChecklistItem(BaseModel):
|
||||
"""Individual item in health checklist"""
|
||||
icon: str = Field(..., description="Icon name: check, warning, alert")
|
||||
text: str = Field(..., description="Checklist item text")
|
||||
text: Optional[str] = Field(None, description="Deprecated: Use textKey instead")
|
||||
textKey: Optional[str] = Field(None, description="i18n translation key")
|
||||
textParams: Optional[Dict[str, Any]] = Field(None, description="Parameters for i18n translation")
|
||||
actionRequired: bool = Field(..., description="Whether action is required")
|
||||
|
||||
|
||||
class BakeryHealthStatusResponse(BaseModel):
|
||||
"""Overall bakery health status"""
|
||||
status: str = Field(..., description="Health status: green, yellow, red")
|
||||
headline: str = Field(..., description="Human-readable status headline")
|
||||
headline: HeadlineData = Field(..., description="i18n-ready status headline")
|
||||
lastOrchestrationRun: Optional[str] = Field(None, description="ISO timestamp of last orchestration")
|
||||
nextScheduledRun: str = Field(..., description="ISO timestamp of next scheduled run")
|
||||
checklistItems: List[HealthChecklistItem] = Field(..., description="Status checklist")
|
||||
@@ -83,7 +91,7 @@ class ProductionBatchSummary(BaseModel):
|
||||
class OrchestrationSummaryResponse(BaseModel):
|
||||
"""What the orchestrator did for the user"""
|
||||
runTimestamp: Optional[str] = Field(None, description="When the orchestration ran")
|
||||
runNumber: Optional[int] = Field(None, description="Run sequence number")
|
||||
runNumber: Optional[str] = Field(None, description="Run number identifier")
|
||||
status: str = Field(..., description="Run status")
|
||||
purchaseOrdersCreated: int = Field(..., description="Number of POs created")
|
||||
purchaseOrdersSummary: List[PurchaseOrderSummary] = Field(default_factory=list)
|
||||
|
||||
@@ -92,13 +92,14 @@ class DashboardService:
|
||||
if production_delays == 0:
|
||||
checklist_items.append({
|
||||
"icon": "check",
|
||||
"text": "Production on schedule",
|
||||
"textKey": "dashboard.health.production_on_schedule",
|
||||
"actionRequired": False
|
||||
})
|
||||
else:
|
||||
checklist_items.append({
|
||||
"icon": "warning",
|
||||
"text": f"{production_delays} production batch{'es' if production_delays != 1 else ''} delayed",
|
||||
"textKey": "dashboard.health.production_delayed",
|
||||
"textParams": {"count": production_delays},
|
||||
"actionRequired": True
|
||||
})
|
||||
|
||||
@@ -106,13 +107,14 @@ class DashboardService:
|
||||
if out_of_stock_count == 0:
|
||||
checklist_items.append({
|
||||
"icon": "check",
|
||||
"text": "All ingredients in stock",
|
||||
"textKey": "dashboard.health.all_ingredients_in_stock",
|
||||
"actionRequired": False
|
||||
})
|
||||
else:
|
||||
checklist_items.append({
|
||||
"icon": "alert",
|
||||
"text": f"{out_of_stock_count} ingredient{'s' if out_of_stock_count != 1 else ''} out of stock",
|
||||
"textKey": "dashboard.health.ingredients_out_of_stock",
|
||||
"textParams": {"count": out_of_stock_count},
|
||||
"actionRequired": True
|
||||
})
|
||||
|
||||
@@ -120,13 +122,14 @@ class DashboardService:
|
||||
if pending_approvals == 0:
|
||||
checklist_items.append({
|
||||
"icon": "check",
|
||||
"text": "No pending approvals",
|
||||
"textKey": "dashboard.health.no_pending_approvals",
|
||||
"actionRequired": False
|
||||
})
|
||||
else:
|
||||
checklist_items.append({
|
||||
"icon": "warning",
|
||||
"text": f"{pending_approvals} purchase order{'s' if pending_approvals != 1 else ''} awaiting approval",
|
||||
"textKey": "dashboard.health.approvals_awaiting",
|
||||
"textParams": {"count": pending_approvals},
|
||||
"actionRequired": True
|
||||
})
|
||||
|
||||
@@ -134,13 +137,14 @@ class DashboardService:
|
||||
if system_errors == 0 and critical_alerts == 0:
|
||||
checklist_items.append({
|
||||
"icon": "check",
|
||||
"text": "All systems operational",
|
||||
"textKey": "dashboard.health.all_systems_operational",
|
||||
"actionRequired": False
|
||||
})
|
||||
else:
|
||||
checklist_items.append({
|
||||
"icon": "alert",
|
||||
"text": f"{critical_alerts + system_errors} critical issue{'s' if (critical_alerts + system_errors) != 1 else ''}",
|
||||
"textKey": "dashboard.health.critical_issues",
|
||||
"textParams": {"count": critical_alerts + system_errors},
|
||||
"actionRequired": True
|
||||
})
|
||||
|
||||
@@ -193,19 +197,34 @@ class DashboardService:
|
||||
status: str,
|
||||
critical_alerts: int,
|
||||
pending_approvals: int
|
||||
) -> str:
|
||||
"""Generate human-readable headline based on status"""
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate i18n-ready headline based on status"""
|
||||
if status == HealthStatus.GREEN:
|
||||
return "Your bakery is running smoothly"
|
||||
return {
|
||||
"key": "dashboard.health.headline_green",
|
||||
"params": {}
|
||||
}
|
||||
elif status == HealthStatus.YELLOW:
|
||||
if pending_approvals > 0:
|
||||
return f"Please review {pending_approvals} pending approval{'s' if pending_approvals != 1 else ''}"
|
||||
return {
|
||||
"key": "dashboard.health.headline_yellow_approvals",
|
||||
"params": {"count": pending_approvals}
|
||||
}
|
||||
elif critical_alerts > 0:
|
||||
return f"You have {critical_alerts} alert{'s' if critical_alerts != 1 else ''} needing attention"
|
||||
return {
|
||||
"key": "dashboard.health.headline_yellow_alerts",
|
||||
"params": {"count": critical_alerts}
|
||||
}
|
||||
else:
|
||||
return "Some items need your attention"
|
||||
return {
|
||||
"key": "dashboard.health.headline_yellow_general",
|
||||
"params": {}
|
||||
}
|
||||
else: # RED
|
||||
return "Critical issues require immediate action"
|
||||
return {
|
||||
"key": "dashboard.health.headline_red",
|
||||
"params": {}
|
||||
}
|
||||
|
||||
async def _get_last_orchestration_run(self, tenant_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get the most recent orchestration run"""
|
||||
@@ -286,18 +305,16 @@ class DashboardService:
|
||||
"message": "No orchestration has been run yet. Click 'Run Daily Planning' to generate your first plan."
|
||||
}
|
||||
|
||||
# Parse results from JSONB
|
||||
results = run.results or {}
|
||||
# Use actual model columns instead of non-existent results attribute
|
||||
po_count = run.purchase_orders_created or 0
|
||||
batch_count = run.production_batches_created or 0
|
||||
forecasts_count = run.forecasts_generated or 0
|
||||
|
||||
# Extract step results
|
||||
step_results = results.get("steps", {})
|
||||
forecasting_step = step_results.get("1", {})
|
||||
production_step = step_results.get("2", {})
|
||||
procurement_step = step_results.get("3", {})
|
||||
# Get metadata if available
|
||||
run_metadata = run.run_metadata or {}
|
||||
|
||||
# Count created entities
|
||||
po_count = procurement_step.get("purchase_orders_created", 0)
|
||||
batch_count = production_step.get("production_batches_created", 0)
|
||||
# Extract forecast data if available
|
||||
forecast_data = run.forecast_data or {}
|
||||
|
||||
# Get detailed summaries (these would come from the actual services in real implementation)
|
||||
# For now, provide structure that the frontend expects
|
||||
@@ -311,14 +328,14 @@ class DashboardService:
|
||||
"productionBatchesCreated": batch_count,
|
||||
"productionBatchesSummary": [], # Will be filled by separate service calls
|
||||
"reasoningInputs": {
|
||||
"customerOrders": forecasting_step.get("orders_analyzed", 0),
|
||||
"historicalDemand": forecasting_step.get("success", False),
|
||||
"inventoryLevels": procurement_step.get("success", False),
|
||||
"aiInsights": results.get("ai_insights_used", False)
|
||||
"customerOrders": forecasts_count,
|
||||
"historicalDemand": run.forecasting_status == "success",
|
||||
"inventoryLevels": run.procurement_status == "success",
|
||||
"aiInsights": (run.ai_insights_generated or 0) > 0
|
||||
},
|
||||
"userActionsRequired": po_count, # POs need approval
|
||||
"durationSeconds": run.duration_seconds,
|
||||
"aiAssisted": results.get("ai_insights_used", False)
|
||||
"aiAssisted": (run.ai_insights_generated or 0) > 0
|
||||
}
|
||||
|
||||
async def get_action_queue(
|
||||
|
||||
Reference in New Issue
Block a user