Refactor auth #65

Merged
killer-cf merged 8 commits from refactor-auth into master 2025-12-13 22:13:28 +00:00
90 changed files with 2256 additions and 2197 deletions

View File

@@ -14,6 +14,13 @@
"mcp", "mcp",
"start" "start"
] ]
},
"ark-ui": {
"command": "npx",
"args": [
"-y",
"@ark-ui/mcp"
]
} }
} }
} }

View File

@@ -32,6 +32,7 @@
"vite": "^7.1.2" "vite": "^7.1.2"
}, },
"dependencies": { "dependencies": {
"@ark-ui/svelte": "^5.15.0",
"@convex-dev/better-auth": "^0.9.7", "@convex-dev/better-auth": "^0.9.7",
"@dicebear/collection": "^9.2.4", "@dicebear/collection": "^9.2.4",
"@dicebear/core": "^9.2.4", "@dicebear/core": "^9.2.4",
@@ -60,6 +61,7 @@
"marked": "^17.0.1", "marked": "^17.0.1",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"svelte-sonner": "^1.0.5", "svelte-sonner": "^1.0.5",
"theme-change": "^2.5.0",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"xlsx-js-style": "^1.2.0", "xlsx-js-style": "^1.2.0",
"zod": "^4.1.12" "zod": "^4.1.12"

View File

@@ -21,405 +21,348 @@
@apply border-error bg-base-100 hover:bg-error/60 active:bg-error text-error flex items-center justify-center gap-2 rounded-xl border px-4 py-2 text-center font-medium transition-colors hover:text-white active:text-white; @apply border-error bg-base-100 hover:bg-error/60 active:bg-error text-error flex items-center justify-center gap-2 rounded-xl border px-4 py-2 text-center font-medium transition-colors hover:text-white active:text-white;
} }
/* Tema Aqua (padrão roxo/azul) - customizado para garantir funcionamento */ /* Tema Aqua (padrão roxo/azul) - redefinido como custom para garantir compatibilidade */
html[data-theme='aqua'], @plugin 'daisyui/theme' {
html[data-theme='aqua'] body, name: 'aqua';
[data-theme='aqua'] { default: true;
color-scheme: light; color-scheme: light;
--p: 217 91% 60%; /* Azul principal (ligeiramente mais escuro que o anterior) */
--pf: 217 91% 50%; --color-primary: hsl(217 91% 55%);
--pc: 0 0% 100%; --color-primary-content: hsl(0 0% 100%);
--s: 217 91% 60%; --color-secondary: hsl(217 91% 55%);
--sf: 217 91% 50%; --color-secondary-content: hsl(0 0% 100%);
--sc: 0 0% 100%; --color-accent: hsl(217 91% 55%);
--a: 217 91% 60%; --color-accent-content: hsl(0 0% 100%);
--af: 217 91% 50%; --color-neutral: hsl(217 20% 17%);
--ac: 0 0% 100%; --color-neutral-content: hsl(0 0% 100%);
--n: 217 20% 17%; --color-base-100: hsl(0 0% 100%);
--nf: 217 20% 10%; --color-base-200: hsl(217 20% 95%);
--nc: 0 0% 100%; --color-base-300: hsl(217 20% 90%);
--b1: 0 0% 100%; --color-base-content: hsl(217 20% 17%);
--b2: 217 20% 95%; --color-info: hsl(217 91% 60%);
--b3: 217 20% 90%; --color-info-content: hsl(0 0% 100%);
--bc: 217 20% 17%; --color-success: hsl(142 76% 36%);
--in: 217 91% 60%; --color-success-content: hsl(0 0% 100%);
--inc: 0 0% 100%; --color-warning: hsl(38 92% 50%);
--su: 142 76% 36%; --color-warning-content: hsl(0 0% 100%);
--suc: 0 0% 100%; --color-error: hsl(0 84% 60%);
--wa: 38 92% 50%; --color-error-content: hsl(0 0% 100%);
--wac: 0 0% 100%; --radius-selector: 0.5rem;
--er: 0 84% 60%; --radius-field: 0.5rem;
--erc: 0 0% 100%; --radius-box: 1rem;
--rounded-box: 1rem; --size-selector: 0.25rem;
--rounded-btn: 0.5rem; --size-field: 0.25rem;
--rounded-badge: 1.9rem; --border: 1px;
--animation-btn: 0.25s; --depth: 1;
--animation-input: 0.2s; --noise: 0;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
/* Temas customizados para SGSE - Azul */ /* Temas customizados para SGSE */
html[data-theme='sgse-blue'],
html[data-theme='sgse-blue'] body, /* Azul */
[data-theme='sgse-blue'] { @plugin 'daisyui/theme' {
name: 'sgse-blue';
color-scheme: light; color-scheme: light;
--p: 217 91% 60%; --color-primary: hsl(217 91% 55%);
--pf: 217 91% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(217 91% 55%);
--s: 217 91% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 217 91% 50%; --color-accent: hsl(217 91% 55%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 217 91% 60%; --color-neutral: hsl(217 20% 17%);
--af: 217 91% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 217 20% 17%; --color-base-200: hsl(217 20% 95%);
--nf: 217 20% 10%; --color-base-300: hsl(217 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(217 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(217 91% 60%);
--b2: 217 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 217 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 217 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 217 91% 60%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
/* Garantir que todas as variáveis CSS sejam aplicadas em todos os elementos */ /* Verde */
html[data-theme] { @plugin 'daisyui/theme' {
color-scheme: var(--color-scheme, light); name: 'sgse-green';
}
html[data-theme] * {
color-scheme: inherit;
}
html[data-theme='sgse-green'],
html[data-theme='sgse-green'] body,
[data-theme='sgse-green'] {
color-scheme: light; color-scheme: light;
--p: 142 76% 36%; --color-primary: hsl(142 76% 36%);
--pf: 142 76% 26%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(142 76% 36%);
--s: 142 76% 36%; --color-secondary-content: hsl(0 0% 100%);
--sf: 142 76% 26%; --color-accent: hsl(142 76% 36%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 142 76% 36%; --color-neutral: hsl(142 20% 17%);
--af: 142 76% 26%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 142 20% 17%; --color-base-200: hsl(142 20% 95%);
--nf: 142 20% 10%; --color-base-300: hsl(142 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(142 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(142 76% 36%);
--b2: 142 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 142 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 142 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 142 76% 36%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
html[data-theme='sgse-orange'], /* Laranja */
html[data-theme='sgse-orange'] body, @plugin 'daisyui/theme' {
[data-theme='sgse-orange'] { name: 'sgse-orange';
color-scheme: light; color-scheme: light;
--p: 25 95% 53%; --color-primary: hsl(25 95% 53%);
--pf: 25 95% 43%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(25 95% 53%);
--s: 25 95% 53%; --color-secondary-content: hsl(0 0% 100%);
--sf: 25 95% 43%; --color-accent: hsl(25 95% 53%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 25 95% 53%; --color-neutral: hsl(25 20% 17%);
--af: 25 95% 43%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 25 20% 17%; --color-base-200: hsl(25 20% 95%);
--nf: 25 20% 10%; --color-base-300: hsl(25 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(25 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(25 95% 53%);
--b2: 25 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 25 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 25 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 25 95% 53%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
html[data-theme='sgse-red'], /* Vermelho */
html[data-theme='sgse-red'] body, @plugin 'daisyui/theme' {
[data-theme='sgse-red'] { name: 'sgse-red';
color-scheme: light; color-scheme: light;
--p: 0 84% 60%; --color-primary: hsl(0 84% 60%);
--pf: 0 84% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(0 84% 60%);
--s: 0 84% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 0 84% 50%; --color-accent: hsl(0 84% 60%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 0 84% 60%; --color-neutral: hsl(0 20% 17%);
--af: 0 84% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 0 20% 17%; --color-base-200: hsl(0 20% 95%);
--nf: 0 20% 10%; --color-base-300: hsl(0 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(0 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(0 84% 60%);
--b2: 0 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 0 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 0 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 0 84% 60%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
html[data-theme='sgse-pink'], /* Rosa */
html[data-theme='sgse-pink'] body, @plugin 'daisyui/theme' {
[data-theme='sgse-pink'] { name: 'sgse-pink';
color-scheme: light; color-scheme: light;
--p: 330 81% 60%; --color-primary: hsl(330 81% 60%);
--pf: 330 81% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(330 81% 60%);
--s: 330 81% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 330 81% 50%; --color-accent: hsl(330 81% 60%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 330 81% 60%; --color-neutral: hsl(330 20% 17%);
--af: 330 81% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 330 20% 17%; --color-base-200: hsl(330 20% 95%);
--nf: 330 20% 10%; --color-base-300: hsl(330 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(330 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(330 81% 60%);
--b2: 330 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 330 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 330 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 330 81% 60%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
html[data-theme='sgse-teal'], /* Teal */
html[data-theme='sgse-teal'] body, @plugin 'daisyui/theme' {
[data-theme='sgse-teal'] { name: 'sgse-teal';
color-scheme: light; color-scheme: light;
--p: 173 80% 40%; --color-primary: hsl(173 80% 40%);
--pf: 173 80% 30%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(173 80% 40%);
--s: 173 80% 40%; --color-secondary-content: hsl(0 0% 100%);
--sf: 173 80% 30%; --color-accent: hsl(173 80% 40%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 173 80% 40%; --color-neutral: hsl(173 20% 17%);
--af: 173 80% 30%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 173 20% 17%; --color-base-200: hsl(173 20% 95%);
--nf: 173 20% 10%; --color-base-300: hsl(173 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(173 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(173 80% 40%);
--b2: 173 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 173 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 173 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 173 80% 40%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
html[data-theme='sgse-corporate'], /* Corporativo (Dark Blue) */
html[data-theme='sgse-corporate'] body, @plugin 'daisyui/theme' {
[data-theme='sgse-corporate'] { name: 'sgse-corporate';
color-scheme: dark; color-scheme: dark;
--p: 217 91% 60%; --color-primary: hsl(217 91% 55%);
--pf: 217 91% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(217 91% 55%);
--s: 217 91% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 217 91% 50%; --color-accent: hsl(217 91% 55%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 217 91% 60%; --color-neutral: hsl(217 30% 15%);
--af: 217 91% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; /* Aproxima do fundo do login (Tailwind slate-900 = #0f172a) */
--n: 217 30% 15%; --color-base-100: hsl(222 47% 11%);
--nf: 217 30% 8%; /* Escala de contraste (slate-800 / slate-700 aproximados) */
--nc: 0 0% 100%; --color-base-200: hsl(215 28% 17%);
--b1: 217 30% 10%; --color-base-300: hsl(215 25% 23%);
--b2: 217 30% 15%; --color-base-content: hsl(217 10% 90%);
--b3: 217 30% 20%; --color-info: hsl(217 91% 60%);
--bc: 217 10% 90%; --color-info-content: hsl(0 0% 100%);
--in: 217 91% 60%; --color-success: hsl(142 76% 36%);
--inc: 0 0% 100%; --color-success-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-warning: hsl(38 92% 50%);
--suc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --color-error: hsl(0 84% 60%);
--wac: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--er: 0 84% 60%; --radius-selector: 0.5rem;
--erc: 0 0% 100%; --radius-field: 0.5rem;
--rounded-box: 1rem; --radius-box: 1rem;
--rounded-btn: 0.5rem; --size-selector: 0.25rem;
--rounded-badge: 1.9rem; --size-field: 0.25rem;
--animation-btn: 0.25s; --border: 1px;
--animation-input: 0.2s; --depth: 1;
--btn-focus-scale: 0.95; --noise: 0;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
/* Tema Light customizado para garantir funcionamento completo */ /* Light */
html[data-theme='light'], @plugin 'daisyui/theme' {
html[data-theme='light'] body, name: 'light';
[data-theme='light'] {
color-scheme: light; color-scheme: light;
--p: 217 91% 60%; --color-primary: hsl(217 91% 55%);
--pf: 217 91% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(217 91% 55%);
--s: 217 91% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 217 91% 50%; --color-accent: hsl(217 91% 55%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 217 91% 60%; --color-neutral: hsl(217 20% 17%);
--af: 217 91% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(0 0% 100%);
--n: 217 20% 17%; --color-base-200: hsl(217 20% 95%);
--nf: 217 20% 10%; --color-base-300: hsl(217 20% 90%);
--nc: 0 0% 100%; --color-base-content: hsl(217 20% 17%);
--b1: 0 0% 100%; --color-info: hsl(217 91% 60%);
--b2: 217 20% 95%; --color-info-content: hsl(0 0% 100%);
--b3: 217 20% 90%; --color-success: hsl(142 76% 36%);
--bc: 217 20% 17%; --color-success-content: hsl(0 0% 100%);
--in: 217 91% 60%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }
/* Tema Dark customizado para garantir funcionamento completo */ /* Dark */
html[data-theme='dark'], @plugin 'daisyui/theme' {
html[data-theme='dark'] body, name: 'dark';
[data-theme='dark'] {
color-scheme: dark; color-scheme: dark;
--p: 217 91% 60%; --color-primary: hsl(217 91% 55%);
--pf: 217 91% 50%; --color-primary-content: hsl(0 0% 100%);
--pc: 0 0% 100%; --color-secondary: hsl(217 91% 55%);
--s: 217 91% 60%; --color-secondary-content: hsl(0 0% 100%);
--sf: 217 91% 50%; --color-accent: hsl(217 91% 55%);
--sc: 0 0% 100%; --color-accent-content: hsl(0 0% 100%);
--a: 217 91% 60%; --color-neutral: hsl(217 30% 15%);
--af: 217 91% 50%; --color-neutral-content: hsl(0 0% 100%);
--ac: 0 0% 100%; --color-base-100: hsl(217 30% 10%);
--n: 217 30% 15%; --color-base-200: hsl(217 30% 15%);
--nf: 217 30% 8%; --color-base-300: hsl(217 30% 20%);
--nc: 0 0% 100%; --color-base-content: hsl(217 10% 90%);
--b1: 217 30% 10%; --color-info: hsl(217 91% 60%);
--b2: 217 30% 15%; --color-info-content: hsl(0 0% 100%);
--b3: 217 30% 20%; --color-success: hsl(142 76% 36%);
--bc: 217 10% 90%; --color-success-content: hsl(0 0% 100%);
--in: 217 91% 60%; --color-warning: hsl(38 92% 50%);
--inc: 0 0% 100%; --color-warning-content: hsl(0 0% 100%);
--su: 142 76% 36%; --color-error: hsl(0 84% 60%);
--suc: 0 0% 100%; --color-error-content: hsl(0 0% 100%);
--wa: 38 92% 50%; --radius-selector: 0.5rem;
--wac: 0 0% 100%; --radius-field: 0.5rem;
--er: 0 84% 60%; --radius-box: 1rem;
--erc: 0 0% 100%; --size-selector: 0.25rem;
--rounded-box: 1rem; --size-field: 0.25rem;
--rounded-btn: 0.5rem; --border: 1px;
--rounded-badge: 1.9rem; --depth: 1;
--animation-btn: 0.25s; --noise: 0;
--animation-input: 0.2s;
--btn-focus-scale: 0.95;
--border-btn: 1px;
--tab-border: 1px;
--tab-radius: 0.5rem;
} }

View File

@@ -111,8 +111,17 @@
(function () { (function () {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
var html = document.documentElement; var html = document.documentElement;
if (!html.getAttribute('data-theme')) { if (html && !html.getAttribute('data-theme')) {
html.setAttribute('data-theme', 'aqua'); var tema = null;
try {
// theme-change usa por padrão a chave "theme"
tema = localStorage.getItem('theme');
} catch (e) {
tema = null;
}
// Fallback para o tema padrão se não houver persistência
html.setAttribute('data-theme', tema || 'aqua');
} }
} }
})(); })();

View File

@@ -1,71 +0,0 @@
<script lang="ts">
import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { loginModalStore } from '$lib/stores/loginModal.svelte';
import { TriangleAlert } from 'lucide-svelte';
interface Props {
recurso: string;
acao: string;
children?: any;
}
let { recurso, acao, children }: Props = $props();
let verificando = $state(true);
let permitido = $state(false);
// Usuário atual
const currentUser = useQuery(api.auth.getCurrentUser, {});
let permissaoQuery = $derived(
currentUser?.data
? useQuery(api.permissoesAcoes.verificarAcao, {
usuarioId: currentUser.data._id as Id<'usuarios'>,
recurso,
acao
})
: null
);
$effect(() => {
if (!currentUser?.data) {
verificando = false;
permitido = false;
const currentPath = window.location.pathname;
loginModalStore.open(currentPath);
return;
}
if (permissaoQuery?.error) {
verificando = false;
permitido = false;
} else if (permissaoQuery && !permissaoQuery.isLoading) {
// Backend retorna null quando permitido
verificando = false;
permitido = true;
}
});
</script>
{#if verificando}
<div class="flex min-h-screen items-center justify-center">
<div class="text-center">
<span class="loading loading-spinner loading-lg text-primary"></span>
<p class="text-base-content/70 mt-4">Verificando permissões...</p>
</div>
</div>
{:else if permitido}
{@render children?.()}
{:else}
<div class="flex min-h-screen items-center justify-center">
<div class="text-center">
<div class="bg-error/10 mb-4 inline-block rounded-full p-4">
<TriangleAlert class="text-error h-16 w-16" strokeWidth={2} />
</div>
<h2 class="text-base-content mb-2 text-2xl font-bold">Acesso Negado</h2>
<p class="text-base-content/70">Você não tem permissão para acessar esta ação.</p>
</div>
</div>
{/if}

View File

@@ -0,0 +1,16 @@
<script lang="ts">
interface Props {
class?: string;
}
let { class: className = '' }: Props = $props();
</script>
<div class={['absolute inset-0 h-full w-full', className]}>
<div
class="bg-primary/20 absolute top-[-10%] left-[-10%] h-[40%] w-[40%] animate-pulse rounded-full blur-[120px]"
></div>
<div
class="bg-secondary/20 absolute right-[-10%] bottom-[-10%] h-[40%] w-[40%] animate-pulse rounded-full blur-[120px] delay-700"
></div>
</div>

View File

@@ -0,0 +1,14 @@
<script lang="ts">
interface Props {
class?: string;
}
let { class: className = '' }: Props = $props();
</script>
<div
class={[
'via-primary absolute top-0 left-0 h-1 w-full bg-linear-to-r from-transparent to-transparent opacity-50',
className
]}
></div>

View File

@@ -0,0 +1,22 @@
<script lang="ts">
import { XCircle } from 'lucide-svelte';
interface Props {
message?: string | null;
class?: string;
}
let { message = null, class: className = '' }: Props = $props();
</script>
{#if message}
<div
class={[
'border-error/20 bg-error/10 text-error-content/90 mb-6 flex items-center gap-3 rounded-lg border p-4 backdrop-blur-md',
className
]}
>
<XCircle class="h-5 w-5 shrink-0" />
<span class="text-sm font-medium">{message}</span>
</div>
{/if}

View File

@@ -0,0 +1,57 @@
<script lang="ts">
import { resolve } from '$app/paths';
const currentYear = new Date().getFullYear();
</script>
<footer class="bg-base-200 text-base-content border-base-300 mt-16 border-t">
<div class="container mx-auto px-4 py-8">
<div class="grid grid-cols-1 gap-8 text-center md:grid-cols-3 md:text-left">
<div>
<h3 class="text-primary mb-4 text-lg font-bold">SGSE</h3>
<p class="mx-auto max-w-xs text-sm opacity-75 md:mx-0">
Sistema de Gestão de Secretaria<br />
Simplificando processos e conectando pessoas.
</p>
</div>
<div>
<h3 class="mb-4 text-lg font-bold">Links Úteis</h3>
<ul class="space-y-2 text-sm opacity-75">
<li>
<a
href="https://www.pe.gov.br/"
target="_blank"
class="hover:text-primary transition-colors">Portal do Governo</a
>
</li>
<li>
<a href={resolve('/privacidade')} class="hover:text-primary transition-colors"
>Política de Privacidade</a
>
</li>
<li>
<a href={resolve('/abrir-chamado')} class="hover:text-primary transition-colors"
>Suporte</a
>
</li>
</ul>
</div>
<div>
<h3 class="mb-4 text-lg font-bold">Contato</h3>
<p class="text-sm opacity-75">
Secretaria de Educação<br />
Recife - PE
</p>
</div>
</div>
<div class="divider mt-8 mb-4"></div>
<div class="flex flex-col items-center justify-between text-sm opacity-60 md:flex-row">
<p>&copy; {currentYear} Governo de Pernambuco. Todos os direitos reservados.</p>
<p class="mt-2 md:mt-0">Desenvolvido com tecnologia de ponta.</p>
</div>
</div>
</footer>

View File

@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
class?: string;
children?: Snippet;
}
let { class: className = '', children }: Props = $props();
</script>
<div
class={[
'border-base-content/10 bg-base-content/5 ring-base-content/10 relative overflow-hidden rounded-2xl border p-8 shadow-2xl ring-1 backdrop-blur-xl transition-all duration-300',
className
]}
>
{@render children?.()}
</div>

View File

@@ -1,7 +1,60 @@
<script lang="ts"> <script lang="ts">
import { resolve } from '$app/paths';
import logo from '$lib/assets/logo_governo_PE.png'; import logo from '$lib/assets/logo_governo_PE.png';
import type { Snippet } from 'svelte';
type HeaderProps = {
left?: Snippet;
right?: Snippet;
};
const { left, right }: HeaderProps = $props();
</script> </script>
<div class="navbar bg-base-200 w-76 p-4 shadow-sm"> <header
<img src={logo} alt="Logo" class="" /> class="bg-base-200 border-base-100 sticky top-0 z-50 w-full border-b py-3 shadow-sm backdrop-blur-md transition-all duration-300"
>
<div class=" flex h-16 w-full items-center justify-between px-4">
<div class="flex items-center gap-3">
{#if left}
{@render left()}
{/if}
<a
href={resolve('/')}
class="group flex items-center gap-3 transition-transform hover:scale-[1.02]"
>
<img src={logo} alt="Logo Governo PE" class="h-10 w-auto object-contain drop-shadow-sm" />
<div class="hidden flex-col sm:flex">
<span class="text-primary text-2xl font-bold tracking-wider uppercase">SGSE</span>
<span class="text-base-content -mt-1 text-lg leading-none font-extrabold tracking-tight"
>Sistema de Gestão da Secretaria de Esportes</span
>
</div> </div>
</a>
</div>
<div class="flex items-center gap-2">
<select
class="select select-sm bg-base-100 border-base-300 w-40"
aria-label="Selecionar tema"
data-choose-theme
>
<option value="aqua">Aqua</option>
<option value="sgse-blue">Azul</option>
<option value="sgse-green">Verde</option>
<option value="sgse-orange">Laranja</option>
<option value="sgse-red">Vermelho</option>
<option value="sgse-pink">Rosa</option>
<option value="sgse-teal">Verde-água</option>
<option value="sgse-corporate">Corporativo</option>
<option value="light">Claro</option>
<option value="dark">Escuro</option>
</select>
{#if right}
{@render right()}
{/if}
</div>
</div>
</header>

View File

@@ -1,163 +0,0 @@
<script lang="ts">
import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { loginModalStore } from '$lib/stores/loginModal.svelte';
import { onMount } from 'svelte';
interface MenuProtectionProps {
menuPath: string;
requireGravar?: boolean;
children?: any;
redirectTo?: string;
}
let {
menuPath,
requireGravar = false,
children,
redirectTo = '/'
}: MenuProtectionProps = $props();
let verificando = $state(true);
let temPermissao = $state(false);
let motivoNegacao = $state('');
// Query para verificar permissões (só executa se o usuário estiver autenticado)
const currentUser = useQuery(api.auth.getCurrentUser, {});
let permissaoQuery = $derived(
currentUser?.data
? useQuery(api.menuPermissoes.verificarAcesso, {
usuarioId: currentUser.data._id as Id<'usuarios'>,
menuPath: menuPath
})
: null
);
onMount(() => {
verificarPermissoes();
});
$effect(() => {
// Re-verificar quando o status do usuário atual mudar
verificarPermissoes();
});
$effect(() => {
// Re-verificar quando a query carregar
if (permissaoQuery?.data) {
verificarPermissoes();
}
});
function verificarPermissoes() {
// Dashboard e abertura de chamados são públicos
if (menuPath === '/' || menuPath === '/abrir-chamado') {
verificando = false;
temPermissao = true;
return;
}
// Se não está autenticado
if (!currentUser?.data) {
verificando = false;
temPermissao = false;
motivoNegacao = 'auth_required';
// Abrir modal de login e salvar rota de redirecionamento
const currentPath = window.location.pathname;
loginModalStore.open(currentPath);
// NÃO redirecionar, apenas mostrar o modal
// O usuário verá a mensagem "Verificando permissões..." enquanto o modal está aberto
return;
}
// Se está autenticado, verificar permissões
if (permissaoQuery?.data) {
const permissao = permissaoQuery.data;
// Se não pode acessar
if (!permissao.podeAcessar) {
verificando = false;
temPermissao = false;
motivoNegacao = 'access_denied';
return;
}
// Se requer gravação mas não tem permissão
if (requireGravar && !permissao.podeGravar) {
verificando = false;
temPermissao = false;
motivoNegacao = 'write_denied';
return;
}
// Tem permissão!
verificando = false;
temPermissao = true;
} else if (permissaoQuery?.error) {
verificando = false;
temPermissao = false;
motivoNegacao = 'error';
}
}
</script>
{#if verificando}
<div class="flex min-h-screen items-center justify-center">
<div class="text-center">
{#if motivoNegacao === 'auth_required'}
<div class="bg-warning/10 mb-4 inline-block rounded-full p-4">
<svg
xmlns="http://www.w3.org/2000/svg"
class="text-warning h-16 w-16"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg>
</div>
<h2 class="text-base-content mb-2 text-2xl font-bold">Acesso Restrito</h2>
<p class="text-base-content/70 mb-4">
Esta área requer autenticação.<br />
Por favor, faça login para continuar.
</p>
{:else}
<span class="loading loading-spinner loading-lg text-primary"></span>
<p class="text-base-content/70 mt-4">Verificando permissões...</p>
{/if}
</div>
</div>
{:else if temPermissao}
{@render children?.()}
{:else}
<div class="flex min-h-screen items-center justify-center">
<div class="text-center">
<div class="bg-error/10 mb-4 inline-block rounded-full p-4">
<svg
xmlns="http://www.w3.org/2000/svg"
class="text-error h-16 w-16"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
</div>
<h2 class="text-base-content mb-2 text-2xl font-bold">Acesso Negado</h2>
<p class="text-base-content/70">Você não tem permissão para acessar esta página.</p>
</div>
</div>
{/if}

View File

@@ -0,0 +1,85 @@
<script lang="ts">
import { prefersReducedMotion, Spring } from 'svelte/motion';
interface Props {
open: boolean;
class?: string;
stroke?: number;
}
let { open, class: className = '', stroke = 2 }: Props = $props();
const progress = Spring.of(() => (open ? 1 : 0), {
stiffness: 0.25,
damping: 0.65,
precision: 0.001
});
const clamp01 = (n: number) => Math.max(0, Math.min(1, n));
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
let t = $derived(prefersReducedMotion.current ? (open ? 1 : 0) : progress.current);
let tFast = $derived(clamp01(t * 1.15));
// Fechado: hambúrguer. Aberto: "outro menu" (linhas deslocadas + comprimentos diferentes).
// Continua sendo ícone de menu (não vira X).
let topY = $derived(lerp(-6, -7, tFast));
let botY = $derived(lerp(6, 7, tFast));
let topX = $derived(lerp(0, 3.25, t));
let midX = $derived(lerp(0, -2.75, t));
let botX = $derived(lerp(0, 1.75, t));
// micro-inclinação só pra dar “vida”, sem cruzar em X
let topR = $derived(lerp(0, 2.5, tFast));
let botR = $derived(lerp(0, -2.5, tFast));
let topScaleX = $derived(lerp(1, 0.62, tFast));
let midScaleX = $derived(lerp(1, 0.92, tFast));
let botScaleX = $derived(lerp(1, 0.72, tFast));
let topOpacity = $derived(1);
let midOpacity = $derived(1);
let botOpacity = $derived(1);
</script>
<span class="menu-toggle-icon {className}" aria-hidden="true" style="--stroke: {stroke}px">
<span
class="line"
style="--x: {topX}px; --y: {topY}px; --r: {topR}deg; --o: {topOpacity}; --sx: {topScaleX}"
></span>
<span
class="line"
style="--x: {midX}px; --y: 0px; --r: 0deg; --o: {midOpacity}; --sx: {midScaleX}"
></span>
<span
class="line"
style="--x: {botX}px; --y: {botY}px; --r: {botR}deg; --o: {botOpacity}; --sx: {botScaleX}"
></span>
</span>
<style>
.menu-toggle-icon {
position: relative;
display: inline-block;
width: 1.25rem;
height: 1.25rem;
color: currentColor;
}
.line {
position: absolute;
left: 0;
right: 0;
top: 50%;
margin-top: calc(var(--stroke) / -2);
height: var(--stroke);
border-radius: 9999px;
background: currentColor;
opacity: var(--o, 1);
transform-origin: center;
transform: translateX(var(--x, 0px)) translateY(var(--y, 0px)) rotate(var(--r, 0deg))
scaleX(var(--sx, 1));
will-change: transform, opacity;
}
</style>

View File

@@ -0,0 +1,14 @@
<script lang="ts">
interface Props {
class?: string;
}
let { class: className = '' }: Props = $props();
</script>
<div
class={[
'via-base-content/20 absolute inset-0 -translate-x-full bg-linear-to-r from-transparent to-transparent transition-transform duration-1000 group-hover:translate-x-full',
className
]}
></div>

View File

@@ -1,54 +1,21 @@
<script lang="ts"> <script lang="ts">
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useQuery } from 'convex-svelte';
import type { FunctionReference } from 'convex/server'; import type { FunctionReference } from 'convex/server';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { import {
Home, ChevronDown,
User,
UserPlus,
XCircle,
Users,
DollarSign,
ClipboardCheck, ClipboardCheck,
FileText, FileText,
ShoppingCart,
Scale,
Megaphone,
Trophy,
Briefcase,
UserCog,
Monitor,
ChevronDown,
GitMerge, GitMerge,
Home,
Settings, Settings,
Check, Tag,
LogIn, Users,
Menu, Briefcase,
Plus, UserPlus
Tag
} from 'lucide-svelte'; } from 'lucide-svelte';
import type { Snippet } from 'svelte';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { page } from '$app/state'; import { page } from '$app/state';
import logo from '$lib/assets/logo_governo_PE.png';
import { authClient } from '$lib/auth';
import ChatWidget from '$lib/components/chat/ChatWidget.svelte';
import NotificationBell from '$lib/components/chat/NotificationBell.svelte';
import PresenceManager from '$lib/components/chat/PresenceManager.svelte';
import { loginModalStore } from '$lib/stores/loginModal.svelte';
import { obterIPPublico } from '$lib/utils/deviceInfo';
interface GPSLocation {
latitude?: number;
longitude?: number;
precisao?: number;
endereco?: string;
cidade?: string;
estado?: string;
pais?: string;
}
interface MenuItemPermission { interface MenuItemPermission {
recurso: string; recurso: string;
@@ -211,26 +178,22 @@
type IconType = typeof Home; type IconType = typeof Home;
const { children }: { children: Snippet } = $props(); type SidebarProps = {
onNavigate?: () => void;
};
const { onNavigate }: SidebarProps = $props();
let currentPath = $derived(page.url.pathname); let currentPath = $derived(page.url.pathname);
let matricula = $state('');
let senha = $state('');
let erroLogin = $state('');
let carregandoLogin = $state(false);
let showAboutModal = $state(false);
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>, {});
const permissionsQuery = useQuery(api.menu.getUserPermissions as FunctionReference<'query'>, {}); const permissionsQuery = useQuery(api.menu.getUserPermissions as FunctionReference<'query'>, {});
// Filtrar menu baseado nas permissões do usuário // Filtrar menu baseado nas permissões do usuário
function filterSubmenusByPermissions( function filterSubmenusByPermissions(
items: SubMenuItem[], items: readonly SubMenuItem[],
isMaster: boolean, isMaster: boolean,
permissionsSet: Set<string> permissionsSet: Set<string>
): SubMenuItem[] { ): SubMenuItem[] {
if (isMaster) return items; if (isMaster) return [...items];
return items.filter((item) => { return items.filter((item) => {
if (!item.permission) return true; if (!item.permission) return true;
@@ -240,11 +203,11 @@
} }
function filterMenuByPermissions( function filterMenuByPermissions(
items: MenuItem[], items: readonly MenuItem[],
isMaster: boolean, isMaster: boolean,
permissionsSet: Set<string> permissionsSet: Set<string>
): MenuItem[] { ): MenuItem[] {
if (isMaster) return items; if (isMaster) return [...items];
const filtered: MenuItem[] = []; const filtered: MenuItem[] = [];
@@ -283,31 +246,16 @@
return filterMenuByPermissions(MENU_STRUCTURE, data.isMaster, permissionsSet); return filterMenuByPermissions(MENU_STRUCTURE, data.isMaster, permissionsSet);
}); });
const convexClient = useConvexClient();
const iconMap: Record<string, IconType> = { const iconMap: Record<string, IconType> = {
Home, Home,
User,
UserPlus, UserPlus,
XCircle,
Users, Users,
DollarSign,
ClipboardCheck, ClipboardCheck,
FileText, FileText,
ShoppingCart,
Scale,
Megaphone,
Trophy,
Briefcase, Briefcase,
UserCog,
Monitor,
ChevronDown, ChevronDown,
GitMerge, GitMerge,
Settings, Settings,
Check,
LogIn,
Menu,
Plus,
Tag Tag
}; };
@@ -350,359 +298,11 @@
function getSolicitarClasses(active: boolean) { function getSolicitarClasses(active: boolean) {
return getMenuClasses(active); return getMenuClasses(active);
} }
// Função para obter a URL do avatar/foto do usuário
let avatarUrlDoUsuario = $derived.by(() => {
if (!currentUser.data) return null;
// Prioridade: fotoPerfilUrl > avatar > fallback com nome
if (currentUser.data.fotoPerfilUrl) {
return currentUser.data.fotoPerfilUrl;
}
if (currentUser.data.avatar) {
return currentUser.data.avatar;
}
// Fallback: retornar null para usar o ícone User do Lucide
return null;
});
function openLoginModal() {
loginModalStore.open();
matricula = '';
senha = '';
erroLogin = '';
carregandoLogin = false;
}
function closeLoginModal() {
loginModalStore.close();
matricula = '';
senha = '';
erroLogin = '';
carregandoLogin = false;
}
function openAboutModal() {
showAboutModal = true;
}
function closeAboutModal() {
showAboutModal = false;
}
async function handleLogin(e: Event) {
e.preventDefault();
erroLogin = '';
carregandoLogin = true;
// Obter IP público e userAgent (rápido, não bloqueia)
const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : undefined;
// Obter IP público com timeout curto (não bloquear login)
const ipPublicoPromise = obterIPPublico().catch(() => undefined);
const ipPublicoTimeout = new Promise<undefined>(
(resolve) => setTimeout(() => resolve(undefined), 2000) // Timeout de 2 segundos
);
const ipPublico = await Promise.race([ipPublicoPromise, ipPublicoTimeout]);
// Função para coletar GPS em background (não bloqueia login)
async function coletarGPS(): Promise<GPSLocation> {
try {
const { obterLocalizacaoRapida } = await import('$lib/utils/deviceInfo');
// Usar versão rápida com timeout curto (3 segundos máximo)
const gpsPromise = obterLocalizacaoRapida();
const gpsTimeout = new Promise<GPSLocation>((resolve) =>
setTimeout(() => resolve({}), 3000)
);
return await Promise.race([gpsPromise, gpsTimeout]);
} catch (err) {
console.warn('Erro ao obter GPS (não bloqueia login):', err);
return {};
}
}
// Iniciar coleta de GPS em background (não esperar)
const gpsPromise = coletarGPS();
const result = await authClient.signIn.email(
{ email: matricula.trim(), password: senha },
{
onError: async (ctx) => {
// Registrar tentativa de login falha
try {
// Tentar obter GPS se já estiver disponível (não esperar)
let localizacaoGPS: GPSLocation = {};
try {
localizacaoGPS = await Promise.race([
gpsPromise,
new Promise<GPSLocation>((resolve) => setTimeout(() => resolve({}), 100))
]);
} catch {
// Ignorar se GPS não estiver pronto
}
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
matriculaOuEmail: matricula.trim(),
sucesso: false,
motivoFalha: ctx.error?.message || 'Erro desconhecido',
userAgent: userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
} catch (err) {
console.error('Erro ao registrar tentativa de login falha:', err);
}
alert(ctx.error.message);
}
}
);
if (result.data) {
// Registrar tentativa de login bem-sucedida
// Fazer de forma assíncrona para não bloquear o login
// Não tentamos buscar getCurrentUser aqui porque pode causar timeout
// O useQuery no componente já busca o usuário automaticamente quando a sessão estiver pronta
(async () => {
try {
// Aguardar um pouco para o usuário ser sincronizado no Convex
await new Promise((resolve) => setTimeout(resolve, 500));
// Tentar obter GPS se já estiver disponível (não esperar)
let localizacaoGPS: GPSLocation = {};
try {
localizacaoGPS = await Promise.race([
gpsPromise,
new Promise<GPSLocation>((resolve) => setTimeout(() => resolve({}), 100))
]);
} catch {
// Ignorar se GPS não estiver pronto
}
// Buscar o usuário no Convex usando getCurrentUser
// (o typesafe FunctionReference pode falhar aqui; tipamos o retorno e mantemos a chamada)
const usuario = (await convexClient.query(
api.auth.getCurrentUser as unknown as FunctionReference<'query'>,
{}
)) as { _id?: Id<'usuarios'> } | null;
if (usuario && usuario._id) {
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
usuarioId: usuario._id,
matriculaOuEmail: matricula.trim(),
sucesso: true,
userAgent: userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
} else {
// Se não encontrou o usuário, registrar sem usuarioId (será atualizado depois)
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
matriculaOuEmail: matricula.trim(),
sucesso: true,
userAgent: userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
}
} catch (err) {
console.error('Erro ao registrar tentativa de login:', err);
// Não bloquear o login se houver erro ao registrar
}
})();
closeLoginModal();
goto(resolve('/'));
} else {
erroLogin = 'Erro ao fazer login';
}
carregandoLogin = false;
}
async function handleLogout() {
const result = await authClient.signOut();
if (result.error) {
console.error('Sign out error:', result.error);
}
// Resetar tema para padrão ao fazer logout
const { aplicarTemaPadrao } = await import('$lib/utils/temas');
aplicarTemaPadrao();
goto(resolve('/'));
}
</script> </script>
<!-- Header Fixo acima de tudo --> <nav
<!-- Header Fixo Minimalista & Premium --> class="menu text-base-content bg-base-200 border-base-100 h-[calc(100vh-64px)] w-full flex-col gap-2 overflow-y-auto p-4"
<div
class="navbar border-primary/10 from-primary/10 via-primary/5 to-primary/10 fixed top-0 right-0 left-0 z-50 h-16 border-b bg-linear-to-r px-4 shadow-sm"
> >
<div class="flex-none lg:hidden">
<label for="my-drawer-3" class="btn btn-square btn-ghost btn-sm" aria-label="Abrir menu">
<Menu class="h-5 w-5" />
</label>
</div>
<div class="flex flex-1 items-center gap-4 lg:gap-6">
<!-- Logo Visível e Ajustado -->
<div class="flex items-center gap-3">
<div class="relative h-10 overflow-hidden rounded-lg shadow-sm lg:h-14">
<img src={logo} alt="Logo do Governo de PE" class="h-full w-full object-contain p-1" />
</div>
<div class="flex flex-col justify-center">
<h1 class="text-base-content text-xl leading-none font-bold tracking-tight lg:text-2xl">
SGSE
</h1>
<span class="text-base-content/60 font-medium tracking-wider uppercase"
>Secretaria de Esportes</span
>
</div>
</div>
</div>
<div class="ml-auto flex flex-none items-center gap-3 lg:gap-5">
{#if currentUser.data}
<!-- Nome e Perfil -->
<div class="hidden flex-col items-end lg:flex">
<span class="text-base-content text-sm leading-tight font-semibold"
>{currentUser.data.nome}</span
>
<span class="text-base-content/60 text-xs leading-tight">{currentUser.data.role?.nome}</span
>
</div>
<div class="dropdown dropdown-end">
<!-- Botão de Perfil com Avatar -->
<button
type="button"
tabindex="0"
class="btn avatar ring-base-200 hover:ring-primary/50 h-12 w-12 p-0 ring-2 ring-offset-2 transition-all"
aria-label="Menu do usuário"
>
<div class="h-full w-full overflow-hidden rounded-full">
{#if avatarUrlDoUsuario}
<img
src={avatarUrlDoUsuario}
alt={currentUser.data?.nome || 'Usuário'}
class="h-full w-full object-cover"
/>
{:else}
<div
class="bg-primary/10 text-primary flex h-full w-full items-center justify-center"
>
<User class="h-6 w-6" />
</div>
{/if}
</div>
</button>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box ring-base-content/5 z-1 mt-3 w-56 p-2 shadow-xl ring-1"
>
<li class="menu-title border-base-200 mb-2 border-b px-4 py-2">
<span class="text-base-content font-bold">{currentUser.data?.nome}</span>
<span class="text-base-content/60 text-xs font-normal">{currentUser.data.email}</span>
</li>
<li>
<a href={resolve('/perfil')} class="active:bg-primary/10 active:text-primary"
><UserCog class="mr-2 h-4 w-4" /> Meu Perfil</a
>
</li>
<li>
<a href={resolve('/alterar-senha')} class="active:bg-primary/10 active:text-primary"
><Settings class="mr-2 h-4 w-4" /> Alterar Senha</a
>
</li>
<div class="divider my-1"></div>
<li>
<button type="button" onclick={handleLogout} class="text-error hover:bg-error/10"
><LogIn class="mr-2 h-4 w-4 rotate-180" /> Sair</button
>
</li>
</ul>
</div>
<!-- Sino de notificações -->
<div class="relative">
<NotificationBell />
</div>
{:else}
<button
type="button"
class="btn btn-primary btn-sm rounded-full px-6"
onclick={() => openLoginModal()}
>
Entrar
</button>
{/if}
</div>
</div>
<div class="drawer lg:drawer-open" style="margin-top: 64px;">
<input id="my-drawer-3" type="checkbox" class="drawer-toggle" />
<div
class="drawer-content flex flex-col transition-all duration-300 lg:ml-72"
style="min-height: calc(100vh - 64px);"
>
<!-- Page content -->
<div class="flex-1 overflow-y-auto p-6">
{@render children?.()}
</div>
<!-- Footer Minimalista -->
<footer
class="footer footer-center text-base-content/60 border-base-200 bg-base-100/50 border-t p-4 text-xs backdrop-blur-sm"
>
<div class="flex flex-wrap justify-center gap-6">
<button
type="button"
class="hover:text-primary font-medium transition-colors"
onclick={() => openAboutModal()}>Sobre</button
>
<a href={resolve('/')} class="hover:text-primary font-medium transition-colors">Contato</a>
<a href={resolve('/abrir-chamado')} class="hover:text-primary font-medium transition-colors"
>Suporte</a
>
<span class="text-base-content/30"></span>
<a
href={resolve('/abrir-chamado')}
class="link link-hover hover:text-primary transition-colors">Suporte</a
>
<span class="text-base-content/30"></span>
<a
href={resolve('/privacidade')}
class="link link-hover hover:text-primary transition-colors">Privacidade</a
>
</div>
<div class="mt-2 opacity-70">
<p>© {new Date().getFullYear()} Governo de Pernambuco - Secretaria de Esportes</p>
</div>
</footer>
</div>
<div class="drawer-side fixed z-40" style="margin-top: 64px;">
<label for="my-drawer-3" aria-label="close sidebar" class="drawer-overlay"></label>
<div
class="menu text-base-content border-base-300 from-primary/10 via-primary/5 to-primary/10 h-[calc(100vh-64px)] w-72 flex-col gap-2 overflow-y-auto border-r-2 bg-linear-to-b p-4 backdrop-blur-sm"
>
<!-- Sidebar menu items -->
<!-- Sidebar menu items -->
{#snippet menuItem(item: MenuItem)} {#snippet menuItem(item: MenuItem)}
{@const Icon = getIconComponent(item.icon)} {@const Icon = getIconComponent(item.icon)}
{@const isActive = isRouteActive(item.link, { {@const isActive = isRouteActive(item.link, {
@@ -737,7 +337,11 @@
exact: sub.exact exact: sub.exact
})} })}
<li> <li>
<a href={resolve(sub.link)} class={getMenuClasses(isSubActive, true)}> <a
href={resolve(sub.link as any)}
class={getMenuClasses(isSubActive, true)}
onclick={() => onNavigate?.()}
>
<span>{sub.label}</span> <span>{sub.label}</span>
</a> </a>
</li> </li>
@@ -747,9 +351,10 @@
</details> </details>
{:else} {:else}
<a <a
href={resolve(item.link)} href={resolve(item.link as any)}
aria-current={isActive ? 'page' : undefined} aria-current={isActive ? 'page' : undefined}
class={getMenuClasses(isActive)} class={getMenuClasses(isActive)}
onclick={() => onNavigate?.()}
> >
<Icon class="h-5 w-5" strokeWidth={2} /> <Icon class="h-5 w-5" strokeWidth={2} />
<span>{item.label}</span> <span>{item.label}</span>
@@ -779,289 +384,16 @@
<a <a
href={resolve('/abrir-chamado')} href={resolve('/abrir-chamado')}
class={getSolicitarClasses(currentPath === '/abrir-chamado')} class={getSolicitarClasses(currentPath === '/abrir-chamado')}
onclick={() => onNavigate?.()}
> >
<UserPlus class="h-5 w-5" strokeWidth={2} /> <UserPlus class="h-5 w-5" strokeWidth={2} />
<span>Abrir Chamado</span> <span>Abrir Chamado</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </nav>
</div>
</div>
<!-- Modal de Login -->
{#if loginModalStore.showModal}
<dialog class="modal modal-open">
<div
class="modal-box from-base-100 via-base-100 to-primary/5 relative max-w-md overflow-hidden bg-linear-to-br shadow-2xl backdrop-blur-sm"
>
<!-- Botão de fechar moderno -->
<button
type="button"
class="btn btn-sm btn-circle btn-ghost hover:bg-error/20 hover:text-error absolute top-4 right-4 z-10 transition-all duration-200"
onclick={closeLoginModal}
aria-label="Fechar modal"
>
<XCircle class="h-5 w-5" strokeWidth={2.5} />
</button>
<!-- Decoração de fundo -->
<div class="bg-primary/10 absolute -top-20 -right-20 h-40 w-40 rounded-full blur-3xl"></div>
<div class="bg-primary/5 absolute -bottom-20 -left-20 h-40 w-40 rounded-full blur-3xl"></div>
<div class="relative z-10 p-8">
<!-- Header com logo e título -->
<div class="mb-8 text-center">
<div class="avatar mx-auto mb-5">
<div
class="group ring-primary/20 relative w-24 overflow-hidden rounded-2xl bg-white p-4 shadow-xl ring-2 transition-all duration-300 hover:scale-105 hover:shadow-2xl"
>
<div
class="from-primary/10 absolute inset-0 bg-linear-to-br to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100"
></div>
<img src={logo} alt="Logo SGSE" class="relative z-10 h-full w-full object-contain" />
</div>
</div>
<h3 class="text-primary mb-2 text-4xl font-bold tracking-tight">Login</h3>
<p class="text-base-content/70 text-sm font-medium">
Acesse o sistema com suas credenciais
</p>
</div>
<!-- Mensagem de erro -->
{#if erroLogin}
<div
class="alert alert-error border-error/30 bg-error/10 mb-6 shadow-lg backdrop-blur-sm"
>
<XCircle class="h-5 w-5 shrink-0 stroke-current" strokeWidth={2.5} />
<span class="font-medium">{erroLogin}</span>
</div>
{/if}
<!-- Formulário -->
<form class="space-y-5" onsubmit={handleLogin}>
<!-- Campo Matrícula/E-mail -->
<div class="form-control">
<label class="label pb-2" for="login-matricula">
<span class="text-primary label-text text-sm font-semibold">Matrícula ou E-mail</span>
</label>
<div class="relative">
<input
id="login-matricula"
type="text"
placeholder="Digite sua matrícula ou e-mail"
class="input input-bordered input-primary focus:border-primary focus:shadow-primary/20 w-full border-2 transition-all duration-200 focus:shadow-lg disabled:opacity-50"
bind:value={matricula}
required
disabled={carregandoLogin}
autocomplete="username"
/>
</div>
</div>
<!-- Campo Senha -->
<div class="form-control">
<label class="label pb-2" for="login-password">
<span class="text-primary label-text text-sm font-semibold">Senha</span>
</label>
<div class="relative">
<input
id="login-password"
type="password"
placeholder="Digite sua senha"
class="input input-bordered input-primary focus:border-primary focus:shadow-primary/20 w-full border-2 transition-all duration-200 focus:shadow-lg disabled:opacity-50"
bind:value={senha}
required
disabled={carregandoLogin}
autocomplete="current-password"
/>
</div>
</div>
<!-- Botão de submit -->
<div class="form-control pt-2">
<button
type="submit"
class="btn btn-primary btn-lg group from-primary via-primary to-primary/90 relative w-full overflow-hidden border-0 bg-linear-to-r shadow-xl transition-all duration-300 hover:scale-[1.02] hover:shadow-2xl disabled:opacity-50"
disabled={carregandoLogin}
>
<!-- Efeito de brilho animado -->
<div
class="absolute inset-0 -translate-x-full bg-linear-to-r from-transparent via-white/30 to-transparent transition-transform duration-1000 group-hover:translate-x-full"
></div>
{#if carregandoLogin}
<span class="loading loading-spinner loading-sm"></span>
<span class="font-semibold">Entrando...</span>
{:else}
<LogIn
class="h-5 w-5 transition-transform duration-300 group-hover:scale-110"
strokeWidth={2.5}
/>
<span class="font-semibold">Entrar</span>
{/if}
</button>
</div>
<!-- Links auxiliares -->
<div class="space-y-3 pt-4 text-center">
<a
href={resolve('/abrir-chamado')}
class="link link-primary block text-sm font-medium transition-all duration-200 hover:scale-105"
onclick={closeLoginModal}
>
Abrir Chamado
</a>
<a
href={resolve('/esqueci-senha')}
class="link link-secondary block text-sm font-medium transition-all duration-200 hover:scale-105"
onclick={closeLoginModal}
>
Esqueceu sua senha?
</a>
</div>
</form>
</div>
</div>
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<form method="dialog" class="modal-backdrop" onclick={closeLoginModal}>
<button type="button">close</button>
</form>
</dialog>
{/if}
<!-- Modal Sobre -->
{#if showAboutModal}
<dialog class="modal modal-open">
<div
class="modal-box from-base-100 to-base-200 relative max-w-md overflow-hidden bg-linear-to-br shadow-xl"
>
<button
type="button"
class="btn btn-sm btn-circle btn-ghost hover:bg-base-300 absolute top-2 right-2 z-10"
onclick={closeAboutModal}
>
</button>
<div class="space-y-5 px-6 py-6 text-center">
<!-- Logo e Título -->
<div class="flex flex-col items-center gap-3">
<div class="avatar">
<div class="ring-primary/20 w-20 rounded-xl bg-white p-3 shadow-lg ring-2">
<img src={logo} alt="Logo SGSE" class="h-full w-full object-contain" />
</div>
</div>
<div class="space-y-1">
<h3 class="text-primary text-2xl font-bold tracking-tight">SGSE</h3>
<p class="text-base-content/70 text-sm font-medium">
Sistema de Gerenciamento de Secretaria
</p>
</div>
</div>
<!-- Divider -->
<div class="divider my-1"></div>
<!-- Informações de Versão -->
<div
class="from-primary/10 to-primary/5 border-primary/10 space-y-2 rounded-xl border bg-linear-to-br p-4 shadow-sm"
>
<div class="flex items-center justify-center gap-2">
<Tag class="text-primary h-4 w-4" strokeWidth={2} />
<p class="text-base-content/60 text-xs font-medium tracking-wide uppercase">Versão</p>
</div>
<p class="text-primary text-2xl font-bold tracking-tight">1.0 11_2025</p>
<div class="badge badge-warning badge-sm gap-1.5 px-3 py-1.5 text-xs">
<Plus class="h-3.5 w-3.5" strokeWidth={2} />
Em Desenvolvimento
</div>
</div>
<!-- Desenvolvido por -->
<div class="space-y-1.5">
<p class="text-base-content/50 text-xs font-medium tracking-wide uppercase">
Desenvolvido por
</p>
<p class="text-primary text-sm font-semibold">Secretaria de Esportes de Pernambuco</p>
</div>
<!-- Divider -->
<div class="divider my-1"></div>
<!-- Informações Adicionais -->
<div class="grid grid-cols-2 gap-3">
<div
class="bg-base-200/60 border-base-300/50 rounded-lg border p-3 shadow-sm transition-all hover:shadow-md"
>
<p class="text-primary mb-1 text-xs font-semibold tracking-wide uppercase">Governo</p>
<p class="text-base-content/60 text-xs font-medium">Estado de Pernambuco</p>
</div>
<div
class="bg-base-200/60 border-base-300/50 rounded-lg border p-3 shadow-sm transition-all hover:shadow-md"
>
<p class="text-primary mb-1 text-xs font-semibold tracking-wide uppercase">Ano</p>
<p class="text-base-content/60 text-xs font-medium">2025</p>
</div>
</div>
<!-- Botão OK -->
<div class="pt-3">
<button
type="button"
class="btn btn-primary btn-sm mx-auto w-full max-w-xs shadow-md transition-all duration-200 hover:shadow-lg"
onclick={closeAboutModal}
>
<Check class="h-4 w-4" strokeWidth={2} />
OK
</button>
</div>
</div>
</div>
<div
class="modal-backdrop"
onclick={closeAboutModal}
role="button"
tabindex="0"
onkeydown={(e) => e.key === 'Escape' && closeAboutModal()}
></div>
</dialog>
{/if}
<!-- Componentes de Chat (apenas se autenticado) -->
{#if currentUser.data}
<PresenceManager />
<ChatWidget />
{/if}
<style> <style>
/* Animação de pulso sutil para o anel do botão de perfil */
@keyframes pulse-ring-subtle {
0%,
100% {
opacity: 0.1;
transform: scale(1);
}
50% {
opacity: 0.3;
transform: scale(1.05);
}
}
/* Animação de pulso para o badge de status online */
@keyframes pulse-dot {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.1);
}
}
/* Remove default details marker */ /* Remove default details marker */
details > summary { details > summary {
list-style: none; list-style: none;

View File

@@ -0,0 +1,124 @@
<script lang="ts">
import { api } from '@sgse-app/backend/convex/_generated/api';
import { useQuery } from 'convex-svelte';
import type { FunctionReference } from 'convex/server';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { LogIn, Settings, User, UserCog } from 'lucide-svelte';
import { authClient } from '$lib/auth';
import NotificationBell from '$lib/components/chat/NotificationBell.svelte';
let currentPath = $derived(page.url.pathname);
const currentUser = useQuery(api.auth.getCurrentUser as FunctionReference<'query'>, {});
// Função para obter a URL do avatar/foto do usuário
let avatarUrlDoUsuario = $derived.by(() => {
if (!currentUser.data) return null;
// Prioridade: fotoPerfilUrl > avatar > fallback com nome
if (currentUser.data.fotoPerfilUrl) {
return currentUser.data.fotoPerfilUrl;
}
if (currentUser.data.avatar) {
return currentUser.data.avatar;
}
// Fallback: retornar null para usar o ícone User do Lucide
return null;
});
function goToLogin(redirectTo?: string) {
const target = redirectTo || currentPath || '/';
goto(`${resolve('/login')}?redirect=${encodeURIComponent(target)}`);
}
async function handleLogout() {
const result = await authClient.signOut();
if (result.error) {
console.error('Sign out error:', result.error);
}
// Resetar tema para padrão ao fazer logout
const { aplicarTemaPadrao } = await import('$lib/utils/temas');
aplicarTemaPadrao();
goto(resolve('/home'));
}
</script>
<div class="flex items-center gap-3">
{#if currentUser.data}
<!-- Nome e Perfil -->
<div class="hidden flex-col items-end lg:flex">
<span class="text-base-content text-sm leading-tight font-semibold"
>{currentUser.data.nome}</span
>
<span class="text-base-content/60 text-xs leading-tight">{currentUser.data.role?.nome}</span>
</div>
<div class="dropdown dropdown-end">
<!-- Botão de Perfil com Avatar -->
<button
type="button"
tabindex="0"
class="btn avatar ring-base-200 hover:ring-primary/50 h-10 w-10 p-0 ring-2 ring-offset-2 transition-all"
aria-label="Menu do usuário"
>
<div class="h-full w-full overflow-hidden rounded-full">
{#if avatarUrlDoUsuario}
<img
src={avatarUrlDoUsuario}
alt={currentUser.data?.nome || 'Usuário'}
class="h-full w-full object-cover"
/>
{:else}
<div class="bg-primary/10 text-primary flex h-full w-full items-center justify-center">
<User class="h-5 w-5" />
</div>
{/if}
</div>
</button>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<ul
tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box ring-base-content/5 z-1 mt-3 w-56 p-2 shadow-xl ring-1"
>
<li class="menu-title border-base-200 mb-2 border-b px-4 py-2">
<span class="text-base-content font-bold">{currentUser.data?.nome}</span>
<span class="text-base-content/60 text-xs font-normal">{currentUser.data.email}</span>
</li>
<li>
<a href={resolve('/perfil')} class="active:bg-primary/10 active:text-primary"
><UserCog class="mr-2 h-4 w-4" /> Meu Perfil</a
>
</li>
<li>
<a href={resolve('/alterar-senha')} class="active:bg-primary/10 active:text-primary"
><Settings class="mr-2 h-4 w-4" /> Alterar Senha</a
>
</li>
<div class="divider my-1"></div>
<li>
<button type="button" onclick={handleLogout} class="text-error hover:bg-error/10"
><LogIn class="mr-2 h-4 w-4 rotate-180" /> Sair</button
>
</li>
</ul>
</div>
<!-- Sino de notificações -->
<div class="relative">
<NotificationBell />
</div>
{:else}
<button
type="button"
class="btn btn-primary btn-sm rounded-full px-6"
onclick={() => goToLogin()}
>
Entrar
</button>
{/if}
</div>

View File

@@ -0,0 +1,62 @@
<script lang="ts">
import { Field } from '@ark-ui/svelte/field';
import type { Snippet } from 'svelte';
import type { HTMLInputAttributes } from 'svelte/elements';
interface Props {
id: string;
label: string;
type?: string;
placeholder?: string;
autocomplete?: HTMLInputAttributes['autocomplete'];
disabled?: boolean;
required?: boolean;
error?: string | null;
right?: Snippet;
value?: string;
}
let {
id,
label,
type = 'text',
placeholder = '',
autocomplete,
disabled = false,
required = false,
error = null,
right,
value = $bindable('')
}: Props = $props();
const invalid = $derived(!!error);
</script>
<Field.Root {invalid} {required} class="space-y-2">
<div class="flex items-center justify-between gap-3">
<Field.Label
for={id}
class="text-base-content/60 text-xs font-semibold tracking-wider uppercase"
>
{label}
</Field.Label>
{@render right?.()}
</div>
<div class="group relative">
<Field.Input
{id}
{type}
{placeholder}
{disabled}
{autocomplete}
{required}
bind:value
class="border-base-content/10 bg-base-200/25 text-base-content placeholder-base-content/40 focus:border-primary/50 focus:bg-base-200/35 focus:ring-primary/20 w-full rounded-xl border px-4 py-3 transition-all duration-300 focus:ring-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60"
/>
</div>
{#if error}
<Field.ErrorText class="text-error text-sm font-medium">{error}</Field.ErrorText>
{/if}
</Field.Root>

View File

@@ -153,20 +153,15 @@ export function aplicarTema(temaId: TemaId | string | null | undefined): void {
const nomeDaisyUI = obterNomeDaisyUI(temaId || 'purple'); const nomeDaisyUI = obterNomeDaisyUI(temaId || 'purple');
const htmlElement = document.documentElement; const htmlElement = document.documentElement;
const bodyElement = document.body;
if (htmlElement) { if (htmlElement) {
// Remover todos os atributos data-theme existentes primeiro // Remover todos os atributos data-theme existentes primeiro
htmlElement.removeAttribute('data-theme'); htmlElement.removeAttribute('data-theme');
if (bodyElement) { // Evita que `body[data-theme]` sobrescreva o tema do `<html>`
bodyElement.removeAttribute('data-theme'); if (document.body) document.body.removeAttribute('data-theme');
}
// Aplicar o novo tema // Aplicar o novo tema
htmlElement.setAttribute('data-theme', nomeDaisyUI); htmlElement.setAttribute('data-theme', nomeDaisyUI);
if (bodyElement) {
bodyElement.setAttribute('data-theme', nomeDaisyUI);
}
// Forçar reflow para garantir que o CSS seja aplicado // Forçar reflow para garantir que o CSS seja aplicado
void htmlElement.offsetHeight; void htmlElement.offsetHeight;
@@ -230,20 +225,21 @@ export function obterCoresDoTema(): {
} }
const htmlElement = document.documentElement; const htmlElement = document.documentElement;
const getComputedStyle = (varName: string): string => { const readCssVar = (varName: string): string => {
return getComputedStyle(htmlElement).getPropertyValue(varName).trim() || ''; return window.getComputedStyle(htmlElement).getPropertyValue(varName).trim() || '';
}; };
// Tentar obter variáveis CSS do DaisyUI // DaisyUI v5: variáveis `--color-*`
const primary = getComputedStyle('--p') || '#667eea'; // Fallback para v4/legado: variáveis curtas `--p`, `--suc`, etc.
const success = getComputedStyle('--suc') || '#10b981'; const primary = readCssVar('--color-primary') || readCssVar('--p') || '#667eea';
const error = getComputedStyle('--er') || '#ef4444'; const success = readCssVar('--color-success') || readCssVar('--suc') || '#10b981';
const warning = getComputedStyle('--wa') || '#f59e0b'; const error = readCssVar('--color-error') || readCssVar('--er') || '#ef4444';
const info = getComputedStyle('--in') || '#3b82f6'; const warning = readCssVar('--color-warning') || readCssVar('--wa') || '#f59e0b';
const baseContent = getComputedStyle('--bc') || '#1f2937'; const info = readCssVar('--color-info') || readCssVar('--in') || '#3b82f6';
const base100 = getComputedStyle('--b1') || '#ffffff'; const baseContent = readCssVar('--color-base-content') || readCssVar('--bc') || '#1f2937';
const base200 = getComputedStyle('--b2') || '#f3f4f6'; const base100 = readCssVar('--color-base-100') || readCssVar('--b1') || '#ffffff';
const base300 = getComputedStyle('--b3') || '#e5e7eb'; const base200 = readCssVar('--color-base-200') || readCssVar('--b2') || '#f3f4f6';
const base300 = readCssVar('--color-base-300') || readCssVar('--b3') || '#e5e7eb';
return { return {
primary: primary || '#667eea', primary: primary || '#667eea',

View File

@@ -1,14 +1,21 @@
import { createConvexHttpClient } from '@mmailaender/convex-better-auth-svelte/sveltekit'; import { createConvexHttpClient } from '@mmailaender/convex-better-auth-svelte/sveltekit';
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import { error, redirect } from '@sveltejs/kit';
import type { FunctionReference } from 'convex/server';
export const load = async ({ locals }) => { export const load = async ({ locals, url }) => {
if (!locals.token) {
throw redirect(302, '/login?redirect=' + url.pathname);
}
try { try {
const client = createConvexHttpClient({ token: locals.token }); const client = createConvexHttpClient({ token: locals.token });
const currentUser = await client.query(api.auth.getCurrentUser, {}); const currentUser = await client.query(api.auth.getCurrentUser as FunctionReference<'query'>);
if (!currentUser) {
throw redirect(302, '/login?redirect=' + url.pathname);
}
return { currentUser }; return { currentUser };
} catch (error) { } catch {
console.error('Erro ao carregar usuário atual no layout do dashboard:', error); return error(500, 'Ops! Ocorreu um erro, tente novamente mais tarde.');
// Evita quebrar toda a área logada em caso de falha transitória na API/auth
return { currentUser: null };
} }
}; };

View File

@@ -1,103 +1,114 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/state'; import { onMount } from 'svelte';
import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import ActionGuard from '$lib/components/ActionGuard.svelte';
import { Toaster } from 'svelte-sonner'; import { Toaster } from 'svelte-sonner';
import PushNotificationManager from '$lib/components/PushNotificationManager.svelte'; import PushNotificationManager from '$lib/components/PushNotificationManager.svelte';
import Footer from '$lib/components/Footer.svelte';
import Header from '$lib/components/Header.svelte';
import MenuToggleIcon from '$lib/components/MenuToggleIcon.svelte';
import Sidebar from '$lib/components/Sidebar.svelte';
import DashboardHeaderActions from '$lib/components/dashboard/DashboardHeaderActions.svelte';
import ChatWidget from '$lib/components/chat/ChatWidget.svelte';
import PresenceManager from '$lib/components/chat/PresenceManager.svelte';
const { children } = $props(); const { children } = $props();
// Usuário atual e consentimento LGPD let sidebarOpen = $state(false);
const currentUser = useQuery(api.auth.getCurrentUser, {}); let isDesktop = $state(false);
const consentimentoLGPD = useQuery(api.lgpd.verificarConsentimento, { tipo: 'termo_uso' }); const toggleSidebar = () => (sidebarOpen = !sidebarOpen);
const closeSidebar = () => (sidebarOpen = false);
// Redirecionar para o termo de consentimento se obrigatório e não aceito // No desktop, abrir por padrão; no mobile, começar fechado
$effect(() => { onMount(() => {
const p = page.url.pathname; const mql = window.matchMedia('(min-width: 1024px)');
// Rotas públicas/que não exigem termo const update = () => {
if ( isDesktop = mql.matches;
p === '/' || };
p === '/abrir-chamado' ||
p === '/termo-consentimento' ||
p.startsWith('/privacidade') ||
p.startsWith('/api/')
) {
return;
}
// Precisa estar autenticado para exigir LGPD update();
if (!currentUser?.data) { sidebarOpen = mql.matches;
return;
}
// Query ainda carregando ou sem dados mql.addEventListener('change', update);
if (!consentimentoLGPD?.data) { return () => mql.removeEventListener('change', update);
return;
}
const data = consentimentoLGPD.data;
if (data.termoObrigatorio && !data.aceito) {
const redirect = encodeURIComponent(p);
window.location.href = `/termo-consentimento?redirect=${redirect}`;
}
});
// Resolver recurso/ação a partir da rota
const routeAction = $derived.by(() => {
const p = page.url.pathname;
if (p === '/' || p === '/abrir-chamado') return null;
// Funcionários
if (p.startsWith('/recursos-humanos/funcionarios')) {
if (p.includes('/cadastro')) return { recurso: 'funcionarios', acao: 'criar' };
if (p.includes('/excluir')) return { recurso: 'funcionarios', acao: 'excluir' };
if (p.includes('/editar') || p.includes('/funcionarioId'))
return { recurso: 'funcionarios', acao: 'editar' };
return { recurso: 'funcionarios', acao: 'listar' };
}
// Símbolos
if (p.startsWith('/recursos-humanos/simbolos')) {
if (p.includes('/cadastro')) return { recurso: 'simbolos', acao: 'criar' };
if (p.includes('/excluir')) return { recurso: 'simbolos', acao: 'excluir' };
if (p.includes('/editar') || p.includes('/simboloId'))
return { recurso: 'simbolos', acao: 'editar' };
return { recurso: 'simbolos', acao: 'listar' };
}
// Outras áreas (uso genérico: ver)
if (p.startsWith('/financeiro')) return { recurso: 'financeiro', acao: 'ver' };
if (p.startsWith('/controladoria')) return { recurso: 'controladoria', acao: 'ver' };
if (p.startsWith('/licitacoes')) return { recurso: 'licitacoes', acao: 'ver' };
if (p.startsWith('/compras')) return { recurso: 'compras', acao: 'ver' };
if (p.startsWith('/juridico')) return { recurso: 'juridico', acao: 'ver' };
if (p.startsWith('/comunicacao')) return { recurso: 'comunicacao', acao: 'ver' };
if (p.startsWith('/programas-esportivos'))
return { recurso: 'programas_esportivos', acao: 'ver' };
if (p.startsWith('/secretaria-executiva'))
return { recurso: 'secretaria_executiva', acao: 'ver' };
if (p.startsWith('/gestao-pessoas')) return { recurso: 'gestao_pessoas', acao: 'ver' };
return null;
}); });
</script> </script>
{#if routeAction} <div
<ActionGuard recurso={routeAction.recurso} acao={routeAction.acao}> class="bg-base-100 text-base-content selection:bg-primary selection:text-primary-content flex min-h-screen flex-col font-sans"
<main id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4"> >
{@render children()} <Header>
</main> {#snippet left()}
</ActionGuard> <button
{:else} type="button"
<main id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4"> class="btn btn-ghost btn-sm"
{@render children()} aria-label={sidebarOpen ? 'Fechar menu' : 'Abrir menu'}
</main> onclick={toggleSidebar}
>
<MenuToggleIcon open={sidebarOpen} class="h-5 w-5" />
</button>
{/snippet}
{#snippet right()}
<DashboardHeaderActions />
{/snippet}
</Header>
<div class="relative flex min-h-[calc(100vh-4rem)] flex-1">
<!-- Overlay (mobile) -->
{#if sidebarOpen}
<div
class="fixed inset-0 z-40 bg-black/30 backdrop-blur-[1px] lg:hidden"
role="button"
tabindex="0"
aria-label="Fechar menu"
onclick={closeSidebar}
onkeydown={(e) => e.key === 'Escape' && closeSidebar()}
></div>
{/if} {/if}
<!-- Sidebar -->
<aside
class="bg-base-100 border-base-200 fixed top-16 bottom-0 left-0 z-50 w-72 border-r shadow-sm transition-transform duration-200"
class:translate-x-0={sidebarOpen}
class:-translate-x-full={!sidebarOpen}
>
<div class="h-full overflow-y-auto">
<Sidebar onNavigate={() => !isDesktop && closeSidebar()} />
</div>
</aside>
<!-- Conteúdo -->
<div
class="flex min-w-0 flex-1 flex-col justify-between transition-[padding] duration-200 {sidebarOpen
? 'lg:pl-72'
: 'lg:pl-0'}"
>
<div id="container-central" class="w-full max-w-none px-3 py-4 lg:px-4">
{@render children()}
</div>
<Footer />
</div>
</div>
<!-- Componentes de Chat (gerenciam auth internamente) -->
<PresenceManager />
<ChatWidget />
</div>
<!-- Toast Notifications (Sonner) --> <!-- Toast Notifications (Sonner) -->
<Toaster position="top-right" richColors closeButton expand={true} /> <Toaster position="top-right" richColors closeButton expand={true} />
<!-- Push Notification Manager (registra subscription automaticamente) --> <!-- Push Notification Manager (registra subscription automaticamente) -->
<PushNotificationManager /> <PushNotificationManager />
<style>
/* Evita “corredor” quando páginas usam `container mx-auto` */
#container-central :global(.container) {
max-width: none !important;
}
#container-central :global(.container.mx-auto) {
margin-left: 0 !important;
margin-right: 0 !important;
}
</style>

View File

@@ -6,7 +6,6 @@
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { UserPlus, Mail, Clock, Award, TrendingUp, Zap, Users, Database } from 'lucide-svelte'; import { UserPlus, Mail, Clock, Award, TrendingUp, Zap, Users, Database } from 'lucide-svelte';
import ProtectedRoute from '$lib/components/ProtectedRoute.svelte'; import ProtectedRoute from '$lib/components/ProtectedRoute.svelte';
import { loginModalStore } from '$lib/stores/loginModal.svelte';
// Queries para dados do dashboard // Queries para dados do dashboard
const statsQuery = useQuery(api.dashboard.getStats, {}); const statsQuery = useQuery(api.dashboard.getStats, {});
@@ -36,7 +35,12 @@
// Se for erro de autenticação, abrir modal de login automaticamente // Se for erro de autenticação, abrir modal de login automaticamente
if (error === 'auth_required') { if (error === 'auth_required') {
loginModalStore.open(route || to.url.pathname); const redirectTo = route || to.url.pathname;
goto(`${resolve('/login')}?redirect=${encodeURIComponent(redirectTo)}`, {
replaceState: true,
noScroll: true
});
return;
} }
// Limpar URL usando SvelteKit (após router estar inicializado) // Limpar URL usando SvelteKit (após router estar inicializado)
@@ -65,7 +69,12 @@
const route = urlParams.get('route') || urlParams.get('redirect') || ''; const route = urlParams.get('route') || urlParams.get('redirect') || '';
if (error === 'auth_required') { if (error === 'auth_required') {
loginModalStore.open(route || window.location.pathname); const redirectTo = route || window.location.pathname;
goto(`${resolve('/login')}?redirect=${encodeURIComponent(redirectTo)}`, {
replaceState: true,
noScroll: true
});
return;
} }
} }
@@ -131,7 +140,7 @@
</script> </script>
<ProtectedRoute> <ProtectedRoute>
<main class="container mx-auto px-4 py-4"> <main class="w-full px-4 py-4">
<!-- Alerta de Acesso Negado / Autenticação --> <!-- Alerta de Acesso Negado / Autenticação -->
{#if showAlert} {#if showAlert}
{@const alertData = getAlertMessage()} {@const alertData = getAlertMessage()}

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -2,7 +2,6 @@
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient(); const client = useConvexClient();
@@ -232,7 +231,6 @@
</p> </p>
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<ActionGuard recurso="setores" acao="criar">
<button class="btn btn-primary shadow-lg" onclick={openCreateModal}> <button class="btn btn-primary shadow-lg" onclick={openCreateModal}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -250,7 +248,6 @@
</svg> </svg>
Novo Setor Novo Setor
</button> </button>
</ActionGuard>
</div> </div>
</div> </div>
</section> </section>
@@ -307,7 +304,6 @@
<td class="text-base-content/60 text-sm">{formatDate(setor.createdAt)}</td> <td class="text-base-content/60 text-sm">{formatDate(setor.createdAt)}</td>
<td class="text-right"> <td class="text-right">
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<ActionGuard recurso="setores" acao="editar">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
onclick={() => openEditModal(setor)} onclick={() => openEditModal(setor)}
@@ -329,8 +325,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
<ActionGuard recurso="setores" acao="excluir">
<button <button
class="btn btn-ghost btn-sm text-error" class="btn btn-ghost btn-sm text-error"
onclick={() => openDeleteModal(setor)} onclick={() => openDeleteModal(setor)}
@@ -352,7 +346,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
</div> </div>
</td> </td>
</tr> </tr>

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -3,7 +3,6 @@
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient(); const client = useConvexClient();
@@ -164,7 +163,6 @@
<option value="archived">Arquivado</option> <option value="archived">Arquivado</option>
</select> </select>
<ActionGuard recurso="fluxos_templates" acao="criar">
<button class="btn btn-secondary shadow-lg" onclick={openCreateModal}> <button class="btn btn-secondary shadow-lg" onclick={openCreateModal}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -183,7 +181,6 @@
</svg> </svg>
Novo Template Novo Template
</button> </button>
</ActionGuard>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -4,7 +4,6 @@
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { page } from '$app/stores'; import { page } from '$app/stores';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient(); const client = useConvexClient();
let instanceId = $derived($page.params.id as Id<'flowInstances'>); let instanceId = $derived($page.params.id as Id<'flowInstances'>);
@@ -416,7 +415,6 @@
</div> </div>
{#if instance.status === 'active'} {#if instance.status === 'active'}
<ActionGuard recurso="fluxos_instancias" acao="cancelar">
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}> <button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -435,7 +433,6 @@
</svg> </svg>
Cancelar Fluxo Cancelar Fluxo
</button> </button>
</ActionGuard>
{/if} {/if}
</div> </div>
</div> </div>
@@ -616,7 +613,6 @@
{#if instance.status === 'active'} {#if instance.status === 'active'}
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#if step.status === 'pending'} {#if step.status === 'pending'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-info btn-sm" class="btn btn-info btn-sm"
onclick={() => handleStartStep(step._id)} onclick={() => handleStartStep(step._id)}
@@ -624,9 +620,7 @@
> >
Iniciar Iniciar
</button> </button>
</ActionGuard>
{:else if step.status === 'in_progress'} {:else if step.status === 'in_progress'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-success btn-sm" class="btn btn-success btn-sm"
onclick={() => handleCompleteStep(step._id)} onclick={() => handleCompleteStep(step._id)}
@@ -641,9 +635,7 @@
> >
Bloquear Bloquear
</button> </button>
</ActionGuard>
{:else if step.status === 'blocked'} {:else if step.status === 'blocked'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-info btn-sm" class="btn btn-info btn-sm"
onclick={() => handleStartStep(step._id)} onclick={() => handleStartStep(step._id)}
@@ -651,10 +643,8 @@
> >
Desbloquear Desbloquear
</button> </button>
</ActionGuard>
{/if} {/if}
<ActionGuard recurso="fluxos_instancias" acao="atribuir_usuario">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
onclick={() => openReassignModal(step)} onclick={() => openReassignModal(step)}
@@ -676,7 +666,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
@@ -700,7 +689,6 @@
</svg> </svg>
</button> </button>
<ActionGuard recurso="fluxos_documentos" acao="upload">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
onclick={() => openUploadModal(step)} onclick={() => openUploadModal(step)}
@@ -722,7 +710,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
</div> </div>
{/if} {/if}
</div> </div>
@@ -759,7 +746,6 @@
/> />
</svg> </svg>
{doc.name} {doc.name}
<ActionGuard recurso="fluxos_documentos" acao="excluir">
<button <button
class="btn btn-ghost btn-xs text-error" class="btn btn-ghost btn-xs text-error"
onclick={() => handleDeleteDocument(doc._id)} onclick={() => handleDeleteDocument(doc._id)}
@@ -767,7 +753,6 @@
> >
× ×
</button> </button>
</ActionGuard>
</div> </div>
{/each} {/each}
</div> </div>

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -3,7 +3,6 @@
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient(); const client = useConvexClient();
@@ -148,7 +147,6 @@
<option value="cancelled">Cancelado</option> <option value="cancelled">Cancelado</option>
</select> </select>
<ActionGuard recurso="fluxos_instancias" acao="criar">
<button class="btn btn-info shadow-lg" onclick={openCreateModal}> <button class="btn btn-info shadow-lg" onclick={openCreateModal}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -167,7 +165,6 @@
</svg> </svg>
Nova Instância Nova Instância
</button> </button>
</ActionGuard>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -3,7 +3,6 @@
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient, useQuery } from 'convex-svelte'; import { useConvexClient, useQuery } from 'convex-svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient(); const client = useConvexClient();
@@ -148,7 +147,6 @@
<option value="cancelled">Cancelado</option> <option value="cancelled">Cancelado</option>
</select> </select>
<ActionGuard recurso="fluxos_instancias" acao="criar">
<button class="btn btn-info shadow-lg" onclick={openCreateModal}> <button class="btn btn-info shadow-lg" onclick={openCreateModal}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -167,7 +165,6 @@
</svg> </svg>
Novo Fluxo Novo Fluxo
</button> </button>
</ActionGuard>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -5,7 +5,6 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { page } from '$app/stores'; import { page } from '$app/stores';
import ActionGuard from '$lib/components/ActionGuard.svelte';
import RelogioPrazo from '$lib/components/RelogioPrazo.svelte'; import RelogioPrazo from '$lib/components/RelogioPrazo.svelte';
const client = useConvexClient(); const client = useConvexClient();
@@ -651,7 +650,6 @@
</div> </div>
{#if instance.status === 'active'} {#if instance.status === 'active'}
<ActionGuard recurso="fluxos_instancias" acao="cancelar">
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}> <button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -670,7 +668,6 @@
</svg> </svg>
Cancelar Fluxo Cancelar Fluxo
</button> </button>
</ActionGuard>
{/if} {/if}
</div> </div>
</div> </div>
@@ -867,7 +864,6 @@
{#if instance.status === 'active'} {#if instance.status === 'active'}
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
{#if step.status === 'pending'} {#if step.status === 'pending'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-info btn-sm" class="btn btn-info btn-sm"
onclick={() => handleStartStep(step._id)} onclick={() => handleStartStep(step._id)}
@@ -875,9 +871,7 @@
> >
Iniciar Iniciar
</button> </button>
</ActionGuard>
{:else if step.status === 'in_progress'} {:else if step.status === 'in_progress'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-success btn-sm" class="btn btn-success btn-sm"
onclick={() => handleCompleteStep(step._id)} onclick={() => handleCompleteStep(step._id)}
@@ -892,9 +886,7 @@
> >
Bloquear Bloquear
</button> </button>
</ActionGuard>
{:else if step.status === 'blocked'} {:else if step.status === 'blocked'}
<ActionGuard recurso="fluxos_instancias" acao="avancar_passo">
<button <button
class="btn btn-info btn-sm" class="btn btn-info btn-sm"
onclick={() => handleStartStep(step._id)} onclick={() => handleStartStep(step._id)}
@@ -902,10 +894,8 @@
> >
Desbloquear Desbloquear
</button> </button>
</ActionGuard>
{/if} {/if}
<ActionGuard recurso="fluxos_instancias" acao="atribuir_usuario">
<button <button
class="btn btn-ghost btn-sm" class="btn btn-ghost btn-sm"
onclick={() => openReassignModal(step)} onclick={() => openReassignModal(step)}
@@ -928,7 +918,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
</div> </div>
{/if} {/if}
</div> </div>
@@ -1104,7 +1093,6 @@
{/if} {/if}
</div> </div>
{#if instance.status === 'active'} {#if instance.status === 'active'}
<ActionGuard recurso="fluxos_documentos" acao="upload">
<button <button
class="btn btn-ghost btn-xs" class="btn btn-ghost btn-xs"
onclick={() => openUploadModal(step)} onclick={() => openUploadModal(step)}
@@ -1127,7 +1115,6 @@
</svg> </svg>
Enviar Enviar
</button> </button>
</ActionGuard>
{/if} {/if}
</div> </div>
@@ -1158,7 +1145,6 @@
</div> </div>
</div> </div>
{#if instance.status === 'active'} {#if instance.status === 'active'}
<ActionGuard recurso="fluxos_documentos" acao="excluir">
<button <button
class="btn btn-ghost btn-xs text-error shrink-0" class="btn btn-ghost btn-xs text-error shrink-0"
onclick={() => handleDeleteDocument(doc._id)} onclick={() => handleDeleteDocument(doc._id)}
@@ -1180,7 +1166,6 @@
/> />
</svg> </svg>
</button> </button>
</ActionGuard>
{/if} {/if}
</div> </div>
{/each} {/each}

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,4 @@
export const load = async ({ parent }) => {
const { currentUser } = await parent();
console.log(currentUser);
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,3 @@
export const load = async ({ parent }) => {
await parent();
};

View File

@@ -0,0 +1,22 @@
<script lang="ts">
import Footer from '$lib/components/Footer.svelte';
import Header from '$lib/components/Header.svelte';
import { Toaster } from 'svelte-sonner';
let { children } = $props();
</script>
<div
class="bg-base-100 text-base-content selection:bg-primary selection:text-primary-content flex min-h-screen flex-col font-sans"
>
<Header />
<main class="w-full flex-1">
{@render children()}
</main>
<Footer />
<!-- Toast Notifications (Sonner) -->
<Toaster position="top-right" richColors closeButton expand={true} />
</div>

View File

@@ -0,0 +1,134 @@
<script lang="ts">
import { ArrowRight, CheckCircle2, ShieldCheck, Zap, Users, BarChart3 } from 'lucide-svelte';
import { resolve } from '$app/paths';
import logo from '$lib/assets/logo_governo_PE.png';
</script>
<svelte:head>
<title>Home - SGSE</title>
<meta name="description" content="Sistema de Gestão de Secretaria - Governo de Pernambuco" />
</svelte:head>
<div class="flex flex-col">
<!-- Hero Section -->
<section class="relative overflow-hidden bg-base-100 pt-16 pb-32 lg:pt-32 lg:pb-48">
<div class="absolute top-0 left-0 w-full h-full overflow-hidden z-0">
<div class="absolute -top-[30%] -right-[10%] w-[70%] h-[70%] rounded-full bg-primary/5 blur-[100px]"></div>
<div class="absolute bottom-[10%] -left-[10%] w-[50%] h-[50%] rounded-full bg-secondary/5 blur-[100px]"></div>
</div>
<div class="container mx-auto px-4 relative z-10">
<div class="max-w-4xl mx-auto text-center">
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-primary/10 text-primary font-medium text-sm mb-8 animate-fade-in-up">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
</span>
Sistema de Gestão de Secretaria
</div>
<h1 class="text-5xl md:text-7xl font-extrabold tracking-tight mb-8 text-base-content leading-tight">
Simplificando a <span class="bg-clip-text text-transparent bg-gradient-to-r from-primary to-secondary">Gestão Pública</span>
</h1>
<p class="text-xl md:text-2xl text-base-content/70 mb-12 max-w-2xl mx-auto leading-relaxed">
Uma plataforma unificada para otimizar processos, conectar departamentos e garantir eficiência na administração pública.
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
<a href={resolve('/login')} class="btn btn-primary btn-lg gap-2 shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30 transition-all hover:-translate-y-1">
Acessar Sistema
<ArrowRight class="w-5 h-5" />
</a>
<a href="#recursos" class="btn btn-ghost btn-lg gap-2">
Conheça os Recursos
</a>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="recursos" class="py-24 bg-base-200/50">
<div class="container mx-auto px-4">
<div class="text-center mb-16">
<h2 class="text-3xl md:text-4xl font-bold mb-4">Recursos Principais</h2>
<p class="text-lg text-base-content/70 max-w-2xl mx-auto">
Ferramentas desenvolvidas especificamente para atender às necessidades da gestão secretaria.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{#each [
{
icon: Zap,
title: 'Agilidade nos Processos',
description: 'Automatize tarefas repetitivas e reduza o tempo de tramitação de documentos.'
},
{
icon: ShieldCheck,
title: 'Segurança de Dados',
description: 'Proteção avançada para garantir a integridade e confidencialidade das informações.'
},
{
icon: Users,
title: 'Gestão de Pessoas',
description: 'Ferramentas integradas para acompanhamento e desenvolvimento dos servidores.'
},
{
icon: BarChart3,
title: 'Relatórios Inteligentes',
description: 'Dashboards interativos para tomada de decisão baseada em dados reais.'
},
{
icon: CheckCircle2,
title: 'Controle de Ativos',
description: 'Rastreamento completo de bens e recursos da secretaria.'
},
{
icon: Users,
title: 'Colaboração em Tempo Real',
description: 'Conecte equipes e facilite a comunicação interna entre departamentos.'
}
] as feature}
<div class="card bg-base-100 shadow-sm hover:shadow-md transition-shadow duration-300 border border-base-200">
<div class="card-body">
<div class="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center text-primary mb-4">
<feature.icon class="w-6 h-6" />
</div>
<h3 class="card-title text-xl mb-2">{feature.title}</h3>
<p class="text-base-content/70">{feature.description}</p>
</div>
</div>
{/each}
</div>
</div>
</section>
<!-- Call to Action -->
<section class="py-24 relative overflow-hidden">
<div class="absolute inset-0 bg-primary/5"></div>
<div class="container mx-auto px-4 relative z-10">
<div class="bg-base-100 rounded-3xl p-8 md:p-16 text-center shadow-xl border border-base-200 max-w-5xl mx-auto">
<h2 class="text-3xl md:text-5xl font-bold mb-6">Pronto para começar?</h2>
<p class="text-xl text-base-content/70 mb-10 max-w-2xl mx-auto">
Acesse o portal e tenha todo o controle da secretaria na palma da sua mão.
</p>
<a href={resolve('/login')} class="btn btn-primary btn-lg shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30 transition-all hover:-translate-y-1">
Fazer Login Agora
</a>
</div>
</div>
</section>
</div>
<style>
/* Custom animations if needed */
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in-up {
animation: fade-in-up 0.8s ease-out forwards;
}
</style>

View File

@@ -0,0 +1,24 @@
import { createConvexHttpClient } from '@mmailaender/convex-better-auth-svelte/sveltekit';
import { api } from '@sgse-app/backend/convex/_generated/api';
import { redirect } from '@sveltejs/kit';
import type { FunctionReference } from 'convex/server';
export const load = async ({ locals, url }) => {
try {
const client = createConvexHttpClient({ token: locals.token });
const currentUser = await client.query(api.auth.getCurrentUser as FunctionReference<'query'>);
if (currentUser) {
const redirectTo = url.searchParams.get('redirect');
if (redirectTo && redirectTo.startsWith('/')) {
throw redirect(302, redirectTo);
}
throw redirect(302, '/');
}
} catch (error) {
// Se houver falha transitória na API/auth, ainda assim permitir renderizar a página de login.
console.error('Erro ao validar sessão na página de login:', error);
}
return {};
};

View File

@@ -0,0 +1,275 @@
<script lang="ts">
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient } from 'convex-svelte';
import type { FunctionReference } from 'convex/server';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import logo from '$lib/assets/logo_governo_PE.png';
import { authClient } from '$lib/auth';
import { obterIPPublico } from '$lib/utils/deviceInfo';
import AnimatedBackgroundElements from '$lib/components/AnimatedBackgroundElements.svelte';
import DecorativeTopLine from '$lib/components/DecorativeTopLine.svelte';
import ErrorMessage from '$lib/components/ErrorMessage.svelte';
import GlassCard from '$lib/components/GlassCard.svelte';
import LoginInput from '$lib/components/login/LoginInput.svelte';
import ShineEffect from '$lib/components/ShineEffect.svelte';
import { LogIn } from 'lucide-svelte';
interface GPSLocation {
latitude?: number;
longitude?: number;
precisao?: number;
endereco?: string;
cidade?: string;
estado?: string;
pais?: string;
}
let matricula = $state('');
let senha = $state('');
let erroLogin = $state('');
let carregandoLogin = $state(false);
const convexClient = useConvexClient();
const redirectAfterLogin = $derived.by(() => {
const redirectTo = page.url.searchParams.get('redirect');
return redirectTo && redirectTo.startsWith('/') ? redirectTo : '/';
});
async function handleLogin(e: Event) {
e.preventDefault();
erroLogin = '';
carregandoLogin = true;
const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : undefined;
// Obter IP público com timeout curto (não bloquear login)
const ipPublicoPromise = obterIPPublico().catch(() => undefined);
const ipPublicoTimeout = new Promise<undefined>((resolve) =>
setTimeout(() => resolve(undefined), 2000)
);
const ipPublico = await Promise.race([ipPublicoPromise, ipPublicoTimeout]);
// Função para coletar GPS em background (não bloqueia login)
async function coletarGPS(): Promise<GPSLocation> {
try {
const { obterLocalizacaoRapida } = await import('$lib/utils/deviceInfo');
const gpsPromise = obterLocalizacaoRapida();
const gpsTimeout = new Promise<GPSLocation>((resolve) =>
setTimeout(() => resolve({}), 3000)
);
return await Promise.race([gpsPromise, gpsTimeout]);
} catch (err) {
console.warn('Erro ao obter GPS (não bloqueia login):', err);
return {};
}
}
const gpsPromise = coletarGPS();
const result = await authClient.signIn.email(
{ email: matricula.trim(), password: senha },
{
onError: async (ctx) => {
try {
let localizacaoGPS: GPSLocation = {};
try {
localizacaoGPS = await Promise.race([
gpsPromise,
new Promise<GPSLocation>((resolve) => setTimeout(() => resolve({}), 100))
]);
} catch {
// ignorar
}
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
matriculaOuEmail: matricula.trim(),
sucesso: false,
motivoFalha: ctx.error?.message || 'Erro desconhecido',
userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
} catch (err) {
console.error('Erro ao registrar tentativa de login falha:', err);
}
erroLogin = ctx.error?.message || 'Erro ao fazer login';
}
}
);
if (result.data) {
// Registrar tentativa de login bem-sucedida sem bloquear o redirect
(async () => {
try {
await new Promise((resolve) => setTimeout(resolve, 500));
let localizacaoGPS: GPSLocation = {};
try {
localizacaoGPS = await Promise.race([
gpsPromise,
new Promise<GPSLocation>((resolve) => setTimeout(() => resolve({}), 100))
]);
} catch {
// ignorar
}
const usuario = (await convexClient.query(
api.auth.getCurrentUser as unknown as FunctionReference<'query'>,
{}
)) as { _id?: Id<'usuarios'> } | null;
if (usuario && usuario._id) {
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
usuarioId: usuario._id,
matriculaOuEmail: matricula.trim(),
sucesso: true,
userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
} else {
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
matriculaOuEmail: matricula.trim(),
sucesso: true,
userAgent,
ipAddress: ipPublico,
latitudeGPS: localizacaoGPS.latitude,
longitudeGPS: localizacaoGPS.longitude,
precisaoGPS: localizacaoGPS.precisao,
enderecoGPS: localizacaoGPS.endereco,
cidadeGPS: localizacaoGPS.cidade,
estadoGPS: localizacaoGPS.estado,
paisGPS: localizacaoGPS.pais
});
}
} catch (err) {
console.error('Erro ao registrar tentativa de login:', err);
}
})();
await goto(resolve(redirectAfterLogin as string), { replaceState: true });
} else {
erroLogin = result.error?.message || 'Erro ao fazer login';
}
carregandoLogin = false;
}
</script>
<main
class="bg-base-100 relative flex min-h-screen w-full items-center justify-center overflow-hidden"
>
<AnimatedBackgroundElements />
<!-- Glass Card -->
<div class="relative z-10 w-full max-w-md p-6">
<GlassCard>
<DecorativeTopLine />
<!-- Header -->
<div class="mb-10 text-center">
<div
class="bg-base-content/5 ring-base-content/10 mb-6 inline-flex items-center justify-center rounded-2xl p-4 shadow-inner ring-1"
>
<img src={logo} alt="Logo SGSE" class="h-12 w-auto object-contain" />
</div>
<h1 class="text-base-content mb-2 font-sans text-3xl font-bold tracking-tight">
Bem-vindo de volta
</h1>
<p class="text-base-content/60 text-sm font-medium">
Entre com suas credenciais para acessar o sistema
</p>
</div>
<!-- Error Message -->
<ErrorMessage message={erroLogin} />
<!-- Form -->
<form class="space-y-6" onsubmit={handleLogin}>
<LoginInput
id="login-matricula"
label="Matrícula ou E-mail"
placeholder="Digite sua identificação"
bind:value={matricula}
required
disabled={carregandoLogin}
autocomplete="username"
/>
<LoginInput
id="login-password"
label="Senha"
type="password"
placeholder="Digite sua senha"
bind:value={senha}
required
disabled={carregandoLogin}
autocomplete="current-password"
>
{#snippet right()}
<a
href={resolve('/esqueci-senha')}
class="text-primary hover:text-primary-focus text-xs font-medium transition-colors"
>
Esqueceu a senha?
</a>
{/snippet}
</LoginInput>
<button
type="submit"
class="group bg-primary hover:bg-primary-focus hover:shadow-primary/25 text-primary-content relative w-full overflow-hidden rounded-xl px-4 py-3.5 text-sm font-bold shadow-lg transition-all duration-300 disabled:cursor-not-allowed disabled:opacity-50"
disabled={carregandoLogin}
>
<div class="relative z-10 flex items-center justify-center gap-2">
{#if carregandoLogin}
<span
class="border-primary-content/30 border-t-primary-content h-5 w-5 animate-spin rounded-full border-2"
></span>
<span>Autenticando...</span>
{:else}
<span>Entrar no Sistema</span>
<LogIn class="h-4 w-4 transition-transform duration-300 group-hover:translate-x-1" />
{/if}
</div>
<!-- Shine Effect -->
<ShineEffect />
</button>
</form>
<!-- Footer Links -->
<div class="mt-8 text-center">
<p class="text-base-content/40 text-sm">
Precisa de ajuda?
<a
href={resolve('/abrir-chamado')}
class="text-base-content/70 hover:text-base-content font-medium decoration-1 transition-colors hover:underline"
>
Abrir um chamado
</a>
</p>
</div>
</GlassCard>
<!-- Footer Info -->
<div class="text-base-content/40 mt-8 text-center text-xs">
<p>© {new Date().getFullYear()} Governo de Pernambuco. Todos os direitos reservados.</p>
</div>
</div>
</main>

View File

@@ -3,6 +3,10 @@
import { Shield, FileText, Mail, Phone, Calendar } from 'lucide-svelte'; import { Shield, FileText, Mail, Phone, Calendar } from 'lucide-svelte';
import { useQuery } from 'convex-svelte'; import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import { useAuth } from '@mmailaender/convex-better-auth-svelte/svelte';
const auth = useAuth();
const isAuthenticated = $derived(auth.isAuthenticated);
const configLGPD = useQuery(api.lgpd.obterConfiguracaoLGPD, {}); const configLGPD = useQuery(api.lgpd.obterConfiguracaoLGPD, {});
@@ -66,7 +70,7 @@
</p> </p>
<div class="space-y-3"> <div class="space-y-3">
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" /> <FileText class="text-primary mt-1 h-5 w-5 shrink-0" />
<div> <div>
<h3 class="font-semibold">Dados de Identificação</h3> <h3 class="font-semibold">Dados de Identificação</h3>
<p class="text-base-content/70 text-sm"> <p class="text-base-content/70 text-sm">
@@ -76,7 +80,7 @@
</div> </div>
</div> </div>
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" /> <FileText class="text-primary mt-1 h-5 w-5 shrink-0" />
<div> <div>
<h3 class="font-semibold">Dados de Contato</h3> <h3 class="font-semibold">Dados de Contato</h3>
<p class="text-base-content/70 text-sm"> <p class="text-base-content/70 text-sm">
@@ -85,7 +89,7 @@
</div> </div>
</div> </div>
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" /> <FileText class="text-primary mt-1 h-5 w-5 shrink-0" />
<div> <div>
<h3 class="font-semibold">Dados Profissionais</h3> <h3 class="font-semibold">Dados Profissionais</h3>
<p class="text-base-content/70 text-sm"> <p class="text-base-content/70 text-sm">
@@ -95,7 +99,7 @@
</div> </div>
</div> </div>
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" /> <FileText class="text-primary mt-1 h-5 w-5 shrink-0" />
<div> <div>
<h3 class="font-semibold">Dados de Saúde</h3> <h3 class="font-semibold">Dados de Saúde</h3>
<p class="text-base-content/70 text-sm"> <p class="text-base-content/70 text-sm">
@@ -105,7 +109,7 @@
</div> </div>
</div> </div>
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<FileText class="text-primary mt-1 h-5 w-5 flex-shrink-0" /> <FileText class="text-primary mt-1 h-5 w-5 shrink-0" />
<div> <div>
<h3 class="font-semibold">Dados de Acesso</h3> <h3 class="font-semibold">Dados de Acesso</h3>
<p class="text-base-content/70 text-sm"> <p class="text-base-content/70 text-sm">
@@ -346,15 +350,17 @@
</div> </div>
</div> </div>
</div> </div>
{#if isAuthenticated}
<div class="mt-6"> <div class="mt-6">
<a <a
href={resolve('/privacidade/meus-dados')} href={resolve('/perfil/privacidade/meus-dados')}
class="btn btn-primary btn-lg w-full md:w-auto" class="btn btn-primary btn-lg w-full md:w-auto"
> >
<FileText class="h-5 w-5" /> <FileText class="h-5 w-5" />
Solicitar Meus Direitos Solicitar Meus Direitos
</a> </a>
</div> </div>
{/if}
</div> </div>
</section> </section>
@@ -420,8 +426,9 @@
</section> </section>
<!-- Ações --> <!-- Ações -->
{#if isAuthenticated}
<div class="mt-8 flex flex-col gap-4 sm:flex-row"> <div class="mt-8 flex flex-col gap-4 sm:flex-row">
<a href={resolve('/privacidade/meus-dados')} class="btn btn-primary btn-lg flex-1"> <a href={resolve('/perfil/privacidade/meus-dados')} class="btn btn-primary btn-lg flex-1">
<FileText class="h-5 w-5" /> <FileText class="h-5 w-5" />
Solicitar Meus Direitos LGPD Solicitar Meus Direitos LGPD
</a> </a>
@@ -430,5 +437,6 @@
Ver Termo de Consentimento Ver Termo de Consentimento
</a> </a>
</div> </div>
{/if}
</div> </div>
</div> </div>

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import '../app.css'; import '../app.css';
import Sidebar from '$lib/components/Sidebar.svelte'; import { onMount } from 'svelte';
import { createSvelteAuthClient } from '@mmailaender/convex-better-auth-svelte/svelte'; import { createSvelteAuthClient } from '@mmailaender/convex-better-auth-svelte/svelte';
import { authClient } from '$lib/auth'; import { authClient } from '$lib/auth';
// Importar polyfill ANTES de qualquer outro código que possa usar Jitsi // Importar polyfill ANTES de qualquer outro código que possa usar Jitsi
@@ -8,6 +8,7 @@
import { useQuery } from 'convex-svelte'; import { useQuery } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api'; import { api } from '@sgse-app/backend/convex/_generated/api';
import { aplicarTema, aplicarTemaPadrao } from '$lib/utils/temas'; import { aplicarTema, aplicarTemaPadrao } from '$lib/utils/temas';
import { themeChange } from 'theme-change';
const { children } = $props(); const { children } = $props();
@@ -16,6 +17,36 @@
// Buscar usuário atual para aplicar tema // Buscar usuário atual para aplicar tema
const currentUser = useQuery(api.auth.getCurrentUser, {}); const currentUser = useQuery(api.auth.getCurrentUser, {});
function obterTemaPersistido(): string | null {
if (typeof window === 'undefined') return null;
try {
const tema = window.localStorage.getItem('theme');
return tema && tema.trim() ? tema : null;
} catch {
return null;
}
}
function aplicarTemaDaisyUI(tema: string): void {
if (typeof document === 'undefined') return;
const htmlElement = document.documentElement;
htmlElement.setAttribute('data-theme', tema);
// Evita que `body[data-theme]` sobrescreva o tema do `<html>`
if (document.body) document.body.removeAttribute('data-theme');
}
onMount(() => {
// Habilita `data-set-theme` e `data-choose-theme` e persiste em localStorage ("theme")
// Em Svelte, precisamos passar `false` para não depender de `DOMContentLoaded`
themeChange(false);
// Garante que o tema persistido no device tenha prioridade
const temaPersistido = obterTemaPersistido();
if (temaPersistido) {
aplicarTemaDaisyUI(temaPersistido);
}
});
// Aplicar tema quando o usuário for carregado // Aplicar tema quando o usuário for carregado
$effect(() => { $effect(() => {
if (typeof document === 'undefined') return; if (typeof document === 'undefined') return;
@@ -24,6 +55,13 @@
// Aguardar um pouco para garantir que o DOM está pronto // Aguardar um pouco para garantir que o DOM está pronto
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
const temaPersistido = obterTemaPersistido();
if (temaPersistido) {
// Prioridade do device (localStorage) mesmo para usuários logados
aplicarTemaDaisyUI(temaPersistido);
return;
}
if (currentUser?.data?.temaPreferido) { if (currentUser?.data?.temaPreferido) {
// Usuário logado com tema preferido - aplicar tema salvo // Usuário logado com tema preferido - aplicar tema salvo
aplicarTema(currentUser.data.temaPreferido); aplicarTema(currentUser.data.temaPreferido);
@@ -41,6 +79,12 @@
// Aplicar tema padrão imediatamente ao carregar (antes de verificar usuário) // Aplicar tema padrão imediatamente ao carregar (antes de verificar usuário)
$effect(() => { $effect(() => {
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
const temaPersistido = obterTemaPersistido();
if (temaPersistido) {
aplicarTemaDaisyUI(temaPersistido);
return;
}
// Se não há tema aplicado ainda, aplicar o padrão imediatamente // Se não há tema aplicado ainda, aplicar o padrão imediatamente
const htmlElement = document.documentElement; const htmlElement = document.documentElement;
if (!htmlElement.getAttribute('data-theme')) { if (!htmlElement.getAttribute('data-theme')) {
@@ -50,8 +94,4 @@
}); });
</script> </script>
<div> {@render children()}
<div class="flex">
<Sidebar>{@render children()}</Sidebar>
</div>
</div>

View File

@@ -1,153 +0,0 @@
<script lang="ts">
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import { useConvexClient, useQuery } from 'convex-svelte';
let newTodoText = $state('');
let isAdding = $state(false);
let addError = $state<Error | null>(null);
let togglingId = $state<Id<'todos'> | null>(null);
let toggleError = $state<Error | null>(null);
let deletingId = $state<Id<'todos'> | null>(null);
let deleteError = $state<Error | null>(null);
const client = useConvexClient();
const todosQuery = useQuery(api.todos.getAll, {});
async function handleAddTodo(event: SubmitEvent) {
event.preventDefault();
const text = newTodoText.trim();
if (!text || isAdding) return;
isAdding = true;
addError = null;
try {
await client.mutation(api.todos.create, { text });
newTodoText = '';
} catch (err) {
console.error('Failed to add todo:', err);
addError = err instanceof Error ? err : new Error(String(err));
} finally {
isAdding = false;
}
}
async function handleToggleTodo(id: Id<'todos'>, completed: boolean) {
if (togglingId === id || deletingId === id) return;
togglingId = id;
toggleError = null;
try {
await client.mutation(api.todos.toggle, { id, completed: !completed });
} catch (err) {
console.error('Failed to toggle todo:', err);
toggleError = err instanceof Error ? err : new Error(String(err));
} finally {
if (togglingId === id) {
togglingId = null;
}
}
}
async function handleDeleteTodo(id: Id<'todos'>) {
if (togglingId === id || deletingId === id) return;
deletingId = id;
deleteError = null;
try {
await client.mutation(api.todos.deleteTodo, { id });
} catch (err) {
console.error('Failed to delete todo:', err);
deleteError = err instanceof Error ? err : new Error(String(err));
} finally {
if (deletingId === id) {
deletingId = null;
}
}
}
const canAdd = $derived(!isAdding && newTodoText.trim().length > 0);
const isLoadingTodos = $derived(todosQuery.isLoading);
const todos = $derived(todosQuery.data ?? []);
const hasTodos = $derived(todos.length > 0);
</script>
<div class="p-4">
<h1 class="mb-4 text-xl">Todos (Convex)</h1>
<form onsubmit={handleAddTodo} class="mb-4 flex gap-2">
<input
type="text"
bind:value={newTodoText}
placeholder="New task..."
disabled={isAdding}
class="flex-grow p-1"
/>
<button
type="submit"
disabled={!canAdd}
class="rounded bg-blue-500 px-3 py-1 text-white disabled:opacity-50"
>
{#if isAdding}Adding...{:else}Add{/if}
</button>
</form>
{#if isLoadingTodos}
<p>Loading...</p>
{:else if !hasTodos}
<p>No todos yet.</p>
{:else}
<ul class="space-y-1">
{#each todos as todo (todo._id)}
{@const isTogglingThis = togglingId === todo._id}
{@const isDeletingThis = deletingId === todo._id}
{@const isDisabled = isTogglingThis || isDeletingThis}
<li class="flex items-center justify-between p-2" class:opacity-50={isDisabled}>
<div class="flex items-center gap-2">
<input
type="checkbox"
id={`todo-${todo._id}`}
checked={todo.completed}
onchange={() => handleToggleTodo(todo._id, todo.completed)}
disabled={isDisabled}
/>
<label for={`todo-${todo._id}`} class:line-through={todo.completed}>
{todo.text}
</label>
</div>
<button
type="button"
onclick={() => handleDeleteTodo(todo._id)}
disabled={isDisabled}
aria-label="Delete todo"
class="px-1 text-red-500 disabled:opacity-50"
>
{#if isDeletingThis}Deleting...{:else}X{/if}
</button>
</li>
{/each}
</ul>
{/if}
{#if todosQuery.error}
<p class="mt-4 text-red-500">
Error loading: {todosQuery.error?.message ?? 'Unknown error'}
</p>
{/if}
{#if addError}
<p class="mt-4 text-red-500">
Error adding: {addError.message ?? 'Unknown error'}
</p>
{/if}
{#if toggleError}
<p class="mt-4 text-red-500">
Error updating: {toggleError.message ?? 'Unknown error'}
</p>
{/if}
{#if deleteError}
<p class="mt-4 text-red-500">
Error deleting: {deleteError.message ?? 'Unknown error'}
</p>
{/if}
</div>

243
bun.lock
View File

@@ -8,27 +8,29 @@
"@convex-dev/better-auth": "^0.9.7", "@convex-dev/better-auth": "^0.9.7",
"@tanstack/svelte-form": "^1.23.8", "@tanstack/svelte-form": "^1.23.8",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"convex": "^1.31.0",
"lucide-svelte": "^0.552.0", "lucide-svelte": "^0.552.0",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"svelte-chartjs": "^3.1.5", "svelte-chartjs": "^3.1.5",
"svelte-sonner": "^1.0.5", "svelte-sonner": "^1.0.7",
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-svelte": "^3.13.0", "eslint-plugin-svelte": "^3.13.1",
"globals": "^16.5.0", "globals": "^16.5.0",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"prettier": "^3.6.2", "prettier": "^3.7.4",
"prettier-plugin-tailwindcss": "^0.7.1", "prettier-plugin-tailwindcss": "^0.7.2",
"svelte-dnd-action": "^0.9.67", "svelte-dnd-action": "^0.9.68",
"turbo": "^2.6.3", "turbo": "^2.6.3",
"typescript-eslint": "^8.46.3", "typescript-eslint": "^8.49.0",
}, },
}, },
"apps/web": { "apps/web": {
"name": "web", "name": "web",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@ark-ui/svelte": "^5.15.0",
"@convex-dev/better-auth": "^0.9.7", "@convex-dev/better-auth": "^0.9.7",
"@dicebear/collection": "^9.2.4", "@dicebear/collection": "^9.2.4",
"@dicebear/core": "^9.2.4", "@dicebear/core": "^9.2.4",
@@ -57,6 +59,7 @@
"marked": "^17.0.1", "marked": "^17.0.1",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"svelte-sonner": "^1.0.5", "svelte-sonner": "^1.0.5",
"theme-change": "^2.5.0",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"xlsx-js-style": "^1.2.0", "xlsx-js-style": "^1.2.0",
"zod": "^4.1.12", "zod": "^4.1.12",
@@ -130,12 +133,14 @@
}, },
"catalog": { "catalog": {
"@eslint/js": "^9.39.1", "@eslint/js": "^9.39.1",
"better-auth": "1.3.27", "better-auth": "1.3.34",
"convex": "^1.28.2", "convex": "^1.31.0",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"typescript": "^5.9.2", "typescript": "^5.9.2",
}, },
"packages": { "packages": {
"@ark-ui/svelte": ["@ark-ui/svelte@5.15.0", "", { "dependencies": { "@internationalized/date": "3.10.0", "@zag-js/accordion": "1.31.1", "@zag-js/anatomy": "1.31.1", "@zag-js/angle-slider": "1.31.1", "@zag-js/async-list": "1.31.1", "@zag-js/auto-resize": "1.31.1", "@zag-js/avatar": "1.31.1", "@zag-js/bottom-sheet": "1.31.1", "@zag-js/carousel": "1.31.1", "@zag-js/checkbox": "1.31.1", "@zag-js/clipboard": "1.31.1", "@zag-js/collapsible": "1.31.1", "@zag-js/collection": "1.31.1", "@zag-js/color-picker": "1.31.1", "@zag-js/color-utils": "1.31.1", "@zag-js/combobox": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/date-picker": "1.31.1", "@zag-js/date-utils": "1.31.1", "@zag-js/dialog": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/editable": "1.31.1", "@zag-js/file-upload": "1.31.1", "@zag-js/file-utils": "1.31.1", "@zag-js/floating-panel": "1.31.1", "@zag-js/focus-trap": "1.31.1", "@zag-js/highlight-word": "1.31.1", "@zag-js/hover-card": "1.31.1", "@zag-js/i18n-utils": "1.31.1", "@zag-js/image-cropper": "1.31.1", "@zag-js/json-tree-utils": "1.31.1", "@zag-js/listbox": "1.31.1", "@zag-js/marquee": "1.31.1", "@zag-js/menu": "1.31.1", "@zag-js/navigation-menu": "1.31.1", "@zag-js/number-input": "1.31.1", "@zag-js/pagination": "1.31.1", "@zag-js/password-input": "1.31.1", "@zag-js/pin-input": "1.31.1", "@zag-js/popover": "1.31.1", "@zag-js/presence": "1.31.1", "@zag-js/progress": "1.31.1", "@zag-js/qr-code": "1.31.1", "@zag-js/radio-group": "1.31.1", "@zag-js/rating-group": "1.31.1", "@zag-js/scroll-area": "1.31.1", "@zag-js/select": "1.31.1", "@zag-js/signature-pad": "1.31.1", "@zag-js/slider": "1.31.1", "@zag-js/splitter": "1.31.1", "@zag-js/steps": "1.31.1", "@zag-js/svelte": "1.31.1", "@zag-js/switch": "1.31.1", "@zag-js/tabs": "1.31.1", "@zag-js/tags-input": "1.31.1", "@zag-js/timer": "1.31.1", "@zag-js/toast": "1.31.1", "@zag-js/toggle": "1.31.1", "@zag-js/toggle-group": "1.31.1", "@zag-js/tooltip": "1.31.1", "@zag-js/tour": "1.31.1", "@zag-js/tree-view": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" }, "peerDependencies": { "svelte": ">=5.20.0" } }, "sha512-hNplAW5DVObanJd2sCbCqWvlVkv/1l4wXH7yge/akSZ0K2Nb/LPKFmijSpPZwwheKDCdxyQLrhUygiqI7GCqGg=="],
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
"@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
@@ -200,13 +205,15 @@
"@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
"@better-auth/core": ["@better-auth/core@1.3.27", "", { "dependencies": { "better-call": "1.0.19", "zod": "^4.1.5" } }, "sha512-3Sfdax6MQyronY+znx7bOsfQHI6m1SThvJWb0RDscFEAhfqLy95k1sl+/PgGyg0cwc2cUXoEiAOSqYdFYrg3vA=="], "@better-auth/core": ["@better-auth/core@1.3.34", "", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "better-call": "1.0.19", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-rt/Bgl0Xa8OQ2DUMKCZEJ8vL9kUw4NCJsBP9Sj9uRhbsK8NEMPiznUOFMkUY2FvrslvfKN7H/fivwyHz9c7HzQ=="],
"@better-auth/telemetry": ["@better-auth/telemetry@1.3.34", "", { "dependencies": { "@better-auth/core": "1.3.34", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18" } }, "sha512-aQZ3wN90YMqV49diWxAMe1k7s2qb55KCsedCZne5PlgCjU4s3YtnqyjC5FEpzw2KY8l8rvR7DMAsDl13NjObKA=="],
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="], "@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
"@convex-dev/better-auth": ["@convex-dev/better-auth@0.9.7", "", { "dependencies": { "common-tags": "^1.8.2", "convex-helpers": "^0.1.95", "jose": "^6.1.0", "remeda": "^2.32.0", "semver": "^7.7.3", "type-fest": "^4.39.1", "zod": "^3.24.4" }, "peerDependencies": { "better-auth": "1.3.27", "convex": ">=1.28.2 <1.35.0", "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-ni0oLM3IQho8KVBlMoyTk50IIbckhZmlEMxLgaVSixKmFJ4N/kGC6T91MjPTw3+bVLn/qHmIinLp7Dm+NRYzBw=="], "@convex-dev/better-auth": ["@convex-dev/better-auth@0.9.11", "", { "dependencies": { "common-tags": "^1.8.2", "convex-helpers": "^0.1.95", "jose": "^6.1.0", "remeda": "^2.32.0", "semver": "^7.7.3", "type-fest": "^4.39.1", "zod": "^3.24.4" }, "peerDependencies": { "better-auth": "1.3.34", "convex": "^1.25.0", "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-SZHuh/rgLxDydAG8fRMm6H6SECGt9FfgRMPR6/k+cR8MKsksr0YWvPhAVpe7hAwuuXAachP+9SqG/YIwBVNUZA=="],
"@convex-dev/eslint-plugin": ["@convex-dev/eslint-plugin@1.0.0", "", { "dependencies": { "@typescript-eslint/utils": "~8.38.0" } }, "sha512-ublJRBKcLCioNaf1ylkCHD2KzAqWE2RIQ6DA/UgXAXQW5qg4vZSWY8wy+EK11yJkSSxcGfFXDWaE1+cHaWJvNA=="], "@convex-dev/eslint-plugin": ["@convex-dev/eslint-plugin@1.0.0", "", { "dependencies": { "@typescript-eslint/utils": "~8.38.0" } }, "sha512-ublJRBKcLCioNaf1ylkCHD2KzAqWE2RIQ6DA/UgXAXQW5qg4vZSWY8wy+EK11yJkSSxcGfFXDWaE1+cHaWJvNA=="],
@@ -356,6 +363,12 @@
"@fast-csv/parse": ["@fast-csv/parse@4.3.6", "", { "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.groupby": "^4.6.0", "lodash.isfunction": "^3.0.9", "lodash.isnil": "^4.0.0", "lodash.isundefined": "^3.0.1", "lodash.uniq": "^4.5.0" } }, "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA=="], "@fast-csv/parse": ["@fast-csv/parse@4.3.6", "", { "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.groupby": "^4.6.0", "lodash.isfunction": "^3.0.9", "lodash.isnil": "^4.0.0", "lodash.isundefined": "^3.0.1", "lodash.uniq": "^4.5.0" } }, "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA=="],
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
"@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="],
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
"@fullcalendar/core": ["@fullcalendar/core@6.1.19", "", { "dependencies": { "preact": "~10.12.1" } }, "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ=="], "@fullcalendar/core": ["@fullcalendar/core@6.1.19", "", { "dependencies": { "preact": "~10.12.1" } }, "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ=="],
"@fullcalendar/daygrid": ["@fullcalendar/daygrid@6.1.19", "", { "peerDependencies": { "@fullcalendar/core": "~6.1.19" } }, "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ=="], "@fullcalendar/daygrid": ["@fullcalendar/daygrid@6.1.19", "", { "peerDependencies": { "@fullcalendar/core": "~6.1.19" } }, "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ=="],
@@ -378,6 +391,8 @@
"@internationalized/date": ["@internationalized/date@3.10.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw=="], "@internationalized/date": ["@internationalized/date@3.10.0", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw=="],
"@internationalized/number": ["@internationalized/number@3.6.5", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
@@ -640,15 +655,15 @@
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.16", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="], "@tailwindcss/vite": ["@tailwindcss/vite@4.1.16", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="],
"@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.3.4", "", {}, "sha512-eq+PpuutUyubXu+ycC1GIiVwBs86NF/8yYJJAKSpPcJLWl6R/761F1H4F/9ziX6zKezltFUH1ah3Cz8Ah+KJrw=="], "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.3.5", "", {}, "sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw=="],
"@tanstack/form-core": ["@tanstack/form-core@1.24.4", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.3.3", "@tanstack/pacer": "^0.15.3", "@tanstack/store": "^0.7.7" } }, "sha512-+eIR7DiDamit1zvTVgaHxuIRA02YFgJaXMUGxsLRJoBpUjGl/g/nhUocQoNkRyfXqOlh8OCMTanjwDprWSRq6w=="], "@tanstack/form-core": ["@tanstack/form-core@1.27.3", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.3.5", "@tanstack/pacer-lite": "^0.1.1", "@tanstack/store": "^0.7.7" } }, "sha512-dFllZ1JEVmFVwvbXL+l1NWbj4X++FL+D9EK3Xksw7JA+V48wLHq7uzNHQy76al0M+ry0reLqfm65fgd8BWR7/Q=="],
"@tanstack/pacer": ["@tanstack/pacer@0.15.4", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.3.2", "@tanstack/store": "^0.7.5" } }, "sha512-vGY+CWsFZeac3dELgB6UZ4c7OacwsLb8hvL2gLS6hTgy8Fl0Bm/aLokHaeDIP+q9F9HUZTnp360z9uv78eg8pg=="], "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.1.1", "", {}, "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w=="],
"@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="], "@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="],
"@tanstack/svelte-form": ["@tanstack/svelte-form@1.23.8", "", { "dependencies": { "@tanstack/form-core": "1.24.4", "@tanstack/svelte-store": "^0.7.7" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-ZH17T/gOQ9sBpI/38zBCBiuceLsa9c9rOgwB7CRt/FBFunIkaG2gY02IiUBpjZfm1fiKBcTryaJGfR3XAtIH/g=="], "@tanstack/svelte-form": ["@tanstack/svelte-form@1.27.3", "", { "dependencies": { "@tanstack/form-core": "1.27.3", "@tanstack/svelte-store": "^0.7.7" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-urJDvf1LA8HzbOFQAIRZLMToYnAc/mIg+Linn8FgVDZYg9PNlUM5Afb0JURpjE6mXNLXLmNyVs/p432pN03GOA=="],
"@tanstack/svelte-store": ["@tanstack/svelte-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-JeDyY7SxBi6EKzkf2wWoghdaC2bvmwNL9X/dgkx7LKEvJVle+te7tlELI3cqRNGbjXt9sx+97jx9M5dCCHcuog=="], "@tanstack/svelte-store": ["@tanstack/svelte-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-JeDyY7SxBi6EKzkf2wWoghdaC2bvmwNL9X/dgkx7LKEvJVle+te7tlELI3cqRNGbjXt9sx+97jx9M5dCCHcuog=="],
@@ -674,25 +689,171 @@
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.3", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/type-utils": "8.46.3", "@typescript-eslint/utils": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.3", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.49.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/type-utils": "8.49.0", "@typescript-eslint/utils": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.3", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.49.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.3", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.3", "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ=="], "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.49.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.49.0", "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3" } }, "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0" } }, "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.3", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA=="], "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.49.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.46.3", "", {}, "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA=="], "@typescript-eslint/types": ["@typescript-eslint/types@8.49.0", "", {}, "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.3", "@typescript-eslint/tsconfig-utils": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA=="], "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.49.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.49.0", "@typescript-eslint/tsconfig-utils": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g=="], "@typescript-eslint/utils": ["@typescript-eslint/utils@8.49.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" } }, "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg=="], "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.49.0", "", { "dependencies": { "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA=="],
"@zag-js/accordion": ["@zag-js/accordion@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-3sGi4EZpGBz/O1IVkk9dzzWzP5vVVOj4Li6C+jHOnrgaWPouA/mBTP5L9HEL8qtFsECFZwpNo486eqiCmeHoGw=="],
"@zag-js/anatomy": ["@zag-js/anatomy@1.31.1", "", {}, "sha512-BhIhf3Q0tRA0Jugd7AJfUBzeAb/iATBsw7KyYThMGcPWmrWssL7KWr5AB6RufzGKU7+DCb1QEhlqd4NSOJaYxQ=="],
"@zag-js/angle-slider": ["@zag-js/angle-slider@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/rect-utils": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-SfWrgnM0zMLX82rsIJOqWk430UnPA17UFGcDqMDRwXy1Wx4yptmx0aFAsSXnRnw4Ee7WaulF2RWBli6O6iYRCA=="],
"@zag-js/aria-hidden": ["@zag-js/aria-hidden@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-SoNt4S2LkHNWPglQczWN0E5vAV15MT1GoK9MksZzbkMhl+pkDTdLytpXsQ1IgalC1YUng0XNps/Wt6P3uDuzTA=="],
"@zag-js/async-list": ["@zag-js/async-list@1.31.1", "", { "dependencies": { "@zag-js/core": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-BDZEmr4KKh3JASgkXouOwoTWRS1UPE3gdZYZ7Sk7SJ1i8+Pk6zUQ4FnxaoF/cSAdCXyjSSr92Kns2bTk/QuNkQ=="],
"@zag-js/auto-resize": ["@zag-js/auto-resize@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-qzWHibjBekSmFweG+EWY8g0lRzKtok7o9XtQ+JFlOu3s6x4D02z2YDzjDdfSLmS7j0NxISnwQkinWiDAZEYHog=="],
"@zag-js/avatar": ["@zag-js/avatar@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Grosi2hRn4wfDYlPd8l+d4GCIFMsoj6ZFqii+1k14AqTDiCUJ/J0jCvOrRHkvkpEqektjuSD7e/GCX+yawqkuQ=="],
"@zag-js/bottom-sheet": ["@zag-js/bottom-sheet@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/aria-hidden": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-trap": "1.31.1", "@zag-js/remove-scroll": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-ZBbIpYyZX2zQeqW36aODVi9/I4J3zS1XmIHUjeXmfmf6TlQUA1ydgYl7ipREfmCzNWX2LEA5ZnPJQw0UBcrB8w=="],
"@zag-js/carousel": ["@zag-js/carousel@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/scroll-snap": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-228Ol86G/lg8crcomy5cALkUYdOHCHcvJnSOQzeUj80JNjlELzrjBpaAj4lx8dZocfwou2Sg4NyZJ+mISSc+Dg=="],
"@zag-js/checkbox": ["@zag-js/checkbox@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-visible": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-oLS8bqhimckLl6coCNmKPPUmB8wIbVhtkpLwLPLgz4vhhUe7gnpB5dea14Ow2JTBnmug8bMh/bJDtuPa9qQuTw=="],
"@zag-js/clipboard": ["@zag-js/clipboard@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-pv/gOmD9DMg+YmSMjahyd5oSp7/v9K0uQ3att6fPeaNMjB42b3tnY1S1GNVy5Ltf/qHDab6WVwlEN+1zKHXaYw=="],
"@zag-js/collapsible": ["@zag-js/collapsible@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-eCC5G6bBZUwF8z2XULQXUNRxqte9I2Sv+WJ2brycPn1a68uYD76RzFBmLQ2er95VbshUdeo8nRuX8MooAFuYzg=="],
"@zag-js/collection": ["@zag-js/collection@1.31.1", "", { "dependencies": { "@zag-js/utils": "1.31.1" } }, "sha512-ecpfyfCj8Y0/GUPuHYsLxexIrx10VuR3Wd0H+lamcki3lYgQxZrpLRFMwgTqmI/m7t3zhm5QeEvMUJ1H14YMLA=="],
"@zag-js/color-picker": ["@zag-js/color-picker@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/color-utils": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-AWNZth49iEDxqh1DBZNSKpfEM/FF+MjL5bgUHVctnHdkpFsZLynJorWQQ4hNXNDFEc/I5w10KSxVCcO6tsPGFw=="],
"@zag-js/color-utils": ["@zag-js/color-utils@1.31.1", "", { "dependencies": { "@zag-js/utils": "1.31.1" } }, "sha512-HdjTRU8C0tO6hK+PBVlu8iQH1MJaAnJAEdq2FcD97mq0PiPhrSj6iOftnrvPsE4CRieVFjnJWOvaubWFc4VmHA=="],
"@zag-js/combobox": ["@zag-js/combobox@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/aria-hidden": "1.31.1", "@zag-js/collection": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-IT0getSAGzngdRL20iX/iAh2d7DzVoMDDppOsOFBG2owKAgLpj8uLvUhy+lcrm6N8yxYOya89D6Aef7V5KdwlQ=="],
"@zag-js/core": ["@zag-js/core@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-RaMJeqtjxG6k7iFD3WQnlyFJVT3yfQN+pJygAHH37GsMtiNzQQJOoesjb0LV9T27jwMXeNUzrh3MSDr1/0yVcQ=="],
"@zag-js/date-picker": ["@zag-js/date-picker@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/date-utils": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/live-region": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" }, "peerDependencies": { "@internationalized/date": ">=3.0.0" } }, "sha512-AOWN/IskGidVQt5g+uE9cILqJBTclE6OG1GC9WSWuyP/y4F+PdP/781SgYpYCZg/6pMGbL01PFKKb7xOOCeZAg=="],
"@zag-js/date-utils": ["@zag-js/date-utils@1.31.1", "", { "peerDependencies": { "@internationalized/date": ">=3.0.0" } }, "sha512-+Aq9g/rqLeiRmnazgdZMc59gAxqxbw3GGy8AngrtNipgRtMhPlzGa3S4Qsq1yau6OKaHZ13uckUS+MhLNbBY+Q=="],
"@zag-js/dialog": ["@zag-js/dialog@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/aria-hidden": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-trap": "1.31.1", "@zag-js/remove-scroll": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-iaWlYQ6TYoVjM/X5+UZVZzKiMboE50GnEzGUpbhbeRNRiLqSu5dODSFzior1G4kde/ns5eN+BTf/Tm6AT4N2og=="],
"@zag-js/dismissable": ["@zag-js/dismissable@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1", "@zag-js/interact-outside": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-jCdJwQmEkG6PlrN13fUk2l7ZclSu54FZwmT4xOtQpEbaiAiESm5KI5oyFh5jDPY47Goa28UJkEjWXVgKXKWb0g=="],
"@zag-js/dom-query": ["@zag-js/dom-query@1.31.1", "", { "dependencies": { "@zag-js/types": "1.31.1" } }, "sha512-2tCZLwSfoXm62gwl0neiAN6u5VnzUhy5wHtKbX+klqGFatnca3Bm++H9+4PHMrwUWRbPg3H5N151lKFEOQhBfQ=="],
"@zag-js/editable": ["@zag-js/editable@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/interact-outside": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-JMICHw4/x0YqDy/n+I+TeaXlFbTA0j9w3UqOWMwUFQ+dAsq4JLXeqZDXu19MQN6yaTFdOpG1EFw4FEVTsu+d3Q=="],
"@zag-js/file-upload": ["@zag-js/file-upload@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/file-utils": "1.31.1", "@zag-js/i18n-utils": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-cp7qMiXKrIcTfDamOz9wlnJLeBF8gucTI7Y+iKaP+hiIW+OG254GElfQiqXNDad3HUmD+Dt8Tx6uAzL/mw3sbQ=="],
"@zag-js/file-utils": ["@zag-js/file-utils@1.31.1", "", { "dependencies": { "@zag-js/i18n-utils": "1.31.1" } }, "sha512-MDDz52IdPh/mPUYrqUXvh7qDckJHs+mt5gjfx0N89qh2JNXuRU14zPotOKTzIKM4o+HFZkAT6BAfMpr9CX/0ug=="],
"@zag-js/floating-panel": ["@zag-js/floating-panel@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/rect-utils": "1.31.1", "@zag-js/store": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Pjgd/wjdglZ90dtq/LC4o5sc6w0m+RehhPmJcIzq9T+E/Xrb6qrhf06QhxB9LwSj4DG/gIv87gmD2qF1VH7cRQ=="],
"@zag-js/focus-trap": ["@zag-js/focus-trap@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-omgUhAz1r81pYAujqYIIavdTKJzDRExioSiqhnx/xq10a6Q/xavMFflq8w7edMc9JHkTOnr9E5qh9abCVJjhpQ=="],
"@zag-js/focus-visible": ["@zag-js/focus-visible@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-GC59A3yd7tj8aKhzvhrM+CEZZraXm5y/SpfIjz1J7kGV6eeXbUtjkbe75g99Ve8iJYfQVQlAj2GyN3oniHc5Zw=="],
"@zag-js/highlight-word": ["@zag-js/highlight-word@1.31.1", "", {}, "sha512-nQw7t8LgWXW+6Z5E/p6T+OST0DDXp35mrFCzrkJL54aVTZ3GuLyIP2p0/HGQr2hE/KKLbZEs5i6UcXF84tiI4g=="],
"@zag-js/hover-card": ["@zag-js/hover-card@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-R74kz2wPgGwB3jKQeD91kdtlvVKpffWBJHqw8yCBd95GXGVmhym+BPoCToJzcqiemP8+0EtSuVPU9IHaSuJnSg=="],
"@zag-js/i18n-utils": ["@zag-js/i18n-utils@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-SARkFuo1+Q0WcNv4jqvxp5hjCOqu/gBa7p6BTh7v5Bo00QhKRM/bCvVt0EB6V+h2oejrZfkwZ0MwbpQiL6L2aQ=="],
"@zag-js/image-cropper": ["@zag-js/image-cropper@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-hFuy4I3jIJ/iyJsnfbLX1l/cJtN42j7lwhw8TeWVX8Y+hHxFPMSKx7AQirt/hALUbyy7QsQgAd5IslpsYq1Nlg=="],
"@zag-js/interact-outside": ["@zag-js/interact-outside@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-oxBAlBqcatlxGUmhwUCRYTADIBrVoyxM1YrFzR1R8jhvVR/QCaxoLAyKwcA3mWXlZ8+NlXb7n5ELE11BZb/rEg=="],
"@zag-js/json-tree-utils": ["@zag-js/json-tree-utils@1.31.1", "", {}, "sha512-wrNek2UBE69FWpo2f0E2MxiboBS+Uop79LeQU2jNDujA1o3x6b1Lp2r7Fl1sfnUWMdKVVQb44oqfIj2g3CTEmQ=="],
"@zag-js/listbox": ["@zag-js/listbox@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/collection": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-visible": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-LcTIr4I9eN4MR1nSRfQfseWgj4ybOXXAY2o5dBpEBL67dnCSX3swNb/4LQO+ebj077BViQb66pBb1KSoeHGkEQ=="],
"@zag-js/live-region": ["@zag-js/live-region@1.31.1", "", {}, "sha512-RBx8jk1dgvkEUuFs77SBZn0WwvEkeZgVawVu6XUAy4ENfhP0D/qkvwNk+Els8InKmr1gWKajD7sh+g8M40Ex6A=="],
"@zag-js/marquee": ["@zag-js/marquee@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Rt7+zy7CDOxXm0PqaTcmuWxcrZOPOpZY4T6IxOZk4ZcOXJQ2v7CkF3EK0pdI9PyI6Zpk/YIwQkENjidT55db0A=="],
"@zag-js/menu": ["@zag-js/menu@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/rect-utils": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-eJPRM8tlauRTsAoJXchDBzMzL2RhXYSHmHak2IJCDMApCV51p0MqGYP8Er3DbMSQTPUFuTq779uUIarDqW+zmA=="],
"@zag-js/navigation-menu": ["@zag-js/navigation-menu@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-xS4aynqmB9NYicPbEW8lPPakAfDfSgIDL1pRVSD6f1+VXkHD6LgNn6jUNDNbFt65mGhLpA2IczbvLCxv0g/ISQ=="],
"@zag-js/number-input": ["@zag-js/number-input@1.31.1", "", { "dependencies": { "@internationalized/number": "3.6.5", "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-vn+BXEZ2/g2CMIFFyjjye/SbCeW3I/rlszL8EyBmhMcuA1l51OX2WKry6HeQNiU41uMyFg2rb1pb5KVw1gJsCg=="],
"@zag-js/pagination": ["@zag-js/pagination@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-icW6FNzIKNz7iXU+prlQWpMFJedDrhmCKzzI39SY+dv5g1Gnrlc0b44PxvNl5PWFLSkB5KBT/R1WCqd8Kh4cCA=="],
"@zag-js/password-input": ["@zag-js/password-input@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-AivOeNO14a39xhxVMB2TVmIjmQ89OwVz0+2IjX3JjLS2Pmia+gg9xnVd2kBIcKfnqUN4MBnzmk7t46YWJMQVVQ=="],
"@zag-js/pin-input": ["@zag-js/pin-input@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-k3ESoX5ve5sbWBLTCPYAzgLjRU7mVNEUiqAOhRgazOcBGV5wjGh398zWb1jr0FMxPnoAMrXDN/CQwJTmJcMKrg=="],
"@zag-js/popover": ["@zag-js/popover@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/aria-hidden": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-trap": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/remove-scroll": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-uCFJP3DFBkEBAre6lgGLw2xWS2ZIuT/DLeajIXb+8BmC9KCF0wY4c9qojx9F3rGMJQxcGl+WUoXENkOvkTaVhQ=="],
"@zag-js/popper": ["@zag-js/popper@1.31.1", "", { "dependencies": { "@floating-ui/dom": "1.7.4", "@zag-js/dom-query": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-wLXcEqzn9MK1rGbsgnDH26o5ZWqR4oeb6ZepKKy0gcuJl/1S5/dr1VBvxJNMZlf9d6etvYklG5LRnIVkXCbrjA=="],
"@zag-js/presence": ["@zag-js/presence@1.31.1", "", { "dependencies": { "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1" } }, "sha512-tv+WsBnA0abIlDuEfZMh0lRPF4cMs6kWJosNkGBwzeXnGds+KXjzpL2KDtwDgbJgN3sI0xHPMYjRy2v3ZamcDA=="],
"@zag-js/progress": ["@zag-js/progress@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-f9lIDHCRcFAG14LVEKOAPTdqPzphwIIraC6fTr9AwmNlYI6/qFDkz3jOlYVSyk5VsJAIFM/777x/CdqjliiOqg=="],
"@zag-js/qr-code": ["@zag-js/qr-code@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1", "proxy-memoize": "3.0.1", "uqr": "0.1.2" } }, "sha512-Rxh+HF12SgUp5rvTelp1qyLK3xkn37h2fT/L4eBQ0f8OUEo8wfowEbs36+1i61d6UuH7PJt4q/07eIf6vNVevA=="],
"@zag-js/radio-group": ["@zag-js/radio-group@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-visible": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-OfKIdEtSG0EuHM+cFVqcR+04yzZmcDRgG3j0QhoJsyS1my63ZHbwC2HNAtfPFh4U4sJx9yUexwSzPGZ6pOzIdw=="],
"@zag-js/rating-group": ["@zag-js/rating-group@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-BkQUglKm4a+KXYPACYvIvBJSuEyzV0YQqjjiucwJ5UiOlK72C66VBvyGN+DqJRDnkU1K5azt6E1Ja5ANk3fgsg=="],
"@zag-js/rect-utils": ["@zag-js/rect-utils@1.31.1", "", {}, "sha512-lBFheAnz8+3aGDFjqlkw0Iew/F03lFjiIf26hkkcFSZu0ltNZUMG/X3XLHUnHxdfbdBguc8ons6mr2MkVvisng=="],
"@zag-js/remove-scroll": ["@zag-js/remove-scroll@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-gVVJuFKaCjo652RmajYmkjXKgjJWLQ5ZhZLTaLUKWM1mAarvlqnLui8jrHEHLxqpfsjQylfdhJKkWmyF8NAgTA=="],
"@zag-js/scroll-area": ["@zag-js/scroll-area@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-GBXd1K3U0AHwWlJaqAMKQMZyeoxuBO6XYrVgdvzgiftQbJrZs5fuYOFyDvPLDWHTLYxaHso44/f+9EmAUAiytw=="],
"@zag-js/scroll-snap": ["@zag-js/scroll-snap@1.31.1", "", { "dependencies": { "@zag-js/dom-query": "1.31.1" } }, "sha512-YWsfhcQqiffu2X9HuB0fMnEQAu6rEOfGcvQYinvB6pjWPOvIJGxGMi/dYyy21XQDNJ9K1IcWRIo/yuaajoJyQQ=="],
"@zag-js/select": ["@zag-js/select@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/collection": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-vKWb8BiRY83Y3HkDNnimf6cr1yvzJh1HwZlzXFz0y47zEvlikQaf+r96obR78RgTtMjNTTV15tTXdc1/WFoYkw=="],
"@zag-js/signature-pad": ["@zag-js/signature-pad@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1", "perfect-freehand": "^1.2.2" } }, "sha512-bz3WtLuIZoLrJDKcdS7fPAdD/Qi9wKiKACl5cu+ftv9zg8w+qqYNLtjH9HxeUFbCtQRKqcdXjO/UZ8iL07hgsQ=="],
"@zag-js/slider": ["@zag-js/slider@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-FILbLTMd3BnyclZ28+ippfyqzYPGK60qZapxtTERmWDC75Okf8AFnTCQf84Y8jRmBKCS1yhjF+IOtkFAENeB6w=="],
"@zag-js/splitter": ["@zag-js/splitter@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-7SGBT2/xKsOzeSQEg+Otn1XV3RHrAz3jTySjBRKoEmdxubhfREqbKotbGVG65aTve11fQnmJ3Oyt3GJOeraxLA=="],
"@zag-js/steps": ["@zag-js/steps@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-KsBH38V3tH9/q8CDgx4sUSXLYwFdcp1crZy8hTIcN0RUiZ55PmqYKkN2znzBjTbaCW9yhP8kXsbuo2s8OIU5lQ=="],
"@zag-js/store": ["@zag-js/store@1.31.1", "", { "dependencies": { "proxy-compare": "3.0.1" } }, "sha512-d5ZTRciTuXOGQ3nML15kQLaTiR1wJPxT1Fu1nN659X6Rl8DPtubYaRCZ3RCk9Kyiyg2z5HxeVqDswaDvGbM9Rg=="],
"@zag-js/svelte": ["@zag-js/svelte@1.31.1", "", { "dependencies": { "@zag-js/core": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" }, "peerDependencies": { "svelte": ">=5" } }, "sha512-yj9ZzXHk4YV+zcLHypfqcA8BkP5043V58AZ3Hu3WMVczF4/GcmbHn4/nWNK+6j7M+BLCNEAx460SOZovSNemvw=="],
"@zag-js/switch": ["@zag-js/switch@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-visible": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Jii3OSqSa9sQux+hvSRvp9dirzUF09+PAjrLjCQs+BT08EZ0XqeGvVzM0Wqf9LFy07HdLZntai3IUaXLF6byBw=="],
"@zag-js/tabs": ["@zag-js/tabs@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-QBq4ngpBNMNEI7Wuaq8llwHOqgcVbNHHEDC5zHg60Bf7MY5ltP8wSq6Kldu0zZRVwrLzanYoMELDUyf9H0vtnw=="],
"@zag-js/tags-input": ["@zag-js/tags-input@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/auto-resize": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/interact-outside": "1.31.1", "@zag-js/live-region": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-V4lJe/aMIs7WVoXYfszU6E3iARLLRQFMiycu76/slb8NWJiLrkSIaMQ4FAe2pqkodgCWXA83tuaeAZRq7ouTFg=="],
"@zag-js/timer": ["@zag-js/timer@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-bXfeSbneWGOBKlD5dYq06T8CSY9Ky+qb1yIfJAFsRF4n34mpUYRdtfwpNQYyddGpkLD7oH4VibajeZXB7HaL0g=="],
"@zag-js/toast": ["@zag-js/toast@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-MueHEei9ol3H6tWBruLxF7yEUpV3vsJ8brTQVRRtPr/6pqBs5kGzfL4YskhQ2tiwO6egay8YrkbaS3xJfpKt4w=="],
"@zag-js/toggle": ["@zag-js/toggle@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-HbFBuGfdyYkNvOp3cEB8Civ4E92finT4u3e4LKysB4/LboqKA0cJvFhSnHyThbROONTx06W/3CxwoSFR4o8IhA=="],
"@zag-js/toggle-group": ["@zag-js/toggle-group@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Mojc7mex01/gvwXfrUIIThzT7HOktZoMge9rrb6+P7rQX7ulyNXYPjQrW2tay+t54GOJ3xODo9dU7PpRzXeHbw=="],
"@zag-js/tooltip": ["@zag-js/tooltip@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-visible": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-pWEU5XhEPpnyl2VLrGJlyjj7+p+X0UX3Fld+WGhc/hCaWiuW2ZzD/ewDRhSOZu4/TzAO3axrPqG1YhW4fhogKQ=="],
"@zag-js/tour": ["@zag-js/tour@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dismissable": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/focus-trap": "1.31.1", "@zag-js/interact-outside": "1.31.1", "@zag-js/popper": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-ZmcAevXxoENHmHG0xwdIt1oCLe2/DW1CEBFPr7YuGKc+FU3QbBVZMzcBHrJCe0nkKXhUKzHOHM78bOHD/gM76w=="],
"@zag-js/tree-view": ["@zag-js/tree-view@1.31.1", "", { "dependencies": { "@zag-js/anatomy": "1.31.1", "@zag-js/collection": "1.31.1", "@zag-js/core": "1.31.1", "@zag-js/dom-query": "1.31.1", "@zag-js/types": "1.31.1", "@zag-js/utils": "1.31.1" } }, "sha512-Q+VSQz7X1XR8gT7ICWXlQOJIvzTWw/9BlF7B073UpEgAKRFlD11FmERka5y/BYqj8uE0vazcbSEA3Vc2dgCMJA=="],
"@zag-js/types": ["@zag-js/types@1.31.1", "", { "dependencies": { "csstype": "3.2.3" } }, "sha512-mKw5DoeBjFykfUHv3ifCRjcogFTqp0aCCsmqQMfnf+J/mg2aXpAx76AXT1PYXAVVhxdP6qGXNd0mOQZDVrIlSQ=="],
"@zag-js/utils": ["@zag-js/utils@1.31.1", "", {}, "sha512-KLm0pmOtf4ydALbaVLboL7W98TDVxwVVLvSuvtRgV53XTjlsVopTRA5/Xmzq2NhWujDZAXv7bRV603NDgDcjSw=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
@@ -748,7 +909,7 @@
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.8.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q=="],
"better-auth": ["better-auth@1.3.27", "", { "dependencies": { "@better-auth/core": "1.3.27", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.19", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" } }, "sha512-SwiGAJ7yU6dBhNg0NdV1h5M8T5sa7/AszZVc4vBfMDrLLmvUfbt9JoJ0uRUJUEdKRAAxTyl9yA+F3+GhtAD80w=="], "better-auth": ["better-auth@1.3.34", "", { "dependencies": { "@better-auth/core": "1.3.34", "@better-auth/telemetry": "1.3.34", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.19", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" } }, "sha512-LWA52SlvnUBJRbN8VLSTLILPomZY3zZAiLxVJCeSQ5uVmaIKkMBhERitkfJcXB9RJcfl4uP+3EqKkb6hX1/uiw=="],
"better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="], "better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="],
@@ -814,7 +975,7 @@
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"convex": ["convex@1.29.0", "", { "dependencies": { "esbuild": "0.25.4", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-uoIPXRKIp2eLCkkR9WJ2vc9NtgQtx8Pml59WPUahwbrd5EuW2WLI/cf2E7XrUzOSifdQC3kJZepisk4wJNTJaA=="], "convex": ["convex@1.31.0", "", { "dependencies": { "esbuild": "0.25.4", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-ht3dtpWQmxX62T8PT3p/5PDlRzSW5p2IDTP4exKjQ5dqmvhtn1wLFakJAX4CCeu1s0Ch0dKY5g2dk/wETTRAOw=="],
"convex-helpers": ["convex-helpers@0.1.104", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.24.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5", "zod": "^3.22.4 || ^4.0.15" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig=="], "convex-helpers": ["convex-helpers@0.1.104", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "convex": "^1.24.0", "hono": "^4.0.5", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "typescript": "^5.5", "zod": "^3.22.4 || ^4.0.15" }, "optionalPeers": ["@standard-schema/spec", "hono", "react", "typescript", "zod"], "bin": { "convex-helpers": "bin.cjs" } }, "sha512-7CYvx7T3K6n+McDTK4ZQaQNNGBzq5aWezpjzsKbOxPXx7oNcTP9wrpef3JxeXWFzkByJv5hRCjseh9B7eNJ7Ig=="],
@@ -836,6 +997,8 @@
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"daisyui": ["daisyui@5.3.10", "", {}, "sha512-vmjyPmm0hvFhA95KB6uiGmWakziB2pBv6CUcs5Ka/3iMBMn9S+C3SZYx9G9l2JrgTZ1EFn61F/HrPcwaUm2kLQ=="], "daisyui": ["daisyui@5.3.10", "", {}, "sha512-vmjyPmm0hvFhA95KB6uiGmWakziB2pBv6CUcs5Ka/3iMBMn9S+C3SZYx9G9l2JrgTZ1EFn61F/HrPcwaUm2kLQ=="],
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
@@ -914,7 +1077,7 @@
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
"eslint-plugin-svelte": ["eslint-plugin-svelte@3.13.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg=="], "eslint-plugin-svelte": ["eslint-plugin-svelte@3.13.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-Ng+kV/qGS8P/isbNYVE3sJORtubB+yLEcYICMkUWNaDTb0SwZni/JhAYXh/Dz/q2eThUwWY0VMPZ//KYD1n3eQ=="],
"eslint-plugin-turbo": ["eslint-plugin-turbo@2.6.0", "", { "dependencies": { "dotenv": "16.0.3" }, "peerDependencies": { "eslint": ">6.6.0", "turbo": ">2.0.0" } }, "sha512-04TohZhq6YQVXBZVRvrn8ZTj1sUQYZmjUWsfwgFAlaM5Kbk5Fdh5mLBKfhGGzekB55E+Ut9qNzAGh+JW4rjiuA=="], "eslint-plugin-turbo": ["eslint-plugin-turbo@2.6.0", "", { "dependencies": { "dotenv": "16.0.3" }, "peerDependencies": { "eslint": ">6.6.0", "turbo": ">2.0.0" } }, "sha512-04TohZhq6YQVXBZVRvrn8ZTj1sUQYZmjUWsfwgFAlaM5Kbk5Fdh5mLBKfhGGzekB55E+Ut9qNzAGh+JW4rjiuA=="],
@@ -1010,8 +1173,6 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
@@ -1280,6 +1441,8 @@
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"perfect-freehand": ["perfect-freehand@1.2.2", "", {}, "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ=="],
"performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="], "performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
@@ -1304,11 +1467,11 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="],
"prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="], "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="],
"prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.1", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ=="], "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="],
"printj": ["printj@1.1.2", "", { "bin": { "printj": "./bin/printj.njs" } }, "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="], "printj": ["printj@1.1.2", "", { "bin": { "printj": "./bin/printj.njs" } }, "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="],
@@ -1316,6 +1479,10 @@
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"proxy-compare": ["proxy-compare@3.0.1", "", {}, "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q=="],
"proxy-memoize": ["proxy-memoize@3.0.1", "", { "dependencies": { "proxy-compare": "^3.0.0" } }, "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
@@ -1444,11 +1611,11 @@
"svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="],
"svelte-dnd-action": ["svelte-dnd-action@0.9.67", "", { "peerDependencies": { "svelte": ">=3.23.0 || ^5.0.0-next.0" } }, "sha512-yEJQZ9SFy3O4mnOdtjwWyotRsWRktNf4W8k67zgiLiMtMNQnwCyJHBjkGMgZMDh8EGZ4gr88l+GebBWoHDwo+g=="], "svelte-dnd-action": ["svelte-dnd-action@0.9.68", "", { "peerDependencies": { "svelte": ">=3.23.0 || ^5.0.0-next.0" } }, "sha512-maFNIHwimGYbvIG8uOHsU9T/4+VKBIaAaFEGWYFIyo4f8qwUs0BIqwvBfHkaN+MXt8MBB9rByPTvF7fRx0eIjw=="],
"svelte-eslint-parser": ["svelte-eslint-parser@1.4.0", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA=="], "svelte-eslint-parser": ["svelte-eslint-parser@1.4.0", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA=="],
"svelte-sonner": ["svelte-sonner@1.0.5", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-9dpGPFqKb/QWudYqGnEz93vuY+NgCEvyNvxoCLMVGw6sDN/3oVeKV1xiEirW2E1N3vJEyj5imSBNOGltQHA7mg=="], "svelte-sonner": ["svelte-sonner@1.0.7", "", { "dependencies": { "runed": "^0.28.0" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A=="],
"svg-pathdata": ["svg-pathdata@6.0.3", "", {}, "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw=="], "svg-pathdata": ["svg-pathdata@6.0.3", "", {}, "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw=="],
@@ -1460,6 +1627,8 @@
"text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="], "text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="],
"theme-change": ["theme-change@2.5.0", "", {}, "sha512-B/UdsgdHAGhSKHTAQnxg/etN0RaMDpehuJmZIjLMDVJ6DGIliRHGD6pODi1CXLQAN9GV0GSyB3G6yCuK05PkPQ=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
@@ -1504,7 +1673,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.46.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.3", "@typescript-eslint/parser": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA=="], "typescript-eslint": ["typescript-eslint@8.49.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.49.0", "@typescript-eslint/parser": "8.49.0", "@typescript-eslint/typescript-estree": "8.49.0", "@typescript-eslint/utils": "8.49.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg=="],
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
@@ -1516,6 +1685,8 @@
"update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
"uqr": ["uqr@0.1.2", "", {}, "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
@@ -1602,8 +1773,6 @@
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
@@ -1654,8 +1823,6 @@
"@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
"archiver-utils/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "archiver-utils/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],

View File

@@ -8,9 +8,9 @@
"packages/*" "packages/*"
], ],
"catalog": { "catalog": {
"convex": "^1.28.2", "convex": "^1.31.0",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"better-auth": "1.3.27", "better-auth": "1.3.34",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"@eslint/js": "^9.39.1" "@eslint/js": "^9.39.1"
} }
@@ -29,23 +29,24 @@
}, },
"devDependencies": { "devDependencies": {
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-svelte": "^3.13.0", "eslint-plugin-svelte": "^3.13.1",
"globals": "^16.5.0", "globals": "^16.5.0",
"jiti": "^2.6.1", "jiti": "^2.6.1",
"prettier": "^3.6.2", "prettier": "^3.7.4",
"prettier-plugin-tailwindcss": "^0.7.1", "prettier-plugin-tailwindcss": "^0.7.2",
"svelte-dnd-action": "^0.9.67", "svelte-dnd-action": "^0.9.68",
"turbo": "^2.6.3", "turbo": "^2.6.3",
"typescript-eslint": "^8.46.3" "typescript-eslint": "^8.49.0"
}, },
"dependencies": { "dependencies": {
"@convex-dev/better-auth": "^0.9.7",
"@tanstack/svelte-form": "^1.23.8", "@tanstack/svelte-form": "^1.23.8",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"convex": "^1.31.0",
"lucide-svelte": "^0.552.0", "lucide-svelte": "^0.552.0",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"svelte-chartjs": "^3.1.5", "svelte-chartjs": "^3.1.5",
"svelte-sonner": "^1.0.5", "svelte-sonner": "^1.0.7"
"@convex-dev/better-auth": "^0.9.7"
}, },
"packageManager": "bun@1.3.0" "packageManager": "bun@1.3.4"
} }

View File

@@ -352,10 +352,6 @@ export declare const components: {
lastRequest?: null | number; lastRequest?: null | number;
}; };
model: "rateLimit"; model: "rateLimit";
}
| {
data: { count: number; key: string; lastRequest: number };
model: "ratelimit";
}; };
onCreateHandle?: string; onCreateHandle?: string;
select?: Array<string>; select?: Array<string>;
@@ -733,32 +729,6 @@ export declare const components: {
| Array<number> | Array<number>
| null; | null;
}>; }>;
}
| {
model: "ratelimit";
where?: Array<{
connector?: "AND" | "OR";
field: "key" | "count" | "lastRequest" | "_id";
operator?:
| "lt"
| "lte"
| "gt"
| "gte"
| "eq"
| "in"
| "not_in"
| "ne"
| "contains"
| "starts_with"
| "ends_with";
value:
| string
| number
| boolean
| Array<string>
| Array<number>
| null;
}>;
}; };
onDeleteHandle?: string; onDeleteHandle?: string;
paginationOpts: { paginationOpts: {
@@ -1143,32 +1113,6 @@ export declare const components: {
| Array<number> | Array<number>
| null; | null;
}>; }>;
}
| {
model: "ratelimit";
where?: Array<{
connector?: "AND" | "OR";
field: "key" | "count" | "lastRequest" | "_id";
operator?:
| "lt"
| "lte"
| "gt"
| "gte"
| "eq"
| "in"
| "not_in"
| "ne"
| "contains"
| "starts_with"
| "ends_with";
value:
| string
| number
| boolean
| Array<string>
| Array<number>
| null;
}>;
}; };
onDeleteHandle?: string; onDeleteHandle?: string;
}, },
@@ -1190,8 +1134,7 @@ export declare const components: {
| "oauthAccessToken" | "oauthAccessToken"
| "oauthConsent" | "oauthConsent"
| "jwks" | "jwks"
| "rateLimit" | "rateLimit";
| "ratelimit";
offset?: number; offset?: number;
paginationOpts: { paginationOpts: {
cursor: string | null; cursor: string | null;
@@ -1243,8 +1186,7 @@ export declare const components: {
| "oauthAccessToken" | "oauthAccessToken"
| "oauthConsent" | "oauthConsent"
| "jwks" | "jwks"
| "rateLimit" | "rateLimit";
| "ratelimit";
select?: Array<string>; select?: Array<string>;
where?: Array<{ where?: Array<{
connector?: "AND" | "OR"; connector?: "AND" | "OR";
@@ -1753,33 +1695,6 @@ export declare const components: {
| Array<number> | Array<number>
| null; | null;
}>; }>;
}
| {
model: "ratelimit";
update: { count?: number; key?: string; lastRequest?: number };
where?: Array<{
connector?: "AND" | "OR";
field: "key" | "count" | "lastRequest" | "_id";
operator?:
| "lt"
| "lte"
| "gt"
| "gte"
| "eq"
| "in"
| "not_in"
| "ne"
| "contains"
| "starts_with"
| "ends_with";
value:
| string
| number
| boolean
| Array<string>
| Array<number>
| null;
}>;
}; };
onUpdateHandle?: string; onUpdateHandle?: string;
paginationOpts: { paginationOpts: {
@@ -2268,33 +2183,6 @@ export declare const components: {
| Array<number> | Array<number>
| null; | null;
}>; }>;
}
| {
model: "ratelimit";
update: { count?: number; key?: string; lastRequest?: number };
where?: Array<{
connector?: "AND" | "OR";
field: "key" | "count" | "lastRequest" | "_id";
operator?:
| "lt"
| "lte"
| "gt"
| "gte"
| "eq"
| "in"
| "not_in"
| "ne"
| "contains"
| "starts_with"
| "ends_with";
value:
| string
| number
| boolean
| Array<string>
| Array<number>
| null;
}>;
}; };
onUpdateHandle?: string; onUpdateHandle?: string;
}, },

View File

@@ -38,7 +38,7 @@ export type Doc<TableName extends TableNames> = DocumentByName<
* Convex documents are uniquely identified by their `Id`, which is accessible * Convex documents are uniquely identified by their `Id`, which is accessible
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
* *
* Documents can be loaded using `db.get(id)` in query and mutation functions. * Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
* *
* IDs are just strings at runtime, but this type can be used to distinguish them from other * IDs are just strings at runtime, but this type can be used to distinguish them from other
* strings when type checking. * strings when type checking.