forlogic-core 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.note/memory/features/crud-defaults-batteries-included.md +1 -1
- package/dist/auth/services/TokenManager.d.ts +0 -1
- package/dist/components/ui/dialog-wizard.d.ts +82 -0
- package/dist/components/ui/input-group.d.ts +1 -1
- package/dist/components/ui/input.d.ts +5 -23
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/sidebar.d.ts +1 -1
- package/dist/components/ui/step-selector.d.ts +11 -9
- package/dist/components/ui/textarea.d.ts +1 -0
- package/dist/crud/components/CrudTable.d.ts +3 -1
- package/dist/crud/createCrudPage.d.ts +2 -0
- package/dist/crud/hooks/useColumnManager.d.ts +3 -1
- package/dist/exports/ui.d.ts +1 -0
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +3 -0
- package/docs/design-system/README.md +2 -2
- package/docs/design-system/buttons-actions.md +6 -0
- package/docs/design-system/domain.md +10 -0
- package/docs/design-system/inputs.md +20 -0
- package/docs/design-system/navigation.md +5 -0
- package/docs/design-system/notifications-feedback.md +2 -0
- package/package.json +1 -1
- package/dist/assets/AccordionDoc-CGNlubG3.js +0 -31
- package/dist/assets/ActionButtonDoc-CYtkXR0k.js +0 -47
- package/dist/assets/ActionPlanDoc-BSuPRftQ.js +0 -65
- package/dist/assets/AlertDoc-Cpvxneqg.js +0 -37
- package/dist/assets/AliasUrlDoc-DIpUWf4Y.js +0 -189
- package/dist/assets/AppHeaderDoc-DNQErj_t.js +0 -74
- package/dist/assets/AppSidebarDoc-DkeQarDu.js +0 -221
- package/dist/assets/ApprovalFlowDoc-8YgXbhKJ.js +0 -31
- package/dist/assets/AuditLogDoc-BBvNcHIo.js +0 -67
- package/dist/assets/AuditTrailDoc-DgFHO-uo.js +0 -17
- package/dist/assets/AuthDoc-WIA_Aetl.js +0 -200
- package/dist/assets/AvatarDoc-B6go1C1T.js +0 -11
- package/dist/assets/BadgeDoc-BONhfqB_.js +0 -36
- package/dist/assets/BaseFormDoc-CuyUArcj.js +0 -169
- package/dist/assets/BodyContentDoc-CterHC1E.js +0 -83
- package/dist/assets/BreadcrumbDoc-Dwn9nLeO.js +0 -75
- package/dist/assets/ButtonDoc-BOjRseZT.js +0 -41
- package/dist/assets/ButtonGroupDoc-8IS6PPh4.js +0 -7
- package/dist/assets/CalendarDoc-CMwIEqgT.js +0 -81
- package/dist/assets/CardDoc-BZz1CVg2.js +0 -49
- package/dist/assets/ChartDoc-B5vZVtqD.js +0 -76
- package/dist/assets/CheckboxDoc-lAbYO9I5.js +0 -55
- package/dist/assets/ColorPickerDoc-Dpsprp4N.js +0 -10
- package/dist/assets/ColorsFoundationDoc-CCHeSL3p.js +0 -13
- package/dist/assets/ComboTreeDoc-D4dTkIt-.js +0 -46
- package/dist/assets/ComboboxDoc-CqqZPvZq.js +0 -134
- package/dist/assets/ComponentDocTemplate-CQbBhfvZ.js +0 -1
- package/dist/assets/ContextMenuDoc-D3jC-MVA.js +0 -182
- package/dist/assets/ContextsDoc-XFH0-JdS.js +0 -211
- package/dist/assets/CreateCrudPageDoc-CpuiWI-g.js +0 -106
- package/dist/assets/CrudActionBarDoc-wuBGXD9Y.js +0 -112
- package/dist/assets/CrudGridDoc-BYWqSXBH.js +0 -85
- package/dist/assets/CrudOverviewDoc-B_bk2a2t.js +0 -14
- package/dist/assets/CrudPrimitivesDoc-CxaTB94A.js +0 -164
- package/dist/assets/CrudTableDoc-Dga1VgCu.js +0 -113
- package/dist/assets/CustomFormFieldsDoc-C1hwwSl3.js +0 -33
- package/dist/assets/DashboardFormDoc-BUDCmrMl.js +0 -49
- package/dist/assets/DashboardGeneralViewDoc-Cyg1SIiG.js +0 -71
- package/dist/assets/DashboardGridDoc-BavePiRF.js +0 -49
- package/dist/assets/DashboardListDoc-CLyMA6UK.js +0 -37
- package/dist/assets/DashboardOverviewDoc-DRVvNIF1.js +0 -35
- package/dist/assets/DashboardPanelRendererDoc--mfwb8Nc.js +0 -60
- package/dist/assets/DashboardPanelsBasicDoc-BQ2V_52D.js +0 -62
- package/dist/assets/DashboardPanelsCartesianDoc-sy-hcVQY.js +0 -75
- package/dist/assets/DashboardPanelsSpecialDoc-DsIUCRRP.js +0 -83
- package/dist/assets/DashboardViewDoc-CtlCNlEF.js +0 -45
- package/dist/assets/DataListDoc-DUy88lCQ.js +0 -13
- package/dist/assets/DesignSystemHome-DHl9YtbH.js +0 -1
- package/dist/assets/DialogDoc-CMQqnTV-.js +0 -981
- package/dist/assets/DropdownMenuDoc-S7X9csGt.js +0 -175
- package/dist/assets/ElectronicSignatureDialogDoc-BfithaL_.js +0 -57
- package/dist/assets/EmptyStateDoc-CHGCiGIk.js +0 -35
- package/dist/assets/EnvironmentsDoc-DZHJZ2nm.js +0 -96
- package/dist/assets/ErrorBoundaryDoc-DoaAg68p.js +0 -111
- package/dist/assets/ExampleActionPlanPage-C0fIMZCD.js +0 -1
- package/dist/assets/ExampleAppDoc-DzIU81Fn.js +0 -1
- package/dist/assets/ExampleCard-DuLrb3t-.js +0 -1
- package/dist/assets/ExampleCrudReportsPage-M0pz6tdM.js +0 -1
- package/dist/assets/ExampleDashboardPage-CRG5r3Vw.js +0 -1
- package/dist/assets/ExampleIdeasPage-I84ZMLY4.js +0 -1
- package/dist/assets/ExampleImportWizardPage-h4YqrrSe.js +0 -1
- package/dist/assets/ExampleSettingsPage-CwdWqoaP.js +0 -1
- package/dist/assets/FileUploadDoc-9-UujFNX.js +0 -34
- package/dist/assets/FilterBar-DDTqqUfZ.js +0 -1
- package/dist/assets/FormDoc-CVES6n3d.js +0 -81
- package/dist/assets/FoundationOverview-DT0u11Gz.js +0 -1
- package/dist/assets/GridDoc-CbHFSILF.js +0 -28
- package/dist/assets/HooksDoc-Ctxdk6Wq.js +0 -665
- package/dist/assets/HoverCardDoc-8Wkaafdj.js +0 -31
- package/dist/assets/I18nDoc-D3Q2m7ik.js +0 -167
- package/dist/assets/IconPickerDoc-DZ26Gdpg.js +0 -10
- package/dist/assets/IconsFoundationDoc-xOxtC7CW.js +0 -33
- package/dist/assets/InputDoc-BhztAiuJ.js +0 -211
- package/dist/assets/LabelDoc-A4hmTRRV.js +0 -42
- package/dist/assets/LeadershipDoc-CqOSfWsP.js +0 -452
- package/dist/assets/MediaDoc-C78gvC8p.js +0 -459
- package/dist/assets/MenubarDoc-DCnmd2tO.js +0 -165
- package/dist/assets/ModuleAccessDoc-CmD5nHDp.js +0 -153
- package/dist/assets/ModulesDialogDoc-DVit1CA-.js +0 -46
- package/dist/assets/MultiselectPermissionsDoc-tlJMs04L.js +0 -34
- package/dist/assets/NavigationMenuDoc-q1fbc89j.js +0 -116
- package/dist/assets/OnboardingDialogDoc-3A3eBYrq.js +0 -55
- package/dist/assets/PaginationDoc-B8-bMz5J.js +0 -27
- package/dist/assets/PaginationDoc-BkGdxHL3.js +0 -98
- package/dist/assets/PlacesDoc-CKPO6ATs.js +0 -226
- package/dist/assets/PopoverDoc-CJPU4Ags.js +0 -64
- package/dist/assets/ProgressDoc-CpjbTL4o.js +0 -29
- package/dist/assets/QualiexUserFieldDoc-DDwumlRw.js +0 -149
- package/dist/assets/RadioGroupDoc-D6tSZz8G.js +0 -57
- package/dist/assets/RadiusDoc-B4xSnajw.js +0 -7
- package/dist/assets/ReportRequestListDoc-C0LIaU8P.js +0 -15
- package/dist/assets/RequiredFieldsCounterDoc-COesoSdx.js +0 -58
- package/dist/assets/ResizableDoc-CW0-XQuB.js +0 -104
- package/dist/assets/RichTextEditorDoc-C8c_XA9P.js +0 -24
- package/dist/assets/ScrollAreaDoc-BxtoAPaZ.js +0 -28
- package/dist/assets/SecurityDoc-wOVqpg2F.js +0 -204
- package/dist/assets/SelectDoc-C75gtY9D.js +0 -80
- package/dist/assets/SeparatorDoc-BjQBPB1P.js +0 -4
- package/dist/assets/ServicesDoc-CXTctwBl.js +0 -308
- package/dist/assets/ShadowsDoc-C6Lw8_x2.js +0 -9
- package/dist/assets/SignDoc-Bh5ZUg5x.js +0 -66
- package/dist/assets/SkeletonDoc-rTLGK5VE.js +0 -54
- package/dist/assets/SliderDoc-JMAMDub7.js +0 -41
- package/dist/assets/SpacingDoc-RljOrpwA.js +0 -12
- package/dist/assets/SplitButtonDoc-CvShUW3w.js +0 -53
- package/dist/assets/StepSelectorDoc-C-nAap9H.js +0 -41
- package/dist/assets/SwitchDoc-DLnqmkPr.js +0 -56
- package/dist/assets/TableDoc-B8EpWLVg.js +0 -128
- package/dist/assets/TabsDoc-DIBtl_uC.js +0 -42
- package/dist/assets/TeamSelectorDoc-B7OnCbL7.js +0 -10
- package/dist/assets/TermsOfUseDoc-Bb-pw08s.js +0 -16
- package/dist/assets/TextareaDoc-DGnqMqEC.js +0 -46
- package/dist/assets/ToastDoc-DjYyc7ae.js +0 -157
- package/dist/assets/ToggleDoc-C9ZOVjkY.js +0 -51
- package/dist/assets/TooltipDoc-BEx4l9-i.js +0 -58
- package/dist/assets/TruncatedCellDoc-BbV1bRSY.js +0 -12
- package/dist/assets/TypographyFoundationDoc-CUDYjRo9.js +0 -7
- package/dist/assets/UpdatesNotificationDoc-7nyjzLMJ.js +0 -29
- package/dist/assets/UsersGroupsSelectorDoc-C0KlTAL5.js +0 -18
- package/dist/assets/UtilitiesDoc-DGxaHVV1.js +0 -145
- package/dist/assets/ViewerDialogsDoc-CnTPTEz0.js +0 -1
- package/dist/assets/blocks-B6LrJeAM.js +0 -1
- package/dist/assets/building-DeVappnD.js +0 -1
- package/dist/assets/calendar-days-BQ0na5kM.js +0 -1
- package/dist/assets/check-check-C_-PJCJa.js +0 -1
- package/dist/assets/circle-plus-CpIcep-O.js +0 -1
- package/dist/assets/circle-x-jPpBPew0.js +0 -1
- package/dist/assets/clipboard-list-CXNPdciZ.js +0 -1
- package/dist/assets/cloud-upload-BEjzumjl.js +0 -1
- package/dist/assets/crown-CqNsQIsm.js +0 -1
- package/dist/assets/date-picker-BW3eGOe_.js +0 -1
- package/dist/assets/disabled-menu-item-C2YaMvSt.js +0 -1
- package/dist/assets/drawer-D5rflIcD.js +0 -3
- package/dist/assets/file-braces-DFb5X9so.js +0 -1
- package/dist/assets/file-pen-line-CyUGKkEN.js +0 -1
- package/dist/assets/git-branch-BcXv9mpp.js +0 -1
- package/dist/assets/globe-CpMIWAcv.js +0 -1
- package/dist/assets/hash-cQWdKjya.js +0 -1
- package/dist/assets/hourglass-BahQ3eDv.js +0 -1
- package/dist/assets/hover-card-R66N85sZ.js +0 -1
- package/dist/assets/iframe-dialog-V0mW5aBb.js +0 -1
- package/dist/assets/index-DkiftrvI.js +0 -352
- package/dist/assets/index-nmBjO9Th.css +0 -1
- package/dist/assets/life-buoy-ByXiPddz.js +0 -1
- package/dist/assets/loading-state-Cb5_t5uE.js +0 -1
- package/dist/assets/lucide-react-Cp3Yw3Zm.js +0 -1
- package/dist/assets/package-B3-pVvPM.js +0 -1
- package/dist/assets/pen-Bi_lmmKT.js +0 -1
- package/dist/assets/pin-DVsSl8QA.js +0 -1
- package/dist/assets/printer-BnJ8B6m-.js +0 -1
- package/dist/assets/radio-group-BHAaNGsm.js +0 -1
- package/dist/assets/server-CtzFTfKR.js +0 -1
- package/dist/assets/share-2-Dv8Do445.js +0 -1
- package/dist/assets/shield-check-CFXjOV_w.js +0 -1
- package/dist/assets/shield-x-DJTRfVux.js +0 -1
- package/dist/assets/slider-v9tXBSnB.js +0 -1
- package/dist/assets/smartphone-BSNR60L7.js +0 -1
- package/dist/assets/step-selector-ATTh_9Wa.js +0 -1
- package/dist/assets/text-align-start-qE-MbYYw.js +0 -1
- package/dist/assets/thumbs-up-D_XIW_uX.js +0 -1
- package/dist/assets/trash-DTWQwpwA.js +0 -1
- package/dist/assets/trending-up-jip5-leJ.js +0 -1
- package/dist/assets/useMockCrud-CN4vjyOZ.js +0 -1
- package/dist/assets/user-check-BlH3EDWK.js +0 -1
- package/dist/assets/user-plus-BqwXwD-c.js +0 -1
- package/dist/index.html +0 -33
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import{j as e,a,b as t,c as r,d as s,a4 as l,A as n,B as c,ei as m,bs as u}from"./index-DkiftrvI.js";import{C as x}from"./ComponentDocTemplate-CQbBhfvZ.js";import{C as p}from"./circle-x-jPpBPew0.js";import"./ExampleCard-DuLrb3t-.js";function h(){return e.jsxs("div",{className:"space-y-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"Rota Raiz e Catch-All"}),e.jsxs("p",{className:"text-sm text-muted-foreground",children:["A rota ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"/"})," ",e.jsx("strong",{children:"não"})," corresponde ao padrão ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"/:alias/*"}),", portanto o ",e.jsx("code",{children:"AliasRouteGuard"}),' nunca é executado. É obrigatório adicionar um redirect programático para evitar que a página fique presa em "Redirecionando...".']}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-destructive",children:"❌ Errado — Texto estático"})}),e.jsxs(s,{children:[e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`// Nunca redireciona — fica preso para sempre
|
|
2
|
-
<Route path="*" element={
|
|
3
|
-
<div>Redirecionando...</div>
|
|
4
|
-
} />`}),e.jsx("p",{className:"text-xs text-muted-foreground mt-2",children:'Texto estático não executa navegação. O usuário vê "Redirecionando..." indefinidamente.'})]})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-green-600 dark:text-green-400",children:"✅ Correto — Navigate programático"})}),e.jsx(s,{children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`// Redireciona para /:alias/defaultPath
|
|
5
|
-
<Route path="/" element={
|
|
6
|
-
<ProtectedRoute>
|
|
7
|
-
<RootRedirect />
|
|
8
|
-
</ProtectedRoute>
|
|
9
|
-
} />
|
|
10
|
-
|
|
11
|
-
// Catch-all para rotas inválidas
|
|
12
|
-
<Route path="*" element={
|
|
13
|
-
<CatchAllRedirect />
|
|
14
|
-
} />`})})]})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-base",children:"Implementação"})}),e.jsxs(s,{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2",children:"RootRedirect"}),e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`function RootRedirect() {
|
|
15
|
-
const { alias } = useAuth();
|
|
16
|
-
if (!alias) return null; // ProtectedRoute garante auth
|
|
17
|
-
return <Navigate to={\`/\${alias}/i\`} replace />;
|
|
18
|
-
}`})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2",children:"CatchAllRedirect"}),e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`function CatchAllRedirect() {
|
|
19
|
-
const { alias, isAuthenticated } = useAuth();
|
|
20
|
-
if (isAuthenticated && alias) {
|
|
21
|
-
return <Navigate to={\`/\${alias}/i\`} replace />;
|
|
22
|
-
}
|
|
23
|
-
return <Navigate to="/login" replace />;
|
|
24
|
-
}`})]})]})]})]})}function g(){return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2 flex items-center gap-2",children:[e.jsx(l,{className:"h-5 w-5 text-yellow-500"}),"ProtectedRoute — Regra Crítica"]}),e.jsxs("p",{className:"text-sm text-muted-foreground",children:["O ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"ProtectedRoute"})," ",e.jsx("strong",{children:"não deve"})," exibir loading quando o usuário já está autenticado. Se o loading aparecer durante o ",e.jsx("code",{children:"switchUnit()"}),", o ",e.jsx("code",{children:"AliasRouteGuard"})," é desmontado, perdendo o estado interno do ",e.jsx("code",{children:"prevActiveAliasRef"})," e causando reversão da troca de unidade."]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-destructive",children:"❌ Errado"})}),e.jsxs(s,{children:[e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`// Desmonta o guard durante switchUnit!
|
|
25
|
-
if (isLoading) {
|
|
26
|
-
return <Loading />;
|
|
27
|
-
}`}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-2",children:[e.jsx("code",{children:"isLoading"})," fica ",e.jsx("code",{children:"true"})," durante ",e.jsx("code",{children:"switchUnit()"}),", desmontando o ",e.jsx("code",{children:"AliasRouteGuard"})," e seu ",e.jsx("code",{children:"prevActiveAliasRef"}),"."]})]})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-green-600 dark:text-green-400",children:"✅ Correto"})}),e.jsxs(s,{children:[e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`// Loading só na inicialização
|
|
28
|
-
if (isLoading && !isAuthenticated) {
|
|
29
|
-
return <Loading />;
|
|
30
|
-
}`}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-2",children:["O loading só aparece quando o usuário ainda não autenticou. Durante ",e.jsx("code",{children:"switchUnit()"}),", o guard permanece montado."]})]})]})]})]})}const j=[{title:"Alias ausente",icon:l,color:"text-yellow-500",badge:"redirect",badgeVariant:"outline",description:"URL sem alias → redireciona para /{aliasAtivo}/{path}",example:"/documents → /empresa-a/documents"},{title:"Alias diferente",icon:m,color:"text-blue-500",badge:"switchUnit",badgeVariant:"secondary",description:"Alias na URL ≠ ativo → Guard chama switchUnit() para sincronizar sessão com URL",example:"/empresa-b/documents (ativo: empresa-a) → switchUnit(empresa-b)"},{title:"Alias correto",icon:u,color:"text-green-500",badge:"render",badgeVariant:"default",description:"Alias coincide com o ativo → renderiza children",example:"/empresa-a/documents ✓"},{title:"Alias inválido",icon:p,color:"text-destructive",badge:"fallback",badgeVariant:"danger",description:"Alias não existe nas companies → redireciona para alias padrão",example:"/xyz-inexistente/documents → /empresa-a/documents"}];function f(){return e.jsxs("div",{className:"space-y-6",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-base",children:"Arquitetura: URL como Fonte Única de Verdade"})}),e.jsxs(s,{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-3 text-sm flex-wrap",children:[e.jsx("span",{className:"font-mono bg-muted/50 rounded px-2 py-1",children:"Seletor/Header"}),e.jsx(n,{className:"h-4 w-4 text-muted-foreground"}),e.jsxs("span",{className:"font-mono bg-muted/50 rounded px-2 py-1",children:["navigate(/","{alias}",")"]}),e.jsx(n,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"font-mono bg-muted/50 rounded px-2 py-1 text-yellow-600 dark:text-yellow-400",children:"URL muda"}),e.jsx(n,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"font-mono bg-muted/50 rounded px-2 py-1 text-primary",children:"Guard → switchUnit()"})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["O header ",e.jsx("strong",{children:"nunca"})," chama ",e.jsx("code",{children:"switchUnit"})," diretamente. Ele apenas navega para a URL com o novo alias. O ",e.jsx("code",{children:"AliasRouteGuard"})," detecta a mudança e sincroniza a sessão. Isso elimina race conditions entre dois writers concorrentes."]})]})]}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:j.map(i=>e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsxs(r,{className:"flex items-center gap-2 text-base",children:[e.jsx(i.icon,{className:`h-5 w-5 ${i.color}`}),i.title,e.jsx(c,{variant:i.badgeVariant,className:"ml-auto text-xs",children:i.badge})]})}),e.jsxs(s,{className:"space-y-2",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:i.description}),e.jsx("div",{className:"flex items-center gap-2 text-xs font-mono bg-muted/50 rounded px-3 py-2",children:e.jsx("span",{children:i.example})})]})]},i.title))})]})}function b(){const i=[{scenario:"URL sem alias",url:"/documents",action:"navigate(/{alias}/documents, replace)",result:"Redirect automático"},{scenario:"Alias ≠ ativo",url:"/empresa-b/docs",action:"switchUnit(empresa-b)",result:"Guard sincroniza sessão"},{scenario:"Troca via seletor",url:"/empresa-a/docs",action:"Header navega → URL muda → Guard switchUnit()",result:"Sessão segue URL"},{scenario:"Alias = ativo",url:"/empresa-a/docs",action:"Nenhuma",result:"Renderiza children"},{scenario:"Alias inexistente",url:"/xyz/docs",action:"navigate(/{aliasDefault}/docs, replace)",result:"Fallback para alias ativo"}];return e.jsxs("div",{className:"space-y-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"Tabela de Comportamento"}),e.jsx("div",{className:"rounded-lg border overflow-x-auto",children:e.jsxs("table",{className:"w-full",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b bg-muted/50",children:[e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Cenário"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"URL"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Ação do Guard"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Resultado"})]})}),e.jsx("tbody",{children:i.map((o,d)=>e.jsxs("tr",{className:"border-b last:border-0",children:[e.jsx("td",{className:"px-4 py-3 text-sm font-medium",children:o.scenario}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:o.url}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:o.action}),e.jsx("td",{className:"px-4 py-3 text-sm",children:o.result})]},d))})]})})]})}function N(){const i=[{group:"Saber Gestão",name:"Competências",url:"https://competencias.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Saber Gestão",name:"Desempenho",url:"https://desempenho.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Saber Gestão",name:"Educação",url:"https://educacao.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Saber Gestão",name:"PDI",url:"https://pdi.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Saber Gestão",name:"Pulso",url:"https://nr1pulso.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Saber Gestão",name:"Treinamentos",url:"https://treinamentos.sabergestao.com.br/{alias}",strategy:"/{alias} no path"},{group:"Qualiex",name:"OKR",url:"https://okr.qualiex.com/{alias}",strategy:"/{alias} no path"},{group:"Clássicos (v1)",name:"Docs, ISO, Ocorrências...",url:"https://docs.qualiex.com/...",strategy:"Sem alias (v1)"}];return e.jsxs("div",{className:"space-y-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"URLs por Módulo"}),e.jsxs("p",{className:"text-sm text-muted-foreground",children:["Módulos Saber Gestão e OKR usam ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"buildModuleUrl"})," para injetar o alias. Módulos clássicos (v1) não recebem alias no path."]}),e.jsx("div",{className:"rounded-lg border overflow-x-auto",children:e.jsxs("table",{className:"w-full",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b bg-muted/50",children:[e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Grupo"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Módulo"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Template da URL"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Estratégia"})]})}),e.jsx("tbody",{children:i.map((o,d)=>e.jsxs("tr",{className:"border-b last:border-0",children:[e.jsx("td",{className:"px-4 py-3 text-sm font-medium",children:o.group}),e.jsx("td",{className:"px-4 py-3 text-sm",children:o.name}),e.jsx("td",{className:"px-4 py-3 font-mono text-xs text-muted-foreground",children:o.url}),e.jsx("td",{className:"px-4 py-3 text-sm",children:e.jsx(c,{variant:o.strategy.includes("path")?"default":"outline",className:"text-xs",children:o.strategy})})]},d))})]})})]})}function R(){const i=["Atualizar forlogic-core para a versão mais recente","Importar AliasRouteGuard de forlogic-core","Envolver as rotas com /:alias como prefixo","Colocar AliasRouteGuard dentro de ProtectedRoute","Adicionar rota / com RootRedirect dentro de ProtectedRoute","Adicionar rota catch-all /* com CatchAllRedirect (Navigate programático)","Garantir que ProtectedRoute use isLoading && !isAuthenticated no loading","Testar: acessar URL sem alias deve redirecionar","Testar: acessar URL com alias diferente deve trocar unidade","Testar: troca via seletor deve atualizar URL"];return e.jsxs("div",{className:"space-y-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"Guia de Migração"}),e.jsx(a,{children:e.jsx(s,{className:"pt-6",children:e.jsx("ol",{className:"space-y-3",children:i.map((o,d)=>e.jsxs("li",{className:"flex items-start gap-3 text-sm",children:[e.jsx("span",{className:"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground text-xs font-medium",children:d+1}),e.jsx("span",{className:"pt-0.5",children:o})]},d))})})})]})}function v(){return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2 flex items-center gap-2",children:[e.jsx(l,{className:"h-5 w-5 text-yellow-500"}),"Pitfall — Navegação Interna sem Alias"]}),e.jsxs("p",{className:"text-sm text-muted-foreground",children:["Ao usar ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"navigate()"})," ou paths hardcoded dentro de componentes protegidos pelo ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"AliasRouteGuard"}),", o primeiro segmento da URL é interpretado como alias. Se o path não incluir o alias, o guard redireciona para o alias padrão."]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-destructive",children:"❌ Errado"})}),e.jsx(s,{children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`// Path hardcoded sem alias
|
|
31
|
-
navigate('/a/u');
|
|
32
|
-
navigate('/a/c');
|
|
33
|
-
|
|
34
|
-
// O guard interpreta "a" como alias
|
|
35
|
-
// → alias inválido → redireciona para /{alias}/i`})})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-green-600 dark:text-green-400",children:"✅ Correto"})}),e.jsx(s,{children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`const { alias } = useAuth();
|
|
36
|
-
|
|
37
|
-
// Prefixar com alias ativo
|
|
38
|
-
navigate(\`/\${alias}/a/u\`);
|
|
39
|
-
navigate(\`/\${alias}/a/c\`);
|
|
40
|
-
|
|
41
|
-
// O guard reconhece o alias válido
|
|
42
|
-
// → renderiza normalmente`})})]})]}),e.jsx(a,{children:e.jsx(s,{className:"pt-6",children:e.jsxs("p",{className:"text-sm",children:[e.jsx("strong",{children:"Regra:"})," Toda navegação interna (tabs, links, botões) em rotas protegidas pelo ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"AliasRouteGuard"})," deve usar ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"`/${alias}/path`"})," obtendo o alias via ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"useAuth()"}),"."]})})})]})}function L(){return e.jsxs(x,{title:"Alias via URL",description:"Sincronização bidirecional entre o alias (unidade/empresa) ativo na sessão e a URL do navegador. Garante que o alias esteja sempre presente na URL e que trocas de unidade sejam refletidas automaticamente.",component:e.jsx(f,{}),usage:`import { AliasRouteGuard } from 'forlogic-core';
|
|
43
|
-
import { ProtectedRoute } from 'forlogic-core';
|
|
44
|
-
|
|
45
|
-
// No App.tsx — todas as rotas sob /:alias
|
|
46
|
-
<Route element={
|
|
47
|
-
<ProtectedRoute>
|
|
48
|
-
<AliasRouteGuard>
|
|
49
|
-
<Outlet />
|
|
50
|
-
</AliasRouteGuard>
|
|
51
|
-
</ProtectedRoute>
|
|
52
|
-
}>
|
|
53
|
-
<Route path="/:alias/documents" element={<DocumentsPage />} />
|
|
54
|
-
<Route path="/:alias/documents/:id" element={<DocumentDetailPage />} />
|
|
55
|
-
<Route path="/:alias/settings" element={<SettingsPage />} />
|
|
56
|
-
|
|
57
|
-
{/* Catch-all: redireciona rotas sem alias */}
|
|
58
|
-
<Route path="/*" element={<AliasRedirect />} />
|
|
59
|
-
</Route>`,installation:"import { AliasRouteGuard, useAliasFromUrl, buildModuleUrl } from 'forlogic-core';",examples:[{title:"Uso do useAliasFromUrl",description:"Hook para acessar o alias da URL e validá-lo contra as companies do usuário. Útil para lógica customizada.",preview:e.jsx(a,{children:e.jsx(s,{className:"pt-6",children:e.jsxs("div",{className:"space-y-3 font-mono text-sm",children:[e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-muted-foreground",children:"urlAlias"}),e.jsx("span",{children:'"empresa-a"'})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-muted-foreground",children:"isAliasMismatch"}),e.jsx("span",{children:"false"})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-muted-foreground",children:"isValidAlias"}),e.jsx("span",{children:"true"})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-muted-foreground",children:"isMissing"}),e.jsx("span",{children:"false"})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-muted-foreground",children:"matchedCompany"}),e.jsx("span",{children:"{ id, alias, name }"})]})]})})}),code:`import { useAliasFromUrl } from 'forlogic-core';
|
|
60
|
-
|
|
61
|
-
function MyComponent() {
|
|
62
|
-
const {
|
|
63
|
-
urlAlias, // "empresa-a" | null
|
|
64
|
-
isAliasMismatch, // true se alias da URL ≠ alias ativo
|
|
65
|
-
isValidAlias, // true se alias existe nas companies
|
|
66
|
-
isMissing, // true se não há alias na URL
|
|
67
|
-
matchedCompany, // { id, alias, name } | null
|
|
68
|
-
} = useAliasFromUrl();
|
|
69
|
-
|
|
70
|
-
if (isMissing) return <p>Nenhum alias na URL</p>;
|
|
71
|
-
if (!isValidAlias) return <p>Alias inválido</p>;
|
|
72
|
-
|
|
73
|
-
return <p>Unidade: {matchedCompany?.name}</p>;
|
|
74
|
-
}`},{title:"Antes / Depois (Migração)",description:"Como adaptar as rotas de um projeto consumidor existente.",preview:e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-destructive",children:"❌ Antes"})}),e.jsx(s,{children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`<ProtectedRoute>
|
|
75
|
-
<Route path="/documents" />
|
|
76
|
-
<Route path="/settings" />
|
|
77
|
-
</ProtectedRoute>`})})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-green-600 dark:text-green-400",children:"✅ Depois"})}),e.jsx(s,{children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto",children:`<ProtectedRoute>
|
|
78
|
-
<AliasRouteGuard>
|
|
79
|
-
<Route path="/:alias/documents" />
|
|
80
|
-
<Route path="/:alias/settings" />
|
|
81
|
-
<Route path="/*" element={<AliasRedirect />} />
|
|
82
|
-
</AliasRouteGuard>
|
|
83
|
-
</ProtectedRoute>`})})]})]}),code:`// Antes — sem alias na URL
|
|
84
|
-
<Route element={<ProtectedRoute><Outlet /></ProtectedRoute>}>
|
|
85
|
-
<Route path="/documents" element={<DocumentsPage />} />
|
|
86
|
-
<Route path="/settings" element={<SettingsPage />} />
|
|
87
|
-
</Route>
|
|
88
|
-
|
|
89
|
-
// Depois — com alias na URL
|
|
90
|
-
<Route element={
|
|
91
|
-
<ProtectedRoute>
|
|
92
|
-
<AliasRouteGuard>
|
|
93
|
-
<Outlet />
|
|
94
|
-
</AliasRouteGuard>
|
|
95
|
-
</ProtectedRoute>
|
|
96
|
-
}>
|
|
97
|
-
<Route path="/:alias/documents" element={<DocumentsPage />} />
|
|
98
|
-
<Route path="/:alias/settings" element={<SettingsPage />} />
|
|
99
|
-
<Route path="/*" element={<AliasRedirect />} />
|
|
100
|
-
</Route>`},{title:"buildModuleUrl — Navegação Inter-módulos",description:"Helper centralizado que substitui {alias} em templates de URL. Usado internamente pelo ModulesDialog e ModuleAccessGuard.",preview:e.jsx(a,{children:e.jsxs(s,{className:"pt-6 space-y-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-xs font-semibold text-muted-foreground uppercase tracking-wider",children:"Entrada"}),e.jsx("div",{className:"font-mono text-sm bg-muted/50 rounded px-3 py-2",children:'buildModuleUrl("https://okr.qualiex.com/{alias}", "empresa-a")'})]}),e.jsx(n,{className:"h-4 w-4 text-muted-foreground mx-auto"}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-xs font-semibold text-muted-foreground uppercase tracking-wider",children:"Saída"}),e.jsx("div",{className:"font-mono text-sm bg-muted/50 rounded px-3 py-2 text-primary",children:"https://okr.qualiex.com/empresa-a"})]}),e.jsxs("div",{className:"border-t pt-3 space-y-1 text-xs text-muted-foreground",children:[e.jsx("p",{children:"• Sem alias → retorna a URL original sem alteração"}),e.jsx("p",{children:"• Substitui todas as ocorrências de {alias}"})]})]})}),code:`import { buildModuleUrl, handleExternalLink } from 'forlogic-core';
|
|
101
|
-
import { useAuth } from 'forlogic-core';
|
|
102
|
-
|
|
103
|
-
function MyComponent() {
|
|
104
|
-
const { alias } = useAuth();
|
|
105
|
-
|
|
106
|
-
// Uso direto
|
|
107
|
-
const url = buildModuleUrl('https://okr.qualiex.com/{alias}', alias);
|
|
108
|
-
// → "https://okr.qualiex.com/empresa-a"
|
|
109
|
-
|
|
110
|
-
// Sem alias → retorna template original
|
|
111
|
-
const fallback = buildModuleUrl('https://okr.qualiex.com/{alias}');
|
|
112
|
-
// → "https://okr.qualiex.com/{alias}"
|
|
113
|
-
|
|
114
|
-
// Com handleExternalLink
|
|
115
|
-
const handleClick = () => {
|
|
116
|
-
const moduleUrl = buildModuleUrl('https://competencias.sabergestao.com.br/{alias}', alias);
|
|
117
|
-
handleExternalLink(moduleUrl);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
return <button onClick={handleClick}>Abrir Competências</button>;
|
|
121
|
-
}`},{title:"Fluxo Interno do Guard (Decisão Única + Concorrência)",description:'O guard usa 1 effect de decisão + 1 effect de tracking. O switchUnit usa "latest request wins" para evitar reversões.',preview:e.jsxs("div",{className:"space-y-4",children:[e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm",children:"Navegação via URL"})}),e.jsx(s,{children:e.jsxs("div",{className:"flex items-center gap-2 text-xs font-mono flex-wrap",children:[e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"URL muda para /empresa-b"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"prevRef = activeAlias (estável)"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-green-100 dark:bg-green-900/30 rounded px-2 py-1 text-green-700 dark:text-green-400",children:"switchUnit(empresa-b)"})]})})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm",children:"Troca via Seletor"})}),e.jsx(s,{children:e.jsxs("div",{className:"flex items-center gap-2 text-xs font-mono flex-wrap",children:[e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"activeAlias muda"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"prevRef ≠ activeAlias"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-primary/10 rounded px-2 py-1 text-primary",children:"URL sincroniza (sem switchUnit)"})]})})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-muted-foreground",children:"Ordem dos Effects (crítica)"})}),e.jsx(s,{children:e.jsxs("div",{className:"space-y-2 text-xs",children:[e.jsxs("div",{className:"flex items-center gap-2 font-mono",children:[e.jsx("span",{className:"bg-blue-100 dark:bg-blue-900/30 rounded px-2 py-1 text-blue-700 dark:text-blue-400",children:"Effect 1: Decisão"}),e.jsx("span",{className:"text-muted-foreground",children:"lê prevRef (valor anterior)"})]}),e.jsxs("div",{className:"flex items-center gap-2 font-mono",children:[e.jsx("span",{className:"bg-violet-100 dark:bg-violet-900/30 rounded px-2 py-1 text-violet-700 dark:text-violet-400",children:"Effect 2: Tracking"}),e.jsx("span",{className:"text-muted-foreground",children:"atualiza prevRef = activeAlias"})]}),e.jsxs("p",{className:"text-muted-foreground mt-1",children:["O effect de tracking é declarado ",e.jsx("strong",{children:"depois"})," do de decisão, garantindo que a decisão sempre lê o valor anterior do ref."]})]})})]}),e.jsxs(a,{children:[e.jsx(t,{className:"pb-2",children:e.jsx(r,{className:"text-sm text-muted-foreground",children:'Concorrência: "Latest Request Wins"'})}),e.jsxs(s,{children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs font-mono flex-wrap",children:[e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"Guard: switchUnit(A)"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-muted/50 rounded px-2 py-1",children:"Seletor: switchUnit(B)"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-red-100 dark:bg-red-900/30 rounded px-2 py-1 text-red-700 dark:text-red-400",children:"A abortado"}),e.jsx(n,{className:"h-3 w-3 text-muted-foreground"}),e.jsx("span",{className:"bg-green-100 dark:bg-green-900/30 rounded px-2 py-1 text-green-700 dark:text-green-400",children:"B efetivado"})]}),e.jsxs("p",{className:"text-xs text-muted-foreground mt-2",children:["Cada ",e.jsx("code",{children:"switchUnit()"})," recebe um ",e.jsx("code",{children:"requestId"}),". Após cada ",e.jsx("code",{children:"await"}),", verifica se ainda é a requisição mais recente."]})]})]})]}),code:`// Guard: 1 effect de decisão + 1 effect de tracking
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
// Case 1: missing → redirect
|
|
124
|
-
// Case 2: invalid → redirect
|
|
125
|
-
// Case 3: mismatch
|
|
126
|
-
if (activeAlias !== prevActiveAliasRef.current) {
|
|
127
|
-
// Sessão mudou via UI → sync URL
|
|
128
|
-
navigate(buildAliasPath(activeAlias), { replace: true });
|
|
129
|
-
} else {
|
|
130
|
-
// URL mudou → switchUnit()
|
|
131
|
-
switchUnit(matchedCompany);
|
|
132
|
-
}
|
|
133
|
-
}, [urlAlias, activeAlias, ...]);
|
|
134
|
-
|
|
135
|
-
// DEPOIS do effect de decisão
|
|
136
|
-
useEffect(() => {
|
|
137
|
-
prevActiveAliasRef.current = activeAlias;
|
|
138
|
-
}, [activeAlias]);
|
|
139
|
-
|
|
140
|
-
// AuthContext: switchUnit com "latest request wins"
|
|
141
|
-
const switchRequestIdRef = useRef(0);
|
|
142
|
-
|
|
143
|
-
const switchUnit = useCallback(async (company) => {
|
|
144
|
-
if (company.alias === authState.alias) return;
|
|
145
|
-
const requestId = ++switchRequestIdRef.current;
|
|
146
|
-
|
|
147
|
-
const token = await generateToken(...);
|
|
148
|
-
if (switchRequestIdRef.current !== requestId) return;
|
|
149
|
-
|
|
150
|
-
updateAuthState({ alias: newAlias, ... });
|
|
151
|
-
}, [authState.alias, ...]);`},{title:"Estrutura Completa de Rotas (App.tsx)",description:"Exemplo completo mostrando a rota raiz com RootRedirect, rotas protegidas com AliasRouteGuard e catch-all com CatchAllRedirect.",preview:e.jsx(a,{children:e.jsx(s,{className:"pt-6",children:e.jsx("pre",{className:"text-xs font-mono bg-muted/50 rounded p-3 overflow-x-auto whitespace-pre-wrap",children:`<Routes>
|
|
152
|
-
<Route path="/login" element={<LoginPage />} />
|
|
153
|
-
<Route path="/callback" element={<CallbackPage />} />
|
|
154
|
-
|
|
155
|
-
{/* Rota raiz — redireciona para /:alias/i */}
|
|
156
|
-
<Route path="/" element={
|
|
157
|
-
<ProtectedRoute><RootRedirect /></ProtectedRoute>
|
|
158
|
-
} />
|
|
159
|
-
|
|
160
|
-
{/* Rotas protegidas com alias */}
|
|
161
|
-
<Route element={
|
|
162
|
-
<ProtectedRoute>
|
|
163
|
-
<AliasRouteGuard><Outlet /></AliasRouteGuard>
|
|
164
|
-
</ProtectedRoute>
|
|
165
|
-
}>
|
|
166
|
-
<Route path="/:alias/dashboard" element={<DashboardPage />} />
|
|
167
|
-
<Route path="/:alias/settings" element={<SettingsPage />} />
|
|
168
|
-
</Route>
|
|
169
|
-
|
|
170
|
-
{/* Catch-all — redireciona rotas inválidas */}
|
|
171
|
-
<Route path="*" element={<CatchAllRedirect />} />
|
|
172
|
-
</Routes>`})})}),code:`import { Navigate } from 'react-router-dom';
|
|
173
|
-
import { useAuth } from 'forlogic-core';
|
|
174
|
-
|
|
175
|
-
// Redireciona "/" para "/:alias/defaultPath"
|
|
176
|
-
function RootRedirect() {
|
|
177
|
-
const { alias } = useAuth();
|
|
178
|
-
if (!alias) return null; // ProtectedRoute garante auth
|
|
179
|
-
return <Navigate to={\`/\${alias}/i\`} replace />;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Catch-all: redireciona rotas inválidas
|
|
183
|
-
function CatchAllRedirect() {
|
|
184
|
-
const { alias, isAuthenticated } = useAuth();
|
|
185
|
-
if (isAuthenticated && alias) {
|
|
186
|
-
return <Navigate to={\`/\${alias}/i\`} replace />;
|
|
187
|
-
}
|
|
188
|
-
return <Navigate to="/login" replace />;
|
|
189
|
-
}`}],props:[{name:"children",type:"ReactNode",description:"Conteúdo protegido pelo guard (geralmente <Outlet />)"},{name:"paramName",type:"string",default:'"alias"',description:"Nome do param de rota do React Router (ex: :alias)"},{name:"urlAlias",type:"string | null",description:"(useAliasFromUrl) Alias extraído da URL"},{name:"isAliasMismatch",type:"boolean",description:"(useAliasFromUrl) true se alias da URL ≠ alias ativo na sessão"},{name:"isValidAlias",type:"boolean",description:"(useAliasFromUrl) true se o alias da URL existe nas companies do usuário"},{name:"isMissing",type:"boolean",description:"(useAliasFromUrl) true se não há alias na URL"},{name:"matchedCompany",type:"{ id, alias, name } | null",description:"(useAliasFromUrl) Company correspondente ao alias da URL"},{name:"urlTemplate",type:"string",description:"(buildModuleUrl) URL com placeholder {alias} para substituição"},{name:"alias",type:"string | undefined",description:"(buildModuleUrl) Alias a substituir no template. Se ausente, retorna a URL original"}],notes:["AliasRouteGuard deve ser usado DENTRO de ProtectedRoute, nunca fora — depende do contexto de autenticação.","O guard só age após isLoading=false e isAuthenticated=true — não interfere no fluxo de login.","switchUnit() já limpa o cache do React Query automaticamente — sem ação extra necessária.","Todos os redirects usam replace: true para não poluir o histórico do navegador.","URLs sem alias são redirecionadas automaticamente — links antigos continuam funcionando.","A navegação inter-módulos usa buildModuleUrl para substituir {alias} nas URLs dos módulos Saber Gestão e OKR.","Módulos clássicos (v1) não recebem alias no path — mantêm o comportamento original.","O guard usa 1 effect de decisão + 1 effect de tracking (declarado depois). A ordem garante que prevActiveAliasRef é lido antes de ser atualizado.","Troca via seletor: activeAlias muda → prevRef ≠ activeAlias → guard sincroniza URL (sem switchUnit). Troca via URL: activeAlias estável → prevRef = activeAlias → switchUnit().",'switchUnit() usa estratégia "latest request wins" com requestId: chamadas concorrentes são resolvidas — apenas a mais recente efetiva estado.',"switchUnit() faz short-circuit se company.alias === alias atual — evita processamento desnecessário.","switchUnit() NÃO usa setLoading() — manter authIsLoading=false durante a troca preserva a montagem do AliasRouteGuard.",'A rota / deve ter um redirect programático — texto estático como "Redirecionando..." não executa navegação.',"ProtectedRoute deve usar if (isLoading && !isAuthenticated) para não desmontar o guard durante switchUnit().","Navegação interna (tabs, links) em rotas com AliasRouteGuard DEVE usar /${alias}/path — paths hardcoded sem alias são interpretados incorretamente pelo guard."],hideToc:!1,children:[e.jsx(b,{}),e.jsx(N,{}),e.jsx(v,{}),e.jsx(h,{}),e.jsx(g,{}),e.jsx(R,{})]})}export{L as AliasUrlDoc};
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import{j as e,d7 as o,K as i,x as s,ei as a,aJ as u,P as d,ew as n,bk as l,a1 as c,r as x,B as p}from"./index-DkiftrvI.js";import{C as h}from"./ComponentDocTemplate-CQbBhfvZ.js";import"./ExampleCard-DuLrb3t-.js";function f(){const[t,r]=x.useState("");return e.jsx("div",{className:"w-full bg-background border rounded-lg",children:e.jsx("header",{className:"px-4 py-2",children:e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{className:"flex-shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("h1",{className:"text-lg font-semibold",children:"Usuários"}),e.jsx(p,{className:"bg-primary text-primary-foreground text-xs px-2 py-0.5",children:"Módulo"})]}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Gerenciamento de usuários"})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-1 max-w-md",children:[e.jsxs("div",{className:"relative flex-1",children:[e.jsx(o,{className:"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4"}),e.jsx(i,{placeholder:"Buscar...",value:t,onChange:m=>r(m.target.value),className:"w-full pl-10 pr-8"}),t&&e.jsx(s,{variant:"ghost",size:"sm",className:"absolute right-1 top-1/2 transform -translate-y-1/2 h-6 w-6 p-0",onClick:()=>r(""),children:e.jsx(c,{className:"h-3.5 w-3.5"})})]}),e.jsx(s,{variant:"outline",size:"icon",className:"h-10 w-10",children:e.jsx(a,{className:"h-4 w-4"})})]}),e.jsxs("div",{className:"flex-shrink-0 ml-auto flex items-center gap-3",children:[e.jsxs(s,{size:"sm",children:[e.jsx(d,{className:"h-4 w-4 mr-1"}),"Novo"]}),e.jsx(s,{variant:"ghost",className:"h-auto p-2",children:e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("p",{className:"text-sm font-medium",children:"João Silva"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Empresa XYZ"})]}),e.jsx("div",{className:"w-8 h-8 bg-primary rounded-full flex items-center justify-center",children:e.jsx(n,{className:"h-4 w-4 text-primary-foreground"})}),e.jsx(l,{className:"h-4 w-4"})]})})]})]})})})}function g(){return e.jsxs("div",{className:"w-full",children:[e.jsxs("div",{className:"flex items-center justify-between h-14 px-4 border rounded-lg bg-background",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"bg-primary/10 text-primary px-2 py-1 rounded text-xs font-medium",children:"TÍTULO"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"←"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"→"}),e.jsx("div",{className:"bg-secondary/50 text-secondary-foreground px-2 py-1 rounded text-xs font-medium",children:"BUSCA"}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"←"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"→"}),e.jsx("div",{className:"bg-accent/50 text-accent-foreground px-2 py-1 rounded text-xs font-medium",children:"AÇÕES"}),e.jsx("div",{className:"bg-muted text-muted-foreground px-2 py-1 rounded text-xs font-medium",children:"PERFIL"})]})]}),e.jsx("p",{className:"text-xs text-muted-foreground text-center mt-2",children:"Diagrama das áreas internas do AppHeader (altura fixa: 56px)"})]})}function v(){return e.jsxs("div",{className:"flex gap-8 items-start",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-sm font-medium",children:'variant="dropdown"'}),e.jsx("div",{className:"border rounded-lg p-2 bg-background",children:e.jsx(s,{variant:"ghost",className:"h-auto p-2",children:e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsxs("div",{className:"text-left",children:[e.jsx("p",{className:"text-sm font-medium",children:"João Silva"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Empresa XYZ"})]}),e.jsx("div",{className:"w-8 h-8 bg-primary rounded-full flex items-center justify-center",children:e.jsx(n,{className:"h-4 w-4 text-primary-foreground"})}),e.jsx(l,{className:"h-4 w-4"})]})})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-sm font-medium",children:"Menu aberto"}),e.jsxs("div",{className:"border rounded-lg p-2 w-48 bg-background shadow-lg",children:[e.jsxs("div",{className:"py-1.5 px-2 hover:bg-muted rounded flex items-center gap-2 cursor-pointer",children:[e.jsx(a,{className:"h-4 w-4"}),e.jsx("span",{className:"text-sm",children:"Alterar Unidade"})]}),e.jsx("div",{className:"h-px bg-border my-1"}),e.jsxs("div",{className:"py-1.5 px-2 hover:bg-muted rounded flex items-center gap-2 cursor-pointer text-destructive",children:[e.jsx(c,{className:"h-4 w-4"}),e.jsx("span",{className:"text-sm",children:"Sair"})]})]})]})]})}function w(){return e.jsx(h,{title:"AppHeader",description:"Header principal da aplicação com título dinâmico, busca global integrada ao CRUD, ações customizáveis e informações do usuário. Altura fixa de 56px (h-14).",component:e.jsx(f,{}),usage:`import { AppLayout, usePageMetadata } from 'forlogic-core';
|
|
2
|
-
|
|
3
|
-
// O AppHeader é renderizado automaticamente pelo AppLayout
|
|
4
|
-
<AppLayout sidebarConfig={sidebarConfig}>
|
|
5
|
-
<Routes>...</Routes>
|
|
6
|
-
</AppLayout>
|
|
7
|
-
|
|
8
|
-
// Para configurar título, subtítulo e breadcrumbs
|
|
9
|
-
function UsersPage() {
|
|
10
|
-
usePageMetadata({
|
|
11
|
-
title: 'Usuários',
|
|
12
|
-
subtitle: 'Gerenciamento de usuários do sistema'
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Com breadcrumbs
|
|
17
|
-
function EditUserPage() {
|
|
18
|
-
usePageMetadata({
|
|
19
|
-
title: 'Editar Usuário',
|
|
20
|
-
subtitle: 'Atualize os dados cadastrais',
|
|
21
|
-
breadcrumbs: [
|
|
22
|
-
{ label: 'Usuários', href: '/users' },
|
|
23
|
-
{ label: 'João Silva' }
|
|
24
|
-
]
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Subtitle com ReactNode
|
|
29
|
-
function DetailPage() {
|
|
30
|
-
usePageMetadata({
|
|
31
|
-
title: 'Detalhes',
|
|
32
|
-
subtitle: <span>Veja a <Link to="/docs">documentação</Link></span>
|
|
33
|
-
});
|
|
34
|
-
}`,examples:[{title:"Estrutura do Componente",description:"O AppHeader possui 4 áreas internas: Título (esquerda), Busca (centro), Ações (direita) e Perfil (extrema direita).",preview:e.jsx(g,{}),code:`// Áreas do AppHeader
|
|
35
|
-
// 1. Título: Título da página + Subtítulo + Badge do módulo
|
|
36
|
-
// 2. Busca: Input de busca global (visível quando habilitado)
|
|
37
|
-
// 3. Ações: Botões customizáveis via PageMetadataContext
|
|
38
|
-
// 4. Perfil: UserInfo com dropdown de usuário
|
|
39
|
-
|
|
40
|
-
// Altura fixa: 56px (h-14 no Tailwind)`},{title:"UserInfo Integrado",description:"Dropdown com informações do usuário, troca de unidade e logout.",preview:e.jsx(v,{}),code:`// UserInfo com variantes
|
|
41
|
-
<UserInfo variant="dropdown" /> // No header
|
|
42
|
-
<UserInfo variant="card" /> // Em páginas
|
|
43
|
-
|
|
44
|
-
// Menu do usuário inclui:
|
|
45
|
-
// - Alterar Unidade (submenu com todas unidades disponíveis)
|
|
46
|
-
// - Sair (Logout)`},{title:"Busca Global",description:"Campo de busca com debounce sincronizado com URL (?search=termo).",preview:e.jsxs("div",{className:"flex items-center gap-2 max-w-md",children:[e.jsxs("div",{className:"relative flex-1",children:[e.jsx(o,{className:"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground h-4 w-4"}),e.jsx(i,{placeholder:"Buscar...",className:"w-full pl-10 pr-8"})]}),e.jsx(s,{variant:"outline",size:"icon",className:"h-10 w-10",children:e.jsx(a,{className:"h-4 w-4"})})]}),code:`// Configurar campos pesquisáveis no service
|
|
47
|
-
const userService = createSimpleService({
|
|
48
|
-
tableName: 'users',
|
|
49
|
-
schema: 'common',
|
|
50
|
-
searchFields: ['name', 'email', 'department'],
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// A busca é exibida quando isSearchVisible = true no AuthContext`},{title:"Ações Customizáveis",description:"Botões de ação configurados via PageMetadataContext.",preview:e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(s,{variant:"outline",size:"sm",children:[e.jsx(u,{className:"h-4 w-4 mr-1"}),"Exportar"]}),e.jsxs(s,{size:"sm",children:[e.jsx(d,{className:"h-4 w-4 mr-1"}),"Novo"]})]}),code:`// ✅ CORRETO: Apenas botões de ação
|
|
54
|
-
setHeaderActions(
|
|
55
|
-
<>
|
|
56
|
-
<Button variant="outline" size="sm">
|
|
57
|
-
<Download className="h-4 w-4 mr-1" />
|
|
58
|
-
Exportar
|
|
59
|
-
</Button>
|
|
60
|
-
<Button size="sm">
|
|
61
|
-
<Plus className="h-4 w-4 mr-1" />
|
|
62
|
-
Novo
|
|
63
|
-
</Button>
|
|
64
|
-
</>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// ❌ INCORRETO: Formulário no header
|
|
68
|
-
setHeaderActions(
|
|
69
|
-
<form onSubmit={...}>
|
|
70
|
-
<Input />
|
|
71
|
-
<Select />
|
|
72
|
-
<Button type="submit">Salvar</Button>
|
|
73
|
-
</form>
|
|
74
|
-
);`}],props:[{name:"actions",type:"ReactNode",description:"Botões de ação à direita do header (via PageMetadataContext)"},{name:"variant (UserInfo)",type:'"card" | "dropdown"',default:'"card"',description:"Estilo de exibição do UserInfo"},{name:"selectedUnit (UserInfo)",type:"Company | null",description:"Unidade selecionada"},{name:"onUnitChange (UserInfo)",type:"(unit: Company) => void",description:"Callback ao trocar unidade"}],accessibility:["Busca acessível via teclado (Tab para navegar, Escape para limpar)","UserInfo dropdown acessível via teclado","Botões de ação com foco visível (focus-visible)",'Título da página anunciado por leitores de tela (role="heading")',"Atalho de teclado para busca (/ ou Ctrl+K pode ser implementado)"],notes:["✅ Use PageMetadataContext para ações dinâmicas","✅ Mantenha título curto e descritivo (máx. 3 palavras)","✅ Use um único header por página","✅ Limpe ações no useEffect cleanup","✅ Use ícones do Lucide nos botões de ação","❌ Não adicione múltiplos headers na mesma página","❌ Não modifique a altura do header (fixo em 56px)","❌ Não coloque formulários complexos no header","❌ Não use mais de 3 botões de ação","❌ Não esconda o UserInfo em páginas autenticadas"]})}export{w as AppHeaderDoc};
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import{j as e,B as p,aP as I,x as m,P as u,dS as x,aQ as h,o as r,ab as n,e0 as G,aY as D,S as P,a8 as L,bk as R,F as C,dI as A,a_ as B,dL as H,f8 as E,f9 as l,fa as d,fb as c,r as b,gj as k,em as O,e3 as F,gk as _}from"./index-DkiftrvI.js";import{C as q}from"./ComponentDocTemplate-CQbBhfvZ.js";import{a as z,b as M,A as Q,P as V}from"./pin-DVsSl8QA.js";import"./ExampleCard-DuLrb3t-.js";const W=[{icon:x,label:"Visão geral"},{icon:O,label:"Showcase"},{icon:n,label:"Liderança"},{icon:F,label:"Locais"},{icon:P,label:"Gestão",children:[{icon:n,label:"Usuários"},{icon:L,label:"Permissões"}]},{icon:V,label:"Design System"}];function $(){const[a,i]=b.useState(!0),[o,g]=b.useState(!0),[N,f]=b.useState("Visão geral"),[T,U]=b.useState(["Gestão"]);return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx(m,{variant:a?"primary":"outline",size:"sm",onClick:()=>{i(!0),g(!0)},children:"Expandido"}),e.jsx(m,{variant:a?"outline":"primary",size:"sm",onClick:()=>{i(!1),g(!1)},children:"Colapsado"}),e.jsxs("span",{className:"text-xs text-muted-foreground ml-2",children:[o?e.jsx(z,{className:"inline h-3 w-3 mr-1"}):e.jsx(M,{className:"inline h-3 w-3 mr-1"}),o?"Pinada":"Despinada"]})]}),e.jsx(E,{children:e.jsx("div",{className:"relative inline-block",children:e.jsxs("div",{className:r("bg-background border border-border rounded-lg py-4 space-y-3 transition-all duration-300 overflow-visible relative",a?"w-64 px-3":"w-16 px-1.5"),children:[a?e.jsxs("div",{className:"flex items-center gap-2 pl-2 h-10",children:[e.jsx("button",{className:"flex-shrink-0 cursor-pointer",onClick:()=>{},children:e.jsx("img",{src:k.logo,alt:"Logo",className:"h-8 max-w-full object-contain",onError:s=>{s.target.src='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 24"%3E%3Ctext x="0" y="18" font-family="system-ui" font-size="16" font-weight="bold" fill="%23333"%3EForlogic%3C/text%3E%3C/svg%3E'}})}),e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx("button",{className:"flex-1 min-w-0 text-sm font-medium truncate text-center cursor-pointer hover:text-accent-foreground transition-colors",onClick:()=>{},children:"Meu Módulo"})}),e.jsx(c,{side:"bottom",className:"max-w-[200px]",children:e.jsx("p",{children:"Meu Módulo"})})]})]}):e.jsx("div",{className:"flex flex-col items-center justify-center w-full",children:e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx("button",{className:"flex items-center justify-center h-10 w-8 cursor-pointer",onClick:()=>{},children:e.jsx("img",{src:k.smallLogo,alt:"Logo",className:"h-6 w-auto object-contain",onError:s=>{s.target.src='data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 34"%3E%3Crect width="34" height="34" rx="6" fill="%23e5e7eb"/%3E%3Ctext x="50%25" y="55%25" text-anchor="middle" font-family="system-ui" font-size="14" font-weight="bold" fill="%23333"%3EF%3C/text%3E%3C/svg%3E'}})})}),e.jsx(c,{side:"right",children:"Módulos"})]})}),e.jsxs("div",{className:"relative py-2",children:[e.jsx("div",{className:"border-t border-border"}),e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx("button",{onClick:()=>{const s=!a;i(s),g(s)},className:r("absolute top-1/2 -translate-y-1/2 z-[100] h-6 w-6 flex items-center justify-center rounded-full bg-primary hover:bg-primary/90 shadow-md transition-colors",a?"-right-6":"-right-[18px]"),children:a?e.jsx(I,{className:"h-3.5 w-3.5 text-primary-foreground"}):e.jsx(h,{className:"h-3.5 w-3.5 text-primary-foreground"})})}),e.jsx(c,{side:"right",children:e.jsx("p",{children:a?"Recolher":"Expandir"})})]})]}),a?e.jsxs(m,{variant:"outline",className:"w-full gap-2 justify-start h-10",children:[e.jsx(u,{className:"h-4 w-4"}),"Novo Processo"]}):e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx(m,{variant:"outline",size:"icon",className:"w-full h-10",children:e.jsx(u,{className:"h-4 w-4"})})}),e.jsx(c,{side:"right",children:"Novo Processo"})]}),e.jsx("div",{className:"space-y-1",children:W.map(s=>{const y=s.label===N,v="children"in s&&s.children,w=T.includes(s.label),S=()=>{U(t=>t.includes(s.label)?t.filter(j=>j!==s.label):[...t,s.label])};return a?v?e.jsxs("div",{children:[e.jsxs("div",{onClick:S,className:r("flex items-center justify-between h-10 px-3 rounded-md transition-colors cursor-pointer","hover:bg-muted text-foreground"),children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex items-center justify-center h-8 w-8",children:e.jsx(s.icon,{className:"h-4 w-4"})}),e.jsx("span",{className:"text-sm font-medium",children:s.label})]}),e.jsx(h,{className:r("h-4 w-4 transition-transform",w&&"rotate-90")})]}),w&&e.jsx("div",{className:"ml-6 mt-1 space-y-1 border-l border-border pl-3",children:s.children.map(t=>{const j=t.label===N;return e.jsxs("div",{onClick:()=>f(t.label),className:r("flex items-center gap-2 h-8 px-2 rounded-md transition-colors cursor-pointer text-sm",j?"bg-primary/10 text-primary font-medium":"text-muted-foreground hover:bg-muted hover:text-foreground"),children:[e.jsx(t.icon,{className:"h-3.5 w-3.5"}),e.jsx("span",{children:t.label})]},t.label)})})]},s.label):e.jsxs("div",{onClick:()=>f(s.label),className:r("flex items-center gap-3 h-10 px-3 rounded-md transition-colors cursor-pointer",y?"bg-primary/10 text-primary font-medium":"hover:bg-muted text-foreground"),children:[e.jsx("span",{className:"flex items-center justify-center h-8 w-8",children:e.jsx(s.icon,{className:"h-4 w-4"})}),e.jsx("span",{className:"text-sm",children:s.label})]},s.label):e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx("div",{onClick:()=>{v?S():f(s.label)},className:r("flex items-center justify-center h-8 w-8 mx-auto rounded-md transition-colors cursor-pointer",y?"bg-primary/10 text-primary":"hover:bg-accent hover:text-accent-foreground"),children:e.jsx(s.icon,{className:"h-4 w-4"})})}),e.jsxs(c,{side:"right",children:[s.label,v&&" (expandir para ver subitens)"]})]},s.label)})}),a&&e.jsx("div",{className:"absolute inset-y-0 right-0 w-1 hover:bg-primary/20 transition-colors cursor-ew-resize"})]})})})]})}function X(){return e.jsxs("div",{className:"flex gap-8 items-start flex-wrap",children:[e.jsx("div",{className:"relative",children:e.jsxs("div",{className:"w-64 bg-background border border-border rounded-lg py-4 px-3 space-y-3 relative overflow-visible",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2 py-2 border border-dashed border-primary/50 rounded relative",children:[e.jsx(p,{variant:"outline",className:"absolute -top-2.5 left-2 text-xs bg-background",children:"SidebarHeader"}),e.jsx("div",{className:"h-8 w-14 bg-muted rounded flex items-center justify-center text-[10px] flex-shrink-0",children:"Logo"}),e.jsx("span",{className:"text-[10px] truncate text-center flex-1",children:"appName"})]}),e.jsxs("div",{className:"relative py-2",children:[e.jsx("div",{className:"border-t border-dashed border-blue-500/50 relative",children:e.jsx(p,{variant:"outline",className:"absolute -top-2.5 right-0 text-[8px] bg-background text-blue-600 whitespace-nowrap",children:"Pin/Unpin"})}),e.jsx("div",{className:"absolute top-1/2 -translate-y-1/2 -right-5 h-5 w-5 rounded-full bg-primary flex items-center justify-center",children:e.jsx(I,{className:"h-2.5 w-2.5 text-primary-foreground"})})]}),e.jsxs("div",{className:"border border-dashed border-green-500/50 rounded p-2 relative",children:[e.jsx(p,{variant:"outline",className:"absolute -top-2.5 left-2 text-xs bg-background text-green-600",children:"SidebarActionTrigger"}),e.jsxs(m,{variant:"outline",size:"sm",className:"w-full gap-1 text-xs h-7 justify-start",children:[e.jsx(u,{className:"h-3 w-3"}),"Ação"]})]}),e.jsxs("div",{className:"border border-dashed border-orange-500/50 rounded p-2 relative space-y-1",children:[e.jsx(p,{variant:"outline",className:"absolute -top-2.5 left-2 text-xs bg-background text-orange-600",children:"NavigationItems"}),[{label:"Item simples",active:!0},{label:"Item com children",hasChildren:!0}].map((a,i)=>e.jsxs("div",{className:r("flex items-center justify-between px-2 py-1.5 rounded text-xs",a.active?"bg-primary/10 text-primary":"bg-muted/50"),children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(x,{className:"h-3 w-3"}),e.jsx("span",{children:a.label})]}),a.hasChildren&&e.jsx(h,{className:"h-3 w-3"})]},i))]}),e.jsx("div",{className:"absolute inset-y-0 right-0 w-1 border-r-2 border-dashed border-purple-400/50 rounded-r"}),e.jsx(p,{variant:"outline",className:"absolute top-1/2 -right-16 text-[8px] bg-background text-purple-600 whitespace-nowrap",children:"Resize rail"})]})}),e.jsxs("div",{className:"space-y-2 text-xs",children:[e.jsx("p",{className:"font-medium text-sm mb-3",children:"Hierarquia:"}),e.jsx("pre",{className:"bg-muted p-3 rounded text-xs font-mono leading-relaxed",children:`<Sidebar collapsible="icon">
|
|
2
|
-
├── SidebarHeader + Separator + Pin/Unpin button
|
|
3
|
-
│ ├── Logo (clicável → abre ModulesDialog)
|
|
4
|
-
│ └── appName (centralizado, clicável → abre ModulesDialog)
|
|
5
|
-
│ └── Tooltip com nome completo ao hover
|
|
6
|
-
│
|
|
7
|
-
│ Colapsado:
|
|
8
|
-
│ └── SmallLogo (clicável → abre ModulesDialog)
|
|
9
|
-
│ └── Tooltip com appName
|
|
10
|
-
│
|
|
11
|
-
├── SidebarActionTrigger (opcional, abaixo do separador)
|
|
12
|
-
│ ├── variant='button' (padrão)
|
|
13
|
-
│ │ ├── Ação única → executa direto
|
|
14
|
-
│ │ └── Múltiplas → dropdown
|
|
15
|
-
│ └── variant='split-button'
|
|
16
|
-
│ ├── Primeira ação → botão principal
|
|
17
|
-
│ └── Demais ações → dropdown
|
|
18
|
-
│
|
|
19
|
-
└── SidebarContent
|
|
20
|
-
└── NavigationItems
|
|
21
|
-
├── Item simples (Link)
|
|
22
|
-
├── Item com permissionCheck
|
|
23
|
-
└── Item com children
|
|
24
|
-
└── Collapsible > SidebarMenuSub`})]})]})}function K(){return e.jsx("div",{className:"flex gap-4 items-start flex-wrap",children:[{title:"Ativo",className:"bg-primary/10 text-primary font-medium",icon:x,label:"Início"},{title:"Hover",className:"bg-muted",icon:n,label:"Usuários"},{title:"Desabilitado",className:"opacity-50 cursor-not-allowed border",icon:G,label:"Admin"},{title:"Carregando",className:"opacity-50 border",icon:D,label:"Verificando...",spin:!0}].map(a=>e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-xs text-muted-foreground mb-2",children:a.title}),e.jsxs("div",{className:r("flex items-center gap-3 px-3 py-2 rounded-md w-40",a.className),children:[e.jsx(a.icon,{className:r("h-4 w-4",a.spin&&"animate-spin")}),e.jsx("span",{className:"text-sm",children:a.label})]})]},a.title))})}function Y(){return e.jsxs("div",{className:"p-3 border rounded-md w-56 space-y-1 bg-background",children:[e.jsxs("div",{className:"flex items-center gap-3 h-10 px-3 rounded-md hover:bg-muted",children:[e.jsx("span",{className:"flex items-center justify-center h-8 w-8",children:e.jsx(x,{className:"h-4 w-4"})}),e.jsx("span",{className:"text-sm font-medium",children:"Início"})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between h-10 px-3 rounded-md hover:bg-muted cursor-pointer",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"flex items-center justify-center h-8 w-8",children:e.jsx(P,{className:"h-4 w-4"})}),e.jsx("span",{className:"text-sm font-medium",children:"Configurações"})]}),e.jsx(h,{className:"h-4 w-4 rotate-90"})]}),e.jsx("div",{className:"ml-6 mt-1 space-y-1 border-l border-border pl-3",children:[{icon:n,label:"Perfil",active:!0},{icon:L,label:"Segurança"}].map(a=>e.jsxs("div",{className:r("flex items-center gap-2 h-8 px-2 rounded-md text-sm",a.active?"bg-primary/10 text-primary font-medium":"text-muted-foreground hover:bg-muted"),children:[e.jsx(a.icon,{className:"h-3.5 w-3.5"}),e.jsx("span",{children:a.label})]},a.label))})]})]})}function J(){return e.jsxs("div",{className:"flex gap-6 items-start flex-wrap",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-sm font-medium text-center",children:"Uma Ação"}),e.jsxs("div",{className:"w-56 bg-background border rounded-lg p-3 space-y-3",children:[e.jsxs(m,{variant:"outline",className:"w-full gap-2 justify-start",children:[e.jsx(u,{className:"h-4 w-4"}),"Novo Processo"]}),e.jsx("div",{className:"border-t pt-2 space-y-1",children:[{icon:x,label:"Início",active:!0},{icon:n,label:"Usuários"}].map((a,i)=>e.jsxs("div",{className:r("flex items-center gap-3 px-3 py-2 rounded-md text-sm",a.active?"bg-primary/10 text-primary font-medium":"hover:bg-muted"),children:[e.jsx(a.icon,{className:"h-4 w-4"}),e.jsx("span",{children:a.label})]},i))})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-sm font-medium text-center",children:"Múltiplas Ações"}),e.jsxs("div",{className:"w-56 bg-background border rounded-lg p-3 space-y-1",children:[e.jsxs(m,{variant:"outline",className:"w-full gap-2 justify-start",children:[e.jsx(u,{className:"h-4 w-4"}),"Criar",e.jsx(R,{className:"ml-auto h-4 w-4 rotate-180"})]}),e.jsx("div",{className:"border rounded-md bg-popover p-1 shadow-md",children:[{icon:C,label:"Novo Documento"},{icon:A,label:"Nova Pasta"},{icon:n,label:"Novo Grupo"}].map((a,i)=>e.jsxs("div",{className:"flex items-center gap-2 px-2 py-1.5 rounded-sm hover:bg-muted cursor-pointer",children:[e.jsx(a.icon,{className:"h-4 w-4"}),e.jsx("span",{className:"text-sm",children:a.label})]},i))})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("p",{className:"text-sm font-medium text-center",children:"Split Button"}),e.jsxs("div",{className:"w-56 bg-background border rounded-lg p-3 space-y-3",children:[e.jsx(B,{label:"Novo Documento",onClick:()=>{},icon:C,variant:"outline",actions:[{id:"folder",label:"Nova Pasta",icon:A,onClick:()=>{}},{id:"group",label:"Novo Grupo",icon:n,onClick:()=>{}}],className:"w-full",menuAlign:"start"}),e.jsx("div",{className:"border-t pt-2 space-y-1",children:[{icon:x,label:"Início",active:!0},{icon:n,label:"Usuários"}].map((a,i)=>e.jsxs("div",{className:r("flex items-center gap-3 px-3 py-2 rounded-md text-sm",a.active?"bg-primary/10 text-primary font-medium":"hover:bg-muted"),children:[e.jsx(a.icon,{className:"h-4 w-4"}),e.jsx("span",{children:a.label})]},i))})]})]})]})}function Z(){return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3 p-3 border rounded-md bg-muted/30",children:[e.jsx(H,{className:"h-5 w-5 text-muted-foreground"}),e.jsxs("div",{className:"text-sm space-y-1",children:[e.jsx("p",{className:"font-medium",children:"Redimensionamento"}),e.jsxs("p",{className:"text-muted-foreground",children:["Quando ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"resizable=true"}),", um rail aparece na borda direita. Arraste para redimensionar entre ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"minWidth"})," e"," ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"maxWidth"}),"."]}),e.jsxs("p",{className:"text-muted-foreground",children:["A largura é persistida automaticamente em ",e.jsx("code",{className:"bg-muted px-1 rounded text-xs",children:"localStorage"}),"."]})]})]}),e.jsx("pre",{className:"bg-muted p-3 rounded text-xs font-mono",children:`<AppSidebar
|
|
25
|
-
config={sidebarConfig}
|
|
26
|
-
resizable={true}
|
|
27
|
-
minWidth={224} // padrão: 224px (14rem)
|
|
28
|
-
maxWidth={384} // padrão: 384px (24rem)
|
|
29
|
-
/>`})]})}function ee(){return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex gap-4 items-start flex-wrap",children:[e.jsxs("div",{className:"p-3 border rounded-md space-y-2 w-64",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(z,{className:"h-4 w-4 text-primary"}),e.jsx("span",{className:"text-sm font-medium",children:"Pinada"})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Sidebar permanece aberta. Estado salvo em ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"localStorage"}),"."]})]}),e.jsxs("div",{className:"p-3 border rounded-md space-y-2 w-64",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(M,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"text-sm font-medium",children:"Despinada"})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Sidebar inicia colapsada. Expande ao hover (desktop) ou toggle (mobile)."})]})]}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:["O botão circular na borda do separador controla pin/unpin. Em telas ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"<768px"})," (mobile), o botão é ocultado."]})]})}function ae(){return e.jsx("div",{className:"space-y-3 w-full",children:[{badge:"Desktop ≥1024px",variant:"default",text:"Sidebar visível, pode ser pinada ou colapsar"},{badge:"Tablet 768-1023px",variant:"secondary",text:"Sidebar inicia colapsada, expande ao hover/click"},{badge:"Mobile <768px",variant:"outline",text:"Sidebar como drawer/sheet lateral (oculta por padrão)"}].map(a=>e.jsxs("div",{className:"flex gap-4 items-center",children:[e.jsx(p,{variant:a.variant,className:"shrink-0",children:a.badge}),e.jsx("span",{className:"text-sm text-muted-foreground",children:a.text})]},a.badge))})}function se(){const a=`https://${_().storageProjectId}.supabase.co/storage/v1/object/public/library-assets`,i=[{name:"Qualiex",flag:'VITE_IS_QUALIEX="true"',logo:`${a}/logo-qualiex.svg`,smallLogo:`${a}/small-forlogic.svg`,appName:"Plano de Controle"},{name:"Saber Gestão",flag:'VITE_IS_QUALIEX="false"',logo:`${a}/logo.svg`,smallLogo:`${a}/small.svg`,appName:"Plano de Controle"}];return e.jsx(E,{children:e.jsx("div",{className:"flex gap-8 items-start flex-wrap",children:i.map(o=>e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium",children:o.name}),e.jsx("code",{className:"text-xs bg-muted px-1.5 py-0.5 rounded",children:o.flag})]}),e.jsxs("div",{className:"flex gap-4 items-start",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-[10px] text-muted-foreground text-center",children:"Expandido"}),e.jsx("div",{className:"w-64 bg-background border border-border rounded-lg py-4 px-3",children:e.jsxs("div",{className:"flex items-center gap-2 pl-2 h-10",children:[e.jsx("img",{src:o.logo,alt:`${o.name} Logo`,className:"h-8 max-w-[120px] object-contain flex-shrink-0"}),e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsxs("span",{className:"flex-1 min-w-0 text-sm font-medium text-center cursor-pointer hover:text-accent-foreground transition-colors inline-flex items-center justify-center gap-1 leading-tight py-1",children:[e.jsx("span",{className:"line-clamp-2",children:o.appName}),e.jsx(Q,{size:12,className:"flex-shrink-0 opacity-60"})]})}),e.jsx(c,{side:"bottom",className:"max-w-[200px]",children:e.jsx("p",{children:o.appName})})]})]})})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-[10px] text-muted-foreground text-center",children:"Colapsado"}),e.jsx("div",{className:"w-16 bg-background border border-border rounded-lg py-4 px-1.5",children:e.jsx("div",{className:"flex flex-col items-center justify-center w-full",children:e.jsxs(l,{children:[e.jsx(d,{asChild:!0,children:e.jsx("button",{className:"flex items-center justify-center h-10 w-8 cursor-pointer",children:e.jsx("img",{src:o.smallLogo,alt:`${o.name} Small Logo`,className:"h-6 w-auto object-contain"})})}),e.jsx(c,{side:"right",children:e.jsx("p",{children:o.appName})})]})})})]})]})]},o.name))})})}function ne(){return e.jsxs(q,{title:"AppSidebar",description:"Sidebar de navegação principal da aplicação com suporte a pin/unpin, permissões assíncronas, ações de módulo (SidebarActionTrigger), redimensionamento e navegação hierárquica.",component:e.jsx($,{}),usage:`import { AppLayout } from 'forlogic-core';
|
|
30
|
-
import type { SidebarConfig } from 'forlogic-core';
|
|
31
|
-
import { Home, Users, Settings, Shield, Plus, FileText, Folder } from 'lucide-react';
|
|
32
|
-
|
|
33
|
-
const sidebarConfig: SidebarConfig = {
|
|
34
|
-
appName: 'Minha Aplicação',
|
|
35
|
-
|
|
36
|
-
// Ações do módulo (opcional)
|
|
37
|
-
moduleActions: {
|
|
38
|
-
triggerLabel: 'Criar',
|
|
39
|
-
triggerIcon: Plus,
|
|
40
|
-
actions: [
|
|
41
|
-
{ id: 'new-doc', label: 'Novo Documento', icon: FileText, onClick: () => {} },
|
|
42
|
-
{ id: 'new-folder', label: 'Nova Pasta', icon: Folder, onClick: () => {} },
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// Navegação (com suporte a hierarquia)
|
|
47
|
-
navigation: [
|
|
48
|
-
{ label: 'Início', path: '/', icon: Home },
|
|
49
|
-
{ label: 'Usuários', path: '/users', icon: Users },
|
|
50
|
-
{
|
|
51
|
-
label: 'Configurações',
|
|
52
|
-
path: '/settings',
|
|
53
|
-
icon: Settings,
|
|
54
|
-
children: [
|
|
55
|
-
{ label: 'Perfil', path: '/settings/profile', icon: Users },
|
|
56
|
-
{ label: 'Segurança', path: '/settings/security', icon: Shield },
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Uso via AppLayout (recomendado)
|
|
63
|
-
<AppLayout sidebarConfig={sidebarConfig}>
|
|
64
|
-
<Routes>...</Routes>
|
|
65
|
-
</AppLayout>
|
|
66
|
-
|
|
67
|
-
// Uso direto (casos avançados)
|
|
68
|
-
<AppSidebar
|
|
69
|
-
config={sidebarConfig}
|
|
70
|
-
resizable={true}
|
|
71
|
-
minWidth={224}
|
|
72
|
-
maxWidth={384}
|
|
73
|
-
/>`,tocItems:[{id:"tipos-typescript",label:"Tipos TypeScript"},{id:"componente-base",label:"Componente Base: Sidebar"}],examples:[{title:"Anatomia do Sidebar",description:"Estrutura hierárquica completa: Header, Actions, Separator com Pin, Navigation, e Resize rail.",preview:e.jsx(X,{}),code:`// Estrutura do AppSidebar
|
|
74
|
-
<Sidebar collapsible="icon">
|
|
75
|
-
├── SidebarHeader (Logo + ModulesButton)
|
|
76
|
-
├── SidebarActionTrigger (ações de módulo)
|
|
77
|
-
├── Separator + Pin/Unpin button (borda)
|
|
78
|
-
└── SidebarContent
|
|
79
|
-
└── NavigationItems
|
|
80
|
-
├── Item simples (Link)
|
|
81
|
-
├── Item com permissionCheck
|
|
82
|
-
└── Item com children (Collapsible)
|
|
83
|
-
└── SidebarMenuSub`},{title:"Estados Visuais",description:"Itens de navegação possuem 4 estados: Ativo (bg-primary/10), Hover (bg-muted), Desabilitado (opacity-50 + Lock icon), e Carregando (spinner durante permissionCheck).",preview:e.jsx(K,{}),code:`// Matching de rota ativa (hierárquico)
|
|
84
|
-
const isActive = (path: string) =>
|
|
85
|
-
location.pathname === path ||
|
|
86
|
-
location.pathname.startsWith(path + '/');
|
|
87
|
-
|
|
88
|
-
// Item com verificação de permissão assíncrona
|
|
89
|
-
{
|
|
90
|
-
label: 'Gestão',
|
|
91
|
-
path: '/management',
|
|
92
|
-
icon: Settings,
|
|
93
|
-
permissionCheck: async () => {
|
|
94
|
-
return await checkUserPermission('management');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// → Exibe Loader2 (spin) enquanto verifica
|
|
98
|
-
// → Exibe Lock + "Acesso restrito" se false`},{title:"Navegação Hierárquica",description:"Suporte a itens com children (máximo 2 níveis). No modo expandido, usa Collapsible com ChevronRight. No modo colapsado, exibe tooltip.",preview:e.jsx(Y,{}),code:`const navigation: SidebarNavigationItem[] = [
|
|
99
|
-
{ label: 'Início', path: '/', icon: Home },
|
|
100
|
-
{
|
|
101
|
-
label: 'Configurações',
|
|
102
|
-
path: '/settings',
|
|
103
|
-
icon: Settings,
|
|
104
|
-
children: [
|
|
105
|
-
{ label: 'Perfil', path: '/settings/profile', icon: Users },
|
|
106
|
-
{ label: 'Segurança', path: '/settings/security', icon: Shield },
|
|
107
|
-
],
|
|
108
|
-
},
|
|
109
|
-
];`},{title:"Ações de Módulo (SidebarActionTrigger)",description:'Suporta duas variantes: "button" (padrão) com ação direta ou dropdown, e "split-button" com ação principal + dropdown de ações secundárias. No modo colapsado, ambas exibem ícone com tooltip.',preview:e.jsx(J,{}),code:`// variant='button' (padrão) — ação única ou dropdown
|
|
110
|
-
moduleActions: {
|
|
111
|
-
triggerLabel: 'Criar',
|
|
112
|
-
triggerIcon: Plus,
|
|
113
|
-
actions: [
|
|
114
|
-
{ id: 'new-doc', label: 'Novo Documento', icon: FileText, onClick: handleNewDoc },
|
|
115
|
-
{ id: 'new-folder', label: 'Nova Pasta', icon: Folder, onClick: handleNewFolder },
|
|
116
|
-
],
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// variant='split-button' — primeira ação = botão principal, demais = dropdown
|
|
120
|
-
moduleActions: {
|
|
121
|
-
variant: 'split-button',
|
|
122
|
-
actions: [
|
|
123
|
-
{ id: 'new-doc', label: 'Novo Documento', icon: FileText, onClick: handleNewDoc },
|
|
124
|
-
{ id: 'new-folder', label: 'Nova Pasta', icon: Folder, onClick: handleNewFolder },
|
|
125
|
-
{ id: 'import', label: 'Importar', icon: Upload, onClick: handleImport },
|
|
126
|
-
],
|
|
127
|
-
}`},{title:"Pin / Unpin",description:"O botão circular na borda do separador controla se a sidebar permanece aberta (pinada) ou colapsa automaticamente.",preview:e.jsx(ee,{}),code:`// Internamente o AppSidebar usa:
|
|
128
|
-
import { getSidebarPinnedPreference, setSidebarPinnedPreference }
|
|
129
|
-
from './sidebar-utils';
|
|
130
|
-
|
|
131
|
-
// Persistência em localStorage ('forlogic-sidebar-pinned')
|
|
132
|
-
// Botão circular usa bg-primary com ChevronLeft/Right
|
|
133
|
-
// Oculto em mobile (hidden md:block)`},{title:"Redimensionamento",description:"Quando resizable=true, um rail aparece na borda direita para arrastar. A largura é persistida em localStorage.",preview:e.jsx(Z,{}),code:`// useSidebarResize hook
|
|
134
|
-
import { useSidebarResize } from 'forlogic-core';
|
|
135
|
-
|
|
136
|
-
const resize = useSidebarResize({
|
|
137
|
-
minWidth: 224, // Mínimo (14rem)
|
|
138
|
-
maxWidth: 384, // Máximo (24rem)
|
|
139
|
-
defaultWidth: 240, // Padrão (15rem)
|
|
140
|
-
storageKey: 'app-sidebar-width',
|
|
141
|
-
isOpen: true,
|
|
142
|
-
onResize: (width) => console.log(width),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// resize.width, resize.isDragging,
|
|
146
|
-
// resize.dragRef, resize.handleMouseDown`},{title:"Comportamento Responsivo",description:"A sidebar adapta-se automaticamente ao tamanho da tela.",preview:e.jsx(ae,{}),code:`// O AppSidebar usa collapsible="icon"
|
|
147
|
-
// Em desktop: colapsa para 16px (ícones apenas)
|
|
148
|
-
// Em mobile: comportamento de drawer
|
|
149
|
-
|
|
150
|
-
// useSidebar() hook disponível para controle externo
|
|
151
|
-
import { useSidebar } from 'forlogic-core';
|
|
152
|
-
|
|
153
|
-
const { open, setOpen, state, toggleSidebar } = useSidebar();
|
|
154
|
-
// state: "expanded" | "collapsed"`},{title:"Branding: Qualiex vs Saber Gestão",description:"O logo e ícone da sidebar mudam conforme a variável de ambiente VITE_IS_QUALIEX. O logo é clicável e abre o ModulesDialog. O appName aparece centralizado ao lado do logo com tooltip para nomes longos.",preview:e.jsx(se,{}),code:`// lib/assets/index.ts — seleção automática de logo
|
|
155
|
-
import { LOGO_CONFIG } from '../config';
|
|
156
|
-
|
|
157
|
-
export const assets = {
|
|
158
|
-
logo: LOGO_CONFIG.isQualiex ? qualiexLogo : saberLogo,
|
|
159
|
-
smallLogo: LOGO_CONFIG.isQualiex ? qualiexSmall : saberSmall,
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// .env
|
|
163
|
-
VITE_IS_QUALIEX="true" // → Logo Qualiex
|
|
164
|
-
VITE_IS_QUALIEX="false" // → Logo Saber Gestão
|
|
165
|
-
|
|
166
|
-
// SidebarHeader — layout atual:
|
|
167
|
-
// Expandido: [Logo] [appName centralizado] — ambos clicáveis
|
|
168
|
-
// Colapsado: [SmallLogo com tooltip] — clicável`}],props:[{name:"config",type:"SidebarConfig",description:"Configuração principal: appName, navigation[], moduleActions"},{name:"config.appName",type:"string",description:"Nome exibido centralizado ao lado do logo (expandido) ou no tooltip do logo (colapsado). Clicável para abrir ModulesDialog."},{name:"config.navigation",type:"SidebarNavigationItem[]",description:"Itens de navegação com label, path, icon e children opcionais"},{name:"config.moduleActions",type:"SidebarActionsConfig",description:"Ações do módulo: triggerLabel, triggerIcon, actions[]"},{name:"customContent",type:"ReactNode",description:"Conteúdo customizado que substitui a navegação padrão"},{name:"resizable",type:"boolean",default:"false",description:"Habilita redimensionamento com drag na borda direita"},{name:"minWidth",type:"number",default:"224",description:"Largura mínima em pixels (resizable=true)"},{name:"maxWidth",type:"number",default:"384",description:"Largura máxima em pixels (resizable=true)"}],accessibility:["Navegação acessível via teclado (Tab, Arrow keys, Enter)","Submenus expansíveis com Enter ou Space (via Collapsible)","Estado ativo comunicado via isActive no SidebarMenuButton","Itens desabilitados com cursor-not-allowed e opacity reduzida","Tooltips em modo colapsado para identificação de todos os itens","Botão pin/unpin oculto em mobile (hidden md:block) para evitar conflito com drawer","Contraste adequado entre estados ativo/inativo usando tokens semânticos"],notes:["✅ Use ícones consistentes do Lucide React para todos os itens","✅ Máximo 2 níveis de aninhamento (item → children)","✅ Labels curtas: 1-3 palavras, máximo 20 caracteres","✅ Agrupe itens relacionados logicamente","✅ Use permissionCheck para controle de acesso assíncrono","✅ Use moduleActions para ações de criação/adição do módulo","✅ Prefira AppLayout ao invés de usar AppSidebar diretamente","❌ Não use mais de 10 itens no nível raiz","❌ Não crie 3+ níveis de submenu (hierarquia profunda)","❌ Não deixe itens sem ícones","❌ Não misture ícones de bibliotecas diferentes","❌ Não use labels com mais de 20 caracteres"],children:[e.jsxs("div",{id:"tipos-typescript",className:"space-y-6 scroll-mt-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"Tipos TypeScript"}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-lg font-semibold mb-3",children:"SidebarConfig"}),e.jsx("pre",{className:"bg-muted p-4 rounded-lg overflow-x-auto text-sm",children:`interface SidebarConfig {
|
|
169
|
-
appName?: string;
|
|
170
|
-
navigation?: SidebarNavigationItem[];
|
|
171
|
-
moduleActions?: SidebarActionsConfig;
|
|
172
|
-
}`})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-lg font-semibold mb-3",children:"SidebarNavigationItem"}),e.jsx("pre",{className:"bg-muted p-4 rounded-lg overflow-x-auto text-sm",children:`interface SidebarNavigationItem {
|
|
173
|
-
label: string; // Texto do item
|
|
174
|
-
path: string; // Rota de navegação
|
|
175
|
-
icon: LucideIcon; // Ícone obrigatório
|
|
176
|
-
complementaryText?: string; // Texto complementar
|
|
177
|
-
permissionCheck?: () => Promise<boolean>; // Verificação assíncrona
|
|
178
|
-
children?: SidebarNavigationItem[]; // Subitens (máx. 2 níveis)
|
|
179
|
-
}`})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-lg font-semibold mb-3",children:"SidebarActionsConfig"}),e.jsx("pre",{className:"bg-muted p-4 rounded-lg overflow-x-auto text-sm",children:`interface SidebarActionsConfig {
|
|
180
|
-
triggerLabel?: string; // Rótulo do gatilho (default: 'Criar')
|
|
181
|
-
triggerIcon?: LucideIcon; // Ícone do gatilho (default: Plus)
|
|
182
|
-
actions: SidebarModuleAction[];
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
interface SidebarModuleAction {
|
|
186
|
-
id: string; // ID único
|
|
187
|
-
label: string; // Texto exibido
|
|
188
|
-
icon?: LucideIcon; // Ícone opcional
|
|
189
|
-
onClick: () => void; // Callback
|
|
190
|
-
disabled?: boolean; // Desabilitar ação
|
|
191
|
-
permissionCheck?: () => Promise<boolean>; // Verificação de permissão
|
|
192
|
-
}`})]})]}),e.jsxs("div",{id:"componente-base",className:"space-y-6 scroll-mt-4",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight border-b pb-2",children:"Componente Base: Sidebar"}),e.jsxs("p",{className:"text-muted-foreground",children:["O AppSidebar utiliza internamente o componente base ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"Sidebar"})," do Shadcn/Radix. Para sidebars customizadas fora do AppLayout, use o componente base diretamente."]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-lg font-semibold mb-3",children:"Uso do Base"}),e.jsx("pre",{className:"bg-muted p-4 rounded-lg overflow-x-auto text-sm",children:`import {
|
|
193
|
-
Sidebar, SidebarProvider, SidebarHeader, SidebarContent,
|
|
194
|
-
SidebarFooter, SidebarGroup, SidebarGroupLabel,
|
|
195
|
-
SidebarGroupContent, SidebarMenu, SidebarMenuItem,
|
|
196
|
-
SidebarMenuButton, SidebarTrigger,
|
|
197
|
-
} from 'forlogic-core';
|
|
198
|
-
|
|
199
|
-
<SidebarProvider>
|
|
200
|
-
<Sidebar collapsible="icon">
|
|
201
|
-
<SidebarHeader>Logo</SidebarHeader>
|
|
202
|
-
<SidebarContent>
|
|
203
|
-
<SidebarGroup>
|
|
204
|
-
<SidebarGroupLabel>Menu</SidebarGroupLabel>
|
|
205
|
-
<SidebarGroupContent>
|
|
206
|
-
<SidebarMenu>
|
|
207
|
-
<SidebarMenuItem>
|
|
208
|
-
<SidebarMenuButton isActive>
|
|
209
|
-
<Home className="mr-2" /> Home
|
|
210
|
-
</SidebarMenuButton>
|
|
211
|
-
</SidebarMenuItem>
|
|
212
|
-
</SidebarMenu>
|
|
213
|
-
</SidebarGroupContent>
|
|
214
|
-
</SidebarGroup>
|
|
215
|
-
</SidebarContent>
|
|
216
|
-
</Sidebar>
|
|
217
|
-
<main>
|
|
218
|
-
<SidebarTrigger />
|
|
219
|
-
{/* Content */}
|
|
220
|
-
</main>
|
|
221
|
-
</SidebarProvider>`})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-lg font-semibold mb-3",children:"Props do Sidebar Base"}),e.jsx("div",{className:"rounded-lg border",children:e.jsxs("table",{className:"w-full",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b bg-muted/50",children:[e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Prop"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Tipo"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Padrão"}),e.jsx("th",{className:"px-4 py-3 text-left text-sm font-semibold",children:"Descrição"})]})}),e.jsxs("tbody",{children:[e.jsxs("tr",{className:"border-b",children:[e.jsx("td",{className:"px-4 py-3 font-mono text-sm font-medium",children:"side"}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"left" | "right"'}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"left"'}),e.jsx("td",{className:"px-4 py-3 text-sm",children:"Lado da tela"})]}),e.jsxs("tr",{className:"border-b",children:[e.jsx("td",{className:"px-4 py-3 font-mono text-sm font-medium",children:"variant"}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"sidebar" | "floating" | "inset"'}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"sidebar"'}),e.jsx("td",{className:"px-4 py-3 text-sm",children:"Estilo visual"})]}),e.jsxs("tr",{className:"border-b last:border-0",children:[e.jsx("td",{className:"px-4 py-3 font-mono text-sm font-medium",children:"collapsible"}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"offcanvas" | "icon" | "none"'}),e.jsx("td",{className:"px-4 py-3 font-mono text-sm text-muted-foreground",children:'"offcanvas"'}),e.jsx("td",{className:"px-4 py-3 text-sm",children:"Comportamento de collapse"})]})]})]})})]}),e.jsx("div",{className:"p-4 bg-muted/50 rounded-lg border",children:e.jsxs("p",{className:"text-sm",children:[e.jsx("strong",{children:"💡 Recomendação:"})," Para a maioria dos casos, prefira"," ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"AppSidebar"})," via"," ",e.jsx("code",{className:"bg-muted px-1 rounded",children:"AppLayout"}),", que já inclui permissões, módulos, resize e pin/unpin. Use o Sidebar base apenas para customizações avançadas."]})})]})]})}export{ne as AppSidebarDoc};
|