Skip to content

Commit 80f4f1c

Browse files
Autorização multi-tenant com papéis, sistema de convites, troca de workspace e refatorações arquiteturais (#6)
* feat: Atualização de navegação e na lógica do ˋGetContactByIdAsyncˋ * feat: Melhorias da experiência do usuário com verificações de null, previzualização de imagem, e melhorias na responsividade e posição do formulário * feat: Adição de Toast alertando o usuário da criação, edição, e remoção de contato * docs: Atualização do CHANGELOG.md para refletir as ultimas mudanças não lançadas * refactor: Centralização da mascara de telefone, para garantir reuso e não duplicatas * feat: Implementação de página para confirmar exclusão de contato, bem como melhorias na interface de criação, e edição, e visualização de contato * docs: Atualização do CHANGELOG.md para representar novo release * feat: infraestrutura multi-tenant com papéis, convites, tags e audit log - Novos modelos: TenantMembership, TenantInvite, TenantRole, ContactPhone, ContactEmail, ContactAddress, Tag, ContactTag, ContactAuditLog - Serviços: InviteService, TagService, FileUploadService (CRUD + cache) - TenantService reescrito com sessão + IMemoryCache (workspace switcher) - ContatoService reescrito com suporte multi-tenant agregado e projeções - Autorização por papel no tenant (TenantRoleHandler + políticas) - ApplicationDbContext com 10 DbSet, relacionamentos e índices compostos - DbInitializer cria roles Identity (Owner, Admin, User) - Nova migration InitialCreate com todas as tabelas - Compressão de resposta (Brotli + GZip) - Sanitização de comentários redundantes em todo o código-fonte * feat: views e controllers para admin, perfil, tags, contatos e workspace - ContatoController com filtros, favoritos e policies TenantUser/TenantAdmin - AdminController com membros, convites e audit log paginado - ProfileController com upload de foto e edição de perfil - TagController com CRUD e endpoint JSON para AJAX - WorkspaceController para alternar entre tenants - InviteController para aceitar convites via link - Views: Admin (Members, Invites, AuditLog, CreateInvite), Profile, Tag - Views de Contato reescritas com campos dinâmicos e seleção de tags - Layout atualizado com workspace switcher, foto de perfil e links admin - ContactViewModel com AvailableTags e Label renomeado para 'Rótulo (opcional)' - ContactListItemViewModel com PrimaryPhoneLabel e PrimaryEmailLabel * docs: atualização do CHANGELOG com mudanças não documentadas em [Não Lançado] - Documentadas todas as features implementadas: multi-tenant, convites, tags, audit log, upload de arquivos, workspace switcher, painel admin, perfil, rótulos, filtros avançados, compressão - Documentada sanitização de comentários em todo o código-fonte - Adicionado .gitignore para .idea e bin\Debug * feat: migração automática de imagens base64 para wwwroot/uploads - ImageMigrationService idempotente executa na inicialização - Detecta base64 em ProfilePictureUrl e Tenant.LogoUrl - Decodifica e salva como arquivo estático em uploads/profiles e uploads/tenants - Atualiza URLs no banco para caminhos relativos - Limpeza automática de arquivos antigos com extensão diferente * feat: sidebar compacta com expand-on-hover e topbar informativo - Sidebar fixa à esquerda (64px compacta, 220px ao hover) com ícones - Navegação: Dashboard, Contatos, Tags + Admin condicional (Membros, Convites, Histórico) - Profile dropdown fixo no rodapé da sidebar (sempre visível) - Topbar com workspace switcher, busca e toggle de tema - Layout público (visitantes) com topbar simples sem sidebar - Toggle mobile: sidebar desliza com botão hambúrguer - CSS responsivo com media queries e transições suaves - JS: sidebar toggle com click-outside para fechar no mobile * feat: componente _Pager reutilizável com paginação server-side - Partial _Pager.cshtml com botões Anterior/Próxima, input 'Ir para' e seletor pageSize (10/20/30/50) - Contato/List.cshtml usa _Pager via ViewData com filtros preservados na URL - Padrão de 20 itens por página * fix: acesso negado ao listar contatos em 'Todos os Grupos' - ContatoService retorna lista vazia ao invés de lançar exceção quando não há tenants - GetUpcomingBirthdaysAsync retorna vazio ao invés de exceção - ContatoController.List exibe mensagem de erro ao invés de redirecionar para AccessDenied * docs: atualização do CHANGELOG com sidebar, paginação, migração de imagens e fix auth * chore: remover BOM duplicado no site.js * feat: dashboard completo com KPIs, gráfico de grupos e nuvem de tags - 4 cards KPI: Total, Ativos (%), Favoritos, Grupos - Gráfico de barras (Chart.js) com contatos por grupo — cores distintas, dark mode - Nuvem de tags clicável — tamanho proporcional à frequência, redireciona para listagem filtrada - Aniversariantes clicáveis com badge de grupo e link para detalhes - HomeController com queries diretas otimizadas (GroupBy, projeções) - HomeDashboardViewModel expandido com TagCloudItem e GroupContactCount * feat: seleção de tags nos formulários de criação e edição de contatos - ContatoController carrega AvailableTags via TagService nos GETs Create/Edit - Checkboxes com badges coloridas nos formulários Create e Edit - Tags pré-selecionadas no Edit baseadas nos TagIds do contato existente - Adicionado using System.Security.Claims no ContatoController * docs: atualização do CHANGELOG com dashboard e seleção de tags nos formulários * fix(chart): evitar renderização infinita do gráfico Chart.js - Canvas envolto em container com altura fixa (260px) via CSS - Chart.getChart() destrói instância existente antes de criar nova - Mantém maintainAspectRatio:false com container limitado * fix: gráfico infinito, sidebar exibe FullName com truncamento - Chart.js: container com altura fixa (260px) e Chart.getChart() destrói instância antes de recriar - Sidebar: exibe FullName do usuário (query única com ProfilePictureUrl) ao invés do email - CSS: sidebar-label com text-overflow:ellipsis e max-width:140px - Removido BOM duplicado no _Layout.cshtml - Otimização: 2 queries de usuário mescladas em 1 (Select ProfilePictureUrl + FullName) * fix: pager sempre visível, filtro por tags na listagem de contatos - _Pager renderiza quando totalCount > 0 (não apenas totalPages > 1) - TagIds preservados nas URLs do pager - Dropdown de tags com checkboxes no formulário de filtros - ContatoController.List carrega AvailableTags para popular o filtro - PagerTotalCount passado via ViewData * feat: indicador visual de escopo nas tags (Local/Grupo) - TagViewModel com propriedade Scope (string: 'Local' ou 'Grupo') - TagService projeta Scope na query - Manage.cshtml: coluna Escopo com badge e ícone (Local = pessoa, Grupo = pessoas) - Tooltip explicativo em cada badge * fix(audit): registrar alterações completas e modal de detalhes - UpdateContactAsync detecta diffs em: Nome, Anotações, Ativo, Favorito, Data de Nascimento, Telefones, E-mails, Endereços e Tags - Telefones/emails/endereços comparados como strings ordenadas - Tags comparadas por IDs com resolução de nomes - AuditLog.cshtml: resumo com primeiro campo + 'e mais N campo(s)' - Botão Detalhes abre modal com tabela Campo/Anterior/Novo - Formatação visual: anterior em vermelho (del), novo em verde * fix(access): logging no TenantRoleHandler e melhoria na página AccessDenied - TenantRoleHandler com ILogger para debug de falhas de autorização - Logs indicam userId, tenantId, minRole e resultado da verificação - AccessDenied.cshtml com dica sobre workspace e botões de navegação * docs: atualização do CHANGELOG com correções de chart, sidebar, pager, tags, audit log e access - Registradas todas as correções e features desta rodada em [Não Lançado] - Novas entradas: escopo visual de tags, filtro por tags, chart fix, sidebar FullName, pager sempre visível, audit log completo, AccessDenied melhorado * chore(security): add plan file and local secrets to .gitignore * fix(perf): impor limite máximo de PageSize (max 100) e validação de paginação - ContactFilterViewModel: Page mínimo 1, PageSize entre 1-100 com fallback - Evita consultas massivas acidentais (OOM/slow queries) * perf(db): adicionar índices para filtros e joins críticos - Contact: índices compostos (TenantId, IsActive, Name), (TenantId, IsFavorite) - ContactTag: índice em TagId para performance de joins - TenantMembership: índice (UserId, IsActive) para lookups de membros - Nota: gerar migration em ambiente de deploy * fix(race): adicionar RowVersion ao Contact para concorrência otimista - Models/Contact.cs: propriedade [Timestamp] RowVersion - Data/ApplicationDbContext: configurado IsRowVersion() (commit anterior) - Nota: gerar migration para RowVersion * perf(update): aplicar diffs nas coleções ao atualizar contato - Phones/Emails/Addresses: atualiza existentes por Id, adiciona novos, remove ausentes - Tags: adiciona/remove apenas TagIds alterados (diff por conjunto) - Captura DbUpdateConcurrencyException com mensagem amigável - Evita DELETE+INSERT massivo em cada edição de contato * feat(email): infra de envio de e-mails via SMTP Brevo - Services/IEmailSender.cs: interface para envio de e-mails - Services/EmailSettings.cs: POCO com Host, Port, User, Password, UseSsl, From - Services/EmailSenderSmtp.cs: implementação SMTP com fallback para log em dev - appsettings.json: seção EmailSettings com valores padrão (sem credenciais) - Credenciais configuradas via dotnet user-secrets (Brevo SMTP) * feat(auth): fluxos de alterar e-mail, alterar senha, esqueci senha e redefinir senha - ViewModels/AccountSecurityViewModels.cs: VMs de ForgotPassword, ResetPassword, ChangePassword, ChangeEmail - Controllers/AccountController.cs: IEmailSender injetado, novas actions para todos os fluxos de segurança - Views/Account: ForgotPassword, ForgotPasswordConfirmation, ResetPassword, ResetPasswordConfirmation, ChangePassword, ChangeEmail - Views/Account/Login.cshtml: link 'Esqueci minha senha' + fix nullable - Tokens gerados via Identity (GeneratePasswordResetTokenAsync, GenerateChangeEmailTokenAsync) - E-mails enviados via IEmailSender (Brevo SMTP) * perf(counts): cache de contagens do dashboard com TTL de 2 minutos - Services/IDashboardCacheService.cs: interface + DashboardCacheEntry DTO - Services/DashboardCacheService.cs: cache em memória com TTL curto e invalidação por tenant - Controllers/HomeController.cs: usa cache para KPIs, consulta DB apenas em cache miss * feat(import-export): importação e exportação de contatos em CSV, Excel e vCard - Services/IExportService.cs, ExportService.cs: exporta para CSV (CsvHelper), Excel (ClosedXML) e vCard 3.0 - Services/IImportService.cs, ImportService.cs: importa de CSV/Excel/vCard com preview e dedupe (e-mail/telefone) - Controllers/ImportExportController.cs: Export (GET/POST por formato), Import com upload e confirmação - Views/ImportExport/Export.cshtml: cards com botões para os 3 formatos de exportação - Views/ImportExport/Import.cshtml: formulário de upload com opção de ignorar duplicados - Views/ImportExport/ImportPreview.cshtml: tabela de preview com detecção de duplicados - ControleContatos.csproj: adicionados CsvHelper 33.0.1 e ClosedXML 0.104.2 * feat(kanban): mini-CRM com Pipeline, Stage, Deal e board Kanban com drag-and-drop - Models: Pipeline.cs, Stage.cs, Deal.cs, DealTask.cs — modelo de dados CRM com RowVersion - Data/ApplicationDbContext.cs: DbSets + configurações EF Core (índices, relacionamentos, decimal) - ViewModels/KanbanViewModels.cs: Board, Column, Card, Pipeline, Deal e MoveDeal DTOs - Controllers/DealsController.cs: board Kanban, CRUD de Pipeline e Deal, endpoint AJAX MoveDeal - Views/Deals/Index.cshtml: board responsivo com drag & drop HTML5 nativo - Views/Deals/CreatePipeline.cshtml: criação de pipeline com estágios (um por linha) - Views/Deals/CreateDeal.cshtml: formulário de deal com seleção de contato e valor - Nota: gerar migration (dotnet ef migrations add AddKanbanModels) * feat(realtime): SignalR sem Redis para notificações e Kanban em tempo real - Hubs/RealtimeHub.cs: hub com grupos por tenant, eventos CardMoved, DealCreated, DealDeleted, ContactChanged - Program.cs: AddSignalR(), MapHub('/realtime'), registro de todos os serviços (Email, Cache, Import/Export) - DealsController.cs: broadcast via IHubContext ao mover card no kanban - Observação: single-server (in-process), para multi-server integrar Redis backplane * fix(auth+ui): confirmação de e-mail no registro, sidebar e ajustes globais - AccountController: registro exige confirmação por e-mail (EmailConfirmed=false) - AccountController: nova action ConfirmEmail com token do Identity - AccountController: envio de e-mail de confirmação via IEmailSender (Brevo) - Program.cs: RequireConfirmedEmail = true no Identity - DevController: endpoint GET /Dev/SendTestEmail?to= (somente Development) - Login.cshtml: exibe TempData Message/Error (confirmação, erros) - _Layout.cshtml: removidos links alterar senha/email do dropdown - Profile/Index.cshtml: seção Segurança com botões Alterar Senha e Alterar E-mail - site.css: corrigido logo desalinhado na sidebar compacta (flex-shrink no SVG) - site.js: autocomplete=off global em todos os inputs/textareas/selects * feat(realtime): cliente SignalR no Kanban + broadcasts em criar/excluir card - Views/Deals/Index.cshtml: cliente SignalR conecta ao hub /realtime com tenantId - Handlers: CardMoved (mover DOM), DealCreated (reload), DealDeleted (remover card) - DealsController: ViewBag.TenantId para JS, broadcast em CreateDeal e DeleteDeal - Renomeado 'Deal' para 'Card' nos labels do Kanban - Removida exibição do campo Value nos cards * feat(kanban): modal de pipeline com estágios editáveis + Novo Card completo com subtarefas - ViewModels/KanbanViewModels.cs: StageInputDto, SubtaskInputDto, DealPriority enum, campos StartDate/EndDate/Priority no DealViewModel, removido Value - Models/Deal.cs: removido Value, adicionados StartDate, EndDate, Priority - Data/ApplicationDbContext.cs: Priority como string, removido decimal Value - Controllers/DealsController.cs: CreatePipeline aceita List<StageInputDto>, CreateDeal com subtarefas e tags - Views/Deals/CreatePipeline.cshtml: UI interativa com estágios editáveis (cor + nome + adicionar/remover), padrão A Fazer/Em Progresso/Concluído - Views/Deals/CreateDeal.cshtml: renomeado para Novo Card, campos prioridade/datas/tags/subtarefas inline - Nota: gerar migration para remover Value e adicionar StartDate/EndDate/Priority * fix(ui): ajustar filtros de contatos na mesma linha + máscara de telefone nos formulários - Views/Contato/List.cshtml: campos redimensionados (col-md-2/1), Tags como dropdown compacto, botão Filtrar e X inline (col-auto) - Views/Contato/Create.cshtml: phone-mask + inputmode=tel nos inputs de telefone (estático e dinâmico) - Views/Contato/Edit.cshtml: phone-mask + inputmode=tel nos inputs de telefone (estático e dinâmico) - PhoneMask.attach() chamado ao adicionar telefones dinamicamente * chore(db): migration para remover Value e adicionar StartDate, EndDate e Priority no Deal - Migration: AtualizarDealRemoverValueAdicionarCampos - DropColumn Value (decimal), AddColumn EndDate, StartDate (timestamp), Priority (varchar 10) - Executar: dotnet ef database update * feat(kanban): badges de prioridade/datas nos cards + CRUD de subtarefas via AJAX - Views/Deals/Index.cshtml: badge de prioridade (cores), data de fim, contagem de subtarefas - Controllers/DealsController.cs: endpoints ToggleTask, AddTask, DeleteTask (AJAX, protegidos por tenant) - ViewModels/KanbanViewModels.cs: DTOs ToggleTaskDto, AddTaskDto, DeleteTaskDto * chore: template HTML corporativo de e-mail, limpeza de comentários e release v3.0.0 - Services/EmailTemplates.cs: novo template HTML responsivo com CSS inline — cabeçalho azul (#0d6efd), botão CTA, rodapé padronizado. Métodos: ConfirmacaoConta, RedefinirSenha, ConfirmarAlteracaoEmail, TesteSmtp - Controllers/AccountController.cs: substituídos 3 corpos HTML inline por chamadas a EmailTemplates; removido XML doc da classe - Controllers/DevController.cs: substituído corpo HTML inline por EmailTemplates.TesteSmtp; removidos XML docs - Controllers/DealsController.cs: removido XML doc da classe - Hubs/RealtimeHub.cs: removidos todos os XML docs redundantes; simplificadas assinaturas de OnDisconnectedAsync e métodos Notify* - Authorization/TenantRoleHandler.cs: XML doc substituído por comentário conciso de uma linha - Authorization/TenantRoleRequirement.cs: removido XML doc redundante - Mappings/ContactMappingProfile.cs: removido XML doc redundante - ViewModels/KanbanViewModels.cs: removidos todos os XML docs redundantes; separadores de seção simplificados - ViewModels/ContactFilterViewModel.cs: removido XML doc redundante - ViewModels/ContactDetailsViewModel.cs: removido XML doc redundante - ViewModels/ContactViewModel.cs: removido XML doc redundante - Services/IEmailSender.cs: removido XML doc redundante - Services/EmailSettings.cs: removido XML doc redundante - Services/EmailSenderSmtp.cs: removido XML doc redundante - CHANGELOG.md: seção [Não Lançado] movida para [3.0.0] - 2026-02-25 com resumo completo das features, correções e refactors da versão * chore(deploy): Dockerfile multi-stage para Render + .env.example e proteção de secrets - Dockerfile: build multi-stage (sdk:10.0 → aspnet:10.0), publica em Release, expõe porta 8080, respeita variável PORT do Render, diretório /app/wwwroot/uploads criado no runtime - .dockerignore: exclui bin/, obj/, .env, uploads locais, launchSettings.json e Migrations Designer dos layers do Docker - .env.example: template documentado com todas as variáveis necessárias (ConnectionStrings, EmailSettings, ASPNETCORE_ENVIRONMENT, DataProtection) - .env: arquivo real com secrets — NÃO commitado (ignorado pelo .gitignore) - .gitignore: adicionadas entradas para .env, **/bin/, **/obj/, .vs/, .idea/, *.log e diretórios de uploads gerados em runtime * chore(db): aplicar migrations automaticamente quando APPLY_MIGRATIONS=true * chore: Limpeza de migrations * chore: Migrations * chore: Limpeza de arquivos estáticos
1 parent 84c5170 commit 80f4f1c

107 files changed

Lines changed: 9885 additions & 917 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Artefatos de build
2+
**/bin/
3+
**/obj/
4+
**/out/
5+
6+
# User secrets do .NET
7+
**/Properties/launchSettings.json
8+
9+
# Arquivos de ambiente com credenciais
10+
.env
11+
12+
# Logs
13+
*.log
14+
15+
# IDEs
16+
.vs/
17+
.idea/
18+
*.user
19+
*.suo
20+
*.userprefs
21+
22+
# Uploads locais (montados como volume em produção)
23+
ControleContatos/wwwroot/uploads/
24+
25+
# Migrations designer (gerados automaticamente)
26+
**/Migrations/*.Designer.cs
27+

.env.example

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# ──────────────────────────────────────────────────────────
2+
# ControleContatos — variáveis de ambiente de produção
3+
# ──────────────────────────────────────────────────────────
4+
5+
# ── Banco de dados PostgreSQL ──
6+
# Formato: Host=...;Database=...;Username=...;Password=...;Ssl Mode=Require
7+
ConnectionStrings__DefaultConnection=Host=SEU_HOST;Database=SEU_DATABASE;Username=SEU_USER;Password=SUA_SENHA;Ssl Mode=Require;Trust Server Certificate=true
8+
9+
# ── E-mail (SMTP) ──────────────────────────────────
10+
EmailSettings__Host=smtp-host.com
11+
EmailSettings__Port=587
12+
EmailSettings__User=SEU_LOGIN
13+
EmailSettings__Password=SUA_SENHA_SMTP
14+
EmailSettings__UseSsl=true
15+
EmailSettings__FromEmail=SEU_EMAIL_REMETENTE
16+
EmailSettings__FromName=ControleContatos
17+
18+
# ── ASP.NET Core ─────────────────────────────────────────
19+
ASPNETCORE_ENVIRONMENT=Production
20+
21+
# ── Data Protection (geração de tokens Identity) ─────────
22+
# Gere com: openssl rand -base64 32
23+
DataProtection__ApplicationName=ControleContatos
24+

.gitignore

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,32 @@ bin/
22
obj/
33
/packages/
44
riderModule.iml
5-
/_ReSharper.Caches/
5+
/_ReSharper.Caches/.idea/
6+
.idea/
7+
ControleContatos/bin\\Debug/
8+
9+
# Arquivos de plano (não commitar)
10+
*.prompt.md
11+
12+
# User secrets e credenciais
13+
appsettings.*.local.json
14+
15+
# Variáveis de ambiente com credenciais reais
16+
.env
17+
18+
# Artefatos de build
19+
**/bin/
20+
**/obj/
21+
22+
# IDEs
23+
.vs/
24+
.idea/
25+
*.user
26+
*.suo
27+
28+
# Logs
29+
*.log
30+
31+
# Uploads gerados em runtime (montar como volume em produção)
32+
ControleContatos/wwwroot/uploads/tenants/
33+
ControleContatos/wwwroot/uploads/profiles/

CHANGELOG.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,74 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
66

77
## [Não Lançado]
88

9+
---
10+
11+
## [3.0.0] - 2026-02-25
12+
13+
### Adicionado
14+
- Feat: Sistema de papéis por tenant (Dono, Admin, Usuário) com hierarquia de permissões. Políticas de autorização `TenantUser`, `TenantAdmin`, `TenantOwner` aplicadas nos controllers. Arquivos: `Authorization/TenantRoleRequirement.cs`, `Authorization/TenantRoleHandler.cs`, `Program.cs`.
15+
- Feat: Sistema de convites para tenants — criar convite (GUID, validade de 7 dias), aceitar (valida expiração e e-mail), cancelar (Owner cancela qualquer um, Admin só os seus). Arquivos: `Services/InviteService.cs`, `Services/IInviteService.cs`, `Controllers/InviteController.cs`, `Views/Admin/Invites.cshtml`, `Views/Admin/CreateInvite.cshtml`.
16+
- Feat: Workspace switcher — sessão + cache para alternar entre tenants ou "Todos os Grupos". Dropdown no cabeçalho com todos os tenants do usuário. Arquivos: `Services/TenantService.cs`, `Controllers/WorkspaceController.cs`, `Views/Shared/_Layout.cshtml`.
17+
- Feat: Painel de administração (membros, convites, histórico de alterações). Owner pode alterar papéis, desativar e expulsar membros. Arquivos: `Controllers/AdminController.cs`, `Views/Admin/Members.cshtml`, `Views/Admin/AuditLog.cshtml`.
18+
- Feat: Página de perfil do usuário com upload de foto, informações pessoais e lista de grupos. Arquivos: `Controllers/ProfileController.cs`, `Views/Profile/Index.cshtml`.
19+
- Feat: Tags com escopo Local (só o criador vê) e Tenant (todos do grupo veem). CRUD de tags e API JSON para busca via AJAX. Arquivos: `Services/TagService.cs`, `Services/ITagService.cs`, `Controllers/TagController.cs`, `Views/Tag/Manage.cshtml`.
20+
- Feat: Upload de arquivos estáticos (fotos de perfil e logos de grupo) em `wwwroot/uploads/`. Validação de extensão e tamanho. Arquivos: `Services/FileUploadService.cs`, `Services/IFileUploadService.cs`.
21+
- Feat: Múltiplos telefones, e-mails e endereços por contato com campos dinâmicos JS. Primeiro telefone marcado como principal e exibido na listagem/dashboard. Arquivos: `Views/Contato/Create.cshtml`, `Views/Contato/Edit.cshtml`, `ViewModels/ContactViewModel.cs`.
22+
- Feat: Contatos favoritos com toggle rápido na listagem. Arquivos: `Services/ContatoService.cs`, `Controllers/ContatoController.cs`, `Views/Contato/List.cshtml`.
23+
- Feat: Histórico de alterações (audit log) automático — registra criações, edições (com diff de campos) e exclusões de contatos. Arquivos: `Services/ContatoService.cs`, `Models/ContactAuditLog.cs`, `Views/Admin/AuditLog.cshtml`.
24+
- Feat: Filtros avançados por nome, e-mail, telefone, tags, favoritos e ativos na listagem de contatos. Ordenação por nome, data de criação e aniversário. Arquivos: `ViewModels/ContactFilterViewModel.cs`, `Services/ContatoService.cs`, `Views/Contato/List.cshtml`.
25+
- Feat: Compressão de resposta (Brotli + GZip) e cache em memória para tenant e tags. Arquivos: `Program.cs`, `Services/TenantService.cs`, `Services/TagService.cs`.
26+
- Feat: Nova migration inicial `InitialCreate` com todas as tabelas do sistema (Tenant, TenantMembership, TenantInvite, Contact, ContactPhone, ContactEmail, ContactAddress, Tag, ContactTag, ContactAuditLog). Arquivos: `Migrations/*`.
27+
- Feat: Campo "Rótulo (opcional)" nos telefones, e-mails e endereços — exibido na listagem e detalhes do contato. Arquivos: `ViewModels/ContactViewModel.cs`, `ViewModels/ContactListItemViewModel.cs`, `Services/ContatoService.cs`.
28+
- Feat: Propriedade `AvailableTags` no `ContactViewModel` para seleção de tags nos formulários de criação/edição. Arquivo: `ViewModels/ContactViewModel.cs`.
29+
- Feat: Migração automática de imagens base64 para `wwwroot/uploads/` na inicialização. Serviço `ImageMigrationService` idempotente. Arquivos: `Services/ImageMigrationService.cs`, `Program.cs`.
30+
- Feat: Sidebar compacta (64px, expande para 220px ao hover) com ícones autodescritivos à esquerda. Topbar superior com workspace switcher, busca e toggle de tema. Profile dropdown fixo no rodapé da sidebar. Layout público sem sidebar. Arquivos: `Views/Shared/_Layout.cshtml`, `wwwroot/css/site.css`, `wwwroot/js/site.js`.
31+
- Feat: Componente `_Pager.cshtml` reutilizável com paginação server-side — botões Anterior/Próxima, input "Ir para página" e seletor de pageSize (10/20/30/50). Aplicado na listagem de contatos. Arquivos: `Views/Shared/_Pager.cshtml`, `Views/Contato/List.cshtml`.
32+
- Feat: Dashboard completo com 4 cards KPI (Total, Ativos, Favoritos, Grupos), gráfico de barras (Chart.js) de contatos por grupo, nuvem de tags clicável (redireciona para listagem filtrada) e aniversariantes com link para detalhes. Arquivos: `Controllers/HomeController.cs`, `ViewModels/HomeDashboardViewModel.cs`, `Views/Home/Dashboard.cshtml`.
33+
- Feat: Seleção de tags nos formulários de criação e edição — checkboxes com badges coloridas, tags pré-selecionadas no Edit. Arquivos: `Controllers/ContatoController.cs`, `Views/Contato/Create.cshtml`, `Views/Contato/Edit.cshtml`.
34+
- Feat: Indicador visual de escopo nas tags — badge "Local" (só o criador vê) ou "Grupo" (todos do tenant veem) no painel de gerenciamento de tags. Arquivos: `ViewModels/ContactDetailsViewModel.cs`, `Services/TagService.cs`, `Views/Tag/Manage.cshtml`.
35+
- Feat: Filtro por tags na listagem de contatos — dropdown com checkboxes no formulário de filtros, TagIds preservados na paginação. Arquivos: `Views/Contato/List.cshtml`, `Controllers/ContatoController.cs`, `Views/Shared/_Pager.cshtml`.
36+
- Kanban / Mini-CRM: Pipelines, Stages e Cards com board drag-and-drop. Criação de pipeline com estágios editáveis (cor + nome, padrão: A Fazer / Em Progresso / Concluído). Formulário "Novo Card" com título, descrição, prioridade (Baixa/Média/Alta), datas de início e fim, contato vinculado, tags e subtarefas inline. Arquivos: `Models/Pipeline.cs`, `Models/Stage.cs`, `Models/Deal.cs`, `Models/DealTask.cs`, `Controllers/DealsController.cs`, `Views/Deals/Index.cshtml`, `Views/Deals/CreatePipeline.cshtml`, `Views/Deals/CreateDeal.cshtml`.
37+
- Tempo real (SignalR): Hub `/realtime` com grupos por tenant. Eventos `CardMoved`, `DealCreated`, `DealDeleted`, `ContactChanged` — outros usuários do mesmo workspace veem mudanças no Kanban sem recarregar a página. Sem Redis (single-server). Arquivos: `Hubs/RealtimeHub.cs`, `Program.cs`, `Views/Deals/Index.cshtml`.
38+
- CRUD de subtarefas (AJAX): Endpoints `ToggleTask`, `AddTask` e `DeleteTask` no `DealsController` — manipulação de subtarefas em tempo real sem reload. Arquivos: `Controllers/DealsController.cs`, `ViewModels/KanbanViewModels.cs`.
39+
- Badges nos cards Kanban: Exibição de prioridade (cor por nível), data de fim e contagem de subtarefas diretamente nos cards do board. Arquivo: `Views/Deals/Index.cshtml`.
40+
- Template HTML de e-mail: Novo serviço `EmailTemplates` com layout corporativo responsivo (CSS inline, cabeçalho azul, botão CTA, rodapé). Templates para: confirmação de conta, redefinição de senha, alteração de e-mail e teste SMTP. Arquivo: `Services/EmailTemplates.cs`.
41+
- Confirmação de e-mail no registro: Novo usuário recebe e-mail de confirmação com token; login bloqueado até confirmar. `RequireConfirmedEmail = true` no Identity. Arquivos: `Controllers/AccountController.cs`, `Program.cs`.
42+
- Fluxos de segurança: Esquecer senha (ForgotPassword + ResetPassword), alterar senha e alterar e-mail — todos com e-mail de confirmação usando o novo template HTML. Arquivos: `Controllers/AccountController.cs`, `Views/Account/`.
43+
- Alterar senha e e-mail na página de Perfil: Removidos do dropdown do header e centralizados em `Views/Profile/Index.cshtml`.
44+
- Importação e exportação de contatos: CSV, Excel (XLSX) e vCard 3.0 com preview e detecção de duplicados. Arquivos: `Services/ExportService.cs`, `Services/ImportService.cs`, `Controllers/ImportExportController.cs`, `Views/ImportExport/`.
45+
- Cache do dashboard: `DashboardCacheService` com TTL curto e invalidação por tenant. Arquivos: `Services/DashboardCacheService.cs`, `Controllers/HomeController.cs`.
46+
- Endpoint de diagnóstico SMTP: `GET /Dev/SendTestEmail?to=email` (somente Development) para validar configuração Brevo. Arquivo: `Controllers/DevController.cs`.
47+
- Migration `AtualizarDealRemoverValueAdicionarCampos`: Remove campo `Value` do Deal; adiciona `StartDate`, `EndDate` e `Priority`. Arquivo: `Migrations/20260225033944_AtualizarDealRemoverValueAdicionarCampos.cs`.
48+
49+
### Corrigido
50+
- Fix: "Todos os Grupos" não mais redireciona para AccessDenied — `ContatoService` retorna lista vazia quando não há tenants, `GetUpcomingBirthdaysAsync` idem. Arquivos: `Services/ContatoService.cs`, `Controllers/ContatoController.cs`.
51+
- Fix: Gráfico Chart.js no dashboard renderizava infinitamente — canvas envolto em container com altura fixa (260px) e `Chart.getChart()` destrói instância anterior antes de recriar. Arquivo: `Views/Home/Dashboard.cshtml`.
52+
- Fix: Sidebar exibia email ao invés do nome completo — agora usa `FullName` do banco com query única (ProfilePictureUrl + FullName). Nomes longos truncados com ellipsis (max-width:140px). Arquivos: `Views/Shared/_Layout.cshtml`, `wwwroot/css/site.css`.
53+
- Fix: Pager (controles de paginação) agora sempre visível quando existem resultados, não apenas quando há mais de 1 página. Arquivos: `Views/Shared/_Pager.cshtml`, `Views/Contato/List.cshtml`.
54+
- Fix: Audit log registra alterações completas — detecta diffs em todos os campos (Nome, Anotações, Ativo, Favorito, Data de Nascimento, Telefones, E-mails, Endereços, Tags). Modal de detalhes com tabela Campo/Anterior/Novo. Arquivos: `Services/ContatoService.cs`, `Views/Admin/AuditLog.cshtml`.
55+
- Fix: TenantRoleHandler com logging para debug de falhas de autorização. Página AccessDenied melhorada com dica sobre workspace. Arquivos: `Authorization/TenantRoleHandler.cs`, `Views/Account/AccessDenied.cshtml`.
56+
57+
### Alterado
58+
- Refactor: Sanitização de comentários em todo o código-fonte — removidos comentários redundantes, TODOs obsoletos e comentários em inglês; padronizado em português conciso. Arquivos: `Services/*`, `Controllers/*`, `Models/*`, `ViewModels/*`, `Data/*`, `Program.cs`.
59+
- Refactor: `TenantService` reescrito para usar sessão + `IMemoryCache` em vez de claims diretas. Arquivos: `Services/TenantService.cs`, `Services/ITenantService.cs`.
60+
- Refactor: `ContatoService` reescrito com suporte multi-tenant agregado ("Todos os Grupos") e projeções diretas. Arquivo: `Services/ContatoService.cs`.
61+
- Refactor: `ApplicationDbContext` reescrito com 10 `DbSet<>`, relacionamentos explícitos e índices compostos para performance. Arquivo: `Data/ApplicationDbContext.cs`.
62+
- Refactor: `DbInitializer` atualizado para criar roles Identity (Owner, Admin, User) na inicialização. Arquivo: `Data/DbInitializer.cs`.
63+
- Refactor: Views de Contato (Create, Edit, Details, Delete, List) reescritas para suportar múltiplos campos, tags e badges de tenant. Arquivos: `Views/Contato/*`.
64+
65+
## [2.2.0] - 2026-02-24
66+
### Adicionado
67+
- Feat: Toasts de notificação para feedback de usuário (criação, edição e exclusão de contatos). A lógica usa `TempData["Message"]` nas actions do controller e Bootstrap Toasts na view de listagem. Arquivos: `Views/Contato/List.cshtml`, `Controllers/ContatoController.cs`, `Views/Shared/_Layout.cshtml`.
68+
- Feat: Confirmação de exclusão com página dedicada. Ao clicar em deletar na lista o usuário é redirecionado para uma página de confirmação (`GET /Contato/Delete/{id}`) que mostra detalhes do contato antes de confirmar. Arquivos: `Views/Contato/Delete.cshtml`, `Views/Contato/List.cshtml`, `Controllers/ContatoController.cs`.
69+
- Feat: Máscara de telefone centralizada e API reutilizável em `site.js` (`window.PhoneMask`). Agora inputs apenas precisam da classe `phone-mask` para receber o comportamento de máscara/format. Arquivos: `wwwroot/js/site.js`, `Views/Contato/Create.cshtml`, `Views/Contato/Edit.cshtml`, `Views/Account/Register.cshtml`.
70+
- Feat: ViewModel específico para exibição de detalhes (`ContactDetailsViewModel`) e mapeamento AutoMapper para evitar expor entidades de domínio diretamente nas views. Arquivos: `ViewModels/ContactDetailsViewModel.cs`, `Mappings/ContactMappingProfile.cs`, `Controllers/ContatoController.cs`, `Views/Contato/Details.cshtml`.
71+
72+
### Alterado (UI / UX)
73+
- Polish: Redesign visual das páginas de Contato (Criar / Editar / Excluir / Detalhes) para um layout consistente com card, breadcrumb, ícones e botões alinhados. Arquivos: `Views/Contato/Create.cshtml`, `Views/Contato/Edit.cshtml`, `Views/Contato/Delete.cshtml`, `Views/Contato/Details.cshtml`.
74+
- Refactor: Removidos scripts de máscara inline das views e substituídos pela função central `PhoneMask.attach` no `site.js`. Isso reduz duplicação e centraliza comportamento. Arquivos: `wwwroot/js/site.js`, `Views/Contato/Create.cshtml`, `Views/Contato/Edit.cshtml`, `Views/Account/Register.cshtml`.
75+
- Fix: Script de inicialização dos Toasts movido para a section `@section Scripts` das views para garantir que o `bootstrap.bundle.min.js` já tenha sido carregado antes de executar a lógica JS. Arquivo: `Views/Contato/List.cshtml`.
76+
977

1078

1179

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Security.Claims;
2+
using ControleContatos.Data;
3+
using ControleContatos.Models;
4+
using ControleContatos.Services;
5+
using Microsoft.AspNetCore.Authorization;
6+
using Microsoft.EntityFrameworkCore;
7+
8+
namespace ControleContatos.Authorization;
9+
10+
/// <summary>
11+
/// Handler que verifica se o usuário autenticado tem o papel mínimo exigido
12+
/// no tenant ativo (workspace selecionado).
13+
/// </summary>
14+
public class TenantRoleHandler : AuthorizationHandler<TenantRoleRequirement>
15+
{
16+
private readonly ITenantService _tenantService;
17+
private readonly ApplicationDbContext _context;
18+
private readonly ILogger<TenantRoleHandler> _logger;
19+
20+
public TenantRoleHandler(ITenantService tenantService, ApplicationDbContext context, ILogger<TenantRoleHandler> logger)
21+
{
22+
_tenantService = tenantService;
23+
_context = context;
24+
_logger = logger;
25+
}
26+
27+
protected override async Task HandleRequirementAsync(
28+
AuthorizationHandlerContext context,
29+
TenantRoleRequirement requirement)
30+
{
31+
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
32+
if (string.IsNullOrEmpty(userId))
33+
{
34+
_logger.LogDebug("TenantRoleHandler: userId vazio, negando acesso.");
35+
return;
36+
}
37+
38+
var tenantId = _tenantService.GetCurrentTenantId();
39+
if (!tenantId.HasValue)
40+
{
41+
// Workspace "Todos" — verificar se o usuário tem o papel em ALGUM tenant
42+
var hasRole = await _context.TenantMemberships
43+
.AsNoTracking()
44+
.AnyAsync(m => m.UserId == userId && m.IsActive && m.Role >= requirement.MinimumRole);
45+
46+
_logger.LogDebug("TenantRoleHandler: workspace 'Todos', userId={UserId}, minRole={MinRole}, hasRole={HasRole}", userId, requirement.MinimumRole, hasRole);
47+
48+
if (hasRole)
49+
context.Succeed(requirement);
50+
return;
51+
}
52+
53+
// Workspace específico — verificar membership nesse tenant
54+
var membership = await _context.TenantMemberships
55+
.AsNoTracking()
56+
.FirstOrDefaultAsync(m => m.UserId == userId && m.TenantId == tenantId.Value && m.IsActive);
57+
58+
_logger.LogDebug("TenantRoleHandler: tenantId={TenantId}, userId={UserId}, membership={Role}", tenantId.Value, userId, membership?.Role);
59+
60+
if (membership is not null && membership.Role >= requirement.MinimumRole)
61+
{
62+
context.Succeed(requirement);
63+
}
64+
}
65+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using ControleContatos.Models;
2+
using Microsoft.AspNetCore.Authorization;
3+
4+
namespace ControleContatos.Authorization;
5+
6+
public class TenantRoleRequirement : IAuthorizationRequirement
7+
{
8+
public TenantRole MinimumRole { get; }
9+
10+
public TenantRoleRequirement(TenantRole minimumRole)
11+
{
12+
MinimumRole = minimumRole;
13+
}
14+
}
15+

ControleContatos/ControleContatos.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
<ItemGroup>
1111
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
12+
<PackageReference Include="ClosedXML" Version="0.104.2" />
13+
<PackageReference Include="CsvHelper" Version="33.0.1" />
1214
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
1315
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3">
1416
<PrivateAssets>all</PrivateAssets>
@@ -21,5 +23,10 @@
2123
<PackageReference Include="Npgsql" Version="10.0.1" />
2224
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
2325
</ItemGroup>
26+
27+
<ItemGroup>
28+
<Folder Include="wwwroot\uploads\profiles\" />
29+
<Folder Include="wwwroot\uploads\tenants\" />
30+
</ItemGroup>
2431

2532
</Project>

0 commit comments

Comments
 (0)