imports nao usados
This commit is contained in:
parent
7c4884b6ad
commit
880d72d5d6
2 changed files with 193 additions and 104 deletions
|
|
@ -7,7 +7,6 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.beetechgroup.beetask.entities.task.Task;
|
||||
import net.beetechgroup.beetask.entities.task.TaskHistoryItem;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
BarChart3,
|
||||
Calendar,
|
||||
|
|
@ -13,11 +13,13 @@ import {
|
|||
Download,
|
||||
AlertCircle,
|
||||
Loader2,
|
||||
ArrowUpRight,
|
||||
History,
|
||||
Briefcase
|
||||
Briefcase,
|
||||
} from "lucide-react";
|
||||
import { dashboardService, type DashboardData } from "../services/dashboardService";
|
||||
import {
|
||||
dashboardService,
|
||||
type DashboardData,
|
||||
} from "../services/dashboardService";
|
||||
import { cn } from "../lib/utils";
|
||||
import { format, subDays, startOfMonth, endOfMonth } from "date-fns";
|
||||
import { ptBR } from "date-fns/locale";
|
||||
|
|
@ -34,7 +36,7 @@ export const DashboardPage: React.FC = () => {
|
|||
try {
|
||||
const result = await dashboardService.getDashboard(
|
||||
startDate.toISOString(),
|
||||
endDate.toISOString()
|
||||
endDate.toISOString(),
|
||||
);
|
||||
setData(result);
|
||||
} catch (err) {
|
||||
|
|
@ -73,29 +75,29 @@ export const DashboardPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 bg-surface border border-border-soft p-2 rounded-2xl shadow-sm">
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-r border-border-soft">
|
||||
<Calendar size={18} className="text-brand" />
|
||||
<input
|
||||
type="date"
|
||||
value={format(startDate, 'yyyy-MM-dd')}
|
||||
onChange={(e) => setStartDate(new Date(e.target.value))}
|
||||
className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<input
|
||||
type="date"
|
||||
value={format(endDate, 'yyyy-MM-dd')}
|
||||
onChange={(e) => setEndDate(new Date(e.target.value))}
|
||||
className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-r border-border-soft">
|
||||
<Calendar size={18} className="text-brand" />
|
||||
<input
|
||||
type="date"
|
||||
value={format(startDate, "yyyy-MM-dd")}
|
||||
onChange={(e) => setStartDate(new Date(e.target.value))}
|
||||
className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-2">
|
||||
<input
|
||||
type="date"
|
||||
value={format(endDate, "yyyy-MM-dd")}
|
||||
onChange={(e) => setEndDate(new Date(e.target.value))}
|
||||
className="bg-transparent border-none text-sm font-bold text-text-main focus:ring-0 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={fetchDashboardData}
|
||||
className="p-2 bg-brand text-white rounded-xl hover:scale-105 transition-transform active:scale-95"
|
||||
>
|
||||
<Filter size={18} />
|
||||
</button>
|
||||
>
|
||||
<Filter size={18} />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -107,9 +109,14 @@ export const DashboardPage: React.FC = () => {
|
|||
) : error ? (
|
||||
<div className="flex flex-col items-center justify-center min-h-[50vh] text-center p-6 bg-surface rounded-[2.5rem] border border-border-soft">
|
||||
<AlertCircle size={48} className="text-rose-500 mb-4" />
|
||||
<h3 className="text-xl font-bold text-text-main mb-2">Ocorreu um erro</h3>
|
||||
<h3 className="text-xl font-bold text-text-main mb-2">
|
||||
Ocorreu um erro
|
||||
</h3>
|
||||
<p className="text-text-muted max-w-sm mb-6">{error}</p>
|
||||
<button onClick={fetchDashboardData} className="px-6 py-3 bg-brand text-white rounded-xl font-bold hover:bg-brand-dark transition-all">
|
||||
<button
|
||||
onClick={fetchDashboardData}
|
||||
className="px-6 py-3 bg-brand text-white rounded-xl font-bold hover:bg-brand-dark transition-all"
|
||||
>
|
||||
Tentar novamente
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -126,14 +133,21 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="w-12 h-12 bg-brand/10 text-brand rounded-xl flex items-center justify-center mb-6">
|
||||
<Clock size={24} />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-text-muted uppercase tracking-wider">Tempo Total</div>
|
||||
<div className="mt-2 text-4xl font-black text-text-main">{formatMinutes(data.totalMinutesWorked)}</div>
|
||||
<div className="text-sm font-bold text-text-muted uppercase tracking-wider">
|
||||
Tempo Total
|
||||
</div>
|
||||
<div className="mt-2 text-4xl font-black text-text-main">
|
||||
{formatMinutes(data.totalMinutesWorked)}
|
||||
</div>
|
||||
<div className="mt-4 flex items-center gap-1.5 text-[10px] font-black text-brand bg-brand/5 w-fit px-2 py-1 rounded-full uppercase tracking-widest">
|
||||
<TrendingUp size={12} />
|
||||
Foco em alta
|
||||
</div>
|
||||
</div>
|
||||
<Clock size={120} className="absolute -right-10 -bottom-10 text-brand/5 -rotate-12 transition-transform group-hover:rotate-0" />
|
||||
<Clock
|
||||
size={120}
|
||||
className="absolute -right-10 -bottom-10 text-brand/5 -rotate-12 transition-transform group-hover:rotate-0"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
|
|
@ -146,14 +160,21 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="w-12 h-12 bg-accent/10 text-accent rounded-xl flex items-center justify-center mb-6">
|
||||
<CheckCircle2 size={24} />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-text-muted uppercase tracking-wider">Tarefas Concluídas</div>
|
||||
<div className="mt-2 text-4xl font-black text-text-main">{data.finishedTasksInPeriod.length}</div>
|
||||
<div className="text-sm font-bold text-text-muted uppercase tracking-wider">
|
||||
Tarefas Concluídas
|
||||
</div>
|
||||
<div className="mt-2 text-4xl font-black text-text-main">
|
||||
{data.finishedTasksInPeriod.length}
|
||||
</div>
|
||||
<div className="mt-4 flex items-center gap-1.5 text-[10px] font-black text-accent bg-accent/5 w-fit px-2 py-1 rounded-full uppercase tracking-widest">
|
||||
<CheckCircle2 size={12} />
|
||||
Periodo de Entrega
|
||||
</div>
|
||||
</div>
|
||||
<CheckCircle2 size={120} className="absolute -right-10 -bottom-10 text-accent/5 -rotate-12 transition-transform group-hover:rotate-0" />
|
||||
<CheckCircle2
|
||||
size={120}
|
||||
className="absolute -right-10 -bottom-10 text-accent/5 -rotate-12 transition-transform group-hover:rotate-0"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
|
|
@ -166,18 +187,27 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="w-12 h-12 bg-white/20 text-white rounded-xl flex items-center justify-center mb-6">
|
||||
<FileText size={24} />
|
||||
</div>
|
||||
<div className="text-sm font-bold text-white/70 uppercase tracking-wider">Média por Projeto</div>
|
||||
<div className="text-sm font-bold text-white/70 uppercase tracking-wider">
|
||||
Média por Projeto
|
||||
</div>
|
||||
<div className="mt-2 text-4xl font-black">
|
||||
{data.projectStats.length > 0
|
||||
? formatMinutes(Math.round(data.totalMinutesWorked / data.projectStats.length))
|
||||
: '0h 0m'}
|
||||
? formatMinutes(
|
||||
Math.round(
|
||||
data.totalMinutesWorked / data.projectStats.length,
|
||||
),
|
||||
)
|
||||
: "0h 0m"}
|
||||
</div>
|
||||
<button className="mt-6 inline-flex items-center gap-2 text-xs font-black uppercase tracking-widest bg-white/10 hover:bg-white/20 px-4 py-2 rounded-xl transition-all">
|
||||
<Download size={14} />
|
||||
Exportar Relatório
|
||||
</button>
|
||||
</div>
|
||||
<TrendingUp size={160} className="absolute -right-10 -bottom-10 text-white/10 -rotate-12 transition-transform group-hover:rotate-0" />
|
||||
<TrendingUp
|
||||
size={160}
|
||||
className="absolute -right-10 -bottom-10 text-white/10 -rotate-12 transition-transform group-hover:rotate-0"
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
|
|
@ -187,34 +217,51 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="flex items-center justify-between mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<Briefcase size={20} className="text-brand" />
|
||||
<h2 className="text-xl font-black text-text-main">Tempo por Projeto</h2>
|
||||
<h2 className="text-xl font-black text-text-main">
|
||||
Tempo por Projeto
|
||||
</h2>
|
||||
</div>
|
||||
<BarChart3 size={20} className="text-text-muted" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
{data.projectStats.length > 0 ? data.projectStats.map((stat, idx) => {
|
||||
const percentage = (stat.totalMinutes / data.totalMinutesWorked) * 100;
|
||||
return (
|
||||
<div key={stat.projectId} className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-bold text-text-main flex items-center gap-2">
|
||||
<div className={cn("w-2 h-2 rounded-full", idx % 2 === 0 ? "bg-brand" : "bg-accent")} />
|
||||
{stat.projectName}
|
||||
</span>
|
||||
<span className="text-sm font-black text-text-muted">{formatMinutes(stat.totalMinutes)}</span>
|
||||
{data.projectStats.length > 0 ? (
|
||||
data.projectStats.map((stat, idx) => {
|
||||
const percentage =
|
||||
(stat.totalMinutes / data.totalMinutesWorked) * 100;
|
||||
return (
|
||||
<div key={stat.projectId} className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-bold text-text-main flex items-center gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
"w-2 h-2 rounded-full",
|
||||
idx % 2 === 0 ? "bg-brand" : "bg-accent",
|
||||
)}
|
||||
/>
|
||||
{stat.projectName}
|
||||
</span>
|
||||
<span className="text-sm font-black text-text-muted">
|
||||
{formatMinutes(stat.totalMinutes)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-3 w-full bg-surface-muted rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${percentage}%` }}
|
||||
className={cn(
|
||||
"h-full rounded-full",
|
||||
idx % 2 === 0 ? "bg-brand" : "bg-accent",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-3 w-full bg-surface-muted rounded-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${percentage}%` }}
|
||||
className={cn("h-full rounded-full", idx % 2 === 0 ? "bg-brand" : "bg-accent")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}) : (
|
||||
<div className="text-center py-10 opacity-50 font-bold">Nenhum dado por projeto.</div>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="text-center py-10 opacity-50 font-bold">
|
||||
Nenhum dado por projeto.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -224,29 +271,45 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="flex items-center justify-between mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<History size={20} className="text-accent" />
|
||||
<h2 className="text-xl font-black text-text-main">Daily (Ontem)</h2>
|
||||
<h2 className="text-xl font-black text-text-main">
|
||||
Daily (Ontem)
|
||||
</h2>
|
||||
</div>
|
||||
<div className="text-xs font-bold text-text-muted uppercase bg-surface-muted px-2 py-1 rounded-lg">
|
||||
{format(subDays(new Date(), 1), 'dd MMM', { locale: ptBR })}
|
||||
{format(subDays(new Date(), 1), "dd MMM", { locale: ptBR })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 max-h-[300px] overflow-y-auto pr-2 custom-scrollbar">
|
||||
{data.yesterdayTasks.length > 0 ? data.yesterdayTasks.map(task => (
|
||||
<div key={task.id} className="flex items-center justify-between p-4 bg-app-bg rounded-2xl border border-border-soft group hover:border-brand/30 transition-all">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-brand/10 text-brand rounded-xl flex items-center justify-center shrink-0">
|
||||
<Layout size={18} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold text-text-main line-clamp-1">{task.title}</div>
|
||||
<div className="text-[10px] font-black uppercase text-brand tracking-widest">{task.projectName}</div>
|
||||
{data.yesterdayTasks.length > 0 ? (
|
||||
data.yesterdayTasks.map((task) => (
|
||||
<div
|
||||
key={task.id}
|
||||
className="flex items-center justify-between p-4 bg-app-bg rounded-2xl border border-border-soft group hover:border-brand/30 transition-all"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-brand/10 text-brand rounded-xl flex items-center justify-center shrink-0">
|
||||
<Layout size={18} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold text-text-main line-clamp-1">
|
||||
{task.title}
|
||||
</div>
|
||||
<div className="text-[10px] font-black uppercase text-brand tracking-widest">
|
||||
{task.projectName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight
|
||||
size={16}
|
||||
className="text-text-muted group-hover:translate-x-1 transition-transform"
|
||||
/>
|
||||
</div>
|
||||
<ChevronRight size={16} className="text-text-muted group-hover:translate-x-1 transition-transform" />
|
||||
))
|
||||
) : (
|
||||
<div className="text-center py-10 opacity-50 font-bold">
|
||||
Nenhuma atividade registrada ontem.
|
||||
</div>
|
||||
)) : (
|
||||
<div className="text-center py-10 opacity-50 font-bold">Nenhuma atividade registrada ontem.</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -257,7 +320,9 @@ export const DashboardPage: React.FC = () => {
|
|||
<div className="flex items-center justify-between mb-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText size={20} className="text-brand" />
|
||||
<h2 className="text-xl font-black text-text-main">Relatório de Entregas</h2>
|
||||
<h2 className="text-xl font-black text-text-main">
|
||||
Relatório de Entregas
|
||||
</h2>
|
||||
</div>
|
||||
<button className="flex items-center gap-2 text-sm font-bold text-brand hover:underline bg-brand/5 px-4 py-2 rounded-xl transition-all">
|
||||
<Download size={16} />
|
||||
|
|
@ -269,34 +334,59 @@ export const DashboardPage: React.FC = () => {
|
|||
<table className="w-full text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-border-soft">
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest pl-4">Tarefa</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest">Projeto</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest">Concluída em</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest text-right pr-4">Status</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest pl-4">
|
||||
Tarefa
|
||||
</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest">
|
||||
Projeto
|
||||
</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest">
|
||||
Concluída em
|
||||
</th>
|
||||
<th className="pb-4 text-xs font-black uppercase text-text-muted tracking-widest text-right pr-4">
|
||||
Status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border-soft/50">
|
||||
{data.finishedTasksInPeriod.length > 0 ? data.finishedTasksInPeriod.map(task => (
|
||||
<tr key={task.id} className="group hover:bg-app-bg transition-colors">
|
||||
<td className="py-4 pl-4 font-bold text-text-main">{task.title}</td>
|
||||
<td className="py-4 text-sm font-medium text-text-muted">
|
||||
<span className="px-2 py-1 bg-surface-muted rounded-lg border border-border-soft">
|
||||
{task.projectName}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-4 text-sm font-bold text-text-main">
|
||||
{task.finishedAt ? format(new Date(task.finishedAt), 'dd/MM/yyyy HH:mm') : '-'}
|
||||
</td>
|
||||
<td className="py-4 text-right pr-4">
|
||||
<span className="inline-flex items-center gap-1 rounded-lg bg-green-500/10 px-2 py-1 text-[10px] font-black uppercase text-green-600 tracking-tighter">
|
||||
<CheckCircle2 size={12} />
|
||||
Finalizado
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
)) : (
|
||||
{data.finishedTasksInPeriod.length > 0 ? (
|
||||
data.finishedTasksInPeriod.map((task) => (
|
||||
<tr
|
||||
key={task.id}
|
||||
className="group hover:bg-app-bg transition-colors"
|
||||
>
|
||||
<td className="py-4 pl-4 font-bold text-text-main">
|
||||
{task.title}
|
||||
</td>
|
||||
<td className="py-4 text-sm font-medium text-text-muted">
|
||||
<span className="px-2 py-1 bg-surface-muted rounded-lg border border-border-soft">
|
||||
{task.projectName}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-4 text-sm font-bold text-text-main">
|
||||
{task.finishedAt
|
||||
? format(
|
||||
new Date(task.finishedAt),
|
||||
"dd/MM/yyyy HH:mm",
|
||||
)
|
||||
: "-"}
|
||||
</td>
|
||||
<td className="py-4 text-right pr-4">
|
||||
<span className="inline-flex items-center gap-1 rounded-lg bg-green-500/10 px-2 py-1 text-[10px] font-black uppercase text-green-600 tracking-tighter">
|
||||
<CheckCircle2 size={12} />
|
||||
Finalizado
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={4} className="py-10 text-center opacity-50 font-bold">Nenhuma tarefa finalizada neste período.</td>
|
||||
<td
|
||||
colSpan={4}
|
||||
className="py-10 text-center opacity-50 font-bold"
|
||||
>
|
||||
Nenhuma tarefa finalizada neste período.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
|
|
|||
Loading…
Reference in a new issue