diff --git a/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportTemplate.java b/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportTemplate.java index 5d73bc9..2fef038 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportTemplate.java +++ b/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportTemplate.java @@ -18,6 +18,9 @@ public class ReportTemplate { private String fileName; private String storageKey; private User createdBy; + private String managerName; + private String customProjectName; + private String dateFormatPattern; private List sections = new ArrayList<>(); private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -102,6 +105,30 @@ public class ReportTemplate { this.createdBy = createdBy; } + public String getManagerName() { + return managerName; + } + + public void setManagerName(String managerName) { + this.managerName = managerName; + } + + public String getCustomProjectName() { + return customProjectName; + } + + public void setCustomProjectName(String customProjectName) { + this.customProjectName = customProjectName; + } + + public String getDateFormatPattern() { + return dateFormatPattern != null ? dateFormatPattern : "DD/MM/AAAA até DD/MM/AAAA"; + } + + public void setDateFormatPattern(String dateFormatPattern) { + this.dateFormatPattern = dateFormatPattern; + } + public List getSections() { return sections; } diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportTemplateEntity.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportTemplateEntity.java index b968bea..4bdc866 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportTemplateEntity.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportTemplateEntity.java @@ -54,6 +54,15 @@ public class ReportTemplateEntity { @JoinColumn(name = "created_by") private UserEntity createdBy; + @Column(name = "manager_name") + private String managerName; + + @Column(name = "custom_project_name") + private String customProjectName; + + @Column(name = "date_format_pattern") + private String dateFormatPattern = "DD/MM/AAAA até DD/MM/AAAA"; + @OneToMany(mappedBy = "reportTemplate", cascade = CascadeType.ALL, orphanRemoval = true) @OrderBy("orderIndex ASC") private List sections = new ArrayList<>(); @@ -144,6 +153,30 @@ public class ReportTemplateEntity { this.createdBy = createdBy; } + public String getManagerName() { + return managerName; + } + + public void setManagerName(String managerName) { + this.managerName = managerName; + } + + public String getCustomProjectName() { + return customProjectName; + } + + public void setCustomProjectName(String customProjectName) { + this.customProjectName = customProjectName; + } + + public String getDateFormatPattern() { + return dateFormatPattern != null ? dateFormatPattern : "DD/MM/AAAA até DD/MM/AAAA"; + } + + public void setDateFormatPattern(String dateFormatPattern) { + this.dateFormatPattern = dateFormatPattern; + } + public List getSections() { return sections; } diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/mapper/ReportEntityMapper.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/mapper/ReportEntityMapper.java index e351ea3..7a2786e 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/mapper/ReportEntityMapper.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/mapper/ReportEntityMapper.java @@ -21,6 +21,9 @@ public class ReportEntityMapper { entity.setFileUrl(domain.getFileUrl()); entity.setFileName(domain.getFileName()); entity.setStorageKey(domain.getStorageKey()); + entity.setManagerName(domain.getManagerName()); + entity.setCustomProjectName(domain.getCustomProjectName()); + entity.setDateFormatPattern(domain.getDateFormatPattern()); entity.setCreatedAt(domain.getCreatedAt()); entity.setUpdatedAt(domain.getUpdatedAt()); @@ -70,6 +73,9 @@ public class ReportEntityMapper { domain.setFileUrl(entity.getFileUrl()); domain.setFileName(entity.getFileName()); domain.setStorageKey(entity.getStorageKey()); + domain.setManagerName(entity.getManagerName()); + domain.setCustomProjectName(entity.getCustomProjectName()); + domain.setDateFormatPattern(entity.getDateFormatPattern()); domain.setCreatedAt(entity.getCreatedAt()); domain.setUpdatedAt(entity.getUpdatedAt()); diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/CreateReportTemplateForm.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/CreateReportTemplateForm.java index 408582f..a5b0d38 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/CreateReportTemplateForm.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/CreateReportTemplateForm.java @@ -20,6 +20,15 @@ public class CreateReportTemplateForm { @RestForm("projectId") public Long projectId; + @RestForm("managerName") + public String managerName; + + @RestForm("customProjectName") + public String customProjectName; + + @RestForm("dateFormatPattern") + public String dateFormatPattern; + @RestForm("sectionsJson") public String sectionsJson; } diff --git a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/ReportController.java b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/ReportController.java index 7d09ab1..3de1d6c 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/ReportController.java +++ b/backend/src/main/java/net/beetechgroup/beetask/interfaceadapters/controllers/report/ReportController.java @@ -107,6 +107,9 @@ public class ReportController { form.name, form.description, form.scope, + form.managerName, + form.customProjectName, + form.dateFormatPattern, form.file.fileName(), is, form.file.size(), diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportMapper.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportMapper.java index 67d6af2..00a5b7b 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportMapper.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportMapper.java @@ -30,6 +30,9 @@ public class ReportMapper { template.getStorageKey(), template.getCreatedBy() != null ? template.getCreatedBy().getId() : null, template.getCreatedBy() != null ? template.getCreatedBy().getName() : null, + template.getManagerName(), + template.getCustomProjectName(), + template.getDateFormatPattern(), sections, template.getCreatedAt(), template.getUpdatedAt() diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportTemplateOutput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportTemplateOutput.java index 5adef4d..bd03e42 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportTemplateOutput.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportTemplateOutput.java @@ -17,6 +17,9 @@ public record ReportTemplateOutput( String storageKey, Long createdById, String createdByName, + String managerName, + String customProjectName, + String dateFormatPattern, List sections, LocalDateTime createdAt, LocalDateTime updatedAt diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateInput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateInput.java index 6c2eb10..93c924e 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateInput.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateInput.java @@ -10,6 +10,9 @@ public record CreateReportTemplateInput( String name, String description, String scope, + String managerName, + String customProjectName, + String dateFormatPattern, String fileName, InputStream fileInputStream, long fileSize, diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateUseCase.java index 43eb9b6..356cc32 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/create/CreateReportTemplateUseCase.java @@ -91,6 +91,9 @@ public class CreateReportTemplateUseCase { template.setStorageKey(storageKey); template.setFileUrl(fileUrl); template.setCreatedBy(user); + template.setManagerName(input.managerName() != null ? input.managerName().trim() : null); + template.setCustomProjectName(input.customProjectName() != null ? input.customProjectName().trim() : null); + template.setDateFormatPattern(input.dateFormatPattern() != null ? input.dateFormatPattern().trim() : "DD/MM/AAAA até DD/MM/AAAA"); template.setCreatedAt(LocalDateTime.now()); template.setUpdatedAt(LocalDateTime.now()); diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/generate/GenerateReportUseCase.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/generate/GenerateReportUseCase.java index 1572e69..ab8a9db 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/generate/GenerateReportUseCase.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/generate/GenerateReportUseCase.java @@ -141,19 +141,41 @@ public class GenerateReportUseCase { Map variables = new HashMap<>(); String strStart = input.startDate().format(DATE_FORMATTER); String strEnd = input.endDate().format(DATE_FORMATTER); - String strPeriodo = strStart + " a " + strEnd; + + String pattern = template.getDateFormatPattern(); + String formattedPeriod = formatPeriodWithPattern(input.startDate(), input.endDate(), pattern); String monthName = input.startDate().getMonth().getDisplayName(TextStyle.FULL, PT_BR); String mesAno = capitalize(monthName) + "/" + input.startDate().getYear(); + String userName = user.getName() != null && !user.getName().isBlank() ? user.getName() : user.getEmail(); + String managerName = template.getManagerName() != null ? template.getManagerName() : ""; + String projectName = template.getCustomProjectName() != null && !template.getCustomProjectName().isBlank() + ? template.getCustomProjectName() + : (project != null ? project.getName() : "Geral / Todos os Projetos"); + + // Nome do Usuário / Colaborador + variables.put("nome", userName); + variables.put("nome_usuario", userName); + variables.put("colaborador", userName); + variables.put("email_usuario", user.getEmail()); + + // Gestor Responsável + variables.put("gestor", managerName); + variables.put("nome_gestor", managerName); + + // Projeto + variables.put("projeto", projectName); + variables.put("nome_projeto", projectName); + variables.put("organizacao", org.getName()); + + // Período + variables.put("periodo", formattedPeriod); + variables.put("periodo_vigencia", formattedPeriod); variables.put("data_inicio", strStart); variables.put("data_fim", strEnd); - variables.put("periodo_vigencia", strPeriodo); variables.put("mes_ano", mesAno); - variables.put("nome_usuario", user.getName() != null ? user.getName() : user.getEmail()); - variables.put("email_usuario", user.getEmail()); - variables.put("organizacao", org.getName()); - variables.put("projeto", project != null ? project.getName() : "Geral / Todos os Projetos"); + variables.put("total_horas", formattedTotalTime); variables.put("data_emissao", LocalDate.now().format(DATE_FORMATTER)); @@ -251,7 +273,7 @@ public class GenerateReportUseCase { generated.setOrganization(org); generated.setProject(project); generated.setUser(user); - generated.setTitle(template.getName() + " (" + strPeriodo + ")"); + generated.setTitle(template.getName() + " (" + formattedPeriod + ")"); generated.setStartDate(input.startDate()); generated.setEndDate(input.endDate()); generated.setFileUrl(fileUrl); @@ -352,4 +374,34 @@ public class GenerateReportUseCase { if (text == null || text.isEmpty()) return ""; return text.substring(0, 1).toUpperCase(PT_BR) + text.substring(1); } + + private String formatPeriodWithPattern(LocalDate start, LocalDate end, String pattern) { + if (pattern == null || pattern.isBlank()) { + pattern = "DD/MM/AAAA até DD/MM/AAAA"; + } + + String strStart = start.format(DATE_FORMATTER); + String strEnd = end.format(DATE_FORMATTER); + + return switch (pattern) { + case "DD/MM/AAAA até DD/MM/AAAA" -> strStart + " até " + strEnd; + case "DD/MM/AAAA a DD/MM/AAAA" -> strStart + " a " + strEnd; + case "DD de Mês de AAAA até DD de Mês de AAAA" -> { + String mStart = capitalize(start.getMonth().getDisplayName(TextStyle.FULL, PT_BR)); + String mEnd = capitalize(end.getMonth().getDisplayName(TextStyle.FULL, PT_BR)); + yield String.format("%02d de %s de %d até %02d de %s de %d", + start.getDayOfMonth(), mStart, start.getYear(), + end.getDayOfMonth(), mEnd, end.getYear()); + } + case "MM/AAAA" -> String.format("%02d/%d", start.getMonthValue(), start.getYear()); + case "Mês de AAAA" -> { + String mStart = capitalize(start.getMonth().getDisplayName(TextStyle.FULL, PT_BR)); + yield mStart + " de " + start.getYear(); + } + case "DD/MM - DD/MM/AAAA" -> String.format("%02d/%02d - %02d/%02d/%d", + start.getDayOfMonth(), start.getMonthValue(), + end.getDayOfMonth(), end.getMonthValue(), end.getYear()); + default -> strStart + " até " + strEnd; + }; + } } diff --git a/backend/src/main/resources/db/migration/V11__add_report_template_header_fields.sql b/backend/src/main/resources/db/migration/V11__add_report_template_header_fields.sql new file mode 100644 index 0000000..e36b7cd --- /dev/null +++ b/backend/src/main/resources/db/migration/V11__add_report_template_header_fields.sql @@ -0,0 +1,3 @@ +ALTER TABLE report_templates ADD COLUMN manager_name VARCHAR(255); +ALTER TABLE report_templates ADD COLUMN custom_project_name VARCHAR(255); +ALTER TABLE report_templates ADD COLUMN date_format_pattern VARCHAR(100) DEFAULT 'DD/MM/AAAA até DD/MM/AAAA'; diff --git a/frontend/src/components/reports/CreateTemplateModal.tsx b/frontend/src/components/reports/CreateTemplateModal.tsx index 890272f..6fdd5b5 100644 --- a/frontend/src/components/reports/CreateTemplateModal.tsx +++ b/frontend/src/components/reports/CreateTemplateModal.tsx @@ -8,6 +8,9 @@ import { Tag, AlertCircle, Folder, + UserCheck, + Calendar, + User, } from 'lucide-react' import { CustomSelect } from '../ui/Select' import { reportService, type ReportTemplateOutput } from '../../services/reportService' @@ -43,6 +46,9 @@ export const CreateTemplateModal: React.FC = ({ const [description, setDescription] = useState('') const [scope, setScope] = useState<'ORGANIZATION' | 'PROJECT'>('ORGANIZATION') const [projectId, setProjectId] = useState(undefined) + const [managerName, setManagerName] = useState('') + const [customProjectName, setCustomProjectName] = useState('') + const [dateFormatPattern, setDateFormatPattern] = useState('DD/MM/AAAA até DD/MM/AAAA') const [file, setFile] = useState(null) const [sections, setSections] = useState([ { @@ -129,6 +135,9 @@ export const CreateTemplateModal: React.FC = ({ formData.append('name', name.trim()) formData.append('description', description.trim()) formData.append('scope', scope) + formData.append('managerName', managerName.trim()) + formData.append('customProjectName', customProjectName.trim()) + formData.append('dateFormatPattern', dateFormatPattern) if (scope === 'PROJECT' && projectId) { formData.append('projectId', String(projectId)) } @@ -261,6 +270,93 @@ export const CreateTemplateModal: React.FC = ({ /> + {/* Seção Padrão (Identificação, Gestão e Período) */} +
+
+ +
+

+ Seção Padrão: Identificação e Gestão +

+

+ Valores configurados no modelo que alimentam o cabeçalho e identificação no Word. +

+
+
+ +
+ {/* Colaborador / Usuário (Automático) */} +
+ +
+ Preenchido automaticamente com o usuário logado +
+
+ + {/* Nome do Gestor */} +
+ + setManagerName(e.target.value)} + placeholder="Ex.: Dr. Roberto Silva" + className="w-full px-3 py-2 rounded-xl border border-border-soft bg-surface text-xs text-foreground focus:outline-hidden focus:border-brand shadow-xs" + /> +
+ + {/* Nome do Projeto */} +
+ + setCustomProjectName(e.target.value)} + placeholder="Ex.: Projeto BeeTask Inovação Tecnológica" + className="w-full px-3 py-2 rounded-xl border border-border-soft bg-surface text-xs text-foreground focus:outline-hidden focus:border-brand shadow-xs" + /> +
+ + {/* Formato da Data do Período */} +
+ + setDateFormatPattern(String(val))} + options={[ + { value: 'DD/MM/AAAA até DD/MM/AAAA', label: 'DD/MM/AAAA até DD/MM/AAAA (ex: 01/10/2026 até 31/10/2026)' }, + { value: 'DD/MM/AAAA a DD/MM/AAAA', label: 'DD/MM/AAAA a DD/MM/AAAA (ex: 01/10/2026 a 31/10/2026)' }, + { value: 'DD de Mês de AAAA até DD de Mês de AAAA', label: '01 de Outubro de 2026 até 31 de Outubro de 2026' }, + { value: 'MM/AAAA', label: 'MM/AAAA (ex: 10/2026)' }, + { value: 'Mês de AAAA', label: 'Mês de AAAA (ex: Outubro de 2026)' }, + { value: 'DD/MM - DD/MM/AAAA', label: 'DD/MM - DD/MM/AAAA (ex: 01/10 - 31/10/2026)' }, + ]} + /> +
+
+
+ {/* Upload de Arquivo .DOCX */}
+ {/* Dados da Seção Padrão */} +
+ {tpl.managerName && ( +
+ Gestor: + {tpl.managerName} +
+ )} + {(tpl.customProjectName || tpl.projectName) && ( +
+ Projeto: + {tpl.customProjectName || tpl.projectName} +
+ )} +
+ Padrão de Data: + {tpl.dateFormatPattern || 'DD/MM/AAAA até DD/MM/AAAA'} +
+
+ {/* Tags configuradas */}
diff --git a/frontend/src/services/reportService.ts b/frontend/src/services/reportService.ts index 9dd737f..f15096d 100644 --- a/frontend/src/services/reportService.ts +++ b/frontend/src/services/reportService.ts @@ -27,6 +27,9 @@ export interface ReportTemplateOutput { storageKey: string createdById?: number createdByName?: string + managerName?: string + customProjectName?: string + dateFormatPattern?: string sections: ReportSectionOutput[] createdAt: string updatedAt: string