Migración a shadcn-vue + Tailwind CSS v4

- Tailwind CSS v4 con @tailwindcss/vite
- shadcn-vue: 19 componentes UI (button, card, dialog, table, select,
  tabs, sidebar, separator, breadcrumb, badge, avatar, dropdown-menu,
  tooltip, input, switch, sheet, skeleton)
- Sidebar collapsible con íconos Lucide
- Theme Teloprax en CSS variables (rojo #E63946, negro #1A1A2E)
- LoginView, DashboardView, CalendarView, SchedulerView migrados
- Eliminado AppShell.vue manual (reemplazado por SidebarProvider)
- Layout con breadcrumb, sidebar trigger, header unificado
This commit is contained in:
2026-05-22 22:15:19 -05:00
parent 66fd4e175a
commit c0b983e016
146 changed files with 4769 additions and 842 deletions
+41 -158
View File
@@ -1,11 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
const auth = useAuthStore()
const email = ref('')
const password = ref('')
const showPassword = ref(false)
async function handleLogin() {
if (!email.value || !password.value) return
@@ -14,171 +16,52 @@ async function handleLogin() {
</script>
<template>
<div class="login-bg">
<div class="login-card">
<div class="login-circles">
<span class="c c1"></span>
<span class="c c2"></span>
<span class="c c3"></span>
</div>
<h1 class="login-brand">teloprax</h1>
<p class="login-tagline">Tecnología con prescripción</p>
<div class="login-divider"></div>
<p class="login-sub">Centro de diagnóstico multi-proyecto</p>
<form @submit.prevent="handleLogin" class="login-form">
<div class="field">
<label>Email</label>
<input v-model="email" type="email" placeholder="ricardo@..." autocomplete="email" />
<div class="flex min-h-svh flex-col items-center justify-center bg-background p-6">
<Card class="w-full max-w-[400px]">
<CardHeader class="text-center pb-4">
<div class="flex items-center justify-center gap-1.5 mb-3">
<span class="block rounded-full border-2 border-primary opacity-35 w-[10px] h-[10px]" />
<span class="block rounded-full border-2 border-primary opacity-65 w-[14px] h-[14px]" />
<span class="block rounded-full bg-primary w-[18px] h-[18px]" />
</div>
<div class="field">
<label>Contraseña</label>
<div class="password-wrap">
<input
<CardTitle class="font-display text-2xl font-bold">teloprax</CardTitle>
<CardDescription class="text-xs mt-1">Tecnología con prescripción</CardDescription>
</CardHeader>
<CardContent>
<form @submit.prevent="handleLogin" class="flex flex-col gap-4">
<div class="space-y-1.5">
<label class="text-xs font-medium text-muted-foreground uppercase tracking-wider">Email</label>
<Input
v-model="email"
type="email"
placeholder="ricardo@..."
autocomplete="email"
/>
</div>
<div class="space-y-1.5">
<label class="text-xs font-medium text-muted-foreground uppercase tracking-wider">Contraseña</label>
<Input
v-model="password"
:type="showPassword ? 'text' : 'password'"
type="password"
placeholder="••••••••"
autocomplete="current-password"
/>
<button type="button" class="toggle-btn" @click="showPassword = !showPassword">
{{ showPassword ? '' : '' }}
</button>
</div>
</div>
<p v-if="auth.error" class="error-msg">{{ auth.error }}</p>
<div v-if="auth.error" class="text-xs text-destructive bg-destructive/10 rounded-md px-3 py-2">
{{ auth.error }}
</div>
<button type="submit" class="login-btn" :disabled="auth.loading">
{{ auth.loading ? 'Ingresando...' : 'Iniciar diagnóstico' }}
</button>
</form>
<Button type="submit" class="w-full mt-2" :disabled="auth.loading">
{{ auth.loading ? 'Ingresando...' : 'Iniciar diagnóstico' }}
</Button>
</form>
<p class="login-footer">kappa.lambdaanalytics.co</p>
</div>
<p class="text-center text-[11px] text-muted-foreground/50 mt-6">
kappa.lambdaanalytics.co
</p>
</CardContent>
</Card>
</div>
</template>
<style scoped>
.login-bg {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #1A1A2E;
}
.login-card {
width: 420px;
padding: 44px 40px;
background: #141428;
border: 1px solid #2A2A45;
border-radius: 12px;
text-align: center;
}
.login-circles {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
margin-bottom: 20px;
}
.c {
display: block;
border-radius: 50%;
border: 2px solid #E63946;
}
.c1 { width: 10px; height: 10px; opacity: 0.35; }
.c2 { width: 14px; height: 14px; opacity: 0.65; }
.c3 { width: 18px; height: 18px; background: #E63946; border: none; }
.login-brand {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700;
font-size: 26px;
color: #FFFFFF;
letter-spacing: -0.5px;
margin: 0;
}
.login-tagline {
margin: 4px 0 0;
font-size: 13px;
color: #E63946;
font-weight: 500;
letter-spacing: 0.2px;
}
.login-divider {
width: 40px;
height: 2px;
background: #2A2A45;
margin: 20px auto;
}
.login-sub {
margin: 0 0 28px;
font-size: 13px;
color: #8888AA;
}
.login-form { display: flex; flex-direction: column; gap: 14px; text-align: left; }
.field { display: flex; flex-direction: column; gap: 5px; }
.field label { font-size: 11px; color: #8888AA; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; }
.field input {
padding: 10px 12px;
background: #1A1A2E;
border: 1px solid #2A2A45;
border-radius: 6px;
color: #E6EDF3;
font-size: 14px;
outline: none;
transition: border-color 0.15s;
}
.field input:focus {
border-color: #E63946;
box-shadow: 0 0 0 2px rgba(230,57,70,0.12);
}
.field input::placeholder { color: #555577; }
.password-wrap { position: relative; }
.password-wrap input { width: 100%; box-sizing: border-box; padding-right: 40px; }
.toggle-btn {
position: absolute;
right: 8px; top: 50%;
transform: translateY(-50%);
background: none; border: none;
color: #8888AA; font-size: 16px;
cursor: pointer; padding: 4px;
}
.toggle-btn:hover { color: #E6EDF3; }
.error-msg {
margin: 0;
font-size: 12px;
color: #F85149;
padding: 8px 12px;
background: rgba(248,81,73,0.08);
border: 1px solid rgba(248,81,73,0.15);
border-radius: 6px;
}
.login-btn {
padding: 11px;
background: #E63946;
color: #fff;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
margin-top: 6px;
transition: background 0.15s;
}
.login-btn:hover { background: #C62E3A; }
.login-btn:disabled { opacity: 0.5; cursor: default; }
.login-footer {
margin: 28px 0 0;
font-size: 11px;
color: #444466;
}
</style>