feat(checklist): exibe descricao do template e do item no ticket
- Adiciona campo templateDescription ao schema do checklist - Copia descricao do template ao aplicar checklist no ticket - Exibe ambas descricoes na visualizacao do ticket (template em italico) - Adiciona documentacao de desenvolvimento local (docs/LOCAL-DEV.md) - Corrige prisma-client.mjs para usar PostgreSQL em vez de SQLite 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6430d33c7c
commit
6e8a6fe890
8 changed files with 212 additions and 71 deletions
|
|
@ -19,8 +19,9 @@ REPORTS_CRON_SECRET=reports-cron-secret
|
||||||
# Diretório para arquivamento local de tickets (JSONL/backup)
|
# Diretório para arquivamento local de tickets (JSONL/backup)
|
||||||
ARCHIVE_DIR=./archives
|
ARCHIVE_DIR=./archives
|
||||||
|
|
||||||
# PostgreSQL database
|
# PostgreSQL database (versao 18)
|
||||||
# Para desenvolvimento local, use Docker: docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
|
# Para desenvolvimento local, use Docker:
|
||||||
|
# docker run -d --name postgres-chamados -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
|
||||||
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
|
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
|
||||||
|
|
||||||
# SMTP Configuration (production values in docs/SMTP.md)
|
# SMTP Configuration (production values in docs/SMTP.md)
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,7 @@ export default defineSchema({
|
||||||
required: v.optional(v.boolean()),
|
required: v.optional(v.boolean()),
|
||||||
templateId: v.optional(v.id("ticketChecklistTemplates")),
|
templateId: v.optional(v.id("ticketChecklistTemplates")),
|
||||||
templateItemId: v.optional(v.string()),
|
templateItemId: v.optional(v.string()),
|
||||||
|
templateDescription: v.optional(v.string()), // Descricao do template (copiada ao aplicar)
|
||||||
createdAt: v.optional(v.number()),
|
createdAt: v.optional(v.number()),
|
||||||
createdBy: v.optional(v.id("users")),
|
createdBy: v.optional(v.id("users")),
|
||||||
doneAt: v.optional(v.number()),
|
doneAt: v.optional(v.number()),
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export type TicketChecklistItem = {
|
||||||
required?: boolean
|
required?: boolean
|
||||||
templateId?: Id<"ticketChecklistTemplates">
|
templateId?: Id<"ticketChecklistTemplates">
|
||||||
templateItemId?: string
|
templateItemId?: string
|
||||||
|
templateDescription?: string // Descricao do template (copiada ao aplicar)
|
||||||
createdAt?: number
|
createdAt?: number
|
||||||
createdBy?: Id<"users">
|
createdBy?: Id<"users">
|
||||||
doneAt?: number
|
doneAt?: number
|
||||||
|
|
@ -30,6 +31,7 @@ export type TicketChecklistTemplateItem = {
|
||||||
|
|
||||||
export type TicketChecklistTemplateLike = {
|
export type TicketChecklistTemplateLike = {
|
||||||
_id: Id<"ticketChecklistTemplates">
|
_id: Id<"ticketChecklistTemplates">
|
||||||
|
description?: string
|
||||||
items: TicketChecklistTemplateItem[]
|
items: TicketChecklistTemplateItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +81,7 @@ export function applyChecklistTemplateToItems(
|
||||||
required: typeof tplItem.required === "boolean" ? tplItem.required : true,
|
required: typeof tplItem.required === "boolean" ? tplItem.required : true,
|
||||||
templateId: template._id,
|
templateId: template._id,
|
||||||
templateItemId,
|
templateItemId,
|
||||||
|
templateDescription: template.description,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
createdBy: options.actorId,
|
createdBy: options.actorId,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
166
docs/LOCAL-DEV.md
Normal file
166
docs/LOCAL-DEV.md
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
# Desenvolvimento Local
|
||||||
|
|
||||||
|
Guia para rodar o projeto localmente conectando aos dados de producao.
|
||||||
|
|
||||||
|
## Pre-requisitos
|
||||||
|
|
||||||
|
- [Bun](https://bun.sh/) 1.3+
|
||||||
|
- [Docker](https://www.docker.com/) (para PostgreSQL)
|
||||||
|
- Node.js 20+ (opcional, usado pelo tsx)
|
||||||
|
|
||||||
|
## 1. Subir o PostgreSQL
|
||||||
|
|
||||||
|
O sistema usa PostgreSQL para autenticacao (Better Auth). Os dados de tickets ficam no Convex.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name postgres-chamados \
|
||||||
|
-p 5432:5432 \
|
||||||
|
-e POSTGRES_PASSWORD=dev \
|
||||||
|
-e POSTGRES_DB=sistema_chamados \
|
||||||
|
postgres:18
|
||||||
|
```
|
||||||
|
|
||||||
|
Para verificar se esta rodando:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps | grep postgres-chamados
|
||||||
|
```
|
||||||
|
|
||||||
|
Para parar/iniciar posteriormente:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stop postgres-chamados
|
||||||
|
docker start postgres-chamados
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Configurar variaveis de ambiente
|
||||||
|
|
||||||
|
O arquivo `.env.local` ja vem configurado para desenvolvimento local apontando para o Convex de producao:
|
||||||
|
|
||||||
|
```env
|
||||||
|
NODE_ENV=development
|
||||||
|
|
||||||
|
# URLs locais
|
||||||
|
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||||
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# Convex de producao (dados reais)
|
||||||
|
NEXT_PUBLIC_CONVEX_URL=https://convex.esdrasrenan.com.br
|
||||||
|
CONVEX_INTERNAL_URL=https://convex.esdrasrenan.com.br
|
||||||
|
|
||||||
|
# PostgreSQL local (apenas autenticacao)
|
||||||
|
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Instalar dependencias
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Gerar cliente Prisma e aplicar schema
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run prisma:generate
|
||||||
|
bunx prisma db push
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Criar usuarios de desenvolvimento
|
||||||
|
|
||||||
|
O seed cria usuarios locais para autenticacao:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DATABASE_URL="postgresql://postgres:dev@localhost:5432/sistema_chamados" bun tsx scripts/seed-auth.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Credenciais padrao
|
||||||
|
|
||||||
|
| Usuario | Email | Senha | Role |
|
||||||
|
|---------------|----------------------|------------|-------|
|
||||||
|
| Administrador | `admin@sistema.dev` | `admin123` | admin |
|
||||||
|
| Agentes | `*@rever.com.br` | `agent123` | agent |
|
||||||
|
|
||||||
|
## 6. Iniciar o servidor de desenvolvimento
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev:bun
|
||||||
|
```
|
||||||
|
|
||||||
|
Acesse: http://localhost:3000
|
||||||
|
|
||||||
|
## Arquitetura Local vs Producao
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ DESENVOLVIMENTO LOCAL │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ localhost:3000 (Next.js) │
|
||||||
|
│ │ │
|
||||||
|
│ ├──► PostgreSQL local (porta 5432) │
|
||||||
|
│ │ └── Autenticacao (Better Auth) │
|
||||||
|
│ │ └── Usuarios, sessoes, contas │
|
||||||
|
│ │ │
|
||||||
|
│ └──► convex.esdrasrenan.com.br (remoto) │
|
||||||
|
│ └── Dados de producao │
|
||||||
|
│ └── Tickets, empresas, filas, etc. │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comandos uteis
|
||||||
|
|
||||||
|
| Comando | Descricao |
|
||||||
|
|---------|-----------|
|
||||||
|
| `bun run dev:bun` | Inicia servidor de desenvolvimento com Turbopack |
|
||||||
|
| `bun run build:bun` | Build de producao |
|
||||||
|
| `bun run lint` | Verificar codigo com ESLint |
|
||||||
|
| `bun test` | Rodar testes |
|
||||||
|
| `bunx prisma studio` | Interface visual do banco de dados |
|
||||||
|
|
||||||
|
## Solucao de problemas
|
||||||
|
|
||||||
|
### Erro de conexao com PostgreSQL
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: P1001: Can't reach database server at localhost:5432
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solucao:** Verifique se o container Docker esta rodando:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker start postgres-chamados
|
||||||
|
```
|
||||||
|
|
||||||
|
### Erro de migracao (tipo DATETIME)
|
||||||
|
|
||||||
|
Se aparecer erro sobre tipo `DATETIME` ao rodar migrations, use `db push` em vez de `migrate`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bunx prisma db push --accept-data-loss
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usuario nao consegue logar
|
||||||
|
|
||||||
|
Os usuarios de autenticacao ficam no PostgreSQL local, nao no Convex. Rode o seed novamente:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DATABASE_URL="postgresql://postgres:dev@localhost:5432/sistema_chamados" bun tsx scripts/seed-auth.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Limpar banco e recriar
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stop postgres-chamados
|
||||||
|
docker rm postgres-chamados
|
||||||
|
docker run -d --name postgres-chamados -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
|
||||||
|
bunx prisma db push
|
||||||
|
DATABASE_URL="postgresql://postgres:dev@localhost:5432/sistema_chamados" bun tsx scripts/seed-auth.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proximos passos
|
||||||
|
|
||||||
|
- Para deploy em producao, consulte `docs/OPERACAO-PRODUCAO.md`
|
||||||
|
- Para configuracao de SMTP, consulte `docs/SMTP.md`
|
||||||
|
- Para testes automatizados, consulte `docs/testes-vitest.md`
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
Este índice consolida a documentação viva e move conteúdos históricos para um arquivo. O objetivo é simplificar o onboarding e a operação.
|
Este índice consolida a documentação viva e move conteúdos históricos para um arquivo. O objetivo é simplificar o onboarding e a operação.
|
||||||
|
|
||||||
## Visão Geral
|
## Visão Geral
|
||||||
|
- **Desenvolvimento local**: `docs/LOCAL-DEV.md` (setup rapido para rodar localmente)
|
||||||
- Operações (produção): `docs/operations.md`
|
- Operações (produção): `docs/operations.md`
|
||||||
- Guia de desenvolvimento: `docs/DEV.md`
|
- Guia de desenvolvimento: `docs/DEV.md`
|
||||||
- Desktop (Tauri):
|
- Desktop (Tauri):
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,24 @@
|
||||||
import path from "node:path"
|
import pg from "pg"
|
||||||
|
|
||||||
// NOTE: This helper imports the generated Prisma client from TypeScript files.
|
// NOTE: This helper imports the generated Prisma client from TypeScript files.
|
||||||
// Run scripts that rely on it via a transpiling runner (e.g. `tsx` or Bun).
|
// Run scripts that rely on it via a transpiling runner (e.g. `tsx` or Bun).
|
||||||
import { PrismaClient } from "../../src/generated/prisma/client.ts"
|
import { PrismaClient } from "../../src/generated/prisma/client.ts"
|
||||||
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3"
|
import { PrismaPg } from "@prisma/adapter-pg"
|
||||||
|
|
||||||
const PROJECT_ROOT = process.cwd()
|
const { Pool } = pg
|
||||||
const PRISMA_DIR = path.join(PROJECT_ROOT, "prisma")
|
|
||||||
|
|
||||||
function resolveFileUrl(url) {
|
|
||||||
if (!url.startsWith("file:")) {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = url.slice("file:".length)
|
|
||||||
|
|
||||||
if (filePath.startsWith("//")) {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.isAbsolute(filePath)) {
|
|
||||||
return `file:${path.normalize(filePath)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = path.normalize(filePath)
|
|
||||||
const prismaPrefix = `prisma${path.sep}`
|
|
||||||
const relativeToPrisma = normalized.startsWith(prismaPrefix)
|
|
||||||
? normalized.slice(prismaPrefix.length)
|
|
||||||
: normalized
|
|
||||||
|
|
||||||
const absolutePath = path.resolve(PRISMA_DIR, relativeToPrisma)
|
|
||||||
|
|
||||||
if (!absolutePath.startsWith(PROJECT_ROOT)) {
|
|
||||||
throw new Error(`DATABASE_URL path escapes project directory: ${filePath}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return `file:${absolutePath}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeDatasourceUrl(envUrl) {
|
|
||||||
const trimmed = envUrl?.trim()
|
|
||||||
if (trimmed) {
|
|
||||||
return resolveFileUrl(trimmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") {
|
|
||||||
return "file:/app/data/db.sqlite"
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolveFileUrl("file:./db.dev.sqlite")
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPrismaClient() {
|
export function createPrismaClient() {
|
||||||
const resolvedDatabaseUrl = normalizeDatasourceUrl(process.env.DATABASE_URL)
|
const databaseUrl = process.env.DATABASE_URL
|
||||||
process.env.DATABASE_URL = resolvedDatabaseUrl
|
|
||||||
|
|
||||||
const adapter = new PrismaBetterSqlite3({
|
if (!databaseUrl) {
|
||||||
url: resolvedDatabaseUrl,
|
throw new Error("DATABASE_URL environment variable is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: databaseUrl,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const adapter = new PrismaPg(pool)
|
||||||
|
|
||||||
return new PrismaClient({ adapter })
|
return new PrismaClient({ adapter })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -369,24 +369,17 @@ export function TicketChecklistCard({
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<p
|
||||||
<p
|
className={`text-sm ${item.done ? "text-neutral-500 line-through" : "text-neutral-900"}`}
|
||||||
className={`text-sm ${item.done ? "text-neutral-500 line-through" : "text-neutral-900"}`}
|
title={item.text}
|
||||||
title={item.text}
|
onDoubleClick={() => {
|
||||||
onDoubleClick={() => {
|
if (!canEdit || isResolved) return
|
||||||
if (!canEdit || isResolved) return
|
setEditingId(item.id)
|
||||||
setEditingId(item.id)
|
setEditingText(item.text)
|
||||||
setEditingText(item.text)
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{item.text}
|
||||||
{item.text}
|
</p>
|
||||||
</p>
|
|
||||||
{item.description && (
|
|
||||||
<p className="mt-0.5 text-xs text-slate-500">
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isQuestion && options.length > 0 && (
|
{isQuestion && options.length > 0 && (
|
||||||
|
|
@ -446,6 +439,20 @@ export function TicketChecklistCard({
|
||||||
</Badge>
|
</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
{(item.templateDescription || item.description) && (
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
{item.templateDescription && (
|
||||||
|
<p className="text-xs italic text-slate-400">
|
||||||
|
{item.templateDescription}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{item.description && (
|
||||||
|
<p className="text-xs text-slate-500">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ export const ticketChecklistItemSchema = z.object({
|
||||||
required: z.boolean().optional(),
|
required: z.boolean().optional(),
|
||||||
templateId: z.string().optional(),
|
templateId: z.string().optional(),
|
||||||
templateItemId: z.string().optional(),
|
templateItemId: z.string().optional(),
|
||||||
|
templateDescription: z.string().optional(),
|
||||||
createdAt: z.number().optional(),
|
createdAt: z.number().optional(),
|
||||||
createdBy: z.string().optional(),
|
createdBy: z.string().optional(),
|
||||||
doneAt: z.number().optional(),
|
doneAt: z.number().optional(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue