Liga o adapter SMTP existente (packages/integrations, já era fail-soft) a um consumidor de verdade e adiciona o fluxo completo de recuperação de senha, faltante desde a Fase 1. - UsersService.invite() agora envia e-mail com a senha temporária (mantém o retorno na resposta também — dupla entrega, eden.md §4.8). - POST /v1/auth/forgot-password + POST /v1/auth/reset-password: token de 256 bits, só o hash SHA-256 persistido (users.reset_token_hash/ reset_token_expires_at — colunas que já existiam desde a baseline da Fase 1, nunca usadas até agora), validade de 1h, uso único, revoga todas as sessões ativas do usuário ao concluir. Mesma disciplina anti-enumeração do login: resposta idêntica para e-mail cadastrado ou não. - Frontend: link "Esqueci minha senha" no LoginPage, novas ForgotPasswordPage/ResetPasswordPage. Teste de integração novo (password-reset.spec.ts, 3 testes) usa vi.mock para interceptar sendMail e recuperar o token real do corpo do e-mail — única forma de testar o caminho feliz de ponta a ponta, já que o token nunca é devolvido na resposta da API. Suíte completa: 33/33 (30→33). pnpm turbo run typecheck build (28/28). Testado ao vivo através do proxy same-origin: anti-enumeração confirmada com curl real, token gravado no Postgres, token bogus rejeitado (400). Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
147 lines
9.2 KiB
TypeScript
147 lines
9.2 KiB
TypeScript
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
|
import { AuthProvider } from "./lib/auth/AuthContext.js";
|
|
import { AuthenticatedLayout } from "./routes/AuthenticatedLayout.js";
|
|
import { DashboardPage } from "./routes/DashboardPage.js";
|
|
import { LoginPage } from "./routes/LoginPage.js";
|
|
import { ProtectedRoute } from "./routes/ProtectedRoute.js";
|
|
import { CompaniesListPage } from "./routes/companies/CompaniesListPage.js";
|
|
import { CompanyFormPage } from "./routes/companies/CompanyFormPage.js";
|
|
import { ProductsListPage } from "./routes/products/ProductsListPage.js";
|
|
import { ProductFormPage } from "./routes/products/ProductFormPage.js";
|
|
import { ResellersListPage } from "./routes/resellers/ResellersListPage.js";
|
|
import { ResellerFormPage } from "./routes/resellers/ResellerFormPage.js";
|
|
import { LeadsListPage } from "./routes/crm/LeadsListPage.js";
|
|
import { LeadFormPage } from "./routes/crm/LeadFormPage.js";
|
|
import { ConvertLeadPage } from "./routes/crm/ConvertLeadPage.js";
|
|
import { OpportunitiesListPage } from "./routes/crm/OpportunitiesListPage.js";
|
|
import { OpportunityFormPage } from "./routes/crm/OpportunityFormPage.js";
|
|
import { QuotesListPage } from "./routes/quotes/QuotesListPage.js";
|
|
import { QuoteFormPage } from "./routes/quotes/QuoteFormPage.js";
|
|
import { QuoteDetailPage } from "./routes/quotes/QuoteDetailPage.js";
|
|
import { ClientRegistrationsListPage } from "./routes/client-registrations/ClientRegistrationsListPage.js";
|
|
import { ClientRegistrationFormPage } from "./routes/client-registrations/ClientRegistrationFormPage.js";
|
|
import { ClientRegistrationDetailPage } from "./routes/client-registrations/ClientRegistrationDetailPage.js";
|
|
import { ContractsListPage } from "./routes/contracts/ContractsListPage.js";
|
|
import { ContractFormPage } from "./routes/contracts/ContractFormPage.js";
|
|
import { ContractDetailPage } from "./routes/contracts/ContractDetailPage.js";
|
|
import { WarehousesListPage } from "./routes/inventory/WarehousesListPage.js";
|
|
import { WarehouseFormPage } from "./routes/inventory/WarehouseFormPage.js";
|
|
import { AssetsListPage } from "./routes/inventory/AssetsListPage.js";
|
|
import { AssetFormPage } from "./routes/inventory/AssetFormPage.js";
|
|
import { AssetDetailPage } from "./routes/inventory/AssetDetailPage.js";
|
|
import { MovementsListPage } from "./routes/inventory/MovementsListPage.js";
|
|
import { ReservationsListPage } from "./routes/inventory/ReservationsListPage.js";
|
|
import { SubscriptionsListPage } from "./routes/billing/SubscriptionsListPage.js";
|
|
import { SubscriptionFormPage } from "./routes/billing/SubscriptionFormPage.js";
|
|
import { BillingRunsListPage } from "./routes/billing/BillingRunsListPage.js";
|
|
import { BillingRunFormPage } from "./routes/billing/BillingRunFormPage.js";
|
|
import { BillingRunDetailPage } from "./routes/billing/BillingRunDetailPage.js";
|
|
import { InvoicesListPage } from "./routes/billing/InvoicesListPage.js";
|
|
import { NcmListPage } from "./routes/fiscal/NcmListPage.js";
|
|
import { CfopListPage } from "./routes/fiscal/CfopListPage.js";
|
|
import { MunicipalitiesListPage } from "./routes/fiscal/MunicipalitiesListPage.js";
|
|
import { OperationNaturesListPage } from "./routes/fiscal/OperationNaturesListPage.js";
|
|
import { TicketsListPage } from "./routes/support/TicketsListPage.js";
|
|
import { TicketFormPage } from "./routes/support/TicketFormPage.js";
|
|
import { TicketDetailPage } from "./routes/support/TicketDetailPage.js";
|
|
import { TemplatesListPage } from "./routes/documents/TemplatesListPage.js";
|
|
import { TemplateFormPage } from "./routes/documents/TemplateFormPage.js";
|
|
import { TemplateDetailPage } from "./routes/documents/TemplateDetailPage.js";
|
|
import { GenerationsListPage } from "./routes/documents/GenerationsListPage.js";
|
|
import { GenerationFormPage } from "./routes/documents/GenerationFormPage.js";
|
|
import { DevicesListPage } from "./routes/hr-timeclock/DevicesListPage.js";
|
|
import { DeviceFormPage } from "./routes/hr-timeclock/DeviceFormPage.js";
|
|
import { AfdImportPage } from "./routes/hr-timeclock/AfdImportPage.js";
|
|
import { AfdRecordsListPage } from "./routes/hr-timeclock/AfdRecordsListPage.js";
|
|
import { ForgotPasswordPage } from "./routes/ForgotPasswordPage.js";
|
|
import { ResetPasswordPage } from "./routes/ResetPasswordPage.js";
|
|
|
|
export function App() {
|
|
return (
|
|
<BrowserRouter>
|
|
<AuthProvider>
|
|
<Routes>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
|
<Route path="/reset-password" element={<ResetPasswordPage />} />
|
|
<Route element={<ProtectedRoute />}>
|
|
<Route element={<AuthenticatedLayout />}>
|
|
<Route path="/" element={<DashboardPage />} />
|
|
|
|
<Route path="/companies" element={<CompaniesListPage />} />
|
|
<Route path="/companies/new" element={<CompanyFormPage />} />
|
|
<Route path="/companies/:id/edit" element={<CompanyFormPage />} />
|
|
|
|
<Route path="/products" element={<ProductsListPage />} />
|
|
<Route path="/products/new" element={<ProductFormPage />} />
|
|
<Route path="/products/:id/edit" element={<ProductFormPage />} />
|
|
|
|
<Route path="/resellers" element={<ResellersListPage />} />
|
|
<Route path="/resellers/new" element={<ResellerFormPage />} />
|
|
<Route path="/resellers/:id/edit" element={<ResellerFormPage />} />
|
|
|
|
<Route path="/leads" element={<LeadsListPage />} />
|
|
<Route path="/leads/new" element={<LeadFormPage />} />
|
|
<Route path="/leads/:id/edit" element={<LeadFormPage />} />
|
|
<Route path="/leads/:id/convert" element={<ConvertLeadPage />} />
|
|
|
|
<Route path="/opportunities" element={<OpportunitiesListPage />} />
|
|
<Route path="/opportunities/new" element={<OpportunityFormPage />} />
|
|
<Route path="/opportunities/:id/edit" element={<OpportunityFormPage />} />
|
|
|
|
<Route path="/quotes" element={<QuotesListPage />} />
|
|
<Route path="/quotes/new" element={<QuoteFormPage />} />
|
|
<Route path="/quotes/:id" element={<QuoteDetailPage />} />
|
|
|
|
<Route path="/client-registrations" element={<ClientRegistrationsListPage />} />
|
|
<Route path="/client-registrations/new" element={<ClientRegistrationFormPage />} />
|
|
<Route path="/client-registrations/:id" element={<ClientRegistrationDetailPage />} />
|
|
|
|
<Route path="/contracts" element={<ContractsListPage />} />
|
|
<Route path="/contracts/new" element={<ContractFormPage />} />
|
|
<Route path="/contracts/:id" element={<ContractDetailPage />} />
|
|
|
|
<Route path="/inventory/assets" element={<AssetsListPage />} />
|
|
<Route path="/inventory/assets/new" element={<AssetFormPage />} />
|
|
<Route path="/inventory/assets/:id" element={<AssetDetailPage />} />
|
|
<Route path="/inventory/warehouses" element={<WarehousesListPage />} />
|
|
<Route path="/inventory/warehouses/new" element={<WarehouseFormPage />} />
|
|
<Route path="/inventory/warehouses/:id/edit" element={<WarehouseFormPage />} />
|
|
<Route path="/inventory/movements" element={<MovementsListPage />} />
|
|
<Route path="/inventory/reservations" element={<ReservationsListPage />} />
|
|
|
|
<Route path="/billing/subscriptions" element={<SubscriptionsListPage />} />
|
|
<Route path="/billing/subscriptions/new" element={<SubscriptionFormPage />} />
|
|
<Route path="/billing/runs" element={<BillingRunsListPage />} />
|
|
<Route path="/billing/runs/new" element={<BillingRunFormPage />} />
|
|
<Route path="/billing/runs/:id" element={<BillingRunDetailPage />} />
|
|
<Route path="/billing/invoices" element={<InvoicesListPage />} />
|
|
|
|
<Route path="/fiscal/ncm" element={<NcmListPage />} />
|
|
<Route path="/fiscal/cfop" element={<CfopListPage />} />
|
|
<Route path="/fiscal/municipalities" element={<MunicipalitiesListPage />} />
|
|
<Route path="/fiscal/operation-natures" element={<OperationNaturesListPage />} />
|
|
|
|
<Route path="/support/tickets" element={<TicketsListPage />} />
|
|
<Route path="/support/tickets/new" element={<TicketFormPage />} />
|
|
<Route path="/support/tickets/:id" element={<TicketDetailPage />} />
|
|
|
|
<Route path="/documents/templates" element={<TemplatesListPage />} />
|
|
<Route path="/documents/templates/new" element={<TemplateFormPage />} />
|
|
<Route path="/documents/templates/:id" element={<TemplateDetailPage />} />
|
|
<Route path="/documents/generations" element={<GenerationsListPage />} />
|
|
<Route path="/documents/generations/new" element={<GenerationFormPage />} />
|
|
|
|
<Route path="/hr-timeclock/devices" element={<DevicesListPage />} />
|
|
<Route path="/hr-timeclock/devices/new" element={<DeviceFormPage />} />
|
|
<Route path="/hr-timeclock/devices/:id/edit" element={<DeviceFormPage />} />
|
|
<Route path="/hr-timeclock/afd-imports" element={<AfdImportPage />} />
|
|
<Route path="/hr-timeclock/afd-records" element={<AfdRecordsListPage />} />
|
|
</Route>
|
|
</Route>
|
|
</Routes>
|
|
</AuthProvider>
|
|
</BrowserRouter>
|
|
);
|
|
}
|