@neetru/cli 2.13.1 → 2.14.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/CHANGELOG.md +11 -0
- package/dist/commands/ai.js +8 -8
- package/dist/commands/autocomplete.d.ts +1 -1
- package/dist/commands/autocomplete.js +1 -0
- package/dist/commands/autocomplete.js.map +1 -1
- package/dist/commands/code.d.ts +48 -0
- package/dist/commands/code.js +433 -0
- package/dist/commands/code.js.map +1 -0
- package/dist/commands/fn.d.ts +6 -0
- package/dist/commands/fn.js +88 -0
- package/dist/commands/fn.js.map +1 -0
- package/dist/commands/init.js +147 -147
- package/dist/commands/marketplace.d.ts +36 -0
- package/dist/commands/marketplace.js +585 -0
- package/dist/commands/marketplace.js.map +1 -0
- package/dist/commands/ui.d.ts +1 -1
- package/dist/commands/ui.js +9 -0
- package/dist/commands/ui.js.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/ai/context.js +91 -91
- package/dist/lib/ai/orchestrator.js +6 -1
- package/dist/lib/ai/orchestrator.js.map +1 -1
- package/dist/lib/api-client.d.ts +1 -0
- package/dist/lib/api-client.js +4 -3
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/code-agent-files.d.ts +9 -0
- package/dist/lib/code-agent-files.js +213 -0
- package/dist/lib/code-agent-files.js.map +1 -0
- package/dist/lib/code-agent-protocol.d.ts +16 -0
- package/dist/lib/code-agent-protocol.js +34 -0
- package/dist/lib/code-agent-protocol.js.map +1 -0
- package/dist/lib/code-agent-settings.d.ts +20 -0
- package/dist/lib/code-agent-settings.js +38 -0
- package/dist/lib/code-agent-settings.js.map +1 -0
- package/package.json +1 -1
- package/templates/auth/callback.ts +22 -22
- package/templates/auth/sign-in.tsx +41 -41
- package/templates/billing/checkout.ts +22 -22
- package/templates/billing/page.tsx +43 -43
- package/templates/support/ticket-form.tsx +68 -68
- package/templates/usage/track.ts +30 -30
- package/templates/users/profile.tsx +43 -43
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/auth/sign-in.tsx` (gerado por `neetru add auth`).
|
|
3
|
-
*
|
|
4
|
-
* Botão Google → redireciona pra auth.neetru.com via SDK (`client.auth.signIn`).
|
|
5
|
-
*
|
|
6
|
-
* Customização: ajuste `redirectUri` se hospedar fora do domínio default.
|
|
7
|
-
*/
|
|
8
|
-
'use client';
|
|
9
|
-
|
|
10
|
-
import { useState } from 'react';
|
|
11
|
-
import { createNeetruClient } from '@neetru/sdk';
|
|
12
|
-
|
|
13
|
-
const client = createNeetruClient({
|
|
14
|
-
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
15
|
-
productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export function SignInButton() {
|
|
19
|
-
const [loading, setLoading] = useState(false);
|
|
20
|
-
|
|
21
|
-
async function handleSignIn() {
|
|
22
|
-
setLoading(true);
|
|
23
|
-
try {
|
|
24
|
-
await client.auth.signIn();
|
|
25
|
-
} catch (err) {
|
|
26
|
-
console.error('signIn failed', err);
|
|
27
|
-
setLoading(false);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<button
|
|
33
|
-
type="button"
|
|
34
|
-
onClick={handleSignIn}
|
|
35
|
-
disabled={loading}
|
|
36
|
-
className="inline-flex items-center justify-center px-4 py-2 border bg-white hover:bg-zinc-50 text-zinc-900 font-medium"
|
|
37
|
-
>
|
|
38
|
-
{loading ? 'Redirecionando...' : 'Entrar com Neetru'}
|
|
39
|
-
</button>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/auth/sign-in.tsx` (gerado por `neetru add auth`).
|
|
3
|
+
*
|
|
4
|
+
* Botão Google → redireciona pra auth.neetru.com via SDK (`client.auth.signIn`).
|
|
5
|
+
*
|
|
6
|
+
* Customização: ajuste `redirectUri` se hospedar fora do domínio default.
|
|
7
|
+
*/
|
|
8
|
+
'use client';
|
|
9
|
+
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import { createNeetruClient } from '@neetru/sdk';
|
|
12
|
+
|
|
13
|
+
const client = createNeetruClient({
|
|
14
|
+
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
15
|
+
productId: process.env.NEXT_PUBLIC_NEETRU_PRODUCT_ID,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export function SignInButton() {
|
|
19
|
+
const [loading, setLoading] = useState(false);
|
|
20
|
+
|
|
21
|
+
async function handleSignIn() {
|
|
22
|
+
setLoading(true);
|
|
23
|
+
try {
|
|
24
|
+
await client.auth.signIn();
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error('signIn failed', err);
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
onClick={handleSignIn}
|
|
35
|
+
disabled={loading}
|
|
36
|
+
className="inline-flex items-center justify-center px-4 py-2 border bg-white hover:bg-zinc-50 text-zinc-900 font-medium"
|
|
37
|
+
>
|
|
38
|
+
{loading ? 'Redirecionando...' : 'Entrar com Neetru'}
|
|
39
|
+
</button>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/billing/checkout.ts` (gerado por `neetru add billing`).
|
|
3
|
-
*
|
|
4
|
-
* Inicia checkout Stripe via Core. Server route handler.
|
|
5
|
-
*/
|
|
6
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
7
|
-
|
|
8
|
-
export const runtime = 'nodejs';
|
|
9
|
-
|
|
10
|
-
export async function POST(request: NextRequest) {
|
|
11
|
-
const body = await request.json();
|
|
12
|
-
const slug = body?.slug as string | undefined;
|
|
13
|
-
if (!slug) {
|
|
14
|
-
return NextResponse.json({ error: 'slug required' }, { status: 400 });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Hit Core REST: POST /api/v1/billing/checkout
|
|
18
|
-
// (Sprint 9 expõe via SDK direto)
|
|
19
|
-
return NextResponse.json({
|
|
20
|
-
redirectUrl: `https://minhaconta.neetru.com/checkout?slug=${slug}`,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/billing/checkout.ts` (gerado por `neetru add billing`).
|
|
3
|
+
*
|
|
4
|
+
* Inicia checkout Stripe via Core. Server route handler.
|
|
5
|
+
*/
|
|
6
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
7
|
+
|
|
8
|
+
export const runtime = 'nodejs';
|
|
9
|
+
|
|
10
|
+
export async function POST(request: NextRequest) {
|
|
11
|
+
const body = await request.json();
|
|
12
|
+
const slug = body?.slug as string | undefined;
|
|
13
|
+
if (!slug) {
|
|
14
|
+
return NextResponse.json({ error: 'slug required' }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Hit Core REST: POST /api/v1/billing/checkout
|
|
18
|
+
// (Sprint 9 expõe via SDK direto)
|
|
19
|
+
return NextResponse.json({
|
|
20
|
+
redirectUrl: `https://minhaconta.neetru.com/checkout?slug=${slug}`,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/billing/page.tsx` (gerado por `neetru add billing`).
|
|
3
|
-
*
|
|
4
|
-
* Página simples: lista planos + botão checkout via SDK.
|
|
5
|
-
*/
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import { useEffect, useState } from 'react';
|
|
9
|
-
import { createNeetruClient, type Product } from '@neetru/sdk';
|
|
10
|
-
|
|
11
|
-
const client = createNeetruClient({
|
|
12
|
-
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export default function BillingPage() {
|
|
16
|
-
const [products, setProducts] = useState<Product[]>([]);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
client.catalog
|
|
19
|
-
.list()
|
|
20
|
-
.then(({ products }) => setProducts(products))
|
|
21
|
-
.catch((err) => console.error(err));
|
|
22
|
-
}, []);
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<main className="p-8">
|
|
26
|
-
<h1 className="text-3xl font-bold mb-6">Planos</h1>
|
|
27
|
-
<div className="grid gap-4 md:grid-cols-3">
|
|
28
|
-
{products.map((p) => (
|
|
29
|
-
<article key={p.slug} className="border p-6">
|
|
30
|
-
<h2 className="text-xl font-semibold">{p.name}</h2>
|
|
31
|
-
<p className="text-sm text-zinc-600 mt-1">{p.tagline}</p>
|
|
32
|
-
<a
|
|
33
|
-
href={`/checkout?slug=${p.slug}`}
|
|
34
|
-
className="inline-block mt-4 px-4 py-2 bg-zinc-900 text-white"
|
|
35
|
-
>
|
|
36
|
-
Assinar
|
|
37
|
-
</a>
|
|
38
|
-
</article>
|
|
39
|
-
))}
|
|
40
|
-
</div>
|
|
41
|
-
</main>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/billing/page.tsx` (gerado por `neetru add billing`).
|
|
3
|
+
*
|
|
4
|
+
* Página simples: lista planos + botão checkout via SDK.
|
|
5
|
+
*/
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
|
+
import { createNeetruClient, type Product } from '@neetru/sdk';
|
|
10
|
+
|
|
11
|
+
const client = createNeetruClient({
|
|
12
|
+
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default function BillingPage() {
|
|
16
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
client.catalog
|
|
19
|
+
.list()
|
|
20
|
+
.then(({ products }) => setProducts(products))
|
|
21
|
+
.catch((err) => console.error(err));
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<main className="p-8">
|
|
26
|
+
<h1 className="text-3xl font-bold mb-6">Planos</h1>
|
|
27
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
28
|
+
{products.map((p) => (
|
|
29
|
+
<article key={p.slug} className="border p-6">
|
|
30
|
+
<h2 className="text-xl font-semibold">{p.name}</h2>
|
|
31
|
+
<p className="text-sm text-zinc-600 mt-1">{p.tagline}</p>
|
|
32
|
+
<a
|
|
33
|
+
href={`/checkout?slug=${p.slug}`}
|
|
34
|
+
className="inline-block mt-4 px-4 py-2 bg-zinc-900 text-white"
|
|
35
|
+
>
|
|
36
|
+
Assinar
|
|
37
|
+
</a>
|
|
38
|
+
</article>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
</main>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/support/ticket-form.tsx` (gerado por `neetru add support`).
|
|
3
|
-
*
|
|
4
|
-
* Form de suporte: cria ticket via SDK.
|
|
5
|
-
*/
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import { useState } from 'react';
|
|
9
|
-
import { createNeetruClient } from '@neetru/sdk';
|
|
10
|
-
|
|
11
|
-
const client = createNeetruClient({
|
|
12
|
-
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export function SupportTicketForm() {
|
|
16
|
-
const [subject, setSubject] = useState('');
|
|
17
|
-
const [message, setMessage] = useState('');
|
|
18
|
-
const [submitting, setSubmitting] = useState(false);
|
|
19
|
-
const [result, setResult] = useState<string | null>(null);
|
|
20
|
-
|
|
21
|
-
async function handleSubmit(e: React.FormEvent) {
|
|
22
|
-
e.preventDefault();
|
|
23
|
-
setSubmitting(true);
|
|
24
|
-
try {
|
|
25
|
-
const ticket = await client.support.createTicket({ subject, message });
|
|
26
|
-
setResult(`Ticket criado: ${ticket.id}`);
|
|
27
|
-
setSubject('');
|
|
28
|
-
setMessage('');
|
|
29
|
-
} catch (err) {
|
|
30
|
-
setResult('Falha ao criar ticket.');
|
|
31
|
-
console.error(err);
|
|
32
|
-
} finally {
|
|
33
|
-
setSubmitting(false);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<form onSubmit={handleSubmit} className="grid gap-3 max-w-md">
|
|
39
|
-
<label className="grid gap-1">
|
|
40
|
-
<span className="text-sm font-medium">Assunto</span>
|
|
41
|
-
<input
|
|
42
|
-
required
|
|
43
|
-
value={subject}
|
|
44
|
-
onChange={(e) => setSubject(e.target.value)}
|
|
45
|
-
className="border px-3 py-2"
|
|
46
|
-
/>
|
|
47
|
-
</label>
|
|
48
|
-
<label className="grid gap-1">
|
|
49
|
-
<span className="text-sm font-medium">Mensagem</span>
|
|
50
|
-
<textarea
|
|
51
|
-
required
|
|
52
|
-
rows={5}
|
|
53
|
-
value={message}
|
|
54
|
-
onChange={(e) => setMessage(e.target.value)}
|
|
55
|
-
className="border px-3 py-2"
|
|
56
|
-
/>
|
|
57
|
-
</label>
|
|
58
|
-
<button
|
|
59
|
-
type="submit"
|
|
60
|
-
disabled={submitting}
|
|
61
|
-
className="px-4 py-2 bg-zinc-900 text-white"
|
|
62
|
-
>
|
|
63
|
-
{submitting ? 'Enviando...' : 'Enviar'}
|
|
64
|
-
</button>
|
|
65
|
-
{result && <p className="text-sm">{result}</p>}
|
|
66
|
-
</form>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/support/ticket-form.tsx` (gerado por `neetru add support`).
|
|
3
|
+
*
|
|
4
|
+
* Form de suporte: cria ticket via SDK.
|
|
5
|
+
*/
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
import { createNeetruClient } from '@neetru/sdk';
|
|
10
|
+
|
|
11
|
+
const client = createNeetruClient({
|
|
12
|
+
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export function SupportTicketForm() {
|
|
16
|
+
const [subject, setSubject] = useState('');
|
|
17
|
+
const [message, setMessage] = useState('');
|
|
18
|
+
const [submitting, setSubmitting] = useState(false);
|
|
19
|
+
const [result, setResult] = useState<string | null>(null);
|
|
20
|
+
|
|
21
|
+
async function handleSubmit(e: React.FormEvent) {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
setSubmitting(true);
|
|
24
|
+
try {
|
|
25
|
+
const ticket = await client.support.createTicket({ subject, message });
|
|
26
|
+
setResult(`Ticket criado: ${ticket.id}`);
|
|
27
|
+
setSubject('');
|
|
28
|
+
setMessage('');
|
|
29
|
+
} catch (err) {
|
|
30
|
+
setResult('Falha ao criar ticket.');
|
|
31
|
+
console.error(err);
|
|
32
|
+
} finally {
|
|
33
|
+
setSubmitting(false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<form onSubmit={handleSubmit} className="grid gap-3 max-w-md">
|
|
39
|
+
<label className="grid gap-1">
|
|
40
|
+
<span className="text-sm font-medium">Assunto</span>
|
|
41
|
+
<input
|
|
42
|
+
required
|
|
43
|
+
value={subject}
|
|
44
|
+
onChange={(e) => setSubject(e.target.value)}
|
|
45
|
+
className="border px-3 py-2"
|
|
46
|
+
/>
|
|
47
|
+
</label>
|
|
48
|
+
<label className="grid gap-1">
|
|
49
|
+
<span className="text-sm font-medium">Mensagem</span>
|
|
50
|
+
<textarea
|
|
51
|
+
required
|
|
52
|
+
rows={5}
|
|
53
|
+
value={message}
|
|
54
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
55
|
+
className="border px-3 py-2"
|
|
56
|
+
/>
|
|
57
|
+
</label>
|
|
58
|
+
<button
|
|
59
|
+
type="submit"
|
|
60
|
+
disabled={submitting}
|
|
61
|
+
className="px-4 py-2 bg-zinc-900 text-white"
|
|
62
|
+
>
|
|
63
|
+
{submitting ? 'Enviando...' : 'Enviar'}
|
|
64
|
+
</button>
|
|
65
|
+
{result && <p className="text-sm">{result}</p>}
|
|
66
|
+
</form>
|
|
67
|
+
);
|
|
68
|
+
}
|
package/templates/usage/track.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/usage/track.ts` (gerado por `neetru add usage`).
|
|
3
|
-
*
|
|
4
|
-
* Helper para reportar uso de resources metered ao Core.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { reportApiCall } from '@/lib/neetru/usage/track';
|
|
9
|
-
* await reportApiCall();
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
import { createNeetruClient } from '@neetru/sdk';
|
|
13
|
-
|
|
14
|
-
const client = createNeetruClient({
|
|
15
|
-
apiKey: process.env.NEETRU_API_KEY,
|
|
16
|
-
productId: process.env.NEETRU_PRODUCT_ID,
|
|
17
|
-
tenantId: process.env.NEETRU_TENANT_ID,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export async function reportApiCall(qty = 1): Promise<void> {
|
|
21
|
-
try {
|
|
22
|
-
await client.usage.report('api_calls', qty);
|
|
23
|
-
} catch (err) {
|
|
24
|
-
console.warn('usage.report failed', err);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function checkApiCallLimit() {
|
|
29
|
-
return client.usage.check('api_calls');
|
|
30
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/usage/track.ts` (gerado por `neetru add usage`).
|
|
3
|
+
*
|
|
4
|
+
* Helper para reportar uso de resources metered ao Core.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { reportApiCall } from '@/lib/neetru/usage/track';
|
|
9
|
+
* await reportApiCall();
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { createNeetruClient } from '@neetru/sdk';
|
|
13
|
+
|
|
14
|
+
const client = createNeetruClient({
|
|
15
|
+
apiKey: process.env.NEETRU_API_KEY,
|
|
16
|
+
productId: process.env.NEETRU_PRODUCT_ID,
|
|
17
|
+
tenantId: process.env.NEETRU_TENANT_ID,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export async function reportApiCall(qty = 1): Promise<void> {
|
|
21
|
+
try {
|
|
22
|
+
await client.usage.report('api_calls', qty);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.warn('usage.report failed', err);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function checkApiCallLimit() {
|
|
29
|
+
return client.usage.check('api_calls');
|
|
30
|
+
}
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template — `src/lib/neetru/users/profile.tsx` (gerado por `neetru add users`).
|
|
3
|
-
*
|
|
4
|
-
* Componente de perfil mostra dados do user via SDK.
|
|
5
|
-
*/
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import { useEffect, useState } from 'react';
|
|
9
|
-
import { createNeetruClient, type NeetruUser } from '@neetru/sdk';
|
|
10
|
-
|
|
11
|
-
const client = createNeetruClient({
|
|
12
|
-
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export function UserProfile() {
|
|
16
|
-
const [user, setUser] = useState<NeetruUser | null>(null);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
return client.auth.onAuthStateChanged(setUser);
|
|
19
|
-
}, []);
|
|
20
|
-
|
|
21
|
-
if (!user) return <p>Não autenticado.</p>;
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<div className="border p-4">
|
|
25
|
-
<p>
|
|
26
|
-
<strong>Nome:</strong> {user.displayName ?? '—'}
|
|
27
|
-
</p>
|
|
28
|
-
<p>
|
|
29
|
-
<strong>Email:</strong> {user.email}
|
|
30
|
-
</p>
|
|
31
|
-
<p>
|
|
32
|
-
<strong>UID:</strong> {user.uid}
|
|
33
|
-
</p>
|
|
34
|
-
<button
|
|
35
|
-
type="button"
|
|
36
|
-
onClick={() => client.auth.signOut()}
|
|
37
|
-
className="mt-4 px-3 py-1 border"
|
|
38
|
-
>
|
|
39
|
-
Sair
|
|
40
|
-
</button>
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template — `src/lib/neetru/users/profile.tsx` (gerado por `neetru add users`).
|
|
3
|
+
*
|
|
4
|
+
* Componente de perfil mostra dados do user via SDK.
|
|
5
|
+
*/
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
|
+
import { createNeetruClient, type NeetruUser } from '@neetru/sdk';
|
|
10
|
+
|
|
11
|
+
const client = createNeetruClient({
|
|
12
|
+
apiKey: process.env.NEXT_PUBLIC_NEETRU_API_KEY,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export function UserProfile() {
|
|
16
|
+
const [user, setUser] = useState<NeetruUser | null>(null);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
return client.auth.onAuthStateChanged(setUser);
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
if (!user) return <p>Não autenticado.</p>;
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="border p-4">
|
|
25
|
+
<p>
|
|
26
|
+
<strong>Nome:</strong> {user.displayName ?? '—'}
|
|
27
|
+
</p>
|
|
28
|
+
<p>
|
|
29
|
+
<strong>Email:</strong> {user.email}
|
|
30
|
+
</p>
|
|
31
|
+
<p>
|
|
32
|
+
<strong>UID:</strong> {user.uid}
|
|
33
|
+
</p>
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
onClick={() => client.auth.signOut()}
|
|
37
|
+
className="mt-4 px-3 py-1 border"
|
|
38
|
+
>
|
|
39
|
+
Sair
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|