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.
- package/V2_README.md +414 -0
- package/dist/chunk-2NUS77CI.js +195 -0
- package/dist/chunk-5KPALVCK.js +280 -0
- package/dist/{chunk-2WFUL4XJ.js → chunk-5PZUUNHS.js} +717 -751
- package/dist/{chunk-E4KJZEXX.js → chunk-AJH2I5ZI.js} +5 -0
- package/dist/chunk-LVJ3GJRQ.js +360 -0
- package/dist/{chunk-PNKVD2UK.js → chunk-MXA7TAAG.js} +11 -1
- package/dist/cli.js +5 -4
- package/dist/firebase.cjs +296 -0
- package/dist/firebase.d.cts +136 -0
- package/dist/firebase.d.ts +136 -0
- package/dist/firebase.js +17 -0
- package/dist/hooks.cjs +236 -0
- package/dist/hooks.d.cts +184 -0
- package/dist/hooks.d.ts +184 -0
- package/dist/hooks.js +31 -0
- package/dist/index.cjs +1622 -1092
- package/dist/index.d.cts +16 -195
- package/dist/index.d.ts +16 -195
- package/dist/index.js +68 -262
- package/dist/runtime.cjs +366 -0
- package/dist/runtime.d.cts +229 -0
- package/dist/runtime.d.ts +229 -0
- package/dist/runtime.js +25 -0
- package/dist/symbols.js +2 -2
- package/examples/app-simbolico-completo.tsx +249 -0
- package/examples/nextjs-integration.tsx +446 -0
- package/examples/v2-demo.tsx +377 -0
- package/package.json +23 -11
- package/dist/react/index.cjs +0 -342
- package/dist/react/index.d.cts +0 -751
- package/dist/react/index.d.ts +0 -751
- package/dist/react/index.js +0 -284
|
@@ -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
|
+
*/
|