feat(reports): add standard header section with manager, project name and configurable period format
This commit is contained in:
parent
55fd823b2d
commit
5b4135b125
14 changed files with 277 additions and 11 deletions
|
|
@ -18,6 +18,9 @@ public class ReportTemplate {
|
||||||
private String fileName;
|
private String fileName;
|
||||||
private String storageKey;
|
private String storageKey;
|
||||||
private User createdBy;
|
private User createdBy;
|
||||||
|
private String managerName;
|
||||||
|
private String customProjectName;
|
||||||
|
private String dateFormatPattern;
|
||||||
private List<ReportSection> sections = new ArrayList<>();
|
private List<ReportSection> sections = new ArrayList<>();
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
@ -102,6 +105,30 @@ public class ReportTemplate {
|
||||||
this.createdBy = createdBy;
|
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<ReportSection> getSections() {
|
public List<ReportSection> getSections() {
|
||||||
return sections;
|
return sections;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,15 @@ public class ReportTemplateEntity {
|
||||||
@JoinColumn(name = "created_by")
|
@JoinColumn(name = "created_by")
|
||||||
private UserEntity createdBy;
|
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)
|
@OneToMany(mappedBy = "reportTemplate", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
@OrderBy("orderIndex ASC")
|
@OrderBy("orderIndex ASC")
|
||||||
private List<ReportSectionEntity> sections = new ArrayList<>();
|
private List<ReportSectionEntity> sections = new ArrayList<>();
|
||||||
|
|
@ -144,6 +153,30 @@ public class ReportTemplateEntity {
|
||||||
this.createdBy = createdBy;
|
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<ReportSectionEntity> getSections() {
|
public List<ReportSectionEntity> getSections() {
|
||||||
return sections;
|
return sections;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ public class ReportEntityMapper {
|
||||||
entity.setFileUrl(domain.getFileUrl());
|
entity.setFileUrl(domain.getFileUrl());
|
||||||
entity.setFileName(domain.getFileName());
|
entity.setFileName(domain.getFileName());
|
||||||
entity.setStorageKey(domain.getStorageKey());
|
entity.setStorageKey(domain.getStorageKey());
|
||||||
|
entity.setManagerName(domain.getManagerName());
|
||||||
|
entity.setCustomProjectName(domain.getCustomProjectName());
|
||||||
|
entity.setDateFormatPattern(domain.getDateFormatPattern());
|
||||||
entity.setCreatedAt(domain.getCreatedAt());
|
entity.setCreatedAt(domain.getCreatedAt());
|
||||||
entity.setUpdatedAt(domain.getUpdatedAt());
|
entity.setUpdatedAt(domain.getUpdatedAt());
|
||||||
|
|
||||||
|
|
@ -70,6 +73,9 @@ public class ReportEntityMapper {
|
||||||
domain.setFileUrl(entity.getFileUrl());
|
domain.setFileUrl(entity.getFileUrl());
|
||||||
domain.setFileName(entity.getFileName());
|
domain.setFileName(entity.getFileName());
|
||||||
domain.setStorageKey(entity.getStorageKey());
|
domain.setStorageKey(entity.getStorageKey());
|
||||||
|
domain.setManagerName(entity.getManagerName());
|
||||||
|
domain.setCustomProjectName(entity.getCustomProjectName());
|
||||||
|
domain.setDateFormatPattern(entity.getDateFormatPattern());
|
||||||
domain.setCreatedAt(entity.getCreatedAt());
|
domain.setCreatedAt(entity.getCreatedAt());
|
||||||
domain.setUpdatedAt(entity.getUpdatedAt());
|
domain.setUpdatedAt(entity.getUpdatedAt());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,15 @@ public class CreateReportTemplateForm {
|
||||||
@RestForm("projectId")
|
@RestForm("projectId")
|
||||||
public Long projectId;
|
public Long projectId;
|
||||||
|
|
||||||
|
@RestForm("managerName")
|
||||||
|
public String managerName;
|
||||||
|
|
||||||
|
@RestForm("customProjectName")
|
||||||
|
public String customProjectName;
|
||||||
|
|
||||||
|
@RestForm("dateFormatPattern")
|
||||||
|
public String dateFormatPattern;
|
||||||
|
|
||||||
@RestForm("sectionsJson")
|
@RestForm("sectionsJson")
|
||||||
public String sectionsJson;
|
public String sectionsJson;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@ public class ReportController {
|
||||||
form.name,
|
form.name,
|
||||||
form.description,
|
form.description,
|
||||||
form.scope,
|
form.scope,
|
||||||
|
form.managerName,
|
||||||
|
form.customProjectName,
|
||||||
|
form.dateFormatPattern,
|
||||||
form.file.fileName(),
|
form.file.fileName(),
|
||||||
is,
|
is,
|
||||||
form.file.size(),
|
form.file.size(),
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ public class ReportMapper {
|
||||||
template.getStorageKey(),
|
template.getStorageKey(),
|
||||||
template.getCreatedBy() != null ? template.getCreatedBy().getId() : null,
|
template.getCreatedBy() != null ? template.getCreatedBy().getId() : null,
|
||||||
template.getCreatedBy() != null ? template.getCreatedBy().getName() : null,
|
template.getCreatedBy() != null ? template.getCreatedBy().getName() : null,
|
||||||
|
template.getManagerName(),
|
||||||
|
template.getCustomProjectName(),
|
||||||
|
template.getDateFormatPattern(),
|
||||||
sections,
|
sections,
|
||||||
template.getCreatedAt(),
|
template.getCreatedAt(),
|
||||||
template.getUpdatedAt()
|
template.getUpdatedAt()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ public record ReportTemplateOutput(
|
||||||
String storageKey,
|
String storageKey,
|
||||||
Long createdById,
|
Long createdById,
|
||||||
String createdByName,
|
String createdByName,
|
||||||
|
String managerName,
|
||||||
|
String customProjectName,
|
||||||
|
String dateFormatPattern,
|
||||||
List<ReportSectionOutput> sections,
|
List<ReportSectionOutput> sections,
|
||||||
LocalDateTime createdAt,
|
LocalDateTime createdAt,
|
||||||
LocalDateTime updatedAt
|
LocalDateTime updatedAt
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ public record CreateReportTemplateInput(
|
||||||
String name,
|
String name,
|
||||||
String description,
|
String description,
|
||||||
String scope,
|
String scope,
|
||||||
|
String managerName,
|
||||||
|
String customProjectName,
|
||||||
|
String dateFormatPattern,
|
||||||
String fileName,
|
String fileName,
|
||||||
InputStream fileInputStream,
|
InputStream fileInputStream,
|
||||||
long fileSize,
|
long fileSize,
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ public class CreateReportTemplateUseCase {
|
||||||
template.setStorageKey(storageKey);
|
template.setStorageKey(storageKey);
|
||||||
template.setFileUrl(fileUrl);
|
template.setFileUrl(fileUrl);
|
||||||
template.setCreatedBy(user);
|
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.setCreatedAt(LocalDateTime.now());
|
||||||
template.setUpdatedAt(LocalDateTime.now());
|
template.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,19 +141,41 @@ public class GenerateReportUseCase {
|
||||||
Map<String, String> variables = new HashMap<>();
|
Map<String, String> variables = new HashMap<>();
|
||||||
String strStart = input.startDate().format(DATE_FORMATTER);
|
String strStart = input.startDate().format(DATE_FORMATTER);
|
||||||
String strEnd = input.endDate().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 monthName = input.startDate().getMonth().getDisplayName(TextStyle.FULL, PT_BR);
|
||||||
String mesAno = capitalize(monthName) + "/" + input.startDate().getYear();
|
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_inicio", strStart);
|
||||||
variables.put("data_fim", strEnd);
|
variables.put("data_fim", strEnd);
|
||||||
variables.put("periodo_vigencia", strPeriodo);
|
|
||||||
variables.put("mes_ano", mesAno);
|
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("total_horas", formattedTotalTime);
|
||||||
variables.put("data_emissao", LocalDate.now().format(DATE_FORMATTER));
|
variables.put("data_emissao", LocalDate.now().format(DATE_FORMATTER));
|
||||||
|
|
||||||
|
|
@ -251,7 +273,7 @@ public class GenerateReportUseCase {
|
||||||
generated.setOrganization(org);
|
generated.setOrganization(org);
|
||||||
generated.setProject(project);
|
generated.setProject(project);
|
||||||
generated.setUser(user);
|
generated.setUser(user);
|
||||||
generated.setTitle(template.getName() + " (" + strPeriodo + ")");
|
generated.setTitle(template.getName() + " (" + formattedPeriod + ")");
|
||||||
generated.setStartDate(input.startDate());
|
generated.setStartDate(input.startDate());
|
||||||
generated.setEndDate(input.endDate());
|
generated.setEndDate(input.endDate());
|
||||||
generated.setFileUrl(fileUrl);
|
generated.setFileUrl(fileUrl);
|
||||||
|
|
@ -352,4 +374,34 @@ public class GenerateReportUseCase {
|
||||||
if (text == null || text.isEmpty()) return "";
|
if (text == null || text.isEmpty()) return "";
|
||||||
return text.substring(0, 1).toUpperCase(PT_BR) + text.substring(1);
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
@ -8,6 +8,9 @@ import {
|
||||||
Tag,
|
Tag,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
Folder,
|
Folder,
|
||||||
|
UserCheck,
|
||||||
|
Calendar,
|
||||||
|
User,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { CustomSelect } from '../ui/Select'
|
import { CustomSelect } from '../ui/Select'
|
||||||
import { reportService, type ReportTemplateOutput } from '../../services/reportService'
|
import { reportService, type ReportTemplateOutput } from '../../services/reportService'
|
||||||
|
|
@ -43,6 +46,9 @@ export const CreateTemplateModal: React.FC<CreateTemplateModalProps> = ({
|
||||||
const [description, setDescription] = useState('')
|
const [description, setDescription] = useState('')
|
||||||
const [scope, setScope] = useState<'ORGANIZATION' | 'PROJECT'>('ORGANIZATION')
|
const [scope, setScope] = useState<'ORGANIZATION' | 'PROJECT'>('ORGANIZATION')
|
||||||
const [projectId, setProjectId] = useState<number | undefined>(undefined)
|
const [projectId, setProjectId] = useState<number | undefined>(undefined)
|
||||||
|
const [managerName, setManagerName] = useState('')
|
||||||
|
const [customProjectName, setCustomProjectName] = useState('')
|
||||||
|
const [dateFormatPattern, setDateFormatPattern] = useState('DD/MM/AAAA até DD/MM/AAAA')
|
||||||
const [file, setFile] = useState<File | null>(null)
|
const [file, setFile] = useState<File | null>(null)
|
||||||
const [sections, setSections] = useState<SectionFormItem[]>([
|
const [sections, setSections] = useState<SectionFormItem[]>([
|
||||||
{
|
{
|
||||||
|
|
@ -129,6 +135,9 @@ export const CreateTemplateModal: React.FC<CreateTemplateModalProps> = ({
|
||||||
formData.append('name', name.trim())
|
formData.append('name', name.trim())
|
||||||
formData.append('description', description.trim())
|
formData.append('description', description.trim())
|
||||||
formData.append('scope', scope)
|
formData.append('scope', scope)
|
||||||
|
formData.append('managerName', managerName.trim())
|
||||||
|
formData.append('customProjectName', customProjectName.trim())
|
||||||
|
formData.append('dateFormatPattern', dateFormatPattern)
|
||||||
if (scope === 'PROJECT' && projectId) {
|
if (scope === 'PROJECT' && projectId) {
|
||||||
formData.append('projectId', String(projectId))
|
formData.append('projectId', String(projectId))
|
||||||
}
|
}
|
||||||
|
|
@ -261,6 +270,93 @@ export const CreateTemplateModal: React.FC<CreateTemplateModalProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Seção Padrão (Identificação, Gestão e Período) */}
|
||||||
|
<div className="p-4 rounded-2xl border border-brand/25 bg-brand-soft/20 dark:bg-brand/10 space-y-3.5">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<UserCheck className="w-4 h-4 text-brand" />
|
||||||
|
<div>
|
||||||
|
<h4 className="text-xs font-extrabold text-brand uppercase tracking-wider">
|
||||||
|
Seção Padrão: Identificação e Gestão
|
||||||
|
</h4>
|
||||||
|
<p className="text-[11px] text-muted">
|
||||||
|
Valores configurados no modelo que alimentam o cabeçalho e identificação no Word.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 pt-1">
|
||||||
|
{/* Colaborador / Usuário (Automático) */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-[11px] font-bold text-foreground flex items-center justify-between">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<User className="w-3 h-3 text-muted" /> Nome do Colaborador
|
||||||
|
</span>
|
||||||
|
<code className="text-[10px] text-brand font-mono font-bold">{'{{nome}}'}</code>
|
||||||
|
</label>
|
||||||
|
<div className="px-3 py-2 rounded-xl border border-border-soft bg-surface/60 text-xs text-muted font-medium">
|
||||||
|
Preenchido automaticamente com o usuário logado
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Nome do Gestor */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-[11px] font-bold text-foreground flex items-center justify-between">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<UserCheck className="w-3 h-3 text-muted" /> Nome do Gestor Responsável
|
||||||
|
</span>
|
||||||
|
<code className="text-[10px] text-brand font-mono font-bold">{'{{nome_gestor}}'}</code>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={managerName}
|
||||||
|
onChange={(e) => 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"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Nome do Projeto */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-[11px] font-bold text-foreground flex items-center justify-between">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Folder className="w-3 h-3 text-muted" /> Nome do Projeto no Relatório
|
||||||
|
</span>
|
||||||
|
<code className="text-[10px] text-brand font-mono font-bold">{'{{nome_projeto}}'}</code>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={customProjectName}
|
||||||
|
onChange={(e) => 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"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Formato da Data do Período */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-[11px] font-bold text-foreground flex items-center justify-between">
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<Calendar className="w-3 h-3 text-muted" /> Formato do Período
|
||||||
|
</span>
|
||||||
|
<code className="text-[10px] text-brand font-mono font-bold">{'{{periodo}}'}</code>
|
||||||
|
</label>
|
||||||
|
<CustomSelect
|
||||||
|
size="sm"
|
||||||
|
value={dateFormatPattern}
|
||||||
|
onChange={(val) => 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)' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Upload de Arquivo .DOCX */}
|
{/* Upload de Arquivo .DOCX */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-xs font-bold text-foreground flex items-center justify-between">
|
<label className="text-xs font-bold text-foreground flex items-center justify-between">
|
||||||
|
|
|
||||||
|
|
@ -94,14 +94,16 @@ export const ReportsPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const standardTags = [
|
const standardTags = [
|
||||||
|
{ tag: '{{nome}}', desc: 'Nome do colaborador (pega automaticamente do usuário logado)' },
|
||||||
|
{ tag: '{{nome_gestor}}', desc: 'Nome do gestor responsável (pré-definido no modelo)' },
|
||||||
|
{ tag: '{{nome_projeto}}', desc: 'Nome do projeto (pré-definido no cadastro do modelo)' },
|
||||||
|
{ tag: '{{periodo}}', desc: 'Período formatado no padrão escolhido no modelo (ex.: DD/MM/AAAA até DD/MM/AAAA)' },
|
||||||
{ tag: '{{data_inicio}}', desc: 'Data inicial do período escolhido (ex: 01/10/2026)' },
|
{ tag: '{{data_inicio}}', desc: 'Data inicial do período escolhido (ex: 01/10/2026)' },
|
||||||
{ tag: '{{data_fim}}', desc: 'Data final do período escolhido (ex: 31/10/2026)' },
|
{ tag: '{{data_fim}}', desc: 'Data final do período escolhido (ex: 31/10/2026)' },
|
||||||
{ tag: '{{periodo_vigencia}}', desc: 'Intervalo formatado (ex: 01/10/2026 a 31/10/2026)' },
|
{ tag: '{{periodo_vigencia}}', desc: 'Intervalo formatado no modelo (ex: 01/10/2026 até 31/10/2026)' },
|
||||||
{ tag: '{{mes_ano}}', desc: 'Mês e ano por extenso (ex: Outubro/2026)' },
|
{ tag: '{{mes_ano}}', desc: 'Mês e ano por extenso (ex: Outubro/2026)' },
|
||||||
{ tag: '{{nome_usuario}}', desc: 'Nome completo do usuário logado' },
|
{ tag: '{{email_usuario}}', desc: 'E-mail do usuário logado' },
|
||||||
{ tag: '{{email_usuario}}', desc: 'E-mail do usuário' },
|
|
||||||
{ tag: '{{organizacao}}', desc: 'Nome da organização / workspace ativo' },
|
{ tag: '{{organizacao}}', desc: 'Nome da organização / workspace ativo' },
|
||||||
{ tag: '{{projeto}}', desc: 'Nome do projeto selecionado ou Geral' },
|
|
||||||
{ tag: '{{total_horas}}', desc: 'Total de horas trabalhadas no período (ex: 142h 30m)' },
|
{ tag: '{{total_horas}}', desc: 'Total de horas trabalhadas no período (ex: 142h 30m)' },
|
||||||
{ tag: '{{data_emissao}}', desc: 'Data de geração do relatório (ex: 31/10/2026)' },
|
{ tag: '{{data_emissao}}', desc: 'Data de geração do relatório (ex: 31/10/2026)' },
|
||||||
]
|
]
|
||||||
|
|
@ -272,6 +274,26 @@ export const ReportsPage: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Dados da Seção Padrão */}
|
||||||
|
<div className="p-2.5 rounded-xl bg-surface-raised/60 border border-border-soft text-[11px] space-y-1">
|
||||||
|
{tpl.managerName && (
|
||||||
|
<div className="flex items-center justify-between gap-1 text-muted">
|
||||||
|
<span>Gestor:</span>
|
||||||
|
<span className="font-semibold text-foreground truncate">{tpl.managerName}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(tpl.customProjectName || tpl.projectName) && (
|
||||||
|
<div className="flex items-center justify-between gap-1 text-muted">
|
||||||
|
<span>Projeto:</span>
|
||||||
|
<span className="font-semibold text-foreground truncate">{tpl.customProjectName || tpl.projectName}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center justify-between gap-1 text-muted">
|
||||||
|
<span>Padrão de Data:</span>
|
||||||
|
<span className="font-mono text-[10px] text-brand truncate">{tpl.dateFormatPattern || 'DD/MM/AAAA até DD/MM/AAAA'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Tags configuradas */}
|
{/* Tags configuradas */}
|
||||||
<div className="space-y-1.5 pt-1">
|
<div className="space-y-1.5 pt-1">
|
||||||
<span className="text-[10px] font-bold text-muted uppercase tracking-wider block">
|
<span className="text-[10px] font-bold text-muted uppercase tracking-wider block">
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ export interface ReportTemplateOutput {
|
||||||
storageKey: string
|
storageKey: string
|
||||||
createdById?: number
|
createdById?: number
|
||||||
createdByName?: string
|
createdByName?: string
|
||||||
|
managerName?: string
|
||||||
|
customProjectName?: string
|
||||||
|
dateFormatPattern?: string
|
||||||
sections: ReportSectionOutput[]
|
sections: ReportSectionOutput[]
|
||||||
createdAt: string
|
createdAt: string
|
||||||
updatedAt: string
|
updatedAt: string
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue