Alpha v0.1.0 — KAPPA Hub inicial
- Auth con KAPPA (login + token Bearer) - Cliente HTTP para 10 endpoints (proyectos, HUs, bitácoras, planeaciones) - Dashboard multi-proyecto con concepto médico Teloprax - Calendario colombiano con 19 feriados (Ley Emiliani + Pascua) - Scheduler tipo cron con Dexie (reglas recurrentes, toasts, log) - Diseño marca Teloprax: Inter, Space Grotesk, #1A1A2E, rojo #E63946 - Stack: Vue 3 + TypeScript + Pinia + Vite + Bun
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const auth = useAuthStore()
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const showPassword = ref(false)
|
||||
|
||||
async function handleLogin() {
|
||||
if (!email.value || !password.value) return
|
||||
await auth.login({ email: email.value, password: password.value })
|
||||
}
|
||||
</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>
|
||||
<div class="field">
|
||||
<label>Contraseña</label>
|
||||
<div class="password-wrap">
|
||||
<input
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : '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>
|
||||
|
||||
<button type="submit" class="login-btn" :disabled="auth.loading">
|
||||
{{ auth.loading ? 'Ingresando...' : 'Iniciar diagnóstico' }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="login-footer">kappa.lambdaanalytics.co</p>
|
||||
</div>
|
||||
</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>
|
||||
Reference in New Issue
Block a user