diff --git a/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportSection.java b/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportSection.java index 6d41d4b..e61fe62 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportSection.java +++ b/backend/src/main/java/net/beetechgroup/beetask/entities/report/ReportSection.java @@ -8,8 +8,9 @@ public class ReportSection { private Long reportTemplateId; private String sectionKey; private String title; - private String sectionType; // 'FREE_TEXT', 'FIXED_TEXT', 'TASK_LIST' + private String sectionType; // 'FREE_TEXT', 'FIXED_TEXT', 'DATE', 'TASK_LIST' private String defaultValue; + private String dateFormat; private Category filterCategory; private String filterStatus; // 'ALL', 'COMPLETED', 'IN_PROGRESS' private Boolean includeTime; @@ -96,6 +97,14 @@ public class ReportSection { this.orderIndex = orderIndex; } + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + public LocalDateTime getCreatedAt() { return createdAt; } diff --git a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportSectionEntity.java b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportSectionEntity.java index bf3bea5..6154237 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportSectionEntity.java +++ b/backend/src/main/java/net/beetechgroup/beetask/frameworks/persistence/entities/ReportSectionEntity.java @@ -30,11 +30,14 @@ public class ReportSectionEntity { private String title; @Column(name = "section_type", nullable = false, length = 50) - private String sectionType; // 'FREE_TEXT', 'FIXED_TEXT', 'TASK_LIST' + private String sectionType; // 'FREE_TEXT', 'FIXED_TEXT', 'DATE', 'TASK_LIST' @Column(name = "default_value", columnDefinition = "TEXT") private String defaultValue; + @Column(name = "date_format", length = 100) + private String dateFormat; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "filter_category_id") private CategoryEntity filterCategory; @@ -131,6 +134,14 @@ public class ReportSectionEntity { this.orderIndex = orderIndex; } + public String getDateFormat() { + return dateFormat; + } + + public void setDateFormat(String dateFormat) { + this.dateFormat = dateFormat; + } + public LocalDateTime getCreatedAt() { return createdAt; } 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 0e88cf7..8c7550a 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 @@ -53,6 +53,7 @@ public class ReportEntityMapper { entity.setTitle(domain.getTitle()); entity.setSectionType(domain.getSectionType()); entity.setDefaultValue(domain.getDefaultValue()); + entity.setDateFormat(domain.getDateFormat()); entity.setFilterStatus(domain.getFilterStatus()); entity.setIncludeTime(domain.getIncludeTime()); entity.setOrderIndex(domain.getOrderIndex()); @@ -106,6 +107,7 @@ public class ReportEntityMapper { domain.setTitle(entity.getTitle()); domain.setSectionType(entity.getSectionType()); domain.setDefaultValue(entity.getDefaultValue()); + domain.setDateFormat(entity.getDateFormat()); domain.setFilterStatus(entity.getFilterStatus()); domain.setIncludeTime(entity.getIncludeTime()); domain.setOrderIndex(entity.getOrderIndex()); 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 e0b8666..35022c2 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 @@ -47,6 +47,7 @@ public class ReportMapper { section.getTitle(), section.getSectionType(), section.getDefaultValue(), + section.getDateFormat(), section.getFilterCategory() != null ? section.getFilterCategory().getId() : null, section.getFilterCategory() != null ? section.getFilterCategory().getName() : null, section.getFilterStatus(), diff --git a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportSectionOutput.java b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportSectionOutput.java index e591619..b5279a7 100644 --- a/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportSectionOutput.java +++ b/backend/src/main/java/net/beetechgroup/beetask/usecase/report/ReportSectionOutput.java @@ -8,6 +8,7 @@ public record ReportSectionOutput( String title, String sectionType, String defaultValue, + String dateFormat, Long filterCategoryId, String filterCategoryName, String filterStatus, 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 8aad81b..84645fb 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 @@ -23,6 +23,7 @@ public record CreateReportTemplateInput( String title, String sectionType, String defaultValue, + String dateFormat, Long filterCategoryId, String filterStatus, Boolean includeTime, 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 7f76559..84bf615 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 @@ -108,6 +108,7 @@ public class CreateReportTemplateUseCase { section.setTitle(sInput.title() != null ? sInput.title().trim() : section.getSectionKey()); section.setSectionType(sInput.sectionType() != null ? sInput.sectionType() : "FREE_TEXT"); section.setDefaultValue(sInput.defaultValue() != null ? sInput.defaultValue().trim() : null); + section.setDateFormat(sInput.dateFormat() != null ? sInput.dateFormat().trim() : null); section.setFilterStatus(sInput.filterStatus() != null ? sInput.filterStatus() : "ALL"); section.setIncludeTime(sInput.includeTime() != null ? sInput.includeTime() : true); section.setOrderIndex(sInput.orderIndex() != null ? sInput.orderIndex() : order++); 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 f18748d..af8a20e 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 @@ -185,6 +185,39 @@ public class GenerateReportUseCase { if (("FREE_TEXT".equalsIgnoreCase(sec.getSectionType()) || "FIXED_TEXT".equalsIgnoreCase(sec.getSectionType())) && sec.getDefaultValue() != null) { variables.put(sec.getSectionKey(), sec.getDefaultValue()); + } else if ("DATE".equalsIgnoreCase(sec.getSectionType())) { + String datePattern = sec.getDateFormat() != null && !sec.getDateFormat().isBlank() + ? sec.getDateFormat().trim() + : "DD/MM/AAAA"; + String formattedVal; + String customVal = input.customFields() != null ? input.customFields().get(sec.getSectionKey()) : null; + if (customVal != null && !customVal.isBlank()) { + try { + LocalDate d = LocalDate.parse(customVal.trim()); + formattedVal = formatSingleDate(d, datePattern); + } catch (Exception e) { + formattedVal = customVal.trim(); + } + } else { + if (isPeriodPattern(datePattern) || sec.getSectionKey().contains("periodo") || sec.getSectionKey().contains("vigencia")) { + formattedVal = formatPeriodWithPattern(input.startDate(), input.endDate(), datePattern); + } else if (sec.getSectionKey().contains("fim") || sec.getSectionKey().contains("termino")) { + formattedVal = formatSingleDate(input.endDate(), datePattern); + } else if (sec.getSectionKey().contains("inicio")) { + formattedVal = formatSingleDate(input.startDate(), datePattern); + } else if (sec.getSectionKey().contains("emissao") || sec.getSectionKey().contains("hoje")) { + formattedVal = formatSingleDate(LocalDate.now(), datePattern); + } else { + formattedVal = isPeriodPattern(datePattern) + ? formatPeriodWithPattern(input.startDate(), input.endDate(), datePattern) + : formatSingleDate(input.endDate(), datePattern); + } + } + variables.put(sec.getSectionKey(), formattedVal); + if ("periodo".equalsIgnoreCase(sec.getSectionKey()) || "periodo_vigencia".equalsIgnoreCase(sec.getSectionKey())) { + variables.put("periodo", formattedVal); + variables.put("periodo_vigencia", formattedVal); + } } } } @@ -387,17 +420,52 @@ public class GenerateReportUseCase { return text.substring(0, 1).toUpperCase(PT_BR) + text.substring(1); } + private boolean isPeriodPattern(String pattern) { + if (pattern == null) return false; + return pattern.contains("até") || pattern.contains(" a ") || pattern.contains(" - "); + } + + private String formatSingleDate(LocalDate date, String pattern) { + if (date == null) return ""; + if (pattern == null || pattern.isBlank()) pattern = "DD/MM/AAAA"; + + return switch (pattern) { + case "DD/MM/AAAA" -> date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")); + case "DD-MM-AAAA" -> date.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); + case "AAAA-MM-DD" -> date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + case "DD de Mês de AAAA" -> { + String m = capitalize(date.getMonth().getDisplayName(TextStyle.FULL, PT_BR)); + yield String.format("%02d de %s de %d", date.getDayOfMonth(), m, date.getYear()); + } + case "MM/AAAA" -> date.format(DateTimeFormatter.ofPattern("MM/yyyy")); + case "Mês de AAAA" -> { + String m = capitalize(date.getMonth().getDisplayName(TextStyle.FULL, PT_BR)); + yield m + " de " + date.getYear(); + } + default -> { + if (pattern.contains("-")) { + yield date.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); + } + yield date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")); + } + }; + } + 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); + String strSlashStart = start.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")); + String strSlashEnd = end.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")); + String strDashStart = start.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); + String strDashEnd = end.format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); 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/MM/AAAA até DD/MM/AAAA" -> strSlashStart + " até " + strSlashEnd; + case "DD-MM-AAAA até DD-MM-AAAA" -> strDashStart + " até " + strDashEnd; + case "DD/MM/AAAA a DD/MM/AAAA" -> strSlashStart + " a " + strSlashEnd; + case "DD-MM-AAAA a DD-MM-AAAA" -> strDashStart + " a " + strDashEnd; 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)); @@ -413,7 +481,10 @@ public class GenerateReportUseCase { 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; + case "DD-MM-AAAA" -> strDashStart; + case "DD/MM/AAAA" -> strSlashStart; + case "AAAA-MM-DD" -> start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + default -> strSlashStart + " até " + strSlashEnd; }; } } diff --git a/backend/src/main/resources/db/migration/V13__add_report_section_date_format.sql b/backend/src/main/resources/db/migration/V13__add_report_section_date_format.sql new file mode 100644 index 0000000..c07ebfb --- /dev/null +++ b/backend/src/main/resources/db/migration/V13__add_report_section_date_format.sql @@ -0,0 +1 @@ +ALTER TABLE report_sections ADD COLUMN date_format VARCHAR(100); diff --git a/frontend/src/components/reports/CreateTemplateModal.tsx b/frontend/src/components/reports/CreateTemplateModal.tsx index 4fe80b7..7276d65 100644 --- a/frontend/src/components/reports/CreateTemplateModal.tsx +++ b/frontend/src/components/reports/CreateTemplateModal.tsx @@ -28,8 +28,9 @@ interface CreateTemplateModalProps { interface SectionFormItem { sectionKey: string title: string - sectionType: 'FREE_TEXT' | 'FIXED_TEXT' | 'TASK_LIST' + sectionType: 'FREE_TEXT' | 'FIXED_TEXT' | 'DATE' | 'TASK_LIST' defaultValue?: string + dateFormat?: string filterCategoryId?: number filterStatus?: 'ALL' | 'COMPLETED' | 'IN_PROGRESS' includeTime: boolean @@ -47,7 +48,6 @@ export const CreateTemplateModal: React.FC = ({ const [description, setDescription] = useState('') const [scope, setScope] = useState<'ORGANIZATION' | 'PROJECT'>('ORGANIZATION') const [projectId, setProjectId] = useState(undefined) - const [dateFormatPattern, setDateFormatPattern] = useState('DD/MM/AAAA até DD/MM/AAAA') const [file, setFile] = useState(null) const [sections, setSections] = useState([]) @@ -65,6 +65,7 @@ export const CreateTemplateModal: React.FC = ({ title: `Campo ${nextIndex}`, sectionType: 'FREE_TEXT', defaultValue: '', + dateFormat: 'DD/MM/AAAA', includeTime: false, }, ]) @@ -115,17 +116,17 @@ export const CreateTemplateModal: React.FC = ({ formData.append('name', name.trim()) formData.append('description', description.trim()) formData.append('scope', scope) - formData.append('dateFormatPattern', dateFormatPattern) if (scope === 'PROJECT' && projectId) { formData.append('projectId', String(projectId)) } - // Sanitizar seções com chave segura e valor padrão + // Sanitizar seções com chave segura, valor padrão e formato de data const sanitizedSections = sections.map((s, idx) => ({ sectionKey: s.sectionKey.trim().toLowerCase().replace(/[^a-z0-9_]/g, '_'), title: s.title.trim() || s.sectionKey, sectionType: s.sectionType, defaultValue: s.defaultValue ? s.defaultValue.trim() : '', + dateFormat: s.sectionType === 'DATE' ? (s.dateFormat || 'DD/MM/AAAA') : undefined, filterCategoryId: s.filterCategoryId, filterStatus: s.filterStatus || 'ALL', includeTime: s.includeTime ?? true, @@ -162,7 +163,7 @@ export const CreateTemplateModal: React.FC = ({ Cadastrar Modelo de Relatório (.DOCX)

- Envie seu arquivo Word e configure os campos dinâmicos e valores pré-preenchidos. + Envie seu arquivo Word e configure os campos dinâmicos, tipos e formatos.

@@ -235,56 +236,32 @@ export const CreateTemplateModal: React.FC = ({ )} - {/* Descrição e Formato do Período */} -
-
- - setDescription(e.target.value)} - placeholder="Ex.: Modelo oficial para prestação de contas" - className="w-full px-3.5 py-2 rounded-xl border border-border-soft bg-surface text-xs text-foreground focus:outline-hidden focus:border-brand shadow-xs" - /> -
- -
- - 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)' }, - ]} - /> -
+ {/* Descrição */} +
+ + setDescription(e.target.value)} + placeholder="Ex.: Modelo padrão exigido para prestação de contas mensal." + className="w-full px-3.5 py-2 rounded-xl border border-border-soft bg-surface text-xs text-foreground focus:outline-hidden focus:border-brand shadow-xs" + />
{/* Dica de Tags Nativas */}
-

