You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
orquestrador/admin_app/view/router.py

959 lines
42 KiB
Python

from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse, RedirectResponse, Response
from admin_app.api.dependencies import get_optional_panel_staff_context
from admin_app.core import AdminSettings, AuthenticatedStaffContext, get_admin_settings
from admin_app.services import ToolManagementService
from admin_app.view.assets import PANEL_STATIC_MOUNT_NAME
from admin_app.view.rendering import (
render_bot_monitoring_page,
render_collaborator_management_page,
render_login_page,
render_panel_home,
render_rental_reports_page,
render_sales_revenue_reports_page,
render_system_configuration_page,
render_tool_intake_page,
render_tool_review_page,
)
from admin_app.view.view_models import (
AdminBotMonitoringPageView,
AdminCollaboratorManagementPageView,
AdminLoginPageView,
AdminPanelHomeView,
AdminPanelMetric,
AdminPanelModuleCard,
AdminPanelNavigationItem,
AdminPanelQuickAction,
AdminPanelRoadmapItem,
AdminPanelSurfaceLink,
AdminRentalReportsPageView,
AdminSalesRevenueReportsPageView,
AdminSystemConfigurationPageView,
AdminToolIntakeDomainOption,
AdminToolIntakePageView,
AdminToolIntakeParameterTypeOption,
AdminToolReviewPageView,
AdminToolReviewWorkflowStep,
)
from shared.contracts import AdminPermission, StaffRole, role_has_permission
panel_router = APIRouter(tags=["panel"])
@panel_router.get("/panel", name="panel_entry")
def panel_entry(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> RedirectResponse:
target_route_name = "panel_home" if current_context is not None else "admin_login_view"
return _redirect_to_route(request, target_route_name)
@panel_router.get("/panel/admin", response_class=HTMLResponse, name="panel_home")
def panel_home(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
settings = _resolve_settings(request)
view = _build_home_view(request, settings, current_context)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_panel_home(view, css_href=css_href, js_href=js_href))
@panel_router.get("/login", response_class=HTMLResponse, name="admin_login_view")
def login_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is not None:
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_login_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_login_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/tools/new", response_class=HTMLResponse, name="admin_tool_intake_view")
def tool_intake_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
settings = _resolve_settings(request)
view = _build_tool_intake_view(request, settings, current_context.principal.role)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_tool_intake_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/tools/review", response_class=HTMLResponse, name="admin_tool_review_view")
def tool_review_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
settings = _resolve_settings(request)
view = _build_tool_review_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_tool_review_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/colaboradores/gestao", response_class=HTMLResponse, name="admin_collaborator_management_view")
def collaborator_management_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
if not role_has_permission(current_context.principal.role, AdminPermission.MANAGE_STAFF_ACCOUNTS):
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_collaborator_management_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_collaborator_management_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/sistema/configuracoes", response_class=HTMLResponse, name="admin_system_configuration_view")
def system_configuration_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
if not role_has_permission(current_context.principal.role, AdminPermission.VIEW_SYSTEM):
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_system_configuration_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_system_configuration_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/relatorios/vendas-arrecadacao", response_class=HTMLResponse, name="admin_sales_revenue_reports_view")
def sales_revenue_reports_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
if not role_has_permission(current_context.principal.role, AdminPermission.VIEW_REPORTS):
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_sales_revenue_reports_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_sales_revenue_reports_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/relatorios/locacao", response_class=HTMLResponse, name="admin_rental_reports_view")
def rental_reports_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
if not role_has_permission(current_context.principal.role, AdminPermission.VIEW_REPORTS):
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_rental_reports_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_rental_reports_page(view, css_href=css_href, js_href=js_href))
@panel_router.get("/panel/monitoramento/bot", response_class=HTMLResponse, name="admin_bot_monitoring_view")
def bot_monitoring_page(
request: Request,
current_context: AuthenticatedStaffContext | None = Depends(get_optional_panel_staff_context),
) -> Response:
if current_context is None:
return _redirect_to_route(request, "admin_login_view")
if not role_has_permission(current_context.principal.role, AdminPermission.VIEW_REPORTS):
return _redirect_to_route(request, "panel_home")
settings = _resolve_settings(request)
view = _build_bot_monitoring_view(request, settings)
css_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="styles/panel.css"))
js_href = str(request.url_for(PANEL_STATIC_MOUNT_NAME, path="scripts/panel.js"))
return HTMLResponse(render_bot_monitoring_page(view, css_href=css_href, js_href=js_href))
def _build_home_view(
request: Request,
settings: AdminSettings,
current_context: AuthenticatedStaffContext,
) -> AdminPanelHomeView:
panel_href = str(request.url_for("panel_home"))
tool_intake_view_href = str(request.url_for("admin_tool_intake_view"))
tool_review_view_href = str(request.url_for("admin_tool_review_view"))
collaborator_management_view_href = str(request.url_for("admin_collaborator_management_view"))
system_configuration_view_href = str(request.url_for("admin_system_configuration_view"))
sales_revenue_reports_view_href = str(request.url_for("admin_sales_revenue_reports_view"))
rental_reports_view_href = str(request.url_for("admin_rental_reports_view"))
bot_monitoring_view_href = str(request.url_for("admin_bot_monitoring_view"))
audit_href = _build_prefixed_path(settings.admin_api_prefix, "/audit/events")
can_manage_collaborators = role_has_permission(
current_context.principal.role,
AdminPermission.MANAGE_STAFF_ACCOUNTS,
)
navigation = [
AdminPanelNavigationItem(
label="Dashboard",
href=panel_href,
description="Entrada principal do ambiente interno.",
badge="Ativo",
is_active=True,
),
AdminPanelNavigationItem(
label="Cadastro de tools",
href=tool_intake_view_href,
description="Pre-cadastro validado para novas tools antes da revisao.",
badge="Novo",
),
AdminPanelNavigationItem(
label="Revisao de tools",
href=tool_review_view_href,
description="Fluxo humano de revisao, aprovacao e ativacao.",
badge="Operacao",
),
AdminPanelNavigationItem(
label="Configuracoes do sistema",
href=system_configuration_view_href,
description="Leitura funcional, runtime e governanca do admin em uma tela dedicada.",
badge="Fase 4",
),
AdminPanelNavigationItem(
label="Relatorios comerciais",
href=sales_revenue_reports_view_href,
description="Tela combinada para vendas e arrecadacao dentro da sessao do painel.",
badge="Relatorios",
),
AdminPanelNavigationItem(
label="Relatorios de locacao",
href=rental_reports_view_href,
description="Tela dedicada para frota, contratos e ocupacao na sessao do painel.",
badge="Locacao",
),
AdminPanelNavigationItem(
label="Monitoramento do bot",
href=bot_monitoring_view_href,
description="Fluxo operacional e telemetria conversacional em uma superficie dedicada.",
badge="Bot",
),
AdminPanelNavigationItem(
label="Areas do sistema",
href="#modules",
description="Mapa claro dos modulos internos disponiveis.",
badge="Painel",
),
AdminPanelNavigationItem(
label="Fluxo recomendado",
href="#workflow",
description="Sequencia sugerida para operar o admin.",
badge="Guia",
),
]
quick_actions = [
AdminPanelQuickAction(
label="Cadastrar tool",
href=tool_intake_view_href,
button_class="btn-dark",
),
AdminPanelQuickAction(
label="Revisar tools",
href=tool_review_view_href,
button_class="btn-outline-dark",
),
AdminPanelQuickAction(
label="Config. sistema",
href=system_configuration_view_href,
button_class="btn-outline-secondary",
),
AdminPanelQuickAction(
label="Relatorios",
href=sales_revenue_reports_view_href,
button_class="btn-outline-dark",
),
AdminPanelQuickAction(
label="Locacao",
href=rental_reports_view_href,
button_class="btn-outline-dark",
),
AdminPanelQuickAction(
label="Monitorar bot",
href=bot_monitoring_view_href,
button_class="btn-outline-dark",
),
]
modules = [
AdminPanelModuleCard(
eyebrow="Fluxo de entrada",
title="Cadastro de tools",
description="Tela real para o colaborador preencher metadados, definir parametros e validar o pre-cadastro antes da revisao humana.",
status_label="Tela ativa",
status_variant="success",
highlights=(
"Formulario protegido por sessao web",
"Draft persistido logo apos a validacao",
"Direcao clara para revisao de diretor",
),
cta_label="Abrir cadastro",
href=tool_intake_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Fluxo principal",
title="Revisao de tools",
description="Area operacional do painel para leitura da fila, aprovacao humana e ativacao controlada.",
status_label="Tela ativa",
status_variant="success",
highlights=(
"Fila protegida por sessao web",
"Catalogo ativo para comparacao",
"Leitura clara do workflow de aprovacao",
),
cta_label="Abrir revisao",
href=tool_review_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Acompanhamento",
title="Configuracao do sistema",
description="Tela dedicada para consultar configuracao funcional, runtime administrativo e governanca do sistema sem sair do painel.",
status_label="Tela ativa",
status_variant="primary",
highlights=(
"Catalogo funcional e governanca do bot conectados",
"Runtime e seguranca carregados por permissao",
"Separacao de modelos visivel na mesma superficie",
),
cta_label="Abrir configuracoes",
href=system_configuration_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Relatorios operacionais",
title="Relatorios de vendas e arrecadacao",
description="Tela dedicada para acompanhar o bootstrap de vendas e arrecadacao com leitura pronta para sessao web do painel.",
status_label="Tela ativa",
status_variant="info",
highlights=(
"Vendas e arrecadacao na mesma superficie",
"Leitura protegida por sessao web",
"Materializacao e proximos passos visiveis na UI",
),
cta_label="Abrir relatorios",
href=sales_revenue_reports_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Relatorios operacionais",
title="Relatorios de locacao",
description="Tela dedicada para acompanhar a estrutura inicial de frota e contratos de locacao pela sessao web do painel.",
status_label="Tela ativa",
status_variant="info",
highlights=(
"Frota e contratos lidos na mesma superficie",
"Leitura protegida por sessao web",
"Catalogo inicial e materializacao visiveis na UI",
),
cta_label="Abrir locacao",
href=rental_reports_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Monitoramento operacional",
title="Monitoramento do bot",
description="Tela dedicada para acompanhar fluxo operacional e telemetria conversacional pela sessao web do painel.",
status_label="Tela ativa",
status_variant="info",
highlights=(
"Fluxo do bot e telemetria na mesma superficie",
"Leitura protegida por sessao web",
"Materializacao e proximos passos visiveis na UI",
),
cta_label="Abrir monitoramento",
href=bot_monitoring_view_href,
is_available=True,
),
AdminPanelModuleCard(
eyebrow="Governanca",
title="Auditoria operacional",
description="Eventos de login, logout, aprovacao e publicacao continuam registrados para rastreabilidade.",
status_label="Auditavel",
status_variant="secondary",
highlights=(
"Historico de operacao interna",
"Base para filtros e timeline",
"Suporte a conformidade do fluxo administrativo",
),
),
]
surface_links = [
AdminPanelSurfaceLink(
method="Acesso",
label="Dashboard administrativa",
href=panel_href,
description="Entrada principal do time interno depois do login.",
),
AdminPanelSurfaceLink(
method="Cadastro",
label="Nova tool",
href=tool_intake_view_href,
description="Formulario real para validar o pre-cadastro de uma nova tool.",
),
AdminPanelSurfaceLink(
method="Operacao",
label="Revisao de tools",
href=tool_review_view_href,
description="Area com fila, contrato e catalogo ativo para tomada de decisao.",
),
AdminPanelSurfaceLink(
method="Sistema",
label="Configuracoes do sistema",
href=system_configuration_view_href,
description="Tela dedicada para leitura funcional, runtime e governanca administrativa.",
),
AdminPanelSurfaceLink(
method="Relatorios",
label="Vendas e arrecadacao",
href=sales_revenue_reports_view_href,
description="Tela combinada para a primeira camada visual dos relatorios comerciais da fase 4.",
),
AdminPanelSurfaceLink(
method="Relatorios",
label="Locacao",
href=rental_reports_view_href,
description="Tela dedicada para a estrutura inicial de frota, contratos e ocupacao da fase 4.",
),
AdminPanelSurfaceLink(
method="Monitoramento",
label="Bot operacional",
href=bot_monitoring_view_href,
description="Tela dedicada para fluxo operacional e telemetria conversacional do bot na fase 4.",
),
AdminPanelSurfaceLink(
method="Auditoria",
label="Eventos administrativos",
href=audit_href,
description="Consulta de eventos internos para rastrear operacoes sensiveis.",
),
]
roadmap = [
AdminPanelRoadmapItem(
step="01",
title="Entrar pelo login administrativo",
description="A sessao web libera o ambiente interno e evita navegacao confusa antes da autenticacao.",
status_label="Obrigatorio",
),
AdminPanelRoadmapItem(
step="02",
title="Passar pela dashboard",
description="A home protegida organiza os modulos e mostra por onde comecar a operacao.",
status_label="Entrada",
),
AdminPanelRoadmapItem(
step="03",
title="Cadastrar ou validar o pre-draft",
description="Use a nova tela para descrever a tool, seus parametros e o objetivo operacional antes da revisao.",
status_label="Cadastro",
),
AdminPanelRoadmapItem(
step="04",
title="Abrir revisao de tools",
description="Encaminhe a ferramenta para analise humana, aprovacao e ativacao controlada.",
status_label="Principal",
),
AdminPanelRoadmapItem(
step="05",
title="Consultar runtime e auditoria",
description="Quando necessario, acompanhe configuracao e eventos do admin para suportar a decisao operacional.",
status_label="Suporte",
),
]
if can_manage_collaborators:
navigation.insert(
3,
AdminPanelNavigationItem(
label="Colaboradores",
href=collaborator_management_view_href,
description="Cadastro e governanca de acessos internos, exclusivo para diretor.",
badge="Diretor",
),
)
quick_actions.insert(
2,
AdminPanelQuickAction(
label="Gerir equipe",
href=collaborator_management_view_href,
button_class="btn-outline-dark",
),
)
modules.insert(
2,
AdminPanelModuleCard(
eyebrow="Governanca de acesso",
title="Gestao de colaboradores",
description="Tela dedicada para o diretor criar contas internas, acompanhar o status da equipe e manter a entrada administrativa sob controle.",
status_label="Tela ativa",
status_variant="dark",
highlights=(
"Criacao de colaborador com senha inicial",
"Ativacao e desativacao sem tocar no banco manualmente",
"Fluxo exclusivo para diretor",
),
cta_label="Abrir equipe",
href=collaborator_management_view_href,
is_available=True,
),
)
surface_links.insert(
3,
AdminPanelSurfaceLink(
method="Equipe",
label="Gestao de colaboradores",
href=collaborator_management_view_href,
description="Controle de acesso interno para cadastrar e administrar a equipe administrativa.",
),
)
roadmap = [
AdminPanelRoadmapItem(
step="01",
title="Entrar pelo login administrativo",
description="A sessao web libera o ambiente interno e evita navegacao confusa antes da autenticacao.",
status_label="Obrigatorio",
),
AdminPanelRoadmapItem(
step="02",
title="Passar pela dashboard",
description="A home protegida organiza os modulos e mostra por onde comecar a operacao.",
status_label="Entrada",
),
AdminPanelRoadmapItem(
step="03",
title="Cadastrar ou validar o pre-draft",
description="Use a nova tela para descrever a tool, seus parametros e o objetivo operacional antes da revisao.",
status_label="Cadastro",
),
AdminPanelRoadmapItem(
step="04",
title="Organizar a equipe interna",
description="Diretores podem cadastrar novos colaboradores e controlar rapidamente o status de acesso administrativo.",
status_label="Diretor",
),
AdminPanelRoadmapItem(
step="05",
title="Abrir revisao de tools",
description="Encaminhe a ferramenta para analise humana, aprovacao e ativacao controlada.",
status_label="Principal",
),
AdminPanelRoadmapItem(
step="06",
title="Consultar runtime e auditoria",
description="Quando necessario, acompanhe configuracao e eventos do admin para suportar a decisao operacional.",
status_label="Suporte",
),
]
return AdminPanelHomeView(
service="orquestrador-admin",
app_name=settings.admin_app_name,
panel_title="Painel Administrativo",
panel_subtitle=(
"Area interna protegida para operar o admin com mais clareza, foco e navegacao orientada por fluxo."
),
environment=settings.admin_environment,
version=settings.admin_version,
api_prefix=settings.admin_api_prefix or "/",
release_label="Bootstrap UI v1",
navigation=tuple(navigation),
quick_actions=tuple(quick_actions),
metrics=(
AdminPanelMetric(
label="Runtimes independentes",
value="2",
description="Produto e admin seguem isolados para deploy e operacao.",
),
AdminPanelMetric(
label="Perfis internos",
value=str(len(StaffRole)),
description="Hierarquia base com colaborador e diretor.",
),
AdminPanelMetric(
label="Permissoes administrativas",
value=str(len(AdminPermission)),
description="Camada pronta para crescer por modulo sem misturar contexto.",
),
AdminPanelMetric(
label="Refresh token",
value=f"{settings.admin_auth_refresh_token_ttl_days} dias",
description="Sessao web persistida com renovacao controlada.",
),
),
modules=tuple(modules),
surface_links=tuple(surface_links),
roadmap=tuple(roadmap),
)
def _build_login_view(request: Request, settings: AdminSettings) -> AdminLoginPageView:
dashboard_href = str(request.url_for("panel_home"))
auth_endpoint = _build_prefixed_path(settings.admin_api_prefix, "/panel/auth/login")
password_requirements = []
if settings.admin_auth_password_require_uppercase:
password_requirements.append("maiuscula")
if settings.admin_auth_password_require_lowercase:
password_requirements.append("minuscula")
if settings.admin_auth_password_require_digit:
password_requirements.append("digito")
if settings.admin_auth_password_require_symbol:
password_requirements.append("simbolo")
password_policy_label = (
f"Minimo de {settings.admin_auth_password_min_length} caracteres"
+ (f" com {', '.join(password_requirements)}." if password_requirements else ".")
)
return AdminLoginPageView(
app_name=settings.admin_app_name,
title="Login administrativo",
subtitle=(
"Entre primeiro com sua conta interna. A dashboard e os modulos do sistema so aparecem depois da autenticacao."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=dashboard_href,
auth_endpoint=auth_endpoint,
email_placeholder="voce@empresa.com",
password_placeholder="Sua senha administrativa",
access_token_ttl_label=f"{settings.admin_auth_access_token_ttl_minutes} minutos",
refresh_token_ttl_label=f"{settings.admin_auth_refresh_token_ttl_days} dias",
password_policy_label=password_policy_label,
security_highlights=(
"Identidade separada do usuario de atendimento",
"Rotacao de refresh token ja implementada",
"Trilha de auditoria para login e logout",
),
integration_notes=(
"A dashboard administrativa so aparece depois da autenticacao do StaffAccount.",
"Revisao, configuracao e operacao interna ficam atras da sessao web do painel.",
"Cookies httpOnly e refresh token rotacionado mantem a sessao do navegador protegida.",
),
)
def _build_tool_intake_view(
request: Request,
settings: AdminSettings,
current_role: StaffRole | str | None,
) -> AdminToolIntakePageView:
service = ToolManagementService(settings)
form_payload = service.build_draft_form_payload(submitter_role=current_role)
return AdminToolIntakePageView(
app_name=settings.admin_app_name,
title="Cadastro de nova tool",
subtitle=(
"Formulario guiado para estruturar uma proposta de tool, registrar o draft versionado e encaminhar a triagem humana antes de qualquer geracao de codigo."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
review_href=str(request.url_for("admin_tool_review_view")),
intake_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/tools/drafts/intake"),
domain_options=tuple(
AdminToolIntakeDomainOption(
value=item["value"],
label=item["label"],
description=item["description"],
)
for item in form_payload["domain_options"]
),
parameter_type_options=tuple(
AdminToolIntakeParameterTypeOption(
value=item["code"].value,
label=item["label"],
description=item["description"],
)
for item in form_payload["parameter_types"]
),
naming_rules=tuple(form_payload["naming_rules"]),
submission_notes=tuple(form_payload["submission_notes"]),
approval_notes=tuple(form_payload["approval_notes"]),
)
def _build_tool_review_view(request: Request, settings: AdminSettings) -> AdminToolReviewPageView:
dashboard_href = str(request.url_for("panel_home"))
overview_endpoint = _build_prefixed_path(settings.admin_api_prefix, "/panel/tools/overview")
contracts_endpoint = _build_prefixed_path(settings.admin_api_prefix, "/panel/tools/contracts")
review_queue_endpoint = _build_prefixed_path(settings.admin_api_prefix, "/panel/tools/review-queue")
publications_endpoint = _build_prefixed_path(settings.admin_api_prefix, "/panel/tools/publications")
return AdminToolReviewPageView(
app_name=settings.admin_app_name,
title="Triagem, revisao e ativacao",
subtitle=(
"Hub visual para acompanhar a proposta da tool desde a triagem para geracao, passando pelas iteracoes de codigo, ate a ativacao controlada no catalogo do produto."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=dashboard_href,
overview_endpoint=overview_endpoint,
contracts_endpoint=contracts_endpoint,
review_queue_endpoint=review_queue_endpoint,
publications_endpoint=publications_endpoint,
workflow=(
AdminToolReviewWorkflowStep(
eyebrow="Cadastro manual",
title="Criar a proposta",
description="Receber o cadastro manual da tool e consolidar o draft administrativo sem consumir geracao de codigo.",
status_label="Draft",
status_variant="info",
),
AdminToolReviewWorkflowStep(
eyebrow="Triagem humana",
title="Triar antes de gerar",
description="Decidir se a proposta realmente merece consumir geracao de codigo ou deve ser encerrada antes disso.",
status_label="Triagem",
status_variant="warning",
),
AdminToolReviewWorkflowStep(
eyebrow="Pipeline",
title="Gerar ou refatorar",
description="Executar a geracao isolada, registrar a iteracao correspondente e validar automaticamente o codigo antes da leitura humana.",
status_label="Geracao",
status_variant="info",
),
AdminToolReviewWorkflowStep(
eyebrow="Decisao humana",
title="Ler e decidir",
description="A diretoria revisa a iteracao atual do codigo, pede ajustes quando necessario ou valida a versao para aprovacao final.",
status_label="Revisao",
status_variant="warning",
),
AdminToolReviewWorkflowStep(
eyebrow="Publicacao",
title="Ativar com governanca",
description="Publicar apenas a iteracao aprovada e sincronizar o catalogo que abastece o runtime de produto.",
status_label="Ativacao",
status_variant="success",
),
),
review_notes=(
"Conferir se a proposta esta no gate correto: triagem para geracao, ajustes solicitados ou leitura do codigo atual.",
"Observar se a descricao, o objetivo operacional e os parametros deixam claro o valor de negocio da tool.",
"Ler o codigo completo da iteracao atual antes de validar, solicitar ajustes ou encerrar a proposta.",
),
approval_notes=(
"Verificar nome, descricao, semantica dos parametros e a iteracao que esta sendo aprovada antes da ativacao.",
"Confirmar se a tool respeita a separacao entre admin e product definida nas ADRs e nos contratos compartilhados.",
"Checar se a publicacao planejada e auditavel, segura e vinculada ao codigo mais recente revisado pela diretoria.",
),
activation_notes=(
"Publicacoes ativas exigem papel com permissao publish_tools e aprovacao humana vinculada a iteracao mais recente.",
"A leitura do catalogo e feita via sessao web do painel para facilitar a operacao do navegador e a auditoria do fluxo.",
"Sem permissao de publicacao, a tela continua util para triagem e revisao, mas bloqueia a ativacao no catalogo ativo.",
),
)
def _build_sales_revenue_reports_view(
request: Request,
settings: AdminSettings,
) -> AdminSalesRevenueReportsPageView:
return AdminSalesRevenueReportsPageView(
app_name=settings.admin_app_name,
title="Relatorios de vendas e arrecadacao",
subtitle=(
"Visao unica para acompanhar vendas e arrecadacao no painel administrativo."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
sales_overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/reports/sales/overview"),
revenue_overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/reports/arrecadacao/overview"),
access_notes=(
"A equipe interna pode consultar esta tela com a permissao de relatorios.",
"Os dados exibidos aparecem de forma consolidada para leitura segura no admin.",
"Vendas e arrecadacao ficam juntas para facilitar a rotina do time.",
),
reading_notes=(
"Comece pelos indicadores de cada bloco para ter uma leitura rapida.",
"Use os cards para entender rapidamente o foco de cada relatorio.",
"A area de proximas melhorias mostra o que entra nas etapas seguintes.",
),
)
def _build_rental_reports_view(
request: Request,
settings: AdminSettings,
) -> AdminRentalReportsPageView:
return AdminRentalReportsPageView(
app_name=settings.admin_app_name,
title="Relatorios de locacao",
subtitle=(
"Visao dedicada da locacao para acompanhar frota, contratos e receita operacional."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/reports/locacao/overview"),
access_notes=(
"A equipe interna pode consultar esta tela com a permissao de relatorios.",
"Os dados exibidos aparecem de forma consolidada para leitura segura no admin.",
"Locacao fica em uma tela propria para manter a leitura mais organizada.",
),
reading_notes=(
"Comece pelos indicadores principais para entender o momento da operacao.",
"Use os cards para ver rapidamente os temas cobertos nesta etapa.",
"A area de proximas melhorias indica o que ainda entra nas proximas entregas.",
),
)
def _build_bot_monitoring_view(
request: Request,
settings: AdminSettings,
) -> AdminBotMonitoringPageView:
return AdminBotMonitoringPageView(
app_name=settings.admin_app_name,
title="Monitoramento operacional do bot",
subtitle=(
"Painel unico para acompanhar o fluxo do bot e a telemetria do atendimento."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
bot_flow_overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/reports/fluxo-bot/overview"),
telemetry_overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/reports/telemetria-conversacional/overview"),
access_notes=(
"A equipe interna pode consultar esta tela com a permissao de relatorios.",
"Os dados exibidos aparecem de forma consolidada para leitura segura no painel.",
"Fluxo e telemetria ficam juntos para agilizar a analise da operacao.",
),
reading_notes=(
"Comece pelo fluxo do bot para localizar status, desvios e pontos de atencao.",
"Use a telemetria para acompanhar volume, latencia e saude do atendimento.",
"A area de proximas melhorias resume o que ainda entra antes dos dashboards completos.",
),
)
def _build_system_configuration_view(
request: Request,
settings: AdminSettings,
) -> AdminSystemConfigurationPageView:
return AdminSystemConfigurationPageView(
app_name=settings.admin_app_name,
title="Configuracoes do sistema",
subtitle=(
"Visao unica do catalogo funcional, das regras do atendimento e das protecoes do painel."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
overview_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration"),
runtime_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/runtime"),
security_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/security"),
model_runtimes_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/model-runtimes"),
functional_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/functional"),
functional_detail_base=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/functional"),
bot_governance_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/system/configuration/functional/bot-governance"),
access_notes=(
"A equipe interna ja consegue consultar o catalogo funcional e os ajustes do atendimento nesta tela.",
"Detalhes mais sensiveis do ambiente continuam reservados para perfis com permissao elevada.",
"Toda a superficie segue somente leitura nesta etapa.",
),
governance_notes=(
"Configuracoes do atendimento e da geracao de tools aparecem separadas para evitar confusao.",
"Os ajustes do bot continuam bloqueados para escrita direta nas tabelas operacionais.",
"O foco aqui e leitura clara do estado atual antes da futura tela de edicao.",
),
)
def _build_collaborator_management_view(
request: Request,
settings: AdminSettings,
) -> AdminCollaboratorManagementPageView:
password_requirements = []
if settings.admin_auth_password_require_uppercase:
password_requirements.append("maiuscula")
if settings.admin_auth_password_require_lowercase:
password_requirements.append("minuscula")
if settings.admin_auth_password_require_digit:
password_requirements.append("digito")
if settings.admin_auth_password_require_symbol:
password_requirements.append("simbolo")
password_policy_label = (
f"Minimo de {settings.admin_auth_password_min_length} caracteres"
+ (f" com {', '.join(password_requirements)}." if password_requirements else ".")
)
return AdminCollaboratorManagementPageView(
app_name=settings.admin_app_name,
title="Gestao de colaboradores",
subtitle=(
"Tela exclusiva de diretor para organizar a equipe interna, criar novos acessos administrativos e controlar rapidamente quem segue ativo no painel."
),
environment=settings.admin_environment,
version=settings.admin_version,
dashboard_href=str(request.url_for("panel_home")),
collection_endpoint=_build_prefixed_path(settings.admin_api_prefix, "/panel/colaboradores"),
password_policy_label=password_policy_label,
onboarding_notes=(
"Novos acessos nascem sempre com papel de colaborador.",
"A senha inicial ja respeita a mesma politica do login administrativo.",
"Ativar ou desativar colaborador nao exige acesso direto ao banco.",
),
governance_notes=(
"Somente diretor acessa esta tela e as rotas de gestao de colaboradores.",
"Cada criacao e alteracao de status gera trilha de auditoria administrativa.",
"A conta de diretor continua fora deste fluxo para evitar mudancas acidentais na governanca principal.",
),
)
def _redirect_to_route(request: Request, route_name: str) -> RedirectResponse:
return RedirectResponse(url=str(request.url_for(route_name)), status_code=302)
def _resolve_settings(request: Request) -> AdminSettings:
app_settings = getattr(request.app.state, "admin_settings", None)
if isinstance(app_settings, AdminSettings):
return app_settings
return get_admin_settings()
def _build_prefixed_path(api_prefix: str, path: str) -> str:
normalized_prefix = api_prefix.rstrip("/")
normalized_path = path if path.startswith("/") else f"/{path}"
if not normalized_prefix:
return normalized_path
if normalized_path == "/":
return f"{normalized_prefix}/"
return f"{normalized_prefix}{normalized_path}"