✅ EXECUÇÃO — 15/08/2026
Implementado e validado em produção (homolog). Commits: 2062f6c (fundação: 21 tabelas), 080c857 (features), bbfa277 (lint). Deploy CI verde.
Validação ao vivo: página(s) da feature respondendo 200 com a role correta + procedures tRPC testadas via HTTP (respostas JSON corretas). Ver relatório da sessão.
Detalhes de implementação e decisões: conforme o design acima deste documento (schema, procedures e páginas seguidos conforme especificado; ajustes menores documentados no commit).
📄 Card 45 [M8] — Disco Virtual (Drive de Documentos)
📚 Referência SULTS: https://www.sults.com.br/produtos/disco-virtual Repositório central na nuvem para armazenar, versionar e compartilhar documentos (POPs, contratos, manuais, ativos de marca). Substitui drives compartilhados. 3 níveis de visibilidade: Público, Rede, Unidade.
🎯 Objetivo
Criar o Disco Virtual — drive documental da escola/rede com:
- Pastas e subpastas categorizadas por área/tipo/unidade
- 3 níveis de visibilidade: Público (qualquer um), Rede (toda ANIK), Unidade (isolamento por filial)
- Versionamento (recuperar arquivos apagados e versões anteriores)
- Múltiplos formatos: PDF, planilhas, imagens, vídeos, apresentações, links externos
- Substitui drives compartilhados para documentos institucionais
📚 Documentação de Referência
docs/ARQUITETURA.md— Storage layer (S3 abstrato)src/lib/storage.ts— já existe (Card M5.5-B), reusarprisma/schema.prisma— novo modelDocument
🛠️ Especificação Técnica
Schema Prisma (novo)
model Document {
id String @id @default(cuid())
name String
description String?
type DocumentType // FILE, FOLDER, LINK
mimeType String?
sizeBytes Int @default(0)
s3Key String? // se FILE
externalUrl String? // se LINK
parentId String? // pasta pai (auto-referência)
parent Document? @relation("DocTree", fields: [parentId], references: [id])
children Document[] @relation("DocTree")
visibility DocVisibility // PUBLIC, NETWORK, BRANCH
branchId String? // se BRANCH
branch Branch? @relation(fields: [branchId], references: [id])
tags String[]
version Int @default(1)
versions DocVersion[]
uploadedById String
uploadedBy User @relation(fields: [uploadedById], references: [id])
deletedAt DateTime? // soft delete (lixeira)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model DocVersion {
id String @id @default(cuid())
documentId String
document Document @relation(fields: [documentId], references: [id])
version Int
s3Key String
sizeBytes Int
uploadedById String
uploadedAt DateTime @default(now())
}
enum DocumentType { FILE FOLDER LINK }
enum DocVisibility { PUBLIC NETWORK BRANCH }
Rotas (page.tsx)
/master/disco— MASTER (visão total)/local/disco— LOCAL/SUPERVISOR (visão BRANCH)/pais/discoe/aluno/disco— visão PUBLIC + NETWORK- Componente
<FileExplorer />(árvore de pastas + grid de arquivos)
Procedures tRPC (master.docs, local.docs, student.docs, parent.docs)
tree(estrutura de pastas visíveis ao usuário)list(arquivos de uma pasta, com paginação)upload(presigned URL S3)download(presigned URL S3)createFolder/rename/move/delete(soft)restore(da lixeira)versionHistory(lista versões)restoreVersion
RBAC de visibilidade
PUBLIC: qualquer um logado vêNETWORK: qualquer um da ANIK (todas filiais)BRANCH: só usuários da filial (branchId)- MASTER vê tudo; LOCAL vê próprio BRANCH + PUBLIC + NETWORK
Storage
- S3/MinIO bucket
documents-disco - Tamanho máx por arquivo: 100MB (documentos), 500MB (vídeos institucionais)
- Formatos aceitos: PDF/DOC/DOCX/XLS/XLSX/PPT/PPTX/JPG/PNG/WEBP/MP4/ZIP
✅ Critérios de Aceite
- MASTER gerencia pastas/arquivos em todos os níveis (PUBLIC/NETWORK/BRANCH)
- LOCAL vê só documentos PUBLIC/NETWORK/da própria filial
- STUDENT/PARENT vê só PUBLIC/NETWORK (não veem BRANCH-specific alheio)
- Versionamento: subir nova versão mantém histórico recuperável
- Lixeira: soft delete restaura em até 30 dias
- Upload/download com presigned URL (Card M5.5-B reusado)
- Build passa + lint OK
🔌 Handoff
- Helper
canAccessDoc(userId, doc)(valida visibility + branchId) - Bucket
documents-discono MinIO/S3 - Componente
<FileExplorer />reutilizável em qualquer role
🎯 Próximo
Card 46: Chamados (Help Desk com SLA)