Tags Padrão Automáticas Disponíveis no Word:

+

Tags Nativas Automáticas Disponíveis no Word:

Você pode utilizar diretamente as tags{' '} {'{{nome}}'} (usuário logado),{' '} - {'{{periodo}}'} (período formatado),{' '} + {'{{periodo}}'} (período de vigência),{' '} {'{{data_inicio}}'},{' '} {'{{data_fim}}'} e{' '} - {'{{total_horas}}'} sem precisar cadastrar seções para elas. + {'{{total_horas}}'} sem precisar cadastrar campos para elas.

@@ -324,7 +301,7 @@ export const CreateTemplateModal: React.FC = ({

Campos e Seções do Relatório

- Crie tags que serão substituídas pelo Word. Escolha se o campo é texto livre, texto fixo pré-preenchido ou listagem de tarefas. + Crie tags que serão substituídas pelo Word. Escolha se o campo é texto livre, texto fixo, data (com formato personalizado) ou listagem de tarefas.

) : ( @@ -381,7 +358,7 @@ export const CreateTemplateModal: React.FC = ({ sectionKey: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '_'), }) } - placeholder="ex: gestor ou resumo" + placeholder="ex: gestor ou periodo" className="w-full px-2.5 py-1.5 rounded-lg border border-border-soft bg-surface text-xs font-mono text-foreground focus:outline-hidden focus:border-brand" /> @@ -392,7 +369,7 @@ export const CreateTemplateModal: React.FC = ({ type="text" value={section.title} onChange={(e) => handleUpdateSection(idx, { title: e.target.value })} - placeholder="ex: Nome do Gestor" + placeholder="ex: Período de Vigência" className="w-full px-2.5 py-1.5 rounded-lg border border-border-soft bg-surface text-xs text-foreground focus:outline-hidden focus:border-brand" /> @@ -404,18 +381,51 @@ export const CreateTemplateModal: React.FC = ({ value={section.sectionType} onChange={(val) => handleUpdateSection(idx, { - sectionType: val as 'FREE_TEXT' | 'FIXED_TEXT' | 'TASK_LIST', + sectionType: val as 'FREE_TEXT' | 'FIXED_TEXT' | 'DATE' | 'TASK_LIST', }) } options={[ { value: 'FREE_TEXT', label: 'Texto Livre (Preenchido na emissão)' }, { value: 'FIXED_TEXT', label: 'Texto Fixo (Pré-preenchido no cadastro)' }, + { value: 'DATE', label: 'Data (Com formato personalizado)' }, { value: 'TASK_LIST', label: 'Lista de Tarefas do Período' }, ]} /> + {/* Configuração de Campo de Tipo DATA */} + {section.sectionType === 'DATE' && ( +
+ + handleUpdateSection(idx, { dateFormat: String(val) })} + options={[ + { value: 'DD/MM/AAAA', label: 'DD/MM/AAAA (ex: 31/10/2026)' }, + { value: 'DD-MM-AAAA', label: 'DD-MM-AAAA (ex: 31-10-2026)' }, + { value: 'AAAA-MM-DD', label: 'AAAA-MM-DD (ex: 2026-10-31)' }, + { 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 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-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', label: 'DD de Mês de AAAA (ex: 31 de Outubro de 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)' }, + ]} + /> +
+ )} + {/* Campo de Valor Pré-preenchido (Fixo ou Sugerido) */} {(section.sectionType === 'FIXED_TEXT' || section.sectionType === 'FREE_TEXT') && (
diff --git a/frontend/src/components/reports/GenerateReportModal.tsx b/frontend/src/components/reports/GenerateReportModal.tsx index 2d26f8f..d1ad782 100644 --- a/frontend/src/components/reports/GenerateReportModal.tsx +++ b/frontend/src/components/reports/GenerateReportModal.tsx @@ -94,6 +94,12 @@ export const GenerateReportModal: React.FC = ({ ) }, [selectedTemplate]) + // Seções de data do template selecionado + const dateSections = useMemo(() => { + if (!selectedTemplate) return [] + return selectedTemplate.sections.filter((s) => s.sectionType === 'DATE') + }, [selectedTemplate]) + // Lista de seções de tarefas do template selecionado const taskListSections = useMemo(() => { if (!selectedTemplate) return [] @@ -358,6 +364,70 @@ export const GenerateReportModal: React.FC = ({
+ {/* Campos de Data Customizados do Modelo */} + {dateSections.length > 0 && ( +
+
+

+ + Datas Configuradas no Modelo +

+

+ Campos formatados de acordo com as regras definidas no modelo. +

+
+ +
+ {dateSections.map((sec) => { + const isPeriod = + sec.dateFormat?.includes('até') || + sec.dateFormat?.includes(' a ') || + sec.dateFormat?.includes(' - ') || + sec.sectionKey.includes('periodo') || + sec.sectionKey.includes('vigencia') + + return ( +
+ + + {isPeriod ? ( +
+ Período de Vigência + + {sec.dateFormat || 'DD/MM/AAAA'} + +
+ ) : ( +
+ handleCustomFieldChange(sec.sectionKey, e.target.value)} + 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 no Word:{' '} + {sec.dateFormat || 'DD/MM/AAAA'} + +
+ )} +
+ ) + })} +
+
+ )} + {/* Seções de Texto (Livre ou Pré-preenchido) Dinâmicas */} {textSections.length > 0 && (
diff --git a/frontend/src/pages/ReportsPage.tsx b/frontend/src/pages/ReportsPage.tsx index 61c2e42..f75f35e 100644 --- a/frontend/src/pages/ReportsPage.tsx +++ b/frontend/src/pages/ReportsPage.tsx @@ -272,25 +272,23 @@ export const ReportsPage: React.FC = () => { )}
- {/* 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'} + {/* Informações adicionais se houver */} + {(tpl.managerName || tpl.customProjectName || tpl.projectName) && ( +
+ {tpl.managerName && ( +
+ Gestor: + {tpl.managerName} +
+ )} + {(tpl.customProjectName || tpl.projectName) && ( +
+ Projeto: + {tpl.customProjectName || tpl.projectName} +
+ )}
-
+ )} {/* Tags configuradas */}
@@ -301,10 +299,13 @@ export const ReportsPage: React.FC = () => { {tpl.sections.map((s) => ( {`{{${s.sectionKey}}}`} + {s.sectionType === 'DATE' && ( + [{s.dateFormat || 'data'}] + )} {s.defaultValue && ( )} diff --git a/frontend/src/services/reportService.ts b/frontend/src/services/reportService.ts index 23aa4af..224df46 100644 --- a/frontend/src/services/reportService.ts +++ b/frontend/src/services/reportService.ts @@ -4,8 +4,9 @@ export interface ReportSectionOutput { id: number sectionKey: string title: string - sectionType: 'FREE_TEXT' | 'FIXED_TEXT' | 'TASK_LIST' + sectionType: 'FREE_TEXT' | 'FIXED_TEXT' | 'DATE' | 'TASK_LIST' defaultValue?: string + dateFormat?: string filterCategoryId?: number filterCategoryName?: string filterStatus?: 'ALL' | 'COMPLETED' | 'IN_PROGRESS'