Añadir modo oscuro y mejorar la legibilidad del css separandolo en archivos
This commit is contained in:
parent
9e93d9c9c6
commit
0366137e96
|
|
@ -1,6 +1,15 @@
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
import './styles/sidebar.css'
|
||||||
|
import './styles/login.css'
|
||||||
|
import './styles/dashboard.css'
|
||||||
|
import './styles/facturas.css'
|
||||||
|
import './styles/clients.css'
|
||||||
|
import './styles/create-invoice.css'
|
||||||
|
import './styles/settings.css'
|
||||||
import { initRouter } from './router.js'
|
import { initRouter } from './router.js'
|
||||||
import { initSessionManager } from './services/session.js'
|
import { initSessionManager } from './services/session.js'
|
||||||
|
import { initTheme } from './services/theme.js'
|
||||||
|
|
||||||
|
initTheme()
|
||||||
initSessionManager()
|
initSessionManager()
|
||||||
initRouter()
|
initRouter()
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,13 @@ function getStatusBadge(status) {
|
||||||
return `<span class="badge ${s.cls}">${s.label}</span>`;
|
return `<span class="badge ${s.cls}">${s.label}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusDotStyle(status) {
|
function statusDotClass(status) {
|
||||||
const map = {
|
const map = {
|
||||||
paid: { bg: '#f0fdf4', color: '#16a34a' },
|
paid: 'status-paid',
|
||||||
unpaid: { bg: '#fffbeb', color: '#b45309' },
|
unpaid: 'status-unpaid',
|
||||||
draft: { bg: '#f3f4f6', color: '#6b7280' },
|
draft: 'status-draft',
|
||||||
};
|
};
|
||||||
const s = map[status] || map.draft;
|
return map[status] || 'status-draft';
|
||||||
return `background:${s.bg};color:${s.color}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openInvoiceModal(invoiceId) {
|
function openInvoiceModal(invoiceId) {
|
||||||
|
|
@ -317,7 +316,7 @@ export function renderDashboard() {
|
||||||
} else {
|
} else {
|
||||||
recentList.innerHTML = recent.map(inv => `
|
recentList.innerHTML = recent.map(inv => `
|
||||||
<div class="db-recent-item" data-id="${inv.id}" role="button" tabindex="0">
|
<div class="db-recent-item" data-id="${inv.id}" role="button" tabindex="0">
|
||||||
<div class="db-recent-dot" style="${statusDotStyle(inv.status)}">
|
<div class="db-recent-dot ${statusDotClass(inv.status)}">
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||||
<polyline points="14 2 14 8 20 8"/>
|
<polyline points="14 2 14 8 20 8"/>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { isDarkTheme, toggleTheme } from '../services/theme.js';
|
||||||
|
|
||||||
|
function decodeTokenPayload(token) {
|
||||||
|
try {
|
||||||
|
const payloadPart = token.split('.')[1];
|
||||||
|
if (!payloadPart) return null;
|
||||||
|
const base64 = payloadPart.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
return JSON.parse(atob(base64));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSeconds(totalSeconds) {
|
||||||
|
if (totalSeconds <= 0) return 'Expirada';
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
|
||||||
|
const hh = String(hours).padStart(2, '0');
|
||||||
|
const mm = String(minutes).padStart(2, '0');
|
||||||
|
const ss = String(seconds).padStart(2, '0');
|
||||||
|
return `${hh}:${mm}:${ss}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTokenExpirationInfo() {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token) {
|
||||||
|
return { hasToken: false, expiresAtText: 'No hay token', remainingText: 'No autenticado' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = decodeTokenPayload(token);
|
||||||
|
if (!payload || !payload.exp) {
|
||||||
|
return { hasToken: true, expiresAtText: 'No disponible', remainingText: 'No disponible' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiresAtMs = payload.exp * 1000;
|
||||||
|
const nowMs = Date.now();
|
||||||
|
const remainingSeconds = Math.max(0, Math.floor((expiresAtMs - nowMs) / 1000));
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasToken: true,
|
||||||
|
expiresAtText: new Date(expiresAtMs).toLocaleString('es-ES'),
|
||||||
|
remainingText: formatSeconds(remainingSeconds),
|
||||||
|
remainingSeconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderSettingsPage() {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'settings-page';
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="settings-header">
|
||||||
|
<h1>Configuracion</h1>
|
||||||
|
<p>Preferencias visuales y estado de sesion.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-grid">
|
||||||
|
<section class="settings-card">
|
||||||
|
<h2>Apariencia</h2>
|
||||||
|
<div class="settings-row">
|
||||||
|
<div>
|
||||||
|
<p class="settings-label">Modo oscuro</p>
|
||||||
|
<p class="settings-help">Cambia entre tema claro y oscuro.</p>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="settings-theme-toggle" id="settings-theme-toggle"></button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-card">
|
||||||
|
<h2>Sesion</h2>
|
||||||
|
<div class="settings-session-list">
|
||||||
|
<div class="settings-session-item">
|
||||||
|
<span class="settings-label">Caducidad del token</span>
|
||||||
|
<strong id="token-expire-at">-</strong>
|
||||||
|
</div>
|
||||||
|
<div class="settings-session-item">
|
||||||
|
<span class="settings-label">Tiempo restante</span>
|
||||||
|
<strong id="token-remaining">-</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const themeBtn = container.querySelector('#settings-theme-toggle');
|
||||||
|
const expireAtEl = container.querySelector('#token-expire-at');
|
||||||
|
const remainingEl = container.querySelector('#token-remaining');
|
||||||
|
|
||||||
|
const renderThemeButton = () => {
|
||||||
|
const dark = isDarkTheme();
|
||||||
|
themeBtn.textContent = dark ? 'Activado' : 'Desactivado';
|
||||||
|
themeBtn.setAttribute('aria-pressed', dark ? 'true' : 'false');
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTokenInfo = () => {
|
||||||
|
const info = getTokenExpirationInfo();
|
||||||
|
expireAtEl.textContent = info.expiresAtText;
|
||||||
|
remainingEl.textContent = info.remainingText;
|
||||||
|
|
||||||
|
if (info.remainingSeconds === 0 && info.hasToken) {
|
||||||
|
remainingEl.classList.add('settings-token-expired');
|
||||||
|
} else {
|
||||||
|
remainingEl.classList.remove('settings-token-expired');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderThemeButton();
|
||||||
|
renderTokenInfo();
|
||||||
|
|
||||||
|
themeBtn.addEventListener('click', () => {
|
||||||
|
toggleTheme();
|
||||||
|
renderThemeButton();
|
||||||
|
});
|
||||||
|
|
||||||
|
const intervalId = setInterval(renderTokenInfo, 1000);
|
||||||
|
container.cleanup = () => clearInterval(intervalId);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { renderDashboard } from './DashboardPage.js';
|
||||||
import { renderFacturasPage } from './Facturas.js';
|
import { renderFacturasPage } from './Facturas.js';
|
||||||
import { renderCreateInvoicePage } from './CreateInvoicePage.js';
|
import { renderCreateInvoicePage } from './CreateInvoicePage.js';
|
||||||
import { renderClientesPage } from './ClientesPage.js';
|
import { renderClientesPage } from './ClientesPage.js';
|
||||||
|
import { renderSettingsPage } from './SettingsPage.js';
|
||||||
|
|
||||||
// Registry of all pages available in the application
|
// Registry of all pages available in the application
|
||||||
export const pagesRegistry = [
|
export const pagesRegistry = [
|
||||||
|
|
@ -43,11 +44,7 @@ export const pagesRegistry = [
|
||||||
icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
|
icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
showInSidebar: true,
|
showInSidebar: true,
|
||||||
render: () => {
|
render: renderSettingsPage
|
||||||
const div = document.createElement('div');
|
|
||||||
div.innerHTML = '<h1>Configuración</h1><p>Página en construcción...</p>';
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { renderLoginPage } from './pages/LoginPage.js';
|
||||||
import { createSidebar } from './components/Sidebar.js';
|
import { createSidebar } from './components/Sidebar.js';
|
||||||
import { getAvailablePages, getPageByRoute } from './pages/pagesRegistry.js';
|
import { getAvailablePages, getPageByRoute } from './pages/pagesRegistry.js';
|
||||||
import { showConfirmExitModal } from './components/ConfirmExitModal.js';
|
import { showConfirmExitModal } from './components/ConfirmExitModal.js';
|
||||||
|
import { applyLoginTheme, applySavedTheme } from './services/theme.js';
|
||||||
|
|
||||||
// 🔧 Modo DEV: cambiar a false para activar login
|
// 🔧 Modo DEV: cambiar a false para activar login
|
||||||
const DEV_MODE = false;
|
const DEV_MODE = false;
|
||||||
|
|
@ -34,6 +35,7 @@ export function initRouter() {
|
||||||
const app = document.querySelector('#app');
|
const app = document.querySelector('#app');
|
||||||
let isNavigating = false;
|
let isNavigating = false;
|
||||||
let pendingHash = null;
|
let pendingHash = null;
|
||||||
|
let currentPageCleanup = null;
|
||||||
|
|
||||||
async function navigate(forceNavigate = false) {
|
async function navigate(forceNavigate = false) {
|
||||||
const hash = window.location.hash || (DEV_MODE ? '#dashboard' : '#login');
|
const hash = window.location.hash || (DEV_MODE ? '#dashboard' : '#login');
|
||||||
|
|
@ -71,6 +73,17 @@ export function initRouter() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof currentPageCleanup === 'function') {
|
||||||
|
currentPageCleanup();
|
||||||
|
currentPageCleanup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash === '#login') {
|
||||||
|
applyLoginTheme();
|
||||||
|
} else {
|
||||||
|
applySavedTheme();
|
||||||
|
}
|
||||||
|
|
||||||
app.innerHTML = '';
|
app.innerHTML = '';
|
||||||
|
|
||||||
// For authenticated routes, add sidebar and main content wrapper
|
// For authenticated routes, add sidebar and main content wrapper
|
||||||
|
|
@ -105,6 +118,10 @@ export function initRouter() {
|
||||||
// Render page content using registry
|
// Render page content using registry
|
||||||
const pageContent = page.render();
|
const pageContent = page.render();
|
||||||
|
|
||||||
|
if (pageContent && typeof pageContent.cleanup === 'function') {
|
||||||
|
currentPageCleanup = pageContent.cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
mainContent.appendChild(pageContent);
|
mainContent.appendChild(pageContent);
|
||||||
layout.appendChild(sidebar);
|
layout.appendChild(sidebar);
|
||||||
layout.appendChild(mainContent);
|
layout.appendChild(mainContent);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
const THEME_KEY = 'theme-preference';
|
||||||
|
|
||||||
|
function getStoredUser() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem('user');
|
||||||
|
if (!raw) return null;
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return parsed && typeof parsed === 'object' ? parsed : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserThemeKey() {
|
||||||
|
const user = getStoredUser();
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
|
const identity = user.id || user.userId || user.email || user.username || user.identifier;
|
||||||
|
if (!identity) return null;
|
||||||
|
|
||||||
|
return `${THEME_KEY}:user:${String(identity).toLowerCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRootTheme(theme) {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const finalTheme = theme === 'dark' ? 'dark' : 'light';
|
||||||
|
root.setAttribute('data-theme', finalTheme);
|
||||||
|
root.style.colorScheme = finalTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSystemPrefersDark() {
|
||||||
|
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSavedTheme() {
|
||||||
|
const userKey = getUserThemeKey();
|
||||||
|
const saved = userKey ? localStorage.getItem(userKey) : localStorage.getItem(THEME_KEY);
|
||||||
|
if (saved === 'dark' || saved === 'light') return saved;
|
||||||
|
return getSystemPrefersDark() ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyTheme(theme) {
|
||||||
|
const finalTheme = theme === 'dark' ? 'dark' : 'light';
|
||||||
|
setRootTheme(finalTheme);
|
||||||
|
|
||||||
|
const userKey = getUserThemeKey();
|
||||||
|
if (userKey) {
|
||||||
|
localStorage.setItem(userKey, finalTheme);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(THEME_KEY, finalTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initTheme() {
|
||||||
|
applyTheme(getSavedTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleTheme() {
|
||||||
|
const current = document.documentElement.getAttribute('data-theme') || getSavedTheme();
|
||||||
|
const next = current === 'dark' ? 'light' : 'dark';
|
||||||
|
applyTheme(next);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDarkTheme() {
|
||||||
|
const current = document.documentElement.getAttribute('data-theme') || getSavedTheme();
|
||||||
|
return current === 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login should remain neutral and should not persist or inherit cross-user preferences.
|
||||||
|
export function applyLoginTheme() {
|
||||||
|
setRootTheme('light');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applySavedTheme() {
|
||||||
|
setRootTheme(getSavedTheme());
|
||||||
|
}
|
||||||
2171
src/style.css
2171
src/style.css
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,509 @@
|
||||||
|
/* ============================================
|
||||||
|
Clientes Page
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.clientes-page {
|
||||||
|
padding: var(--space-6);
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientes-header {
|
||||||
|
margin-bottom: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientes-header h1 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientes-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-3);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientes-filters .search-input {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-avatar-col {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-avatar-cell {
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-name-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-company-name {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-code {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-name {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-count {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--primary);
|
||||||
|
background: var(--blue-50);
|
||||||
|
padding: 1px var(--space-2);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
display: inline-block;
|
||||||
|
width: fit-content;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-email .email-text {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-phone {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-actions {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table .client-actions-col {
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table td.client-actions {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table td.client-actions .btn-view {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-client {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px var(--space-2);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-client.status-active {
|
||||||
|
background: var(--green-50);
|
||||||
|
color: var(--green-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-client.status-inactive {
|
||||||
|
background: var(--red-50);
|
||||||
|
color: var(--red-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge-client.status-unknown {
|
||||||
|
background: var(--gray-100);
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-contacts {
|
||||||
|
position: relative;
|
||||||
|
background: var(--blue-50);
|
||||||
|
border: 1px solid rgba(59, 130, 246, 0.15);
|
||||||
|
color: var(--primary);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 2px var(--space-2);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-contacts:hover {
|
||||||
|
background: var(--blue-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-contacts.active {
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-badge {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-expand-row td {
|
||||||
|
padding: 0 !important;
|
||||||
|
background: var(--gray-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-expand-content {
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
border-bottom: 2px solid var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-expand-header h4 {
|
||||||
|
margin: 0 0 var(--space-3) 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-expand-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-expand-card {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-expand-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-expand-name svg {
|
||||||
|
color: var(--primary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-expand-details {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 2px var(--space-3);
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-expand-details span {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Client Modal */
|
||||||
|
.client-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-modal {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 560px;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--space-4) var(--space-5);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-modal-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close-modal {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding: var(--space-1);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
transition: color var(--transition-fast), background-color var(--transition-fast);
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close-modal:hover {
|
||||||
|
background: var(--gray-100);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-modal-body {
|
||||||
|
padding: var(--space-5);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-info-section,
|
||||||
|
.contacts-section {
|
||||||
|
margin-bottom: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-info-section:last-child,
|
||||||
|
.contacts-section:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-info-section h3,
|
||||||
|
.contacts-section h3 {
|
||||||
|
margin: 0 0 var(--space-3) 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-4);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-bottom: var(--space-3);
|
||||||
|
padding-bottom: var(--space-3);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-avatar {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--gray-100);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-full-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--gray-400);
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-contacts {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--space-5);
|
||||||
|
background: var(--gray-50);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.clientes-filters {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table tbody tr.client-item {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: var(--space-3);
|
||||||
|
gap: var(--space-2);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table tbody tr.contacts-expand-row {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-avatar-cell {
|
||||||
|
width: auto;
|
||||||
|
margin-right: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-type-col,
|
||||||
|
.client-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-actions {
|
||||||
|
position: absolute;
|
||||||
|
right: var(--space-3);
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contacts-expand-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.clientes-page {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-table th,
|
||||||
|
.clients-table td {
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-email-col,
|
||||||
|
.client-email,
|
||||||
|
.client-type-col,
|
||||||
|
.client-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .contact-expand-card {
|
||||||
|
background: #0f172a;
|
||||||
|
border-color: #1e2f4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .contacts-expand-row td,
|
||||||
|
:root[data-theme='dark'] .no-contacts {
|
||||||
|
background: rgba(15, 23, 42, 0.9);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
/* ===================== CREATE INVOICE PAGE ===================== */
|
/* ============================================
|
||||||
|
Create Invoice Page
|
||||||
|
============================================ */
|
||||||
|
|
||||||
.create-invoice-page {
|
.create-invoice-page {
|
||||||
padding: 2rem;
|
padding: var(--space-6);
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
@ -10,57 +13,53 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: var(--space-5);
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-header-compact h1 {
|
.invoice-header-compact h1 {
|
||||||
margin: 0;
|
font-size: 1.25rem;
|
||||||
font-size: 1.375rem;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-back {
|
.btn-back {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 0.45rem 0.875rem;
|
padding: var(--space-2) var(--space-3);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.8125rem;
|
font-size: 13px;
|
||||||
transition: all 0.15s ease;
|
transition: border-color var(--transition-fast), color var(--transition-fast);
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-back:hover {
|
.btn-back:hover {
|
||||||
background: #f8fafc;
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border-color: #cbd5e1;
|
border-color: var(--gray-300);
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-form-compact {
|
.invoice-form-compact {
|
||||||
background: var(--card-bg);
|
background: var(--card-bg);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-lg);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 1.75rem;
|
padding: var(--space-5);
|
||||||
box-shadow: var(--shadow-xs);
|
box-shadow: var(--shadow-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-grid-compact {
|
.form-grid-compact {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2fr 1fr 1fr;
|
grid-template-columns: 2fr 1fr 1fr;
|
||||||
gap: 1rem;
|
gap: var(--space-3);
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: var(--space-5);
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notes-grid-compact {
|
.notes-grid-compact {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 1rem;
|
gap: var(--space-3);
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field-compact {
|
.form-field-compact {
|
||||||
|
|
@ -74,24 +73,29 @@
|
||||||
|
|
||||||
.form-field-compact label {
|
.form-field-compact label {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 0.375rem;
|
margin-bottom: var(--space-1);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: 0.8125rem;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field-compact input,
|
.form-field-compact input,
|
||||||
.form-field-compact select,
|
.form-field-compact select,
|
||||||
.form-field-compact textarea {
|
.form-field-compact textarea {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: var(--space-2) var(--space-3);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
font-size: 0.875rem;
|
font-size: 14px;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: #ffffff;
|
background: var(--card-bg);
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 36px;
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
transition: border-color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field-compact textarea {
|
||||||
|
height: auto;
|
||||||
|
min-height: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field-compact input:focus,
|
.form-field-compact input:focus,
|
||||||
|
|
@ -99,10 +103,9 @@
|
||||||
.form-field-compact textarea:focus {
|
.form-field-compact textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08);
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Date input wrapper */
|
|
||||||
.date-input-wrapper {
|
.date-input-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -124,61 +127,60 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0 0.625rem;
|
padding: 0 0.75rem;
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid var(--primary);
|
border: 1px solid var(--primary);
|
||||||
border-top-right-radius: var(--radius-sm);
|
border-top-right-radius: 6px;
|
||||||
border-bottom-right-radius: var(--radius-sm);
|
border-bottom-right-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.15s;
|
transition: background 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-picker-btn:hover {
|
.date-picker-btn:hover {
|
||||||
background: var(--primary-hover);
|
background: var(--primary-hover, #1d4ed8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-btn:active {
|
||||||
|
transform: scale(0.96);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lines section */
|
|
||||||
.lines-section-compact {
|
.lines-section-compact {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-header-compact {
|
.lines-header-compact {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: var(--space-3);
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-header-compact h3 {
|
.lines-header-compact h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.875rem;
|
font-size: 14px;
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.03em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-compact {
|
.btn-add-compact {
|
||||||
background: #f8fafc;
|
background: var(--card-bg);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 0.4rem 0.875rem;
|
padding: var(--space-1) var(--space-3);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.8125rem;
|
font-size: 13px;
|
||||||
transition: all 0.15s;
|
transition: border-color var(--transition-fast);
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-compact:hover {
|
.btn-add-compact:hover {
|
||||||
background: #f1f5f9;
|
border-color: var(--gray-300);
|
||||||
border-color: #cbd5e1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-table-compact {
|
.lines-table-compact {
|
||||||
|
|
@ -186,57 +188,38 @@
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.375rem;
|
gap: var(--space-2);
|
||||||
}
|
|
||||||
|
|
||||||
/* Column headers */
|
|
||||||
.lines-columns-header {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 4fr 0.8fr 1.2fr 0.8fr 1.2fr 40px;
|
|
||||||
gap: 0.75rem;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-row-compact {
|
.line-row-compact {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 4fr 0.8fr 1.2fr 0.8fr 1.2fr 40px;
|
grid-template-columns: 4fr 0.8fr 1.2fr 0.8fr 1.2fr auto;
|
||||||
gap: 0.75rem;
|
gap: var(--space-2);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #f8fafc;
|
background: var(--gray-50);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
padding: 0.625rem 0.75rem;
|
padding: var(--space-2) var(--space-3);
|
||||||
transition: border-color 0.15s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-row-compact:hover {
|
|
||||||
border-color: #cbd5e1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-label-mobile { display: none; }
|
|
||||||
.line-field { display: flex; flex-direction: column; }
|
|
||||||
|
|
||||||
.line-desc-compact,
|
.line-desc-compact,
|
||||||
.line-qty-compact,
|
.line-qty-compact,
|
||||||
.line-price-compact,
|
.line-price-compact,
|
||||||
.line-tax-compact {
|
.line-tax-compact {
|
||||||
padding: 0.4rem 0.5rem;
|
padding: var(--space-1) var(--space-2);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 4px;
|
border-radius: var(--radius-sm);
|
||||||
font-size: 0.8125rem;
|
font-size: 13px;
|
||||||
background: white;
|
background: white;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
transition: border-color 0.15s ease;
|
font-family: inherit;
|
||||||
|
transition: border-color var(--transition-fast);
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-desc-compact { width: 100%; }
|
.line-desc-compact {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.line-desc-compact:focus,
|
.line-desc-compact:focus,
|
||||||
.line-qty-compact:focus,
|
.line-qty-compact:focus,
|
||||||
|
|
@ -244,114 +227,81 @@
|
||||||
.line-tax-compact:focus {
|
.line-tax-compact:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08);
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-total-compact {
|
.line-total-compact {
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: var(--primary);
|
color: var(--text-primary);
|
||||||
font-size: 0.875rem;
|
font-size: 13px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding: 0.4rem 0.25rem;
|
padding-right: var(--space-2);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete-compact {
|
.btn-delete-compact {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-tertiary);
|
color: var(--gray-400);
|
||||||
border: 1px solid transparent;
|
border: none;
|
||||||
width: 32px;
|
width: 28px;
|
||||||
height: 32px;
|
height: 28px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.15s;
|
transition: color var(--transition-fast), background-color var(--transition-fast);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete-compact:hover {
|
.btn-delete-compact:hover {
|
||||||
color: #ef4444;
|
color: var(--danger);
|
||||||
background: #fee2e2;
|
background: var(--red-50);
|
||||||
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grand total */
|
|
||||||
.invoice-grand-total {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.4rem;
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
padding: 0.875rem 1rem;
|
|
||||||
background: #f8fafc;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
max-width: 280px;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grand-total-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.grand-total-row strong {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grand-total-final {
|
|
||||||
padding-top: 0.4rem;
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
font-size: 0.9375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grand-total-final strong {
|
|
||||||
color: var(--primary);
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Form actions */
|
|
||||||
.form-actions-compact {
|
.form-actions-compact {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 0.625rem;
|
gap: var(--space-2);
|
||||||
margin-top: 0;
|
padding-top: var(--space-4);
|
||||||
padding-top: 1.25rem;
|
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel-compact {
|
.btn-cancel-compact {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 0.5rem 1rem;
|
padding: var(--space-2) var(--space-4);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.8125rem;
|
font-size: 14px;
|
||||||
transition: all 0.15s;
|
transition: border-color var(--transition-fast), color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel-compact:hover {
|
.btn-cancel-compact:hover {
|
||||||
background: #f8fafc;
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
border-color: var(--gray-300);
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-submit-compact {
|
.btn-submit-compact {
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.5rem 1.25rem;
|
padding: var(--space-2) var(--space-5);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-md);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.8125rem;
|
font-size: 14px;
|
||||||
transition: background 0.15s;
|
transition: background-color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-submit-compact:hover {
|
.btn-submit-compact:hover {
|
||||||
|
|
@ -359,43 +309,31 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-submit-compact:disabled {
|
.btn-submit-compact:disabled {
|
||||||
background: #e2e8f0;
|
background: var(--gray-200);
|
||||||
color: #94a3b8;
|
color: var(--gray-400);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.create-invoice-page {
|
.create-invoice-page {
|
||||||
padding: 0.5rem;
|
padding: var(--space-3);
|
||||||
height: calc(100vh - 1rem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-grid-compact {
|
.form-grid-compact {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-field-compact.full-width { grid-column: 1; }
|
.form-field-compact.full-width {
|
||||||
.lines-columns-header { display: none; }
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notes-grid-compact {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.line-row-compact {
|
.line-row-compact {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr auto;
|
||||||
gap: 0.5rem;
|
gap: var(--space-2);
|
||||||
padding: 0.625rem;
|
padding: var(--space-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-label-mobile {
|
|
||||||
display: block;
|
|
||||||
font-size: 0.65rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
margin-bottom: 0.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-field-desc { grid-column: 1 / -1; }
|
|
||||||
.line-field-actions { grid-column: 2; justify-self: end; }
|
|
||||||
.invoice-grand-total { max-width: 100%; }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,201 @@
|
||||||
/* ===================== DASHBOARD STYLES ===================== */
|
/* ============================================
|
||||||
.dashboard {
|
Dashboard — Analytics Layout
|
||||||
max-width: 1280px;
|
============================================ */
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-header {
|
.db-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-bottom: 1.75rem;
|
margin-bottom: var(--space-6);
|
||||||
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-header h1 {
|
.db-greeting {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin: 0;
|
font-weight: 700;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-weight: 600;
|
margin: 0 0 var(--space-1) 0;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-subtitle {
|
.db-subtitle {
|
||||||
margin: 0.2rem 0 0;
|
font-size: 13px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: 0.875rem;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stats */
|
.db-kpi-row {
|
||||||
.dashboard-stats {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 1rem;
|
gap: var(--space-4);
|
||||||
margin-bottom: 1.75rem;
|
margin-bottom: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card {
|
.db-kpi-card {
|
||||||
background: var(--card-bg);
|
background: var(--card-bg);
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 1.25rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.875rem;
|
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: var(--space-5);
|
||||||
box-shadow: var(--shadow-xs);
|
box-shadow: var(--shadow-xs);
|
||||||
transition: box-shadow 0.15s ease;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
transition: box-shadow var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card:hover {
|
.db-kpi-card:hover {
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon {
|
.db-kpi-top {
|
||||||
width: 42px;
|
display: flex;
|
||||||
height: 42px;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-kpi-icon {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--gray-100);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-kpi-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-kpi-value {
|
||||||
|
font-size: 1.65rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
line-height: 1.1;
|
||||||
|
min-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-kpi-sub {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
min-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-sub-green { color: var(--green-600); font-weight: 500; }
|
||||||
|
.db-sub-amber { color: var(--amber-600); font-weight: 500; }
|
||||||
|
.db-sub-red { color: var(--red-600); font-weight: 500; }
|
||||||
|
.db-sub-muted { color: var(--text-secondary); }
|
||||||
|
|
||||||
|
.db-kpi-skeleton {
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(90deg, var(--gray-100) 25%, var(--gray-200) 50%, var(--gray-100) 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: db-shimmer 1.4s infinite;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
width: 80px;
|
||||||
|
height: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-kpi-skeleton-sm {
|
||||||
|
width: 120px;
|
||||||
|
height: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes db-shimmer {
|
||||||
|
0% { background-position: 200% 0; }
|
||||||
|
100% { background-position: -200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-mid-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 340px;
|
||||||
|
gap: var(--space-4);
|
||||||
|
margin-bottom: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-chart-card,
|
||||||
|
.db-recent-card,
|
||||||
|
.db-table-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: var(--shadow-xs);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--space-4) var(--space-5);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-card-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-card-badge,
|
||||||
|
.db-card-year {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: var(--gray-100);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 99px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-see-all {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--blue-600);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-see-all:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
.db-chart-body {
|
||||||
|
padding: var(--space-5);
|
||||||
|
height: 260px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-chart-body canvas {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-list {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-item:last-child { border-bottom: none; }
|
||||||
|
.db-recent-item:hover { background: var(--gray-50); }
|
||||||
|
|
||||||
|
.db-recent-dot {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -60,136 +203,258 @@
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon-invoices {
|
.db-recent-dot.status-paid {
|
||||||
background: rgba(37, 99, 235, 0.08);
|
background: #f0fdf4;
|
||||||
color: #2563eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-icon-paid {
|
|
||||||
background: rgba(22, 163, 74, 0.08);
|
|
||||||
color: #16a34a;
|
color: #16a34a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon-unpaid {
|
.db-recent-dot.status-unpaid {
|
||||||
background: rgba(202, 138, 4, 0.08);
|
background: #fffbeb;
|
||||||
color: #ca8a04;
|
color: #b45309;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon-clients {
|
.db-recent-dot.status-draft {
|
||||||
background: rgba(99, 102, 241, 0.08);
|
background: #f3f4f6;
|
||||||
color: #6366f1;
|
color: #6b7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-info {
|
.db-recent-arrow {
|
||||||
|
color: var(--gray-300);
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-item:hover .db-recent-arrow { color: var(--gray-500); }
|
||||||
|
|
||||||
|
.db-recent-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.db-recent-num {
|
||||||
font-size: 1.375rem;
|
font-size: 13px;
|
||||||
font-weight: 700;
|
|
||||||
color: var(--text-primary);
|
|
||||||
line-height: 1.2;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: 0.78rem;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
margin-top: 0.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dashboard grid */
|
|
||||||
.dashboard-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-card {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 1.25rem;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
box-shadow: var(--shadow-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-card h3 {
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-client {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-amount {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-recent-date {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-table-card { margin-bottom: var(--space-4); }
|
||||||
|
.db-table-wrap { overflow-x: auto; }
|
||||||
|
|
||||||
|
.db-monitoring-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-monitoring-table thead th {
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
text-align: left;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.05em;
|
||||||
|
background: var(--gray-50);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Quick actions */
|
.db-th-right { text-align: right !important; }
|
||||||
.quick-actions {
|
.db-monitoring-table thead th:first-child { padding-left: var(--space-5); }
|
||||||
display: flex;
|
.db-monitoring-table thead th:last-child { padding-right: var(--space-5); }
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-action-btn {
|
.db-table-row { cursor: pointer; }
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0.625rem 1rem;
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
font-weight: 500;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: background-color 0.15s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
background: var(--primary);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-action-btn:hover {
|
.db-table-row td {
|
||||||
background: var(--primary-hover);
|
padding: var(--space-3) var(--space-4);
|
||||||
color: #fff;
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
|
||||||
|
|
||||||
.quick-action-btn.secondary {
|
|
||||||
background: var(--primary-subtle);
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-action-btn.secondary:hover {
|
|
||||||
background: rgba(37, 99, 235, 0.12);
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dashboard info */
|
|
||||||
.dashboard-info-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-info-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.55rem 0;
|
|
||||||
border-bottom: 1px solid var(--border-subtle);
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-info-row:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-info-row span:first-child {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-info-value {
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-weight: 600;
|
vertical-align: middle;
|
||||||
|
transition: background var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stat skeleton */
|
.db-table-row:last-child td { border-bottom: none; }
|
||||||
.stat-card .skeleton-value {
|
.db-table-row:hover td { background: var(--gray-50); }
|
||||||
width: 50px;
|
|
||||||
height: 24px;
|
.db-table-row td:first-child { padding-left: var(--space-5); }
|
||||||
|
.db-table-row td:last-child { padding-right: var(--space-5); }
|
||||||
|
|
||||||
|
.db-cell-num {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-cell-client {
|
||||||
|
max-width: 180px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-cell-right { text-align: right; }
|
||||||
|
.db-remain-due { color: var(--red-600); font-weight: 600; }
|
||||||
|
.db-remain-ok { color: var(--green-600); }
|
||||||
|
|
||||||
|
.db-table-loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--space-8) !important;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-loading-inner,
|
||||||
|
.db-no-data,
|
||||||
|
.db-error {
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--space-6);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-error { color: var(--red-600); }
|
||||||
|
.db-td-center { text-align: center; }
|
||||||
|
|
||||||
|
.db-recent-skeleton { pointer-events: none; }
|
||||||
|
|
||||||
|
.db-skel-icon,
|
||||||
|
.db-skel-lines span,
|
||||||
|
.db-skel-right span {
|
||||||
|
background: linear-gradient(90deg, var(--gray-100) 25%, var(--gray-200) 50%, var(--gray-100) 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: db-shimmer 1.4s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-lines {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-lines span {
|
||||||
|
display: block;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-lines span:first-child { width: 70%; }
|
||||||
|
.db-skel-lines span:last-child { width: 50%; }
|
||||||
|
|
||||||
|
.db-skel-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-right span {
|
||||||
|
display: block;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-skel-right span:first-child { width: 60px; }
|
||||||
|
.db-skel-right span:last-child { width: 44px; }
|
||||||
|
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.db-kpi-row { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.db-mid-row { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 580px) {
|
||||||
|
.db-kpi-row { grid-template-columns: 1fr; }
|
||||||
|
.db-greeting { font-size: 1.1rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-subtitle {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
margin: var(--space-1) 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode UX tune */
|
||||||
|
:root[data-theme='dark'] .db-kpi-card,
|
||||||
|
:root[data-theme='dark'] .db-chart-card,
|
||||||
|
:root[data-theme='dark'] .db-recent-card,
|
||||||
|
:root[data-theme='dark'] .db-table-card {
|
||||||
|
background: #081226;
|
||||||
|
border-color: #1e2f4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-card-year,
|
||||||
|
:root[data-theme='dark'] .db-card-badge,
|
||||||
|
:root[data-theme='dark'] .db-kpi-icon {
|
||||||
|
background: rgba(30, 41, 59, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-recent-item,
|
||||||
|
:root[data-theme='dark'] .db-table-row td {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-recent-item:hover,
|
||||||
|
:root[data-theme='dark'] .db-table-row:hover td {
|
||||||
|
background: rgba(30, 58, 110, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-recent-dot.status-paid {
|
||||||
|
background: rgba(34, 197, 94, 0.18);
|
||||||
|
color: #86efac;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-recent-dot.status-unpaid {
|
||||||
|
background: rgba(245, 158, 11, 0.2);
|
||||||
|
color: #fcd34d;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .db-recent-dot.status-draft {
|
||||||
|
background: rgba(148, 163, 184, 0.18);
|
||||||
|
color: #cbd5e1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,270 @@
|
||||||
|
/* ============================================
|
||||||
|
Facturas + Tablas + Paginacion
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.facturas-page {
|
||||||
|
padding: var(--space-6);
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.facturas-header { margin-bottom: var(--space-5); }
|
||||||
|
.facturas-header h1 { font-size: 1.25rem; font-weight: 600; }
|
||||||
|
|
||||||
|
.btn-new-invoice {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
padding: var(--space-2) var(--space-4);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-new-invoice:hover { background-color: var(--primary-hover); }
|
||||||
|
|
||||||
|
.facturas-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-3);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input,
|
||||||
|
.filter-status {
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
height: 36px;
|
||||||
|
transition: border-color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input:focus,
|
||||||
|
.filter-status:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input { flex: 1; max-width: 320px; }
|
||||||
|
.filter-status { min-width: 180px; cursor: pointer; }
|
||||||
|
|
||||||
|
.invoices-table-container,
|
||||||
|
.clients-table-container {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoices-table,
|
||||||
|
.clients-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoices-table thead,
|
||||||
|
.clients-table thead {
|
||||||
|
background: var(--gray-50);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoices-table th,
|
||||||
|
.clients-table th {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoices-table tbody tr,
|
||||||
|
.clients-table tbody tr {
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoices-table tbody tr:hover,
|
||||||
|
.clients-table tbody tr:hover { background-color: var(--gray-50); }
|
||||||
|
|
||||||
|
.invoices-table tbody tr:last-child,
|
||||||
|
.clients-table tbody tr:last-child { border-bottom: none; }
|
||||||
|
|
||||||
|
.invoices-table td,
|
||||||
|
.clients-table td { padding: var(--space-3) var(--space-4); font-size: 14px; }
|
||||||
|
|
||||||
|
.invoice-number { font-weight: 500; color: var(--text-primary); }
|
||||||
|
|
||||||
|
.invoice-client {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-icon { display: flex; align-items: center; color: var(--gray-400); }
|
||||||
|
.invoice-date { color: var(--text-primary); }
|
||||||
|
|
||||||
|
.invoice-total,
|
||||||
|
.invoice-remain {
|
||||||
|
font-weight: 500;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-1);
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-view,
|
||||||
|
.btn-action {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: var(--space-1);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--gray-400);
|
||||||
|
transition: color var(--transition-fast), background-color var(--transition-fast);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-view:hover { color: var(--primary); background-color: var(--blue-50); }
|
||||||
|
.btn-action:hover { background-color: var(--gray-100); color: var(--text-primary); }
|
||||||
|
.btn-delete:hover { background-color: var(--red-50); color: var(--danger); }
|
||||||
|
|
||||||
|
.loading,
|
||||||
|
.error,
|
||||||
|
.no-invoices,
|
||||||
|
.no-clients {
|
||||||
|
padding: var(--space-10);
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error { color: var(--danger); }
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-4);
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-1);
|
||||||
|
align-items: center;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info .current-page,
|
||||||
|
.pagination-info .total-pages {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination {
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
transition: background-color var(--transition-fast), border-color var(--transition-fast);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination:hover:not(:disabled) {
|
||||||
|
background-color: var(--gray-50);
|
||||||
|
border-color: var(--gray-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination:disabled {
|
||||||
|
background-color: var(--gray-50);
|
||||||
|
color: var(--gray-300);
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: var(--gray-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark tune */
|
||||||
|
:root[data-theme='dark'] .invoices-table-container,
|
||||||
|
:root[data-theme='dark'] .clients-table-container,
|
||||||
|
:root[data-theme='dark'] .pagination {
|
||||||
|
background: #081226;
|
||||||
|
border-color: #1e2f4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .invoices-table thead,
|
||||||
|
:root[data-theme='dark'] .clients-table thead {
|
||||||
|
background: rgba(15, 23, 42, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .invoices-table tbody tr:hover,
|
||||||
|
:root[data-theme='dark'] .clients-table tbody tr:hover,
|
||||||
|
:root[data-theme='dark'] .btn-pagination:hover:not(:disabled) {
|
||||||
|
background-color: rgba(30, 58, 110, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.facturas-filters {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-pagination {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 100px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
order: 3;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.facturas-page {
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
/* ============================================
|
||||||
|
Login + Session Notices
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--bg-page);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
padding: var(--space-8);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 380px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-title {
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin: 0 0 var(--space-6) 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: var(--space-1);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-2) var(--space-4);
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
height: 36px;
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button:hover {
|
||||||
|
background-color: var(--primary-hover);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-error {
|
||||||
|
color: var(--danger);
|
||||||
|
text-align: center;
|
||||||
|
margin-top: var(--space-3);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-overlay,
|
||||||
|
.connection-lost-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-overlay {
|
||||||
|
background: rgba(17, 24, 39, 0.45);
|
||||||
|
z-index: 3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-lost-overlay {
|
||||||
|
background: rgba(17, 24, 39, 0.55);
|
||||||
|
z-index: 4000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-modal,
|
||||||
|
.connection-lost-modal {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
padding: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-modal { max-width: 430px; }
|
||||||
|
.connection-lost-modal { max-width: 460px; }
|
||||||
|
|
||||||
|
.session-warning-modal h3 {
|
||||||
|
margin: 0 0 var(--space-3) 0;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-lost-modal h3 {
|
||||||
|
margin: 0 0 var(--space-2) 0;
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-modal p,
|
||||||
|
.connection-lost-modal p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-actions {
|
||||||
|
margin-top: var(--space-5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-logout {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-logout:hover {
|
||||||
|
background: var(--gray-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-continue {
|
||||||
|
background: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-lost-sub {
|
||||||
|
margin-top: var(--space-3) !important;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connection-lost-login-btn {
|
||||||
|
margin-top: var(--space-5);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.session-warning-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-warning-actions button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-close:hover {
|
.btn-close:hover {
|
||||||
background-color: #f1f5f9;
|
background-color: var(--bg-subtle);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@
|
||||||
font-size: 0.9375rem;
|
font-size: 0.9375rem;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
background-color: #ffffff;
|
background-color: var(--card-bg);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,19 +183,19 @@
|
||||||
.form-group input:disabled,
|
.form-group input:disabled,
|
||||||
.form-group textarea:disabled,
|
.form-group textarea:disabled,
|
||||||
.form-group select:disabled {
|
.form-group select:disabled {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group input[readonly] {
|
.form-group input[readonly] {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add Line Form */
|
/* Add Line Form */
|
||||||
.add-line-form {
|
.add-line-form {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
@ -219,7 +219,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-table thead {
|
.lines-table thead {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-table th {
|
.lines-table th {
|
||||||
|
|
@ -236,7 +236,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines-table tbody tr:hover {
|
.lines-table tbody tr:hover {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-lines {
|
.no-lines {
|
||||||
|
|
@ -281,7 +281,7 @@
|
||||||
.invoice-totals {
|
.invoice-totals {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -493,7 +493,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel:hover {
|
.btn-cancel:hover {
|
||||||
background-color: #f8fafc;
|
background-color: var(--bg-subtle);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -519,7 +519,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-action:hover {
|
.btn-action:hover {
|
||||||
background-color: #f1f5f9;
|
background-color: var(--bg-subtle);
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -720,14 +720,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-exit-actions .btn-exit {
|
.confirm-exit-actions .btn-exit {
|
||||||
background-color: #f1f5f9;
|
background-color: var(--bg-subtle);
|
||||||
color: var(--text-primary, #1e293b);
|
color: var(--text-primary, #1e293b);
|
||||||
border: 1px solid var(--border-color, #e2e8f0);
|
border: 1px solid var(--border-color, #e2e8f0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-exit-actions .btn-exit:hover {
|
.confirm-exit-actions .btn-exit:hover {
|
||||||
background-color: #e2e8f0;
|
background-color: rgba(148, 163, 184, 0.18);
|
||||||
border-color: #cbd5e1;
|
border-color: rgba(148, 163, 184, 0.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Confirm Modal */
|
/* Responsive Confirm Modal */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* ============================================
|
||||||
|
Settings Page
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.settings-page {
|
||||||
|
max-width: 980px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header {
|
||||||
|
margin-bottom: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header p {
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: var(--space-5);
|
||||||
|
box-shadow: var(--shadow-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card h2 {
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-label {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-help {
|
||||||
|
margin: var(--space-1) 0 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-theme-toggle {
|
||||||
|
min-width: 116px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-session-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-session-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding-bottom: var(--space-3);
|
||||||
|
border-bottom: 1px dashed var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-session-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-token-expired {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
/* ============================================
|
||||||
|
App Layout + Sidebar
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
.app-layout {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 220px;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: width var(--transition-base);
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: var(--space-4);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--space-1);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle:hover {
|
||||||
|
background-color: var(--gray-100);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--space-2) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color var(--transition-fast), background-color var(--transition-fast);
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link:hover {
|
||||||
|
background-color: var(--gray-100);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-link.active {
|
||||||
|
background-color: var(--blue-50);
|
||||||
|
color: var(--primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .sidebar-link:hover {
|
||||||
|
background-color: rgba(30, 58, 110, 0.18);
|
||||||
|
color: #dbeafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme='dark'] .sidebar-link.active {
|
||||||
|
background-color: rgba(30, 58, 110, 0.32);
|
||||||
|
color: #bfdbfe;
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(96, 165, 250, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed { width: 56px; }
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-header h2,
|
||||||
|
.sidebar.collapsed .sidebar-text {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-icon { margin-right: 0; }
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-link {
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: var(--bg-page);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content > div {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue