fix: exibir a nova senha SIP ao redefinir senha de ramal

O endpoint POST /extensions/:id/reset-password já retornava a senha em
texto puro (mesmo padrão da criação), mas o frontend descartava o
resultado e só mostrava um toast genérico — o usuário nunca via a senha
nova, precisando adivinhar ou reconfigurar de novo. Reaproveita o mesmo
padrão de diálogo 'copie agora' já usado na criação do ramal.
This commit is contained in:
2026-08-27 20:27:25 -03:00
parent 9857904c6d
commit 71555af5da
2 changed files with 46 additions and 3 deletions

View File

@@ -152,6 +152,42 @@ function ExtensionFormDialog({
); );
} }
function ResetPasswordDialog({
result,
onOpenChange,
}: {
result: { number: string; password: string } | null;
onOpenChange: (v: boolean) => void;
}) {
return (
<Dialog open={result !== null} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Senha SIP redefinida ramal {result?.number}</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-3">
<p className="text-sm text-muted-foreground">
Exibida apenas uma vez, copie agora:
</p>
<div className="flex items-center gap-2 rounded-md border border-border bg-muted p-2 font-mono text-sm">
<span className="flex-1 break-all">{result?.password}</span>
<Button
size="icon"
variant="ghost"
onClick={() => result && navigator.clipboard.writeText(result.password)}
>
<Copy className="size-4" />
</Button>
</div>
</div>
<DialogFooter>
<Button onClick={() => onOpenChange(false)}>Fechar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
function ExtensionsContent() { function ExtensionsContent() {
const { can } = useAuth(); const { can } = useAuth();
const { toast } = useToast(); const { toast } = useToast();
@@ -160,6 +196,9 @@ function ExtensionsContent() {
const debouncedSearch = useDebounce(search); const debouncedSearch = useDebounce(search);
const [dialogOpen, setDialogOpen] = React.useState(false); const [dialogOpen, setDialogOpen] = React.useState(false);
const [editing, setEditing] = React.useState<Extension | null>(null); const [editing, setEditing] = React.useState<Extension | null>(null);
const [resetResult, setResetResult] = React.useState<{ number: string; password: string } | null>(
null,
);
const { data, isLoading, isError, refetch } = useQuery({ const { data, isLoading, isError, refetch } = useQuery({
queryKey: ['extensions'], queryKey: ['extensions'],
@@ -168,9 +207,9 @@ function ExtensionsContent() {
const resetPassword = useMutation({ const resetPassword = useMutation({
mutationFn: (id: string) => extensionsService.resetPassword(id), mutationFn: (id: string) => extensionsService.resetPassword(id),
onSuccess: () => { onSuccess: (result) => {
queryClient.invalidateQueries({ queryKey: ['extensions'] }); queryClient.invalidateQueries({ queryKey: ['extensions'] });
toast({ title: 'Senha SIP redefinida', variant: 'success' }); setResetResult({ number: result.number, password: result.sipPassword });
}, },
onError: (err) => toast({ title: 'Erro', description: errorMessage(err), variant: 'destructive' }), onError: (err) => toast({ title: 'Erro', description: errorMessage(err), variant: 'destructive' }),
}); });
@@ -283,6 +322,10 @@ function ExtensionsContent() {
emptyMessage="Nenhum ramal cadastrado." emptyMessage="Nenhum ramal cadastrado."
/> />
<ExtensionFormDialog open={dialogOpen} onOpenChange={setDialogOpen} extension={editing} /> <ExtensionFormDialog open={dialogOpen} onOpenChange={setDialogOpen} extension={editing} />
<ResetPasswordDialog
result={resetResult}
onOpenChange={(v) => !v && setResetResult(null)}
/>
</> </>
); );
} }

View File

@@ -20,6 +20,6 @@ export const extensionsService = {
update: (id: string, input: Partial<ExtensionInput>) => update: (id: string, input: Partial<ExtensionInput>) =>
api.patch<Extension>(`/extensions/${id}`, input), api.patch<Extension>(`/extensions/${id}`, input),
resetPassword: (id: string) => resetPassword: (id: string) =>
api.post<Extension>(`/extensions/${id}/reset-password`), api.post<Extension & { sipPassword: string }>(`/extensions/${id}/reset-password`),
remove: (id: string) => api.delete<void>(`/extensions/${id}`), remove: (id: string) => api.delete<void>(`/extensions/${id}`),
}; };