551 lines
24 KiB
TypeScript
551 lines
24 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { mockDepartments, pipelineStages } from "@/lib/mock-data"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Label } from "@/components/ui/label"
|
|
import { Switch } from "@/components/ui/switch"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
|
import { Separator } from "@/components/ui/separator"
|
|
import {
|
|
Settings,
|
|
Building2,
|
|
Users,
|
|
Bell,
|
|
Mail,
|
|
Shield,
|
|
Workflow,
|
|
Palette,
|
|
Globe,
|
|
Clock,
|
|
Plus,
|
|
Edit,
|
|
Trash2,
|
|
GripVertical,
|
|
CheckCircle2,
|
|
AlertCircle,
|
|
Save,
|
|
RefreshCw
|
|
} from "lucide-react"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
export function SettingsPage() {
|
|
const [departments, setDepartments] = useState(mockDepartments)
|
|
const [isAddDeptOpen, setIsAddDeptOpen] = useState(false)
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">Configuracion</h1>
|
|
<p className="text-muted-foreground">
|
|
Administra la configuracion del sistema
|
|
</p>
|
|
</div>
|
|
<Button className="bg-accent hover:bg-accent/90 text-accent-foreground">
|
|
<Save className="mr-2 h-4 w-4" />
|
|
Guardar Cambios
|
|
</Button>
|
|
</div>
|
|
|
|
<Tabs defaultValue="general" className="space-y-6">
|
|
<TabsList className="grid w-full grid-cols-2 md:grid-cols-6 gap-2">
|
|
<TabsTrigger value="general">General</TabsTrigger>
|
|
<TabsTrigger value="departments">Departamentos</TabsTrigger>
|
|
<TabsTrigger value="workflow">Flujos</TabsTrigger>
|
|
<TabsTrigger value="notifications">Notificaciones</TabsTrigger>
|
|
<TabsTrigger value="templates">Plantillas</TabsTrigger>
|
|
<TabsTrigger value="integrations">Integraciones</TabsTrigger>
|
|
</TabsList>
|
|
|
|
{/* General Settings */}
|
|
<TabsContent value="general" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Building2 className="h-5 w-5 text-primary" />
|
|
Informacion de la Empresa
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Nombre de la Empresa</Label>
|
|
<Input defaultValue="Gomez Lee Marketing" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>RFC</Label>
|
|
<Input defaultValue="GLM123456ABC" />
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Email de Contacto</Label>
|
|
<Input defaultValue="rrhh@gomezlee.com" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Telefono</Label>
|
|
<Input defaultValue="+52 55 1234 5678" />
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Direccion</Label>
|
|
<Textarea defaultValue="Av. Insurgentes Sur 1234, Col. Del Valle, CDMX" />
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Globe className="h-5 w-5 text-primary" />
|
|
Configuracion Regional
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Idioma</Label>
|
|
<Select defaultValue="es">
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="es">Espanol (Mexico)</SelectItem>
|
|
<SelectItem value="en">English (US)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Zona Horaria</Label>
|
|
<Select defaultValue="america_mexico">
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="america_mexico">America/Mexico_City (GMT-6)</SelectItem>
|
|
<SelectItem value="america_monterrey">America/Monterrey (GMT-6)</SelectItem>
|
|
<SelectItem value="america_tijuana">America/Tijuana (GMT-8)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Formato de Fecha</Label>
|
|
<Select defaultValue="dd_mm_yyyy">
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="dd_mm_yyyy">DD/MM/YYYY</SelectItem>
|
|
<SelectItem value="mm_dd_yyyy">MM/DD/YYYY</SelectItem>
|
|
<SelectItem value="yyyy_mm_dd">YYYY-MM-DD</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Shield className="h-5 w-5 text-primary" />
|
|
Seguridad
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>Autenticacion de Dos Factores (2FA)</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Requerir 2FA para todos los administradores
|
|
</p>
|
|
</div>
|
|
<Switch />
|
|
</div>
|
|
<div className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>Expiracion de Sesion</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Cerrar sesion automaticamente despues de inactividad
|
|
</p>
|
|
</div>
|
|
<Select defaultValue="30">
|
|
<SelectTrigger className="w-[140px]">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="15">15 minutos</SelectItem>
|
|
<SelectItem value="30">30 minutos</SelectItem>
|
|
<SelectItem value="60">1 hora</SelectItem>
|
|
<SelectItem value="120">2 horas</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>Politica de Contrasenas</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Requerir contrasenas fuertes (min 8 caracteres, mayusculas, numeros)
|
|
</p>
|
|
</div>
|
|
<Switch defaultChecked />
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
{/* Departments */}
|
|
<TabsContent value="departments" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle className="text-base">Departamentos</CardTitle>
|
|
<CardDescription>Gestiona los departamentos de la organizacion</CardDescription>
|
|
</div>
|
|
<Dialog open={isAddDeptOpen} onOpenChange={setIsAddDeptOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button size="sm">
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
Agregar
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Nuevo Departamento</DialogTitle>
|
|
<DialogDescription>
|
|
Agrega un nuevo departamento a la organizacion
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="space-y-4 py-4">
|
|
<div className="space-y-2">
|
|
<Label>Nombre</Label>
|
|
<Input placeholder="Ej: Recursos Humanos" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Codigo</Label>
|
|
<Input placeholder="Ej: RRHH" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Responsable</Label>
|
|
<Select>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Seleccionar responsable" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="user1">Maria Garcia</SelectItem>
|
|
<SelectItem value="user2">Carlos Lopez</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Centro de Costo</Label>
|
|
<Input placeholder="Ej: CC-001" />
|
|
</div>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => setIsAddDeptOpen(false)}>
|
|
Cancelar
|
|
</Button>
|
|
<Button onClick={() => setIsAddDeptOpen(false)}>
|
|
Crear
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-2">
|
|
{departments.map(dept => (
|
|
<div
|
|
key={dept.id}
|
|
className="flex items-center justify-between p-4 border rounded-lg hover:bg-muted/50"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
<Building2 className="h-5 w-5 text-primary" />
|
|
</div>
|
|
<div>
|
|
<div className="font-medium">{dept.name}</div>
|
|
<div className="text-sm text-muted-foreground">
|
|
Codigo: {dept.code} | CC: {dept.costCenter}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Badge variant={dept.isActive ? "default" : "secondary"}>
|
|
{dept.isActive ? "Activo" : "Inactivo"}
|
|
</Badge>
|
|
<Button variant="ghost" size="icon">
|
|
<Edit className="h-4 w-4" />
|
|
</Button>
|
|
<Button variant="ghost" size="icon" className="text-destructive">
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
{/* Workflow Settings */}
|
|
<TabsContent value="workflow" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Workflow className="h-5 w-5 text-primary" />
|
|
Flujo de Aprobacion de Requisiciones
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Configura los pasos de aprobacion para nuevas requisiciones
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-3">
|
|
{[
|
|
{ step: 1, name: "Gerente de Area", description: "Primera aprobacion por el solicitante" },
|
|
{ step: 2, name: "Recursos Humanos", description: "Validacion de perfil y compensacion" },
|
|
{ step: 3, name: "Administracion/Finanzas", description: "Aprobacion de presupuesto" },
|
|
{ step: 4, name: "Direccion General", description: "Aprobacion final (montos > $50,000)" }
|
|
].map((item, index) => (
|
|
<div key={item.step} className="flex items-center gap-4 p-4 border rounded-lg">
|
|
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-primary text-primary-foreground font-bold text-sm">
|
|
{item.step}
|
|
</div>
|
|
<div className="flex-1">
|
|
<div className="font-medium">{item.name}</div>
|
|
<div className="text-sm text-muted-foreground">{item.description}</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Switch defaultChecked />
|
|
<Button variant="ghost" size="icon">
|
|
<GripVertical className="h-4 w-4 text-muted-foreground" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<Button variant="outline" className="w-full">
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
Agregar Paso
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Etapas del Pipeline de Reclutamiento</CardTitle>
|
|
<CardDescription>
|
|
Configura las etapas del proceso de seleccion
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
{pipelineStages.map((stage, index) => (
|
|
<div key={stage.id} className="flex items-center gap-4 p-3 border rounded-lg">
|
|
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-muted text-muted-foreground text-xs font-bold">
|
|
{index + 1}
|
|
</div>
|
|
<div className="flex-1">
|
|
<Input defaultValue={stage.name} className="h-8" />
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Switch defaultChecked />
|
|
<Button variant="ghost" size="icon">
|
|
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
|
</Button>
|
|
<Button variant="ghost" size="icon">
|
|
<GripVertical className="h-4 w-4 text-muted-foreground" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<Button variant="outline" className="w-full">
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
Agregar Etapa
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
{/* Notifications */}
|
|
<TabsContent value="notifications" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Bell className="h-5 w-5 text-primary" />
|
|
Notificaciones por Email
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Configura cuando se envian notificaciones por email
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{[
|
|
{ title: "Nueva requisicion creada", description: "Notificar a RR.HH. cuando se crea una nueva requisicion" },
|
|
{ title: "Requisicion pendiente de aprobacion", description: "Recordatorio a aprobadores de requisiciones pendientes" },
|
|
{ title: "Requisicion aprobada", description: "Notificar al solicitante cuando su requisicion es aprobada" },
|
|
{ title: "Requisicion rechazada", description: "Notificar al solicitante cuando su requisicion es rechazada" },
|
|
{ title: "Nuevo candidato aplicado", description: "Notificar al reclutador cuando un candidato aplica" },
|
|
{ title: "Entrevista programada", description: "Recordatorio de entrevistas proximas" },
|
|
{ title: "Candidato cambia de etapa", description: "Notificar cuando un candidato avanza en el pipeline" }
|
|
].map(item => (
|
|
<div key={item.title} className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>{item.title}</Label>
|
|
<p className="text-sm text-muted-foreground">{item.description}</p>
|
|
</div>
|
|
<Switch defaultChecked />
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Clock className="h-5 w-5 text-primary" />
|
|
Recordatorios Automaticos
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>Recordatorio de requisiciones pendientes</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Enviar recordatorio despues de X dias sin accion
|
|
</p>
|
|
</div>
|
|
<Select defaultValue="3">
|
|
<SelectTrigger className="w-[120px]">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1">1 dia</SelectItem>
|
|
<SelectItem value="2">2 dias</SelectItem>
|
|
<SelectItem value="3">3 dias</SelectItem>
|
|
<SelectItem value="5">5 dias</SelectItem>
|
|
<SelectItem value="7">7 dias</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<Label>Recordatorio de entrevistas</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Enviar recordatorio antes de la entrevista
|
|
</p>
|
|
</div>
|
|
<Select defaultValue="24">
|
|
<SelectTrigger className="w-[120px]">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1">1 hora</SelectItem>
|
|
<SelectItem value="2">2 horas</SelectItem>
|
|
<SelectItem value="24">24 horas</SelectItem>
|
|
<SelectItem value="48">48 horas</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
{/* Email Templates */}
|
|
<TabsContent value="templates" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base flex items-center gap-2">
|
|
<Mail className="h-5 w-5 text-primary" />
|
|
Plantillas de Email
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Personaliza las plantillas de correo electronico
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{[
|
|
{ name: "Confirmacion de Aplicacion", description: "Enviado a candidatos al aplicar" },
|
|
{ name: "Invitacion a Entrevista", description: "Invitacion para agendar entrevista" },
|
|
{ name: "Rechazo de Candidato", description: "Notificacion de rechazo" },
|
|
{ name: "Oferta de Trabajo", description: "Carta de oferta formal" },
|
|
{ name: "Bienvenida a Nuevo Empleado", description: "Email de onboarding" },
|
|
{ name: "Solicitud de Aprobacion", description: "Notificacion a aprobadores" }
|
|
].map(template => (
|
|
<div key={template.name} className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div>
|
|
<div className="font-medium">{template.name}</div>
|
|
<div className="text-sm text-muted-foreground">{template.description}</div>
|
|
</div>
|
|
<Button variant="outline" size="sm">
|
|
<Edit className="mr-2 h-4 w-4" />
|
|
Editar
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
{/* Integrations */}
|
|
<TabsContent value="integrations" className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-base">Integraciones Disponibles</CardTitle>
|
|
<CardDescription>
|
|
Conecta con servicios externos para mejorar tu flujo de trabajo
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{[
|
|
{ name: "LinkedIn Recruiter", description: "Importar candidatos desde LinkedIn", connected: true },
|
|
{ name: "Google Calendar", description: "Sincronizar entrevistas con Google Calendar", connected: true },
|
|
{ name: "Microsoft Teams", description: "Crear reuniones de Teams automaticamente", connected: false },
|
|
{ name: "Zoom", description: "Generar links de Zoom para entrevistas", connected: false },
|
|
{ name: "Slack", description: "Notificaciones en canales de Slack", connected: false },
|
|
{ name: "Indeed", description: "Publicar vacantes en Indeed", connected: false }
|
|
].map(integration => (
|
|
<div key={integration.name} className="flex items-center justify-between p-4 border rounded-lg">
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
|
<Globe className="h-5 w-5" />
|
|
</div>
|
|
<div>
|
|
<div className="font-medium">{integration.name}</div>
|
|
<div className="text-sm text-muted-foreground">{integration.description}</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
{integration.connected && (
|
|
<Badge variant="outline" className="text-accent border-accent">
|
|
<CheckCircle2 className="mr-1 h-3 w-3" />
|
|
Conectado
|
|
</Badge>
|
|
)}
|
|
<Button variant={integration.connected ? "outline" : "default"} size="sm">
|
|
{integration.connected ? "Configurar" : "Conectar"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
}
|