Fix page with lint errors #15

Merged
killer-cf merged 5 commits from fix-page-with-lint-errors into master 2025-11-12 15:00:40 +00:00
10 changed files with 1758 additions and 1714 deletions

19
.cursor/mcp.json Normal file
View File

@@ -0,0 +1,19 @@
{
"mcpServers": {
"svelte": {
"url": "https://mcp.svelte.dev/mcp"
},
"context7": {
"url": "https://mcp.context7.com/mcp"
},
"convex": {
"command": "npx",
"args": [
"-y",
"convex@latest",
"mcp",
"start"
]
}
}
}

View File

@@ -0,0 +1,27 @@
---
alwaysApply: true
---
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -1,6 +1,7 @@
---
description: Guidelines for TypeScript usage, including type safety rules and Convex query typing
globs: **/*.ts,**/*.tsx,**/*.svelte
globs: **/*.ts,**/*.svelte
alwaysApply: false
---
# TypeScript Guidelines
@@ -8,6 +9,7 @@ globs: **/*.ts,**/*.tsx,**/*.svelte
## Type Safety Rules
### Avoid `any` Type
- **NEVER** use the `any` type in production code
- The only exception is in test files (files matching `*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`)
- Instead of `any`, use:
@@ -20,44 +22,48 @@ globs: **/*.ts,**/*.tsx,**/*.svelte
### Examples
**❌ Bad:**
```typescript
function processData(data: any) {
return data.value;
return data.value;
}
```
**✅ Good:**
```typescript
function processData(data: { value: string }) {
return data.value;
return data.value;
}
// Or with generics
function processData<T extends { value: unknown }>(data: T) {
return data.value;
return data.value;
}
// Or with unknown and type guards
function processData(data: unknown) {
if (typeof data === 'object' && data !== null && 'value' in data) {
return (data as { value: string }).value;
}
throw new Error('Invalid data');
if (typeof data === 'object' && data !== null && 'value' in data) {
return (data as { value: string }).value;
}
throw new Error('Invalid data');
}
```
**✅ Exception (tests only):**
```typescript
// test.ts or *.spec.ts
it('should handle any input', () => {
const input: any = getMockData();
expect(process(input)).toBeDefined();
const input: any = getMockData();
expect(process(input)).toBeDefined();
});
```
## Convex Query Typing
### Frontend Query Usage
- **DO NOT** create manual type definitions for Convex query results in the frontend
- Convex queries already return properly typed results based on their `returns` validator
- The TypeScript types are automatically inferred from the query's return validator
@@ -66,17 +72,19 @@ it('should handle any input', () => {
### Examples
**❌ Bad:**
```typescript
// Don't manually type the result
type UserListResult = Array<{
_id: Id<"users">;
name: string;
_id: Id<'users'>;
name: string;
}>;
const users: UserListResult = useQuery(api.users.list);
```
**✅ Good:**
```typescript
// Let TypeScript infer the type from the query
const users = useQuery(api.users.list);
@@ -84,24 +92,26 @@ const users = useQuery(api.users.list);
// You can still use it with type inference
if (users !== undefined) {
users.forEach(user => {
// TypeScript knows user._id is Id<"users"> and user.name is string
console.log(user.name);
});
users.forEach((user) => {
// TypeScript knows user._id is Id<"users"> and user.name is string
console.log(user.name);
});
}
```
**✅ Good (with explicit type if needed for clarity):**
```typescript
// Only if you need to export or explicitly annotate for documentation
import type { FunctionReturnType } from "convex/server";
import type { api } from "./convex/_generated/api";
import type { FunctionReturnType } from 'convex/server';
import type { api } from './convex/_generated/api';
type UserListResult = FunctionReturnType<typeof api.users.list>;
const users = useQuery(api.users.list);
```
### Best Practices
- Trust Convex's type inference - it's based on your schema and validators
- If you need type annotations, use `FunctionReturnType` from Convex's type utilities
- Only create manual types if you're doing complex transformations that need intermediate types

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "sgse-app",

View File

@@ -1,47 +1,47 @@
{
"name": "sgse-app",
"private": true,
"type": "module",
"workspaces": {
"packages": [
"apps/*",
"packages/*"
],
"catalog": {
"convex": "^1.28.0",
"typescript": "^5.9.2",
"better-auth": "1.3.27",
"eslint": "^9.39.1",
"@eslint/js": "^9.39.1"
}
},
"scripts": {
"check": "biome check --write .",
"dev": "turbo dev",
"build": "turbo build",
"check-types": "turbo check-types",
"dev:native": "turbo -F native dev",
"dev:web": "turbo -F web dev",
"dev:server": "turbo -F @sgse-app/backend dev",
"dev:setup": "turbo -F @sgse-app/backend dev:setup"
},
"devDependencies": {
"eslint": "^9.39.1",
"globals": "^16.5.0",
"jiti": "^2.6.1",
"turbo": "^2.5.8",
"typescript-eslint": "^8.46.3",
"prettier": "^3.6.2",
"eslint-plugin-svelte": "^3.13.0",
"prettier-plugin-tailwindcss": "^0.7.1"
},
"dependencies": {
"@tanstack/svelte-form": "^1.23.8",
"chart.js": "^4.5.1",
"lucide-svelte": "^0.552.0",
"prettier-plugin-svelte": "^3.4.0",
"svelte-chartjs": "^3.1.5",
"svelte-sonner": "^1.0.5"
},
"packageManager": "bun@1.3.0"
"name": "sgse-app",
"private": true,
"type": "module",
"workspaces": {
"packages": [
"apps/*",
"packages/*"
],
"catalog": {
"convex": "^1.28.0",
"typescript": "^5.9.2",
"better-auth": "1.3.27",
"eslint": "^9.39.1",
"@eslint/js": "^9.39.1"
}
},
"scripts": {
"check": "biome check --write .",
"dev": "turbo dev",
"build": "turbo build",
"check-types": "turbo check-types",
"dev:native": "turbo -F native dev",
"dev:web": "turbo -F web dev",
"dev:server": "turbo -F @sgse-app/backend dev",
"dev:setup": "turbo -F @sgse-app/backend dev:setup"
},
"devDependencies": {
"eslint": "^9.39.1",
"eslint-plugin-svelte": "^3.13.0",
"globals": "^16.5.0",
"jiti": "^2.6.1",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.7.1",
"turbo": "^2.5.8",
"typescript-eslint": "^8.46.3"
},
"dependencies": {
"@tanstack/svelte-form": "^1.23.8",
"chart.js": "^4.5.1",
"lucide-svelte": "^0.552.0",
"prettier-plugin-svelte": "^3.4.0",
"svelte-chartjs": "^3.1.5",
"svelte-sonner": "^1.0.5"
},
"packageManager": "bun@1.3.0"
}

View File

@@ -8,7 +8,16 @@ import { getCurrentUserFunction } from './auth';
export const CATALOGO_RECURSOS = [
{
recurso: 'funcionarios',
acoes: ['dashboard', 'ver', 'listar', 'criar', 'editar', 'excluir']
acoes: [
'dashboard',
'ver',
'listar',
'criar',
'editar',
'excluir',
'aprovar_ausencias',
'aprovar_ferias'
]
},
{
recurso: 'simbolos',

View File

@@ -1,35 +1,125 @@
import { v } from "convex/values";
import { query } from "./_generated/server";
import { v } from 'convex/values';
import { query, mutation } from './_generated/server';
import type { Id } from './_generated/dataModel';
import { getCurrentUserFunction } from './auth';
/**
* Listar todas as roles
*/
export const listar = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("roles").collect();
},
args: {},
handler: async (ctx) => {
return await ctx.db.query('roles').collect();
}
});
/**
* Buscar role por ID
*/
export const buscarPorId = query({
args: {
roleId: v.id("roles"),
},
returns: v.union(
v.object({
_id: v.id("roles"),
nome: v.string(),
descricao: v.string(),
nivel: v.number(),
setor: v.optional(v.string()),
}),
v.null()
),
handler: async (ctx, args) => {
return await ctx.db.get(args.roleId);
},
args: {
roleId: v.id('roles')
},
returns: v.union(
v.object({
_id: v.id('roles'),
nome: v.string(),
descricao: v.string(),
nivel: v.number(),
setor: v.optional(v.string())
}),
v.null()
),
handler: async (ctx, args) => {
return await ctx.db.get(args.roleId);
}
});
const slugify = (value: string) =>
value
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, '_')
.replace(/^_+|_+$/g, '')
.replace(/_{2,}/g, '_');
export const criar = mutation({
args: {
nome: v.string(),
descricao: v.string(),
nivel: v.number(),
setor: v.optional(v.string()),
copiarDeRoleId: v.optional(v.id('roles'))
},
returns: v.union(
v.object({ sucesso: v.literal(true), roleId: v.id('roles') }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
const usuarioAtual = await getCurrentUserFunction(ctx);
if (!usuarioAtual) {
return { sucesso: false as const, erro: 'nao_autenticado' };
}
const roleAtual = await ctx.db.get(usuarioAtual.roleId);
if (!roleAtual || roleAtual.nivel > 1) {
return { sucesso: false as const, erro: 'sem_permissao' };
}
const nomeNormalizado = slugify(args.nome);
if (!nomeNormalizado) {
return { sucesso: false as const, erro: 'nome_invalido' };
}
const existente = await ctx.db
.query('roles')
.withIndex('by_nome', (q) => q.eq('nome', nomeNormalizado))
.unique();
if (existente) {
return { sucesso: false as const, erro: 'nome_ja_utilizado' };
}
let permissoesParaCopiar: Array<Id<'permissoes'>> = [];
if (args.copiarDeRoleId) {
const roleOrigem = await ctx.db.get(args.copiarDeRoleId);
if (!roleOrigem) {
return { sucesso: false as const, erro: 'role_origem_nao_encontrada' };
}
const permissoesOrigem = await ctx.db
.query('rolePermissoes')
.withIndex('by_role', (q) => q.eq('roleId', args.copiarDeRoleId!))
.collect();
permissoesParaCopiar = permissoesOrigem.map((item) => item.permissaoId);
}
const nivelAjustado = Math.min(Math.max(Math.round(args.nivel), 0), 10);
const setor = args.setor?.trim();
const roleId = await ctx.db.insert('roles', {
nome: nomeNormalizado,
descricao: args.descricao.trim() || args.nome.trim(),
nivel: nivelAjustado,
setor: setor && setor.length > 0 ? setor : undefined,
customizado: true,
criadoPor: usuarioAtual._id,
editavel: true
});
if (permissoesParaCopiar.length > 0) {
for (const permissaoId of permissoesParaCopiar) {
await ctx.db.insert('rolePermissoes', {
roleId,
permissaoId
});
}
}
return { sucesso: true as const, roleId };
}
});

View File

@@ -562,15 +562,12 @@ export const obterPerfil = query({
),
handler: async (ctx) => {
const usuarioAutenticado = await getCurrentUserFunction(ctx);
console.log('Usuario autenticado:', usuarioAutenticado);
if (!usuarioAutenticado) {
return null;
}
const usuarioAtual = usuarioAutenticado;
console.log('✅ Usuário encontrado:', usuarioAtual.nome);
// Buscar fotoPerfil URL se existir
let fotoPerfilUrl = null;
if (usuarioAtual.fotoPerfil) {