Skip to content

Commit a839fab

Browse files
author
Rodrigo Venturi
committed
feat: initial commit
1 parent 65a8028 commit a839fab

31 files changed

+256
-222
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/documentation')({
4+
component: RouteComponent,
5+
})
6+
7+
function RouteComponent() {
8+
return <div>Hello "/documentation"!</div>
9+
}

src/components/sign-out-dialog.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useNavigate, useLocation } from '@tanstack/react-router'
1+
import { useNavigate } from '@tanstack/react-router'
22
import { useAuthStore } from '@/stores/auth-store'
33
import { ConfirmDialog } from '@/components/confirm-dialog'
44

@@ -9,13 +9,10 @@ interface SignOutDialogProps {
99

1010
export function SignOutDialog({ open, onOpenChange }: SignOutDialogProps) {
1111
const navigate = useNavigate()
12-
const location = useLocation()
1312
const { auth } = useAuthStore()
1413

1514
const handleSignOut = () => {
1615
auth.reset()
17-
// Preserve current location for redirect after sign-in
18-
const currentPath = location.href
1916
navigate({
2017
to: '/sign-in',
2118
replace: true,

src/features/api-keys/components/api-keys-action-dialog.tsx

Lines changed: 174 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useState } from 'react'
44
import { z } from 'zod'
55
import { useForm } from 'react-hook-form'
66
import { zodResolver } from '@hookform/resolvers/zod'
7-
import { format } from 'date-fns'
87
import { Copy, Check, Loader2 } from 'lucide-react'
98
import { toast } from 'sonner'
109
import { Button } from '@/components/ui/button'
@@ -28,13 +27,12 @@ import {
2827
import { Input } from '@/components/ui/input'
2928
import { Switch } from '@/components/ui/switch'
3029
import { Label } from '@/components/ui/label'
31-
import type { ApiKey } from '@/lib/api-keys-api'
3230
import {
3331
createApiKey,
3432
updateApiKey,
33+
type ApiKey,
3534
type CreateApiKeyResponse,
3635
} from '@/lib/api-keys-api'
37-
import { cn } from '@/lib/utils'
3836

3937
const createFormSchema = z.object({
4038
name: z.string().optional(),
@@ -94,8 +92,6 @@ export function ApiKeysActionDialog({
9492
},
9593
})
9694

97-
const form = isEdit ? updateForm : createForm
98-
9995
const onSubmit = async (values: CreateFormValues | UpdateFormValues) => {
10096
setIsLoading(true)
10197
try {
@@ -107,7 +103,7 @@ export function ApiKeysActionDialog({
107103
expiresAt: updateValues.expiresAt === '' ? null : updateValues.expiresAt || undefined,
108104
})
109105
toast.success('API key atualizada com sucesso!')
110-
form.reset()
106+
updateForm.reset()
111107
onOpenChange(false)
112108
onSuccess()
113109
} else {
@@ -164,7 +160,11 @@ export function ApiKeysActionDialog({
164160
}
165161

166162
const handleClose = () => {
167-
form.reset()
163+
if (isEdit) {
164+
updateForm.reset()
165+
} else {
166+
createForm.reset()
167+
}
168168
setCreatedApiKey(null)
169169
setCopied(false)
170170
setCopiedCurl(false)
@@ -263,32 +263,32 @@ export function ApiKeysActionDialog({
263263
: 'Crie uma nova API key para sua organização.'}
264264
</DialogDescription>
265265
</DialogHeader>
266-
<Form {...form}>
267-
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'>
268-
<FormField
269-
control={form.control}
270-
name='name'
271-
render={({ field }) => (
272-
<FormItem>
273-
<FormLabel>Nome (opcional)</FormLabel>
274-
<FormControl>
275-
<Input
276-
placeholder='Ex: API Key Produção'
277-
{...field}
278-
value={field.value || ''}
279-
/>
280-
</FormControl>
281-
<FormDescription>
282-
Um nome descritivo para identificar esta API key.
283-
</FormDescription>
284-
<FormMessage />
285-
</FormItem>
286-
)}
287-
/>
266+
{isEdit ? (
267+
<Form {...updateForm}>
268+
<form onSubmit={updateForm.handleSubmit(onSubmit)} className='space-y-4'>
269+
<FormField
270+
control={updateForm.control}
271+
name='name'
272+
render={({ field }) => (
273+
<FormItem>
274+
<FormLabel>Nome (opcional)</FormLabel>
275+
<FormControl>
276+
<Input
277+
placeholder='Ex: API Key Produção'
278+
{...field}
279+
value={field.value || ''}
280+
/>
281+
</FormControl>
282+
<FormDescription>
283+
Um nome descritivo para identificar esta API key.
284+
</FormDescription>
285+
<FormMessage />
286+
</FormItem>
287+
)}
288+
/>
288289

289-
{isEdit && (
290290
<FormField
291-
control={form.control}
291+
control={updateForm.control}
292292
name='isActive'
293293
render={({ field }) => (
294294
<FormItem className='flex flex-row items-center justify-between rounded-lg border p-4'>
@@ -307,77 +307,162 @@ export function ApiKeysActionDialog({
307307
</FormItem>
308308
)}
309309
/>
310-
)}
311310

312-
<FormField
313-
control={form.control}
314-
name='expiresAt'
315-
render={({ field }) => {
316-
// Converter para formato datetime-local
317-
const getValue = () => {
318-
if (!field.value) return ''
319-
try {
320-
const date = new Date(field.value)
321-
// Ajustar para timezone local
322-
const year = date.getFullYear()
323-
const month = String(date.getMonth() + 1).padStart(2, '0')
324-
const day = String(date.getDate()).padStart(2, '0')
325-
const hours = String(date.getHours()).padStart(2, '0')
326-
const minutes = String(date.getMinutes()).padStart(2, '0')
327-
return `${year}-${month}-${day}T${hours}:${minutes}`
328-
} catch {
329-
return ''
311+
<FormField
312+
control={updateForm.control}
313+
name='expiresAt'
314+
render={({ field }) => {
315+
// Converter para formato datetime-local
316+
const getValue = () => {
317+
if (!field.value) return ''
318+
try {
319+
const date = new Date(field.value)
320+
// Ajustar para timezone local
321+
const year = date.getFullYear()
322+
const month = String(date.getMonth() + 1).padStart(2, '0')
323+
const day = String(date.getDate()).padStart(2, '0')
324+
const hours = String(date.getHours()).padStart(2, '0')
325+
const minutes = String(date.getMinutes()).padStart(2, '0')
326+
return `${year}-${month}-${day}T${hours}:${minutes}`
327+
} catch {
328+
return ''
329+
}
330330
}
331-
}
332331

333-
return (
332+
return (
333+
<FormItem>
334+
<FormLabel>Data de Expiração (opcional)</FormLabel>
335+
<FormControl>
336+
<Input
337+
type='datetime-local'
338+
value={getValue()}
339+
onChange={(e) => {
340+
const value = e.target.value
341+
if (value) {
342+
// Converter para ISO string
343+
const date = new Date(value)
344+
field.onChange(date.toISOString())
345+
} else {
346+
field.onChange(null)
347+
}
348+
}}
349+
/>
350+
</FormControl>
351+
<FormDescription>
352+
Deixe em branco para remover a data de expiração.
353+
</FormDescription>
354+
<FormMessage />
355+
</FormItem>
356+
)
357+
}}
358+
/>
359+
360+
<DialogFooter>
361+
<Button
362+
type='button'
363+
variant='outline'
364+
onClick={handleClose}
365+
disabled={isLoading}
366+
>
367+
Cancelar
368+
</Button>
369+
<Button type='submit' disabled={isLoading}>
370+
{isLoading && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
371+
Salvar Alterações
372+
</Button>
373+
</DialogFooter>
374+
</form>
375+
</Form>
376+
) : (
377+
<Form {...createForm}>
378+
<form onSubmit={createForm.handleSubmit(onSubmit)} className='space-y-4'>
379+
<FormField
380+
control={createForm.control}
381+
name='name'
382+
render={({ field }) => (
334383
<FormItem>
335-
<FormLabel>
336-
Data de Expiração {isEdit ? '(opcional)' : '(opcional)'}
337-
</FormLabel>
384+
<FormLabel>Nome (opcional)</FormLabel>
338385
<FormControl>
339386
<Input
340-
type='datetime-local'
341-
value={getValue()}
342-
onChange={(e) => {
343-
const value = e.target.value
344-
if (value) {
345-
// Converter para ISO string
346-
const date = new Date(value)
347-
field.onChange(date.toISOString())
348-
} else {
349-
field.onChange(isEdit ? null : undefined)
350-
}
351-
}}
387+
placeholder='Ex: API Key Produção'
388+
{...field}
389+
value={field.value || ''}
352390
/>
353391
</FormControl>
354392
<FormDescription>
355-
{isEdit
356-
? 'Deixe em branco para remover a data de expiração.'
357-
: 'Deixe em branco para que a API key nunca expire.'}
393+
Um nome descritivo para identificar esta API key.
358394
</FormDescription>
359395
<FormMessage />
360396
</FormItem>
361-
)
362-
}}
363-
/>
397+
)}
398+
/>
399+
400+
<FormField
401+
control={createForm.control}
402+
name='expiresAt'
403+
render={({ field }) => {
404+
// Converter para formato datetime-local
405+
const getValue = () => {
406+
if (!field.value) return ''
407+
try {
408+
const date = new Date(field.value)
409+
// Ajustar para timezone local
410+
const year = date.getFullYear()
411+
const month = String(date.getMonth() + 1).padStart(2, '0')
412+
const day = String(date.getDate()).padStart(2, '0')
413+
const hours = String(date.getHours()).padStart(2, '0')
414+
const minutes = String(date.getMinutes()).padStart(2, '0')
415+
return `${year}-${month}-${day}T${hours}:${minutes}`
416+
} catch {
417+
return ''
418+
}
419+
}
364420

365-
<DialogFooter>
366-
<Button
367-
type='button'
368-
variant='outline'
369-
onClick={handleClose}
370-
disabled={isLoading}
371-
>
372-
Cancelar
373-
</Button>
374-
<Button type='submit' disabled={isLoading}>
375-
{isLoading && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
376-
{isEdit ? 'Salvar Alterações' : 'Criar API Key'}
377-
</Button>
378-
</DialogFooter>
379-
</form>
380-
</Form>
421+
return (
422+
<FormItem>
423+
<FormLabel>Data de Expiração (opcional)</FormLabel>
424+
<FormControl>
425+
<Input
426+
type='datetime-local'
427+
value={getValue()}
428+
onChange={(e) => {
429+
const value = e.target.value
430+
if (value) {
431+
// Converter para ISO string
432+
const date = new Date(value)
433+
field.onChange(date.toISOString())
434+
} else {
435+
field.onChange(undefined)
436+
}
437+
}}
438+
/>
439+
</FormControl>
440+
<FormDescription>
441+
Deixe em branco para que a API key nunca expire.
442+
</FormDescription>
443+
<FormMessage />
444+
</FormItem>
445+
)
446+
}}
447+
/>
448+
449+
<DialogFooter>
450+
<Button
451+
type='button'
452+
variant='outline'
453+
onClick={handleClose}
454+
disabled={isLoading}
455+
>
456+
Cancelar
457+
</Button>
458+
<Button type='submit' disabled={isLoading}>
459+
{isLoading && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
460+
Criar API Key
461+
</Button>
462+
</DialogFooter>
463+
</form>
464+
</Form>
465+
)}
381466
</DialogContent>
382467
</Dialog>
383468
)

src/features/api-keys/components/api-keys-columns.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export const apiKeysColumns: ColumnDef<ApiKey>[] = [
8585
</Badge>
8686
)
8787
},
88-
filterFn: (row, id, value) => {
88+
filterFn: (row, _id, value) => {
8989
if (!value || !Array.isArray(value) || value.length === 0) return true
9090
const status = getStatusBadge(row.original)
9191
// Converter valores do filtro para comparar com os labels

src/features/api-keys/components/api-keys-delete-dialog.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {
1010
DialogHeader,
1111
DialogTitle,
1212
} from '@/components/ui/dialog'
13-
import type { ApiKey } from '@/lib/api-keys-api'
14-
import { deleteApiKey } from '@/lib/api-keys-api'
13+
import { deleteApiKey, type ApiKey } from '@/lib/api-keys-api'
1514

1615
type ApiKeysDeleteDialogProps = {
1716
currentRow: ApiKey | null

src/features/api-keys/components/api-keys-dialogs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ export function ApiKeysDialogs({ onRefresh }: ApiKeysDialogsProps) {
1818
return (
1919
<>
2020
<ApiKeysActionDialog
21-
currentRow={open === 'edit' ? currentRow : undefined}
21+
currentRow={open === 'edit' ? currentRow ?? undefined : undefined}
2222
open={open === 'create' || open === 'edit'}
2323
onOpenChange={(state) => !state && setOpen(null)}
2424
onSuccess={handleSuccess}
2525
/>
2626
<ApiKeysDeleteDialog
27-
currentRow={currentRow}
27+
currentRow={currentRow ?? null}
2828
open={open === 'delete'}
2929
onOpenChange={(state) => !state && setOpen(null)}
3030
onSuccess={handleSuccess}

0 commit comments

Comments
 (0)