eny-ai 1.0.0 → 2.0.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,446 @@
1
+ /**
2
+ * 🔀 NEXT.JS INTEGRATION COM ENY-AI
3
+ * 90% Simbólico com App Router
4
+ *
5
+ * Uso:
6
+ * - Server Components: 🔀 Router, 🌐 API
7
+ * - Client Components: 📝 Forms, 🎯 UI
8
+ */
9
+
10
+ import React from 'react';
11
+ import {
12
+ useEnyState,
13
+ useEnyEffect,
14
+ 🃏, 🎯, 🔘, 📝, 📦,
15
+ 🔐Manager,
16
+ criarSistema,
17
+ useENY
18
+ } from 'eny-ai';
19
+ import { useRouter, usePathname } from 'next/navigation';
20
+ import { useSearchParams } from 'next/navigation';
21
+
22
+ // ═════════════════════════════════════════════════════════════════════════════════
23
+ // SYMBOLIC ROUTER & NAVIGATION
24
+ // ═════════════════════════════════════════════════════════════════════════════════
25
+
26
+ /**
27
+ * 🔀 Router Simbólico
28
+ * Facilita navegação com API limpa
29
+ */
30
+ export const 🔀Router = {
31
+ // Navegar para rota
32
+ 🚀: (rota: string) => {
33
+ const router = useRouter();
34
+ router.push(rota);
35
+ },
36
+
37
+ // Navegar com parâmetros
38
+ 🎯: (rota: string, params: Record<string, string>) => {
39
+ const router = useRouter();
40
+ const query = new URLSearchParams(params).toString();
41
+ router.push(`${rota}?${query}`);
42
+ },
43
+
44
+ // Voltar
45
+ ◀: () => {
46
+ const router = useRouter();
47
+ router.back();
48
+ },
49
+
50
+ // Atualizar página
51
+ 🔄: () => {
52
+ const router = useRouter();
53
+ router.refresh();
54
+ },
55
+
56
+ // Obter rota atual
57
+ 📍: () => {
58
+ return usePathname();
59
+ },
60
+
61
+ // Obter query params
62
+ 🔍: () => {
63
+ return useSearchParams();
64
+ }
65
+ };
66
+
67
+ // ═════════════════════════════════════════════════════════════════════════════════
68
+ // SYMBOLIC FORMS & DATA BINDING
69
+ // ═════════════════════════════════════════════════════════════════════════════════
70
+
71
+ /**
72
+ * 📋 Formulário Simbólico
73
+ * Manage form state and validation with symbols
74
+ */
75
+ export const useEnyForm = <T extends Record<string, any>>(
76
+ inicial: T,
77
+ onSubmit?: (data: T) => Promise<void>
78
+ ) => {
79
+ const [📝, set📝] = useEnyState<T>('form', inicial);
80
+ const [⚠️, set⚠️] = useEnyState<Partial<Record<keyof T, string>>>('errors', {});
81
+ const [⏳, set⏳] = useEnyState<boolean>('submitting', false);
82
+
83
+ const 🎯 = (campo: keyof T) => (
84
+ e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
85
+ ) => {
86
+ set📝((atual: T) => ({
87
+ ...atual,
88
+ [campo]: e.target.value
89
+ }));
90
+ // Limpar erro
91
+ set⚠️((atual: any) => ({
92
+ ...atual,
93
+ [campo]: undefined
94
+ }));
95
+ };
96
+
97
+ const ⚡ = async (e: React.FormEvent) => {
98
+ e.preventDefault();
99
+ set⏳(true);
100
+ try {
101
+ if (onSubmit) {
102
+ await onSubmit(📝);
103
+ }
104
+ } catch (erro) {
105
+ set⚠️((atual: any) => ({
106
+ ...atual,
107
+ _submit: String(erro)
108
+ }));
109
+ } finally {
110
+ set⏳(false);
111
+ }
112
+ };
113
+
114
+ return {
115
+ 📝, // form data
116
+ set📝, // set form data
117
+ 🎯, // field change handler
118
+ ⚠️, // errors
119
+ set⚠️, // set errors
120
+ ⏳, // is submitting
121
+ ⚡ // handle submit
122
+ };
123
+ };
124
+
125
+ // ═════════════════════════════════════════════════════════════════════════════════
126
+ // SYMBOLIC PAGE COMPONENTS
127
+ // ═════════════════════════════════════════════════════════════════════════════════
128
+
129
+ /**
130
+ * 🔐 Login Page with Server-side Validation
131
+ * 'use client' - Cliente-side component
132
+ */
133
+ export const 🔐Page: React.FC = () => {
134
+ const { 📝, 🎯, ⚠️, ⏳, ⚡ } = useEnyForm(
135
+ { email: '', password: '' },
136
+ async (data) => {
137
+ const usuario = await 🔐Manager.🔐(data.email, data.password);
138
+ if (!usuario) {
139
+ throw new Error('Credenciais inválidas');
140
+ }
141
+ // Navegar para dashboard
142
+ 🔀Router.🚀('/dashboard');
143
+ }
144
+ );
145
+
146
+ return (
147
+ <📦 className="min-h-screen flex items-center justify-center">
148
+ <🃏
149
+ title="🔐 Login Simbólico"
150
+ description="Acesso com 90% código simbólico"
151
+ >
152
+ <form onSubmit={⚡} className="space-y-4">
153
+ <div>
154
+ <📝
155
+ type="email"
156
+ placeholder="📧 Email"
157
+ value={📝.email}
158
+ onChange={🎯('email')}
159
+ />
160
+ {⚠️.email && <span className="text-red-500 text-sm">{⚠️.email}</span>}
161
+ </div>
162
+
163
+ <div>
164
+ <📝
165
+ type="password"
166
+ placeholder="🔒 Senha"
167
+ value={📝.password}
168
+ onChange={🎯('password')}
169
+ />
170
+ {⚠️.password && <span className="text-red-500 text-sm">{⚠️.password}</span>}
171
+ </div>
172
+
173
+ <🔘
174
+ label={⏳ ? '⏳ Entrando...' : '🔑 Entrar'}
175
+ type="submit"
176
+ disabled={⏳}
177
+ className="w-full bg-blue-600 hover:bg-blue-700 text-white"
178
+ />
179
+
180
+ {⚠️._submit && (
181
+ <div className="text-red-500 text-center">❌ {⚠️._submit}</div>
182
+ )}
183
+ </form>
184
+ </🃏>
185
+ </📦>
186
+ );
187
+ };
188
+
189
+ /**
190
+ * 🏠 Dashboard Page
191
+ * Com dados carregados do Firestore
192
+ */
193
+ export const 🏠DashboardPage: React.FC = () => {
194
+ const { Σ } = useENY();
195
+ const [📊, set📊] = useEnyState<any[]>('dados', []);
196
+ const [⏳, set⏳] = useEnyState<boolean>('loading', true);
197
+
198
+ useEnyEffect(() => {
199
+ const carregarDados = async () => {
200
+ try {
201
+ const dados = await 🔐Manager.📖('items');
202
+ set📊(dados || []);
203
+ } catch (erro) {
204
+ console.error('Erro ao carregar:', erro);
205
+ } finally {
206
+ set⏳(false);
207
+ }
208
+ };
209
+
210
+ carregarDados();
211
+ }, []);
212
+
213
+ const ⚡_logout = async () => {
214
+ await 🔐Manager.🚪();
215
+ 🔀Router.🚀('/');
216
+ };
217
+
218
+ return (
219
+ <📦>
220
+ <div className="flex justify-between items-center mb-6">
221
+ <🎯 title="🏠 Dashboard Simbólico" />
222
+ <🔘 onClick={⚡_logout} label="🚪 Logout" className="bg-red-500" />
223
+ </div>
224
+
225
+ {⏳ && <div className="text-center py-8">⏳ Carregando dados...</div>}
226
+
227
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
228
+ {📊?.map((item: any, idx: number) => (
229
+ <🃏
230
+ key={idx}
231
+ title={item.title}
232
+ description={item.description}
233
+ tags={item.tags}
234
+ image={item.image}
235
+ />
236
+ ))}
237
+ </div>
238
+ </📦>
239
+ );
240
+ };
241
+
242
+ /**
243
+ * ➕ Create Item Page
244
+ * Com forma e validação
245
+ */
246
+ export const ➕CreatItemPage: React.FC = () => {
247
+ const router = 🔀Router;
248
+ const { 📝, 🎯, ⚠️, ⏳, ⚡ } = useEnyForm(
249
+ {
250
+ title: '',
251
+ description: '',
252
+ image: '',
253
+ tags: ''
254
+ },
255
+ async (data) => {
256
+ const novo = await 🔐Manager.✍️('items', {
257
+ title: data.title,
258
+ description: data.description,
259
+ image: data.image,
260
+ tags: data.tags.split(',').map((t) => t.trim()),
261
+ createdAt: new Date().toISOString()
262
+ });
263
+
264
+ if (novo) {
265
+ router.🚀('/dashboard');
266
+ }
267
+ }
268
+ );
269
+
270
+ return (
271
+ <📦 className="max-w-2xl mx-auto">
272
+ <🎯 title="➕ Novo Item" />
273
+
274
+ <form onSubmit={⚡} className="space-y-4">
275
+ <div>
276
+ <label className="block text-sm font-medium mb-2">Título</label>
277
+ <📝
278
+ type="text"
279
+ placeholder="Título do item"
280
+ value={📝.title}
281
+ onChange={🎯('title')}
282
+ className="w-full"
283
+ />
284
+ </div>
285
+
286
+ <div>
287
+ <label className="block text-sm font-medium mb-2">Descrição</label>
288
+ <textarea
289
+ placeholder="Descrição"
290
+ value={📝.description}
291
+ onChange={🎯('description')}
292
+ className="w-full border rounded p-2"
293
+ />
294
+ </div>
295
+
296
+ <div>
297
+ <label className="block text-sm font-medium mb-2">Imagem URL</label>
298
+ <📝
299
+ type="url"
300
+ placeholder="https://..."
301
+ value={📝.image}
302
+ onChange={🎯('image')}
303
+ />
304
+ </div>
305
+
306
+ <div>
307
+ <label className="block text-sm font-medium mb-2">Tags (vírgula separado)</label>
308
+ <📝
309
+ type="text"
310
+ placeholder="tag1, tag2, tag3"
311
+ value={📝.tags}
312
+ onChange={🎯('tags')}
313
+ />
314
+ </div>
315
+
316
+ <🔘
317
+ label={⏳ ? '⏳...' : '💾 Salvar'}
318
+ type="submit"
319
+ disabled={⏳}
320
+ className="w-full bg-green-600 hover:bg-green-700"
321
+ />
322
+ </form>
323
+ </📦>
324
+ );
325
+ };
326
+
327
+ /**
328
+ * 🔍 Search Page
329
+ * Com query params simbólicos
330
+ */
331
+ export const 🔍SearchPage: React.FC = () => {
332
+ const searchParams = 🔀Router.🔍();
333
+ const q = searchParams?.get('q') || '';
334
+ const [🔎, set🔎] = useEnyState<string>('query', q);
335
+ const [📋, set📋] = useEnyState<any[]>('results', []);
336
+
337
+ const ⚡_search = async (termo: string) => {
338
+ if (!termo.trim()) return;
339
+
340
+ // Aqui você faria uma busca no Firestore
341
+ // const results = await 🔐Manager.📖('items');
342
+ // const filtered = results.filter(item =>
343
+ // item.title.includes(termo) ||
344
+ // item.description.includes(termo)
345
+ // );
346
+ // set📋(filtered);
347
+ };
348
+
349
+ return (
350
+ <📦>
351
+ <🎯 title="🔍 Buscar Itens" />
352
+
353
+ <div className="mb-6">
354
+ <📝
355
+ type="text"
356
+ placeholder="🔍 Buscar..."
357
+ value={🔎}
358
+ onChange={(e) => set🔎(e.target.value)}
359
+ className="w-full"
360
+ />
361
+ <🔘
362
+ onClick={() => ⚡_search(🔎)}
363
+ label="🔍 Buscar"
364
+ className="mt-2 w-full"
365
+ />
366
+ </div>
367
+
368
+ {📋.map((item, idx) => (
369
+ <🃏
370
+ key={idx}
371
+ title={item.title}
372
+ description={item.description}
373
+ />
374
+ ))}
375
+ </📦>
376
+ );
377
+ };
378
+
379
+ // ═════════════════════════════════════════════════════════════════════════════════
380
+ // LAYOUT ROOT COM PROVIDER
381
+ // ═════════════════════════════════════════════════════════════════════════════════
382
+
383
+ import { ENYProvider } from 'eny-ai';
384
+
385
+ export const 🌍Layout: React.FC<{ children: React.ReactNode }> = ({
386
+ children
387
+ }) => {
388
+ return (
389
+ <ENYProvider>
390
+ <html lang="pt-BR">
391
+ <head>
392
+ <title>🧠 ENY-AI Next.js App</title>
393
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
394
+ </head>
395
+ <body className="bg-gradient-to-br from-indigo-50 to-blue-100">
396
+ <nav className="bg-indigo-600 text-white p-4">
397
+ <div className="max-w-6xl mx-auto flex justify-between items-center">
398
+ <span className="text-xl font-bold">🧠 ENY-AI</span>
399
+ <div className="space-x-4">
400
+ <a href="/dashboard" className="hover:text-indigo-200">
401
+ 🏠 Dashboard
402
+ </a>
403
+ <a href="/create" className="hover:text-indigo-200">
404
+ ➕ Novo
405
+ </a>
406
+ <a href="/search" className="hover:text-indigo-200">
407
+ 🔍 Buscar
408
+ </a>
409
+ </div>
410
+ </div>
411
+ </nav>
412
+ {children}
413
+ </body>
414
+ </html>
415
+ </ENYProvider>
416
+ );
417
+ };
418
+
419
+ // ═════════════════════════════════════════════════════════════════════════════════
420
+ // COMO USAR
421
+ // ═════════════════════════════════════════════════════════════════════════════════
422
+
423
+ /**
424
+ * 1. Criar arquivo: app/page.tsx
425
+ * export default 🔐Page;
426
+ *
427
+ * 2. Criar arquivo: app/dashboard/page.tsx
428
+ * export default 🏠DashboardPage;
429
+ *
430
+ * 3. Criar arquivo: app/create/page.tsx
431
+ * 'use client'
432
+ * export default ➕CreatItemPage;
433
+ *
434
+ * 4. Criar arquivo: app/search/page.tsx
435
+ * 'use client'
436
+ * export default 🔍SearchPage;
437
+ *
438
+ * 5. Atualizar: app/layout.tsx
439
+ * export default 🌍Layout;
440
+ *
441
+ * 6. Instalar dependências:
442
+ * npm install eny-ai firebase next
443
+ *
444
+ * 7. Rodar:
445
+ * npm run dev
446
+ */