@luminix/mui-cms 0.2.13 → 1.1.0

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.
@@ -0,0 +1,261 @@
1
+ # Extensibilidade
2
+
3
+ O `@luminix/mui-cms` foi projetado para ser profundamente customizável sem exigir fork ou modificação do código-fonte. O mecanismo central é o padrão **Reducible** de `@luminix/support`.
4
+
5
+ ## O padrão Reducible
6
+
7
+ Um serviço `Reducible` expõe "pontos de extensão" chamados **redutores**. Cada redutor é uma cadeia de funções que transforma um valor. O resultado final é a composição de todas as funções registradas, aplicadas em ordem de prioridade.
8
+
9
+ ```ts
10
+ // assinatura geral
11
+ ServiceFacade.reducer(
12
+ 'nomeDoReducer', // string — nome do ponto de extensão
13
+ (valorAtual, ...args) => novoValor, // função transformadora
14
+ prioridade // number — menor número roda primeiro (padrão: 10)
15
+ );
16
+ ```
17
+
18
+ Os redutores internos do `CmsServiceProvider` usam prioridade `0`. Registre os seus com prioridade maior para rodar depois (comportamento aditivo) ou menor para rodar antes (comportamento de pré-processamento).
19
+
20
+ ---
21
+
22
+ ## Onde registrar redutores
23
+
24
+ Os redutores devem ser registrados **antes** da aplicação inicializar. O lugar ideal é dentro de um `ServiceProvider` customizado:
25
+
26
+ ```ts
27
+ // src/providers/AppServiceProvider.ts
28
+ import { ServiceProvider } from '@luminix/support';
29
+ import { Cms, Icon, Filter } from '@luminix/mui-cms';
30
+ import { Star } from '@mui/icons-material';
31
+
32
+ class AppServiceProvider extends ServiceProvider {
33
+ register() {
34
+ // registre ícones aqui
35
+ Icon.registerIcon('Star', Star);
36
+ }
37
+
38
+ boot() {
39
+ // registre redutores aqui
40
+ Cms.reducer('menuItems', (items) => [
41
+ ...items,
42
+ {
43
+ key: 'relatorios',
44
+ text: 'Relatórios',
45
+ to: '/relatorios',
46
+ icon: Icon.render('Star'),
47
+ },
48
+ ]);
49
+ }
50
+ }
51
+
52
+ export default AppServiceProvider;
53
+ ```
54
+
55
+ E passe o provider para o `LuminixCms`:
56
+
57
+ ```tsx
58
+ import { LuminixCms } from '@luminix/mui-cms';
59
+ import AppServiceProvider from './providers/AppServiceProvider';
60
+
61
+ <LuminixCms providers={[AppServiceProvider]} />
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Redutores do CmsService
67
+
68
+ ### componentMap
69
+
70
+ Substitui componentes internos do painel:
71
+
72
+ ```ts
73
+ import MeuLayout from './components/MeuLayout';
74
+
75
+ Cms.reducer('componentMap', (map) => ({
76
+ ...map,
77
+ Layout: MeuLayout,
78
+ }));
79
+ ```
80
+
81
+ ### menuItems
82
+
83
+ Personaliza o menu lateral:
84
+
85
+ ```ts
86
+ Cms.reducer('menuItems', (items, models) => {
87
+ // remove modelos do menu
88
+ return items.filter(item => item.key !== 'user');
89
+ });
90
+ ```
91
+
92
+ ### cmsRoutes
93
+
94
+ Adiciona rotas customizadas ao painel. A estrutura de rotas é um array com **um único elemento raiz** que envolve o `Layout` e os providers. Todas as páginas são filhas desse elemento raiz via `children`. Para que o layout (AppBar, Drawer, Notification, Dialog) seja aplicado na nova página, a rota deve ser adicionada em `routes[0].children`:
95
+
96
+ ```ts
97
+ import Relatorios from './views/Relatorios';
98
+
99
+ Cms.reducer('cmsRoutes', (routes, components) => {
100
+ const [root, ...rest] = routes;
101
+ return [
102
+ {
103
+ ...root,
104
+ children: [
105
+ ...(root.children ?? []),
106
+ {
107
+ path: '/relatorios',
108
+ element: <Relatorios />,
109
+ },
110
+ ],
111
+ },
112
+ ...rest,
113
+ ];
114
+ });
115
+ ```
116
+
117
+ > Adicionar a rota fora de `routes[0].children` (ex.: `[...routes, { path: '...' }]`) faz a página renderizar sem o Layout do painel.
118
+
119
+ ### LogoutButton
120
+
121
+ O botão de logout no rodapé do drawer pode ser personalizado de duas formas:
122
+
123
+ **1. Apenas o comportamento ao clicar** — via `Cms.logoutUsing()` no `boot()` do seu `ServiceProvider`:
124
+
125
+ ```ts
126
+ class AppServiceProvider extends ServiceProvider {
127
+ boot() {
128
+ Cms.logoutUsing(() => {
129
+ // lógica de logout customizada
130
+ window.location.href = '/login';
131
+ });
132
+ }
133
+ }
134
+ ```
135
+
136
+ **2. Aparência e comportamento completos** — substituindo o componente `'Layout.Drawer.LogoutButton'`:
137
+
138
+ ```tsx
139
+ import React from 'react';
140
+ import { ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
141
+ import { ExitToApp } from '@mui/icons-material';
142
+ import type { LogoutButtonProps } from '@luminix/mui-cms';
143
+
144
+ const MeuLogout: React.FC<LogoutButtonProps> = ({ collapsed }) => (
145
+ <ListItem disablePadding>
146
+ <ListItemButton onClick={() => { /* minha lógica */ }}>
147
+ <ListItemIcon><ExitToApp /></ListItemIcon>
148
+ {!collapsed && <ListItemText primary="Sair do sistema" />}
149
+ </ListItemButton>
150
+ </ListItem>
151
+ );
152
+
153
+ // No boot() do seu ServiceProvider:
154
+ Cms.reducer('componentMap', (map) => ({
155
+ ...map,
156
+ 'Layout.Drawer.LogoutButton': MeuLogout,
157
+ }));
158
+ ```
159
+
160
+ O componente recebe a prop `collapsed: boolean` — `true` quando o drawer está recolhido no modo desktop.
161
+
162
+ ---
163
+
164
+ ### wireModelFormProps
165
+
166
+ Personaliza o formulário de modelo:
167
+
168
+ ```ts
169
+ import { Cms } from '@luminix/mui-cms';
170
+
171
+ Cms.reducer('wireModelFormProps', (props, item) => {
172
+ if (item?.getType() === 'post') {
173
+ return {
174
+ ...props,
175
+ // adicione campos confirmados, campos ocultos, etc.
176
+ };
177
+ }
178
+ return props;
179
+ });
180
+ ```
181
+
182
+ ### model{Name}Columns
183
+
184
+ Define as colunas da tabela para um modelo específico:
185
+
186
+ ```ts
187
+ Cms.reducer('modelPostColumns', () => [
188
+ { key: 'title', label: 'Título', scope: 'row', component: 'th' },
189
+ { key: 'author', label: 'Autor', align: 'right' },
190
+ { key: 'created_at', label: 'Criado em', align: 'right', size: 'small' },
191
+ ]);
192
+ ```
193
+
194
+ > A convenção de nome é `model` + nome do modelo em StudlyCase + `Columns`.
195
+ > Ex.: `modelBlogPostColumns` para o modelo `blog_post`.
196
+
197
+ ---
198
+
199
+ ## Redutores do FilterService
200
+
201
+ ### filterableColumns
202
+
203
+ Adiciona ou remove colunas do painel de filtros:
204
+
205
+ ```ts
206
+ import { Filter } from '@luminix/mui-cms';
207
+
208
+ Filter.reducer('filterableColumns', (columns, ModelClass) => {
209
+ if (ModelClass.getSchemaName() === 'post') {
210
+ return [
211
+ ...columns,
212
+ {
213
+ key: 'category_id',
214
+ label: 'Categoria',
215
+ type: 'autocomplete',
216
+ nullable: true,
217
+ is_relation: true,
218
+ },
219
+ ];
220
+ }
221
+ return columns;
222
+ });
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Redutores do Model (@luminix/core)
228
+
229
+ O `@luminix/core` expõe o redutor `model` (e `model{Name}`) para customizar a classe de modelo:
230
+
231
+ ```ts
232
+ import { Model } from '@luminix/core';
233
+
234
+ Model.reducer('modelPost', (Base) => {
235
+ return class extends Base {
236
+ static plural() { return 'Posts do Blog'; }
237
+ static icon() { return Icon.render('Article'); }
238
+ };
239
+ });
240
+ ```
241
+
242
+ Consulte a documentação do `@luminix/core` para a lista completa de redutores de modelo.
243
+
244
+ ---
245
+
246
+ ## Prioridades
247
+
248
+ | Prioridade | Quando usar |
249
+ |---|---|
250
+ | `0` | Valores padrão da biblioteca (já usados internamente) |
251
+ | `1`–`9` | Extensões do pacote `luminix/admin` ou integrações |
252
+ | `10` (padrão) | Customizações da aplicação |
253
+ | `> 10` | Overrides que precisam sobrescrever customizações anteriores |
254
+
255
+ ---
256
+
257
+ ## Próximos passos
258
+
259
+ - [Ações](acoes.md) — exemplos práticos de redutores de ações
260
+ - [Facades](facades.md) — referência completa dos redutores disponíveis por serviço
261
+ - [Volta ao índice](index.md)
@@ -0,0 +1,195 @@
1
+ # Facades
2
+
3
+ As facades são a interface pública dos serviços internos da biblioteca. Elas seguem o padrão `Reducible`, o que significa que qualquer método pode ser extendido via redutores.
4
+
5
+ ## Cms
6
+
7
+ Facade do `CmsService`. Gerencia rotas, menu, componentes e ações do painel.
8
+
9
+ ```ts
10
+ import { Cms } from '@luminix/mui-cms';
11
+ ```
12
+
13
+ ### Métodos
14
+
15
+ #### `Cms.getRoutes(): RouteObject[]`
16
+
17
+ Retorna as rotas registradas no painel (formato do `react-router-dom`).
18
+
19
+ #### `Cms.getMenuItems(): MenuItem[]`
20
+
21
+ Retorna os itens do menu lateral. Por padrão inclui o Dashboard e um item para cada modelo do manifesto.
22
+
23
+ #### `Cms.getComponents(): Record<string, React.ComponentType>`
24
+
25
+ Retorna o mapa de componentes registrados (ver [Componentes](componentes.md)).
26
+
27
+ #### `Cms.getComponent(name: string): React.ComponentType`
28
+
29
+ Retorna um componente pelo nome.
30
+
31
+ #### `Cms.getModelFormProps(item: ModelType): ModelFormProps`
32
+
33
+ Retorna as props do `ModelForm` para um item específico.
34
+
35
+ #### `Cms.getMassActions(ModelClass, currentTab): MassAction[]`
36
+
37
+ Retorna as ações em massa disponíveis para o modelo e aba ativos.
38
+
39
+ #### `Cms.getInstanceActions(ModelClass, currentTab): InstanceAction[]`
40
+
41
+ Retorna as ações de instância (por linha) disponíveis.
42
+
43
+ #### `Cms.getStaticActions(ModelClass, currentTab): StaticAction[]`
44
+
45
+ Retorna as ações estáticas (nível de listagem, ex.: "Criar novo").
46
+
47
+ #### `Cms.logoutUsing(callback: () => void): void`
48
+
49
+ Registra um callback customizado para o botão de logout do menu lateral. Substitui o comportamento padrão de `auth().logout()`.
50
+
51
+ ```ts
52
+ Cms.logoutUsing(() => {
53
+ window.location.href = '/login';
54
+ });
55
+ ```
56
+
57
+ Deve ser chamado no `boot()` de um `ServiceProvider`. Veja mais em [Extensibilidade — LogoutButton](extensibilidade.md#logoutbutton).
58
+
59
+ ### Redutores disponíveis
60
+
61
+ | Redutor | Assinatura | Uso |
62
+ |---|---|---|
63
+ | `cmsRoutes` | `(routes, components, models) => routes` | Adicionar/modificar rotas |
64
+ | `componentMap` | `(map) => map` | Substituir componentes internos |
65
+ | `menuItems` | `(items, models) => items` | Personalizar o menu lateral |
66
+ | `wireModelFormProps` | `(props, item) => props` | Alterar props do formulário por modelo |
67
+ | `massActions` | `(actions, ModelClass, tab) => actions` | Adicionar/remover ações em massa |
68
+ | `instanceActions` | `(actions, ModelClass, tab) => actions` | Adicionar/remover ações de instância |
69
+ | `staticActions` | `(actions, ModelClass, tab) => actions` | Adicionar/remover ações estáticas |
70
+ | `model{Name}Columns` | `() => Column[]` | Definir colunas da tabela por modelo |
71
+ | `mass{Name}Actions` | idem massActions | Ações em massa específicas por modelo |
72
+ | `instance{Name}Actions` | idem instanceActions | Ações de instância específicas por modelo |
73
+ | `static{Name}Actions` | idem staticActions | Ações estáticas específicas por modelo |
74
+
75
+ > `{Name}` é o nome do modelo em StudlyCase. Ex.: `modelPostColumns`, `massPostActions`.
76
+
77
+ ---
78
+
79
+ ## Filter
80
+
81
+ Facade do `FilterService`. Gerencia os filtros avançados da tabela.
82
+
83
+ ```ts
84
+ import { Filter } from '@luminix/mui-cms';
85
+ ```
86
+
87
+ ### Métodos
88
+
89
+ #### `Filter.getInputType(type: string): string`
90
+
91
+ Converte o tipo PHP/Eloquent do atributo no tipo de input HTML correspondente.
92
+
93
+ | Tipo do modelo | Input retornado |
94
+ |---|---|
95
+ | `int`, `float`, `number` | `number` |
96
+ | `date` | `date` |
97
+ | `datetime`, `timestamp` | `datetime-local` |
98
+ | `bool`, `boolean` | `boolean` |
99
+ | `autocomplete` | `autocomplete` |
100
+ | demais | `text` |
101
+
102
+ #### `Filter.getOperators(): string[]`
103
+
104
+ Retorna a lista de operadores configurada em `luminix.admin.filter.operators`.
105
+
106
+ #### `Filter.getMatchingOperators(column: FilterColumn): InputOption[]`
107
+
108
+ Retorna os operadores aplicáveis a uma coluna específica, filtrando por tipo e nullable.
109
+
110
+ #### `Filter.getFilterableColumns(ModelClass): FilterColumn[]`
111
+
112
+ Retorna as colunas filtráveis do modelo (atributos não ocultos e relacionamentos).
113
+
114
+ #### `Filter.checkIfCanApplyFilters(columnsFilter: FilteredColumn[]): boolean`
115
+
116
+ Verifica se o estado atual dos filtros é válido para envio (retorna `true` se houver filtro incompleto).
117
+
118
+ ### Redutores disponíveis
119
+
120
+ | Redutor | Assinatura | Uso |
121
+ |---|---|---|
122
+ | `filterableColumns` | `(columns, ModelClass) => columns` | Adicionar/remover colunas filtráveis |
123
+
124
+ ---
125
+
126
+ ## Icon
127
+
128
+ Serviço de gerenciamento de ícones. **Não** é uma facade `Reducible`; é um singleton instanciado diretamente.
129
+
130
+ ```ts
131
+ import { Icon } from '@luminix/mui-cms';
132
+ ```
133
+
134
+ ### Ícones registrados por padrão
135
+
136
+ A biblioteca pré-registra os seguintes ícones do `@mui/icons-material`:
137
+
138
+ `Add`, `AddCircleOutline`, `ArrowDownward`, `ArrowDropDown`, `ArrowUpward`, `CategoryOutlined`, `ChevronLeft`, `ChevronRight`, `Close`, `DashboardOutlined`, `ExpandLess`, `ExpandMore`, `FilterList`, `FirstPage`, `HighlightOffOutlined`, `LastPage`, `Menu`, `MoreVert`, `PeopleOutlined`, `Search`, `SwapVert`
139
+
140
+ ### Métodos
141
+
142
+ #### `Icon.registerIcon(name, component)`
143
+
144
+ Registra um ícone individual:
145
+
146
+ ```ts
147
+ import { Icon } from '@luminix/mui-cms';
148
+ import { Star } from '@mui/icons-material';
149
+
150
+ Icon.registerIcon('Star', Star);
151
+ ```
152
+
153
+ #### `Icon.registerIcon(iconMap)`
154
+
155
+ Registra múltiplos ícones de uma vez:
156
+
157
+ ```ts
158
+ import { Star, Home } from '@mui/icons-material';
159
+
160
+ Icon.registerIcon({ Star, Home });
161
+ ```
162
+
163
+ #### `Icon.make(name: string): React.ComponentType`
164
+
165
+ Retorna o componente do ícone pelo nome.
166
+
167
+ #### `Icon.render(name: string, props?): React.ReactNode`
168
+
169
+ Renderiza o ícone como nó React:
170
+
171
+ ```ts
172
+ Icon.render('Star', { fontSize: 'small', color: 'primary' })
173
+ ```
174
+
175
+ #### `Icon.forModel(model: string, icon: string)`
176
+
177
+ Associa um ícone a um modelo. O nome do modelo deve ser o `schemaName` (snake_case):
178
+
179
+ ```ts
180
+ Icon.forModel('post', 'Article');
181
+ Icon.forModel('user', 'PeopleOutlined'); // já registrado por padrão
182
+ ```
183
+
184
+ #### `Icon.all(): string[]`
185
+
186
+ Lista os nomes de todos os ícones registrados.
187
+
188
+ ---
189
+
190
+ ## Próximos passos
191
+
192
+ - [Hooks](hooks.md) — hooks para uso em componentes customizados
193
+ - [Ações](acoes.md) — como definir ações para os modelos
194
+ - [Extensibilidade](extensibilidade.md) — redutores em profundidade
195
+ - [Volta ao índice](index.md)