Initial commit: SKEEN Derma Experts - Sistema Integral de Gestión Clínica
- Frontend React (SKEEN Brand) con Vite, TypeScript, Tailwind - Frontend Homenest (versión alternativa) - Módulos Odoo 17 custom (citas, pacientes, monedero, pagos, ventas, inventario, whatsapp) - WACRM fork (Next.js 16 + Supabase) - Hermes + Bridge + Skills (Qwen3.6 via Nan Builders) - Scripts de migración y operación - Documentación extensiva en docs/
This commit is contained in:
109
wacrm/src/components/settings/sessions-card.tsx
Normal file
109
wacrm/src/components/settings/sessions-card.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Loader2, LogOut } from 'lucide-react';
|
||||
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
} from '@/components/ui/card';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
|
||||
export function SessionsCard() {
|
||||
const supabase = createClient();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [signingOut, setSigningOut] = useState(false);
|
||||
|
||||
const onConfirm = async () => {
|
||||
setSigningOut(true);
|
||||
try {
|
||||
// scope: 'global' revokes every refresh token for this user
|
||||
// across all devices; the next auth-state change on this tab
|
||||
// triggers the usual redirect.
|
||||
const { error } = await supabase.auth.signOut({ scope: 'global' });
|
||||
if (error) {
|
||||
toast.error(`Sign-out failed: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
window.location.href = '/login';
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : 'Unknown error';
|
||||
toast.error(msg);
|
||||
} finally {
|
||||
setSigningOut(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-foreground">
|
||||
<LogOut className="size-4 text-primary" />
|
||||
Active sessions
|
||||
</CardTitle>
|
||||
<CardDescription className="text-muted-foreground">
|
||||
Sign out of every device where you're logged in — including
|
||||
this one. Useful if you lost a laptop or shared your password.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<LogOut className="size-4" />
|
||||
Sign out of all devices
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Sign out everywhere?</DialogTitle>
|
||||
<DialogDescription>
|
||||
Every device logged into this account will be signed out and
|
||||
will need to log in again. You will be redirected to the login
|
||||
page.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={() => setOpen(false)}
|
||||
disabled={signingOut}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="button" onClick={onConfirm} disabled={signingOut}>
|
||||
{signingOut ? (
|
||||
<>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
Signing out…
|
||||
</>
|
||||
) : (
|
||||
'Sign out everywhere'
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user