forlogic-core 1.5.0 → 1.5.2

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,192 @@
1
+ /**
2
+ * TEMPLATE: App Layout
3
+ * ⚠️ ORDEM DOS PROVIDERS É CRÍTICA: Consulte docs/AI_REFERENCE.md
4
+ */
5
+ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
6
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
7
+ import {
8
+ AuthProvider,
9
+ AppLayout,
10
+ ProtectedRoute,
11
+ SidebarConfig,
12
+ Toaster
13
+ } from 'forlogic-core'
14
+ import { Home, Users, Settings, BarChart3 } from 'lucide-react'
15
+
16
+ // Importar suas páginas
17
+ import { HomePage } from './pages/HomePage'
18
+ import { UsersPage } from './pages/UsersPage'
19
+ import { SettingsPage } from './pages/SettingsPage'
20
+ import { LoginPage } from './pages/LoginPage'
21
+
22
+ // Configurar React Query
23
+ const queryClient = new QueryClient({
24
+ defaultOptions: {
25
+ queries: {
26
+ staleTime: 5 * 60 * 1000, // 5 minutos
27
+ retry: 1
28
+ }
29
+ }
30
+ })
31
+
32
+ // Configuração da sidebar
33
+ const sidebarConfig: SidebarConfig = {
34
+ appName: 'APP_NAME',
35
+ navigation: [
36
+ {
37
+ label: 'Dashboard',
38
+ path: '/',
39
+ icon: BarChart3,
40
+ complementaryText: 'Visão geral do sistema'
41
+ },
42
+ {
43
+ label: 'Usuários',
44
+ path: '/users',
45
+ icon: Users,
46
+ complementaryText: 'Gestão de usuários'
47
+ },
48
+ {
49
+ label: 'Configurações',
50
+ path: '/settings',
51
+ icon: Settings,
52
+ complementaryText: 'Configurações do sistema'
53
+ }
54
+ ]
55
+ }
56
+
57
+ function App() {
58
+ return (
59
+ <QueryClientProvider client={queryClient}>
60
+ <AuthProvider>
61
+ <Router>
62
+ <Routes>
63
+ {/* Rota pública de login */}
64
+ <Route path="/login" element={<LoginPage />} />
65
+
66
+ {/* Rotas protegidas com layout */}
67
+ <Route path="/*" element={
68
+ <ProtectedRoute>
69
+ <AppLayout sidebarConfig={sidebarConfig}>
70
+ <Routes>
71
+ <Route path="/" element={<HomePage />} />
72
+ <Route path="/users" element={<UsersPage />} />
73
+ <Route path="/settings" element={<SettingsPage />} />
74
+ </Routes>
75
+ </AppLayout>
76
+ </ProtectedRoute>
77
+ } />
78
+ </Routes>
79
+ </Router>
80
+ <Toaster />
81
+ </AuthProvider>
82
+ </QueryClientProvider>
83
+ )
84
+ }
85
+
86
+ export default App
87
+
88
+ /*
89
+ ESTRUTURA ALTERNATIVA - Layout por Página:
90
+
91
+ Se preferir controlar o layout por página:
92
+
93
+ function App() {
94
+ return (
95
+ <QueryClientProvider client={queryClient}>
96
+ <AuthProvider>
97
+ <Router>
98
+ <Routes>
99
+ {/* Páginas sem layout */}
100
+ <Route path="/login" element={<LoginPage />} />
101
+
102
+ {/* Páginas com layout */}
103
+ <Route path="/" element={
104
+ <ProtectedRoute>
105
+ <AppLayout sidebarConfig={sidebarConfig}>
106
+ <HomePage />
107
+ </AppLayout>
108
+ </ProtectedRoute>
109
+ } />
110
+
111
+ <Route path="/users" element={
112
+ <ProtectedRoute>
113
+ <AppLayout sidebarConfig={sidebarConfig}>
114
+ <UsersPage />
115
+ </AppLayout>
116
+ </ProtectedRoute>
117
+ } />
118
+ </Routes>
119
+ </Router>
120
+ <Toaster />
121
+ </AuthProvider>
122
+ </QueryClientProvider>
123
+ )
124
+ }
125
+
126
+ EXEMPLO DE PÁGINA DE LOGIN:
127
+
128
+ import { useState } from 'react'
129
+ import { Navigate } from 'react-router-dom'
130
+ import { useAuth, Button, Input, Card, CardContent, CardHeader, CardTitle } from 'forlogic-core'
131
+
132
+ export function LoginPage() {
133
+ const { user, login } = useAuth()
134
+ const [email, setEmail] = useState('')
135
+ const [password, setPassword] = useState('')
136
+ const [loading, setLoading] = useState(false)
137
+
138
+ if (user) {
139
+ return <Navigate to="/" replace />
140
+ }
141
+
142
+ const handleLogin = async (e) => {
143
+ e.preventDefault()
144
+ setLoading(true)
145
+ try {
146
+ await login(email, password)
147
+ } catch (error) {
148
+ console.error('Erro no login:', error)
149
+ } finally {
150
+ setLoading(false)
151
+ }
152
+ }
153
+
154
+ return (
155
+ <div className="min-h-screen flex items-center justify-center bg-gray-50">
156
+ <Card className="w-full max-w-md">
157
+ <CardHeader>
158
+ <CardTitle>Entrar no Sistema</CardTitle>
159
+ </CardHeader>
160
+ <CardContent>
161
+ <form onSubmit={handleLogin} className="space-y-4">
162
+ <Input
163
+ type="email"
164
+ placeholder="Email"
165
+ value={email}
166
+ onChange={(e) => setEmail(e.target.value)}
167
+ required
168
+ />
169
+ <Input
170
+ type="password"
171
+ placeholder="Senha"
172
+ value={password}
173
+ onChange={(e) => setPassword(e.target.value)}
174
+ required
175
+ />
176
+ <Button type="submit" className="w-full" disabled={loading}>
177
+ {loading ? 'Entrando...' : 'Entrar'}
178
+ </Button>
179
+ </form>
180
+ </CardContent>
181
+ </Card>
182
+ </div>
183
+ )
184
+ }
185
+
186
+ CONFIGURAÇÃO DE AMBIENTE:
187
+
188
+ Certifique-se de ter as variáveis de ambiente configuradas:
189
+
190
+ REACT_APP_SUPABASE_URL=your_supabase_url
191
+ REACT_APP_SUPABASE_ANON_KEY=your_supabase_anon_key
192
+ */
@@ -0,0 +1,97 @@
1
+ /**
2
+ * TEMPLATE: CRUD Básico
3
+ * ⚠️ REGRAS: Consulte docs/AI_REFERENCE.md
4
+ */
5
+ import { createSimpleService, createCrudPage, createSimpleSaveHandler } from 'forlogic-core';
6
+
7
+ // 1. Definir interfaces
8
+ interface ENTITY_INTERFACE {
9
+ id: string;
10
+ FIELD_NAME: FIELD_TYPE;
11
+ created_at?: Date;
12
+ updated_at?: Date;
13
+ }
14
+
15
+ interface CreateENTITY_INTERFACEPayload {
16
+ FIELD_NAME: FIELD_TYPE;
17
+ }
18
+
19
+ interface UpdateENTITY_INTERFACEPayload {
20
+ FIELD_NAME: FIELD_TYPE;
21
+ }
22
+
23
+ // 2. Criar service
24
+ const { service: SERVICE_NAME, useCrudHook: useENTITY_NAMECrud } =
25
+ createSimpleService<ENTITY_INTERFACE, CreateENTITY_INTERFACEPayload, UpdateENTITY_INTERFACEPayload>({
26
+ tableName: 'TABLE_NAME',
27
+ entityName: 'ENTITY_NAME_SINGULAR',
28
+ searchFields: ['FIELD_NAME'],
29
+ schemaName: 'YOUR_SCHEMA' // Use o schema do seu projeto (ex: 'central', 'trainings', 'public')
30
+ });
31
+
32
+ // 3. Criar página
33
+ export const ENTITY_NAMEPage = () => {
34
+ const manager = useENTITY_NAMECrud();
35
+
36
+ const handleSave = createSimpleSaveHandler<ENTITY_INTERFACE, CreateENTITY_INTERFACEPayload, UpdateENTITY_INTERFACEPayload>(
37
+ manager,
38
+ (data) => ({ FIELD_NAME: data.FIELD_NAME }),
39
+ (data) => ({ FIELD_NAME: data.FIELD_NAME })
40
+ );
41
+
42
+ const CrudPageComponent = createCrudPage({
43
+ manager,
44
+ config: {
45
+ entityName: 'ENTITY_NAME_SINGULAR',
46
+ entityNamePlural: 'ENTITY_PLURAL',
47
+ columns: [
48
+ { key: 'FIELD_NAME', label: 'FIELD_LABEL', type: 'FIELD_TYPE' as const }
49
+ ],
50
+ formSections: [
51
+ {
52
+ id: 'basic',
53
+ title: 'Informações',
54
+ fields: [
55
+ { name: 'FIELD_NAME', label: 'FIELD_LABEL', type: 'FIELD_TYPE', required: true }
56
+ ]
57
+ }
58
+ ]
59
+ },
60
+ onSave: handleSave
61
+ });
62
+
63
+ return <CrudPageComponent />;
64
+ };
65
+
66
+ /*
67
+ INSTRUÇÕES DE USO:
68
+
69
+ 1. Substitua os placeholders:
70
+ - ENTITY_NAME: Nome do componente (ex: Products)
71
+ - ENTITY_INTERFACE: Interface TypeScript (ex: Product)
72
+ - ENTITY_NAME_SINGULAR: Nome singular em português (ex: produto)
73
+ - ENTITY_PLURAL: Nome plural em português (ex: Produtos)
74
+ - TABLE_NAME: Nome da tabela no Supabase (ex: products)
75
+ - FIELD_NAME: Nome do campo (ex: name)
76
+ - FIELD_LABEL: Label do campo (ex: Nome)
77
+ - FIELD_TYPE: Tipo TypeScript (ex: string, number, boolean)
78
+
79
+ 2. Adicione ao router:
80
+ <Route path="/ROUTE_PATH" element={<ENTITY_NAMEPage />} />
81
+
82
+ 3. Adicione à sidebar se necessário:
83
+ { label: 'LABEL', path: '/ROUTE_PATH', icon: ICON_NAME }
84
+
85
+ 4. Este padrão garante:
86
+ ✅ Interface CRUD completa
87
+ ✅ Formulário com validação
88
+ ✅ Tabela com ações
89
+ ✅ Estados de loading
90
+ ✅ UPDATE que funciona corretamente (preserva ID)
91
+ ✅ Tratamento de erros
92
+
93
+ IMPORTANTE:
94
+ - SEMPRE use useCrudHook() dentro do componente
95
+ - SEMPRE use createSimpleSaveHandler para garantir UPDATE correto
96
+ - NUNCA passe service diretamente para createCrudPage (causa erro "isLoading is undefined")
97
+ */
@@ -0,0 +1,182 @@
1
+ /**
2
+ * TEMPLATE: CRUD 100% Funcional
3
+ * ⚠️ REGRAS: Consulte docs/AI_REFERENCE.md
4
+ */
5
+
6
+ import { createSimpleService, createCrudPage, createSimpleSaveHandler } from 'forlogic-core';
7
+
8
+ // ============= PASSO 1: DEFINIR TIPOS =============
9
+
10
+ interface MyEntity {
11
+ id: string;
12
+ title: string;
13
+ description: string;
14
+ active: boolean;
15
+ created_at?: Date;
16
+ updated_at?: Date;
17
+ }
18
+
19
+ interface CreateMyEntityPayload {
20
+ title: string;
21
+ description: string;
22
+ active: boolean;
23
+ }
24
+
25
+ interface UpdateMyEntityPayload {
26
+ title: string;
27
+ description: string;
28
+ active: boolean;
29
+ }
30
+
31
+ // ============= PASSO 2: CRIAR SERVICE =============
32
+
33
+ const { service, useCrudHook } = createSimpleService<MyEntity, CreateMyEntityPayload, UpdateMyEntityPayload>({
34
+ tableName: 'my_entities', // Nome da tabela no Supabase
35
+ entityName: 'minha entidade', // Nome singular (para mensagens)
36
+ searchFields: ['title', 'description'], // Campos pesquisáveis
37
+ schemaName: 'YOUR_SCHEMA', // Use o schema do seu projeto (ex: 'central', 'trainings', 'public')
38
+ enableQualiexEnrichment: true // Enriquecimento automático com dados do Qualiex
39
+ });
40
+
41
+ // ============= PASSO 3: CRIAR COMPONENTE DA PÁGINA =============
42
+
43
+ export const MyEntitiesPage = () => {
44
+ // Hook que retorna o manager com todos os métodos CRUD
45
+ const manager = useCrudHook();
46
+
47
+ // Handler que garante que UPDATE preserva o ID
48
+ const handleSave = createSimpleSaveHandler<MyEntity, CreateMyEntityPayload, UpdateMyEntityPayload>(
49
+ manager,
50
+ // Transform CREATE - define campos do payload de criação
51
+ (data) => ({
52
+ title: data.title,
53
+ description: data.description || '',
54
+ active: data.active ?? true
55
+ }),
56
+ // Transform UPDATE - define campos do payload de atualização
57
+ (data) => ({
58
+ title: data.title,
59
+ description: data.description || '',
60
+ active: data.active
61
+ })
62
+ );
63
+
64
+ // Cria o componente da página CRUD
65
+ const CrudPageComponent = createCrudPage({
66
+ manager, // SEMPRE passe manager aqui, NUNCA service
67
+ config: {
68
+ entityName: 'Minha Entidade',
69
+ entityNamePlural: 'Minhas Entidades',
70
+
71
+ // Colunas da tabela
72
+ columns: [
73
+ {
74
+ key: 'title',
75
+ label: 'Título',
76
+ type: 'text' as const,
77
+ required: true,
78
+ searchable: true
79
+ },
80
+ {
81
+ key: 'description',
82
+ label: 'Descrição',
83
+ type: 'text' as const
84
+ },
85
+ {
86
+ key: 'active',
87
+ label: 'Ativo',
88
+ type: 'boolean' as const
89
+ }
90
+ ],
91
+
92
+ // Seções do formulário
93
+ formSections: [
94
+ {
95
+ id: 'basic-info',
96
+ title: 'Informações Básicas',
97
+ fields: [
98
+ {
99
+ name: 'title',
100
+ label: 'Título',
101
+ type: 'text',
102
+ required: true,
103
+ placeholder: 'Digite o título'
104
+ },
105
+ {
106
+ name: 'description',
107
+ label: 'Descrição',
108
+ type: 'textarea',
109
+ placeholder: 'Digite a descrição (opcional)'
110
+ },
111
+ {
112
+ name: 'active',
113
+ label: 'Ativo',
114
+ type: 'boolean'
115
+ }
116
+ ]
117
+ }
118
+ ]
119
+ },
120
+ onSave: handleSave // Handler que garante UPDATE correto
121
+ });
122
+
123
+ return <CrudPageComponent />;
124
+ };
125
+
126
+ // ============= CHECKLIST DE USO =============
127
+ /*
128
+ ✅ Definir interface da entidade (MyEntity)
129
+ ✅ Definir interfaces de payload (CreateMyEntityPayload, UpdateMyEntityPayload)
130
+ ✅ Criar service com createSimpleService
131
+ ✅ Usar useCrudHook() dentro do componente
132
+ ✅ Criar handleSave com createSimpleSaveHandler
133
+ ✅ Passar manager (não service) para createCrudPage
134
+ ✅ Configurar columns e formSections
135
+ ✅ Adicionar rota no router
136
+ ✅ Adicionar item na sidebar se necessário
137
+
138
+ ❌ ERROS COMUNS A EVITAR:
139
+ - Passar service para createCrudPage (causa "isLoading is undefined")
140
+ - Não usar createSimpleSaveHandler (causa UPDATE criar novo registro)
141
+ - Esquecer de chamar useCrudHook() (sem manager, componente quebra)
142
+ - Usar interface errada para payloads (validação falha)
143
+ */
144
+
145
+ // ============= EXEMPLOS DE CAMPOS ESPECIAIS =============
146
+ /*
147
+ // Campo select com opções fixas:
148
+ {
149
+ name: 'category',
150
+ label: 'Categoria',
151
+ type: 'select',
152
+ options: [
153
+ { label: 'Opção 1', value: 'opt1' },
154
+ { label: 'Opção 2', value: 'opt2' }
155
+ ],
156
+ required: true
157
+ }
158
+
159
+ // Campo de usuário do Qualiex:
160
+ {
161
+ name: 'id_user',
162
+ label: 'Responsável',
163
+ type: 'simple-qualiex-user-field',
164
+ required: true
165
+ }
166
+
167
+ // Campo de data:
168
+ {
169
+ name: 'due_date',
170
+ label: 'Data de Vencimento',
171
+ type: 'date'
172
+ }
173
+
174
+ // Campo numérico:
175
+ {
176
+ name: 'quantity',
177
+ label: 'Quantidade',
178
+ type: 'number',
179
+ min: 0,
180
+ step: 1
181
+ }
182
+ */