style: elevate logged-in experience with ambient glows, glassmorphism, and landing page visual polish
This commit is contained in:
parent
eeccd176a1
commit
6412bb20d1
13 changed files with 396 additions and 354 deletions
|
|
@ -7,7 +7,11 @@ export const AppShell = () => {
|
|||
const [isMobileOpen, setIsMobileOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-app-bg text-text-main flex font-sans selection:bg-brand-soft selection:text-brand">
|
||||
<div className="min-h-screen bg-app-bg text-text-main flex font-sans selection:bg-brand-soft selection:text-brand relative overflow-x-hidden">
|
||||
{/* Decorative ambient background glows matching Landing Page aesthetic */}
|
||||
<div className="fixed top-0 left-1/4 w-[650px] h-[350px] bg-gradient-to-tr from-accent/10 via-brand/12 to-transparent blur-3xl pointer-events-none rounded-full -z-0" />
|
||||
<div className="fixed bottom-10 right-10 w-[550px] h-[350px] bg-gradient-to-br from-brand/10 via-accent/10 to-transparent blur-3xl pointer-events-none rounded-full -z-0" />
|
||||
|
||||
{/* Sidebar Navigation */}
|
||||
<Sidebar
|
||||
isMobileOpen={isMobileOpen}
|
||||
|
|
@ -15,9 +19,9 @@ export const AppShell = () => {
|
|||
/>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
<div className="flex-1 flex flex-col min-w-0 z-10">
|
||||
<Header onToggleMobileSidebar={() => setIsMobileOpen(true)} />
|
||||
<main className="flex-1 p-4 sm:p-6 lg:p-8 max-w-7xl w-full mx-auto animate-in fade-in duration-200">
|
||||
<main className="flex-1 p-4 sm:p-6 lg:p-8 max-w-7xl w-full mx-auto animate-in fade-in duration-300">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export const Sidebar = ({ isMobileOpen, onCloseMobile }: SidebarProps) => {
|
|||
]
|
||||
|
||||
const navContent = (
|
||||
<div className="flex flex-col h-full bg-surface border-r border-border-soft select-none">
|
||||
<div className="flex flex-col h-full bg-surface/90 backdrop-blur-md border-r border-border-soft select-none">
|
||||
{/* Brand & Logo */}
|
||||
<div className="h-18 px-6 flex items-center justify-between border-b border-border-soft">
|
||||
<NavLink to="/dashboard" className="flex items-center gap-3">
|
||||
|
|
|
|||
|
|
@ -52,10 +52,10 @@ export const TaskCard: React.FC<TaskCardProps> = ({
|
|||
<div
|
||||
draggable
|
||||
onDragStart={handleDragStart}
|
||||
className={`group relative rounded-xl border bg-surface p-4 transition-all duration-200 cursor-grab active:cursor-grabbing hover:shadow-lg hover:border-zinc-700 ${
|
||||
className={`group relative rounded-2xl border bg-surface/90 backdrop-blur-md p-4 transition-all duration-200 cursor-grab active:cursor-grabbing hover:shadow-xl hover:-translate-y-0.5 hover:border-brand/40 ${
|
||||
running
|
||||
? 'border-brand/60 shadow-[0_0_20px_rgba(15,118,110,0.18)] ring-1 ring-brand/40'
|
||||
: 'border-border shadow-xs'
|
||||
? 'border-brand/60 shadow-[0_0_25px_rgba(15,118,110,0.22)] ring-1 ring-brand/40 bg-brand/5'
|
||||
: 'border-border-soft shadow-panel'
|
||||
}`}
|
||||
>
|
||||
{/* Top Row: Project & Quick Status Menu */}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export const TaskKanbanView: React.FC<TaskKanbanViewProps> = ({
|
|||
onDragOver={(e) => handleDragOver(e, col.status)}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={(e) => handleDrop(e, col.status)}
|
||||
className={`flex flex-col rounded-2xl border transition-all duration-200 min-h-[480px] bg-surface-sunken/40 ${
|
||||
className={`flex flex-col rounded-3xl border transition-all duration-200 min-h-[500px] bg-surface/60 backdrop-blur-md shadow-xs ${
|
||||
isOver
|
||||
? 'border-brand ring-2 ring-brand/30 bg-brand/5'
|
||||
: col.borderColor
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const TaskListView: React.FC<TaskListViewProps> = ({
|
|||
const [activeMenuId, setActiveMenuId] = React.useState<number | null>(null)
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-border bg-surface overflow-hidden shadow-xs">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md overflow-hidden shadow-panel">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-xs">
|
||||
<thead>
|
||||
|
|
|
|||
|
|
@ -211,11 +211,12 @@ export const AnalyticsPage: React.FC = () => {
|
|||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent">
|
||||
Analytics & Produtividade
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand animate-pulse" />
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -229,14 +230,14 @@ export const AnalyticsPage: React.FC = () => {
|
|||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar métricas"
|
||||
className="self-start sm:self-auto p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
className="self-start sm:self-auto p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-panel disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-brand' : ''}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-300 text-xs">
|
||||
<div className="flex items-center justify-between p-4 rounded-2xl bg-rose-500/10 border border-rose-500/25 text-rose-300 text-xs shadow-panel backdrop-blur-md">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-rose-400" />
|
||||
<span>{error}</span>
|
||||
|
|
@ -256,7 +257,7 @@ export const AnalyticsPage: React.FC = () => {
|
|||
{[1, 2, 3, 4].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-32 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-4"
|
||||
className="h-32 rounded-3xl border border-border-soft bg-surface-sunken/40 animate-pulse p-5"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -264,86 +265,86 @@ export const AnalyticsPage: React.FC = () => {
|
|||
<>
|
||||
{/* Top 4 KPI Cards */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{/* Total Time */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Tempo Total Dedicado</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400">
|
||||
<Clock className="w-4 h-4" />
|
||||
{/* Total Time */}
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex flex-col justify-between shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Tempo Total Dedicado</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400">
|
||||
<Clock className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl sm:text-3xl font-extrabold text-foreground tracking-tight font-mono">
|
||||
{formatSecondsToTimer(kpis.totalSecondsTracked)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
Equivalente a {formatSecondsToHuman(kpis.totalSecondsTracked)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-foreground tracking-tight font-mono">
|
||||
{formatSecondsToTimer(kpis.totalSecondsTracked)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
Equivalente a {formatSecondsToHuman(kpis.totalSecondsTracked)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Completion Rate */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Taxa de Conclusão</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
{/* Completion Rate */}
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex flex-col justify-between shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Taxa de Conclusão</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl sm:text-3xl font-extrabold text-emerald-400 tracking-tight">
|
||||
{kpis.completionRate}%
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{kpis.completedTasks} de {kpis.totalTasks} demandas finalizadas
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-emerald-400 tracking-tight">
|
||||
{kpis.completionRate}%
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{kpis.completedTasks} de {kpis.totalTasks} demandas finalizadas
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* In Progress vs Backlog */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Em Execução Agora</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<Activity className="w-4 h-4" />
|
||||
{/* In Progress vs Backlog */}
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex flex-col justify-between shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Em Execução Agora</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<Activity className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl sm:text-3xl font-extrabold text-teal-300 tracking-tight">
|
||||
{kpis.inProgressTasks} tarefas
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{kpis.notStartedTasks} no backlog aguardando início
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-teal-300 tracking-tight">
|
||||
{kpis.inProgressTasks} tarefas
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{kpis.notStartedTasks} no backlog aguardando início
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Avg Time per Task */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Média por Entrega</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
{/* Avg Time per Task */}
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex flex-col justify-between shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Média por Entrega</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl sm:text-3xl font-extrabold text-foreground tracking-tight font-mono">
|
||||
{formatSecondsToHuman(kpis.avgSecondsPerCompletedTask)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
Tempo médio gasto em cada entrega concluída
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-foreground tracking-tight font-mono">
|
||||
{formatSecondsToHuman(kpis.avgSecondsPerCompletedTask)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
Tempo médio gasto em cada entrega concluída
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Grid: Projects & Categories Breakdown */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Project Time Distribution */}
|
||||
<div className="p-6 rounded-2xl border border-border bg-surface shadow-xs space-y-5">
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-7 h-7 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<div className="w-8 h-8 rounded-xl bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<Folder className="w-4 h-4" />
|
||||
</div>
|
||||
<h2 className="text-sm font-bold text-foreground">
|
||||
|
|
@ -405,10 +406,10 @@ export const AnalyticsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Category Time Distribution */}
|
||||
<div className="p-6 rounded-2xl border border-border bg-surface shadow-xs space-y-5">
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-7 h-7 rounded-lg bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<div className="w-8 h-8 rounded-xl bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<Tag className="w-4 h-4" />
|
||||
</div>
|
||||
<h2 className="text-sm font-bold text-foreground">
|
||||
|
|
@ -471,7 +472,7 @@ export const AnalyticsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Team Productivity Table */}
|
||||
<div className="rounded-2xl border border-border bg-surface p-6 shadow-xs space-y-4">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-6 shadow-panel space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-7 h-7 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
|
|
|
|||
|
|
@ -157,11 +157,12 @@ export const CategoriesPage: React.FC = () => {
|
|||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-accent via-amber-400 to-brand bg-clip-text text-transparent">
|
||||
Categorias & Tags
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-accent/15 text-accent border border-accent/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-accent/15 text-accent border border-accent/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent animate-pulse" />
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -176,7 +177,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar categorias"
|
||||
className="p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
className="p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-panel disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-accent' : ''}`} />
|
||||
</button>
|
||||
|
|
@ -184,7 +185,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleOpenCreateModal}
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-accent text-white font-semibold text-xs hover:bg-accent-hover shadow-md shadow-accent/20 transition-all"
|
||||
className="flex items-center gap-1.5 px-4 py-2.5 rounded-xl bg-accent text-white font-semibold text-xs hover:bg-accent-hover shadow-md shadow-accent/25 transition-all hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Nova Categoria</span>
|
||||
|
|
@ -193,7 +194,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-300 text-xs">
|
||||
<div className="flex items-center justify-between p-4 rounded-2xl bg-rose-500/10 border border-rose-500/25 text-rose-300 text-xs shadow-panel backdrop-blur-md">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-rose-400" />
|
||||
<span>{error}</span>
|
||||
|
|
@ -209,15 +210,15 @@ export const CategoriesPage: React.FC = () => {
|
|||
)}
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="p-3.5 rounded-2xl border border-border bg-surface flex items-center justify-between gap-3 shadow-xs">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center justify-between gap-3 shadow-panel">
|
||||
<div className="relative w-full sm:w-80">
|
||||
<Search className="w-4 h-4 text-muted absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<Search className="w-4 h-4 text-muted absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Buscar categoria por nome..."
|
||||
className="w-full pl-9 pr-3 py-2 rounded-xl border border-border bg-surface-raised text-xs text-foreground placeholder-muted/60 focus:outline-none focus:ring-2 focus:ring-accent/40 focus:border-accent transition-all"
|
||||
className="w-full pl-9 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-accent/40 focus:border-accent transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-muted font-medium">
|
||||
|
|
@ -227,11 +228,11 @@ export const CategoriesPage: React.FC = () => {
|
|||
|
||||
{/* Grid of Categories */}
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-40 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-5 space-y-3"
|
||||
className="h-44 rounded-3xl border border-border-soft bg-surface-sunken/40 animate-pulse p-6 space-y-3"
|
||||
>
|
||||
<div className="h-5 bg-surface-raised rounded-md w-1/2" />
|
||||
<div className="h-3 bg-surface-raised rounded-md w-3/4" />
|
||||
|
|
@ -239,12 +240,12 @@ export const CategoriesPage: React.FC = () => {
|
|||
))}
|
||||
</div>
|
||||
) : filteredCategories.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-20 px-4 text-center rounded-2xl border border-dashed border-border bg-surface/50">
|
||||
<div className="flex flex-col items-center justify-center py-20 px-4 text-center rounded-3xl border border-dashed border-border-soft bg-surface/60 backdrop-blur-md shadow-panel">
|
||||
<div className="w-14 h-14 rounded-2xl bg-accent/10 border border-accent/20 flex items-center justify-center text-accent mb-4">
|
||||
<Tag className="w-7 h-7" />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-foreground mb-1">
|
||||
Nenhuma categoria cadastrada
|
||||
Nenhum categoria cadastrada
|
||||
</h3>
|
||||
<p className="text-xs text-muted max-w-sm mb-5 leading-relaxed">
|
||||
{searchQuery
|
||||
|
|
@ -254,7 +255,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleOpenCreateModal}
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-accent text-white text-xs font-semibold hover:bg-accent-hover shadow-md shadow-accent/20 transition-all"
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-accent text-white text-xs font-semibold hover:bg-accent-hover shadow-md shadow-accent/25 transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Criar Primeira Categoria</span>
|
||||
|
|
@ -271,7 +272,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
return (
|
||||
<div
|
||||
key={category.id}
|
||||
className="group rounded-2xl border border-border bg-surface p-5 hover:border-zinc-700 hover:shadow-lg transition-all duration-200 flex flex-col justify-between"
|
||||
className="group rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-6 hover:border-accent/40 shadow-panel hover:shadow-xl hover:-translate-y-1 transition-all duration-200 flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-start justify-between gap-3 mb-4">
|
||||
|
|
@ -307,7 +308,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Metrics Bar */}
|
||||
<div className="grid grid-cols-2 gap-2 p-3 rounded-xl bg-surface-sunken/60 border border-border/60 mb-4">
|
||||
<div className="grid grid-cols-2 gap-2 p-3.5 rounded-2xl bg-surface-sunken/60 border border-border-soft mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<ListTodo className="w-3.5 h-3.5 text-muted" />
|
||||
<div>
|
||||
|
|
@ -330,7 +331,7 @@ export const CategoriesPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Footer Link */}
|
||||
<div className="pt-3 border-t border-border/80 flex items-center justify-between">
|
||||
<div className="pt-3 border-t border-border-soft/60 flex items-center justify-between">
|
||||
<span
|
||||
className="inline-block w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: category.color || '#d97706' }}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
Tag,
|
||||
Activity,
|
||||
AlertCircle,
|
||||
Zap,
|
||||
} from 'lucide-react'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { useTimer } from '../contexts/TimerContext'
|
||||
|
|
@ -135,47 +136,55 @@ export const DashboardPage: React.FC = () => {
|
|||
return tasks.find((t) => t.status === 'IN_PROGRESS') || tasks.find((t) => t.status === 'NOT_STARTED') || tasks[0]
|
||||
}, [tasks])
|
||||
|
||||
const firstName = user?.name ? user.name.split(' ')[0] : 'Desenvolvedor'
|
||||
|
||||
return (
|
||||
<div className="space-y-6 max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
Olá, {user?.name?.split(' ')[0] || 'Desenvolvedor'}! 👋
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
<div className="space-y-8 max-w-7xl mx-auto">
|
||||
{/* Top Banner with Landing Page aesthetic */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-5 pb-2">
|
||||
<div className="space-y-1.5">
|
||||
{/* Top pill badge */}
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1 rounded-full bg-surface/90 backdrop-blur-md border border-border-soft shadow-xs text-xs font-semibold text-brand">
|
||||
<span className="flex h-2 w-2 rounded-full bg-accent animate-pulse" />
|
||||
<span>Workspace: {activeOrg?.name || 'BeeTask'}</span>
|
||||
<Sparkles className="w-3.5 h-3.5 text-accent" />
|
||||
</div>
|
||||
<p className="text-xs text-muted">
|
||||
Visão geral da sua produtividade, metas e atividades recentes no workspace.
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl font-extrabold tracking-tight text-foreground leading-tight">
|
||||
Olá, {firstName}!{' '}
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-brand via-brand-strong to-accent">
|
||||
Bom trabalho hoje.
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-xs sm:text-sm text-muted max-w-2xl leading-relaxed">
|
||||
Acompanhe o ritmo de entregas da sua equipe, audite suas horas e mantenha o foco na próxima entrega.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2.5 flex-wrap">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<button
|
||||
type="button"
|
||||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar dashboard"
|
||||
className="p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
title="Recarregar dados"
|
||||
className="p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-xs disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-brand' : ''}`} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/tasks')}
|
||||
className="px-3.5 py-2 rounded-xl border border-border bg-surface text-xs font-semibold text-foreground hover:bg-surface-raised transition-colors shadow-xs"
|
||||
>
|
||||
Quadro Kanban
|
||||
</button>
|
||||
<Link to="/tasks">
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-xs font-bold text-foreground hover:bg-surface-raised hover:border-brand/40 transition-all shadow-xs"
|
||||
>
|
||||
Quadro Kanban
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-brand text-white font-semibold text-xs hover:bg-brand-hover shadow-md shadow-brand/20 transition-all"
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-brand hover:bg-brand-strong text-white font-bold text-xs shadow-lg shadow-brand/25 transition-all duration-200 hover:scale-[1.02] active:scale-[0.98]"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Nova Tarefa</span>
|
||||
|
|
@ -184,8 +193,8 @@ export const DashboardPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-300 text-xs">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center justify-between p-4 rounded-2xl bg-rose-500/10 border border-rose-500/25 text-rose-300 text-xs backdrop-blur-xs">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<AlertCircle className="w-4 h-4 text-rose-400" />
|
||||
<span>{error}</span>
|
||||
</div>
|
||||
|
|
@ -199,183 +208,189 @@ export const DashboardPage: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* KPI Cards */}
|
||||
{/* 4 Premium KPI Cards with shadow-panel & backdrop-blur */}
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-28 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-4"
|
||||
className="h-32 rounded-3xl border border-border-soft bg-surface/60 backdrop-blur-md animate-pulse p-5"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
{/* In Progress */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs hover:border-zinc-700 transition-colors">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-5 shadow-panel hover:shadow-xl hover:-translate-y-0.5 transition-all duration-200 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Tarefas em Andamento</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400">
|
||||
<span className="text-xs font-bold text-muted">Tarefas em Andamento</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400 shadow-xs">
|
||||
<Clock className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-teal-400 tracking-tight">
|
||||
<p className="text-3xl font-extrabold text-teal-400 tracking-tight font-mono">
|
||||
{stats.inProgress}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{stats.notStarted} no backlog para iniciar
|
||||
<p className="text-[11px] text-muted font-medium mt-1">
|
||||
{stats.notStarted} tarefas aguardando no backlog
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Completed */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs hover:border-zinc-700 transition-colors">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-5 shadow-panel hover:shadow-xl hover:-translate-y-0.5 transition-all duration-200 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Entregas Concluídas</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
|
||||
<span className="text-xs font-bold text-muted">Entregas Concluídas</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400 shadow-xs">
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-emerald-400 tracking-tight">
|
||||
<p className="text-3xl font-extrabold text-emerald-400 tracking-tight font-mono">
|
||||
{stats.completed}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
de {tasks.length} tarefas cadastradas
|
||||
<p className="text-[11px] text-muted font-medium mt-1">
|
||||
de {tasks.length} demandas cadastradas
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Time Tracked */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs hover:border-zinc-700 transition-colors">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-5 shadow-panel hover:shadow-xl hover:-translate-y-0.5 transition-all duration-200 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Tempo Total Dedicado</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<span className="text-xs font-bold text-muted">Tempo Total Dedicado</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400 shadow-xs">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-amber-400 tracking-tight font-mono">
|
||||
<p className="text-3xl font-extrabold text-amber-400 tracking-tight font-mono">
|
||||
{formatSecondsToTimer(stats.totalSeconds)}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
{formatSecondsToHuman(stats.totalSeconds)} acumuladas
|
||||
<p className="text-[11px] text-muted font-medium mt-1">
|
||||
{formatSecondsToHuman(stats.totalSeconds)} auditadas no time
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Projects */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface flex flex-col justify-between shadow-xs hover:border-zinc-700 transition-colors">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-5 shadow-panel hover:shadow-xl hover:-translate-y-0.5 transition-all duration-200 flex flex-col justify-between">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<span className="text-xs font-semibold text-muted">Projetos Ativos</span>
|
||||
<div className="w-8 h-8 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<FolderKanban className="w-4 h-4" />
|
||||
<span className="text-xs font-bold text-muted">Projetos Ativos</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-brand-soft text-brand border border-brand/25 flex items-center justify-center shadow-xs">
|
||||
<FolderKanban className="w-4 h-4 text-brand" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-extrabold text-foreground tracking-tight">
|
||||
<p className="text-3xl font-extrabold text-foreground tracking-tight font-mono">
|
||||
{stats.projectsCount}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted mt-1">
|
||||
Iniciativas no workspace
|
||||
<p className="text-[11px] text-muted font-medium mt-1">
|
||||
Iniciativas ativas no workspace
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main Content: Focus Session (Left) + Recent Tasks (Right) */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||
{/* Focus Session Card (Left 5 cols) */}
|
||||
<div className="lg:col-span-5 space-y-5">
|
||||
<div className="rounded-2xl border border-border bg-surface p-6 shadow-xs space-y-5">
|
||||
<div className="flex items-center justify-between pb-3 border-b border-border">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-8 h-8 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<Flame className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-sm font-bold text-foreground">Sessão em Foco</h2>
|
||||
<p className="text-xs text-muted">Cronômetro em tempo real</p>
|
||||
</div>
|
||||
{/* Main Grid: Focus Session (5 cols) + Recent Sprint Tasks (7 cols) */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
|
||||
{/* Focus Session Hero Card (Left 5 cols) */}
|
||||
<div className="lg:col-span-5 space-y-6">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-6 shadow-panel space-y-5 transition-all hover:shadow-2xl">
|
||||
{/* Header with macOS style dots */}
|
||||
<div className="flex items-center justify-between pb-4 border-b border-border-soft">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-rose-400/80" />
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-amber-400/80" />
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-emerald-400/80" />
|
||||
<span className="text-xs font-bold text-foreground ml-1.5 flex items-center gap-1.5">
|
||||
<Flame className="w-3.5 h-3.5 text-accent" />
|
||||
Sessão em Foco
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
className={`px-2.5 py-0.5 rounded-full text-[10px] font-bold border ${
|
||||
activeTask
|
||||
? 'bg-teal-500/15 text-teal-300 border-teal-500/30 animate-pulse'
|
||||
: 'bg-zinc-800 text-zinc-400 border-zinc-700'
|
||||
}`}
|
||||
>
|
||||
{activeTask ? 'ATIVO' : 'EM ESPERA'}
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span
|
||||
className={`inline-block w-2 h-2 rounded-full ${
|
||||
activeTask ? 'bg-emerald-500 animate-ping' : 'bg-zinc-400'
|
||||
}`}
|
||||
/>
|
||||
<span
|
||||
className={`text-xs font-bold ${
|
||||
activeTask ? 'text-teal-400' : 'text-muted'
|
||||
}`}
|
||||
>
|
||||
{activeTask ? 'CRONOMETRANDO' : 'EM ESPERA'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Active or Suggested Task Info */}
|
||||
{/* Active or Suggested Task Info Box */}
|
||||
{activeTask ? (
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-4 rounded-2xl bg-surface-raised border border-border-soft space-y-2 shadow-xs">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{activeTask.project && (
|
||||
<span
|
||||
className="px-2 py-0.5 rounded-md text-[10px] font-medium border"
|
||||
className="px-2 py-0.5 rounded-md text-[10px] font-semibold border"
|
||||
style={{
|
||||
backgroundColor: activeTask.project.color ? `${activeTask.project.color}15` : 'rgba(15,118,110,0.1)',
|
||||
color: activeTask.project.color || '#0f766e',
|
||||
borderColor: activeTask.project.color ? `${activeTask.project.color}30` : 'rgba(15,118,110,0.25)',
|
||||
borderColor: activeTask.project.color ? `${activeTask.project.color}35` : 'rgba(15,118,110,0.25)',
|
||||
}}
|
||||
>
|
||||
{activeTask.project.name}
|
||||
</span>
|
||||
)}
|
||||
{activeTask.category && (
|
||||
<span className="px-2 py-0.5 rounded-md text-[10px] font-medium bg-amber-500/15 text-amber-300 border border-amber-500/25">
|
||||
<span className="px-2 py-0.5 rounded-md text-[10px] font-semibold bg-accent-soft text-accent border border-accent/25">
|
||||
{activeTask.category.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm font-bold text-foreground line-clamp-2">
|
||||
<h3 className="text-sm font-bold text-foreground line-clamp-2 leading-snug">
|
||||
{activeTask.title}
|
||||
</h3>
|
||||
</div>
|
||||
) : suggestedTask ? (
|
||||
<div className="p-4 rounded-xl bg-surface-sunken/60 border border-border space-y-2">
|
||||
<div className="p-4 rounded-2xl bg-surface-muted/60 border border-border-soft space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] font-semibold text-muted uppercase tracking-wider">
|
||||
Próxima da fila
|
||||
<span className="text-[10px] font-bold text-muted uppercase tracking-wider">
|
||||
Sugerida para Focar
|
||||
</span>
|
||||
{suggestedTask.project && (
|
||||
<span className="text-[10px] text-muted truncate max-w-[120px]">
|
||||
<span className="text-[11px] text-muted font-medium truncate max-w-[140px]">
|
||||
{suggestedTask.project.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold text-foreground line-clamp-2">
|
||||
<h3 className="text-sm font-bold text-foreground line-clamp-2 leading-snug">
|
||||
{suggestedTask.title}
|
||||
</h3>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-6 rounded-xl bg-surface-sunken/40 border border-dashed border-border text-center text-xs text-muted">
|
||||
Nenhuma tarefa pendente. Crie uma nova tarefa para iniciar o foco!
|
||||
<div className="p-6 rounded-2xl bg-surface-muted/40 border border-dashed border-border-soft text-center text-xs text-muted">
|
||||
Nenhuma tarefa ativa. Crie uma demanda para iniciar sua sessão produtiva!
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Big Timer Display */}
|
||||
<div className="text-center py-4 rounded-xl bg-surface-sunken border border-border">
|
||||
<span className="text-[10px] font-semibold uppercase tracking-wider text-muted">
|
||||
{activeTask ? 'Tempo Decorrido na Tarefa' : 'Pronto para Cronometrar'}
|
||||
{/* Big Bold Timer Display */}
|
||||
<div className="text-center py-5 rounded-2xl bg-surface border border-border-soft shadow-xs">
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider text-muted">
|
||||
{activeTask ? 'Tempo Decorrido na Tarefa' : 'Pronto para Iniciar'}
|
||||
</span>
|
||||
<p className="text-3xl sm:text-4xl font-extrabold font-mono text-foreground mt-1">
|
||||
<p className="text-4xl sm:text-5xl font-extrabold font-mono text-foreground mt-1.5 tracking-tight">
|
||||
{activeTask ? formatSecondsToTimer(activeSeconds) : '00:00:00'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
{/* Timer Actions */}
|
||||
{activeTask ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => stopTimer(activeTask)}
|
||||
className="w-full py-2.5 rounded-xl bg-rose-500/20 border border-rose-500/30 text-rose-300 hover:bg-rose-500/30 text-xs font-bold transition-all flex items-center justify-center gap-2"
|
||||
className="w-full py-3.5 rounded-xl bg-rose-500/15 border border-rose-500/30 text-rose-300 hover:bg-rose-500/25 text-xs font-bold transition-all flex items-center justify-center gap-2 shadow-xs"
|
||||
>
|
||||
<Square className="w-4 h-4 fill-rose-300" />
|
||||
<span>Pausar Cronômetro</span>
|
||||
|
|
@ -384,60 +399,60 @@ export const DashboardPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggleTimer(suggestedTask)}
|
||||
className="w-full py-2.5 rounded-xl bg-brand text-white hover:bg-brand-hover shadow-md shadow-brand/20 text-xs font-bold transition-all flex items-center justify-center gap-2"
|
||||
className="w-full py-3.5 rounded-xl bg-brand hover:bg-brand-strong text-white text-xs font-bold shadow-lg shadow-brand/25 transition-all flex items-center justify-center gap-2 duration-200 hover:scale-[1.01] active:scale-[0.99]"
|
||||
>
|
||||
<Play className="w-4 h-4 fill-white" />
|
||||
<span>Iniciar Foco Nesta Tarefa</span>
|
||||
<span>Iniciar Tempo nesta Tarefa</span>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="w-full py-2.5 rounded-xl bg-brand text-white hover:bg-brand-hover text-xs font-bold transition-all flex items-center justify-center gap-2"
|
||||
className="w-full py-3.5 rounded-xl bg-brand hover:bg-brand-strong text-white text-xs font-bold shadow-lg shadow-brand/25 transition-all flex items-center justify-center gap-2"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Criar Tarefa para Iniciar</span>
|
||||
<span>Criar Tarefa para Focar</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="pt-2 border-t border-border flex items-center justify-between text-[11px] text-muted">
|
||||
<span>Usuário ativo:</span>
|
||||
<span className="font-semibold text-foreground truncate max-w-[150px]">
|
||||
<div className="pt-2 border-t border-border-soft flex items-center justify-between text-[11px] text-muted">
|
||||
<span>Usuário conectado</span>
|
||||
<span className="font-bold text-foreground truncate max-w-[160px]">
|
||||
{user?.name || user?.email}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Shortcuts */}
|
||||
<div className="rounded-2xl border border-border bg-surface p-5 shadow-xs space-y-3">
|
||||
{/* Quick Shortcuts Card */}
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-5 shadow-panel space-y-3">
|
||||
<span className="text-[11px] font-bold uppercase tracking-wider text-muted block">
|
||||
Atalhos de Gestão
|
||||
Acesso Rápido ao Workspace
|
||||
</span>
|
||||
<div className="grid grid-cols-2 gap-2 text-xs font-semibold">
|
||||
<Link
|
||||
to="/projects"
|
||||
className="p-3 rounded-xl bg-surface-raised hover:bg-brand/10 hover:text-brand border border-border transition-colors flex items-center gap-2 text-foreground"
|
||||
className="p-3.5 rounded-xl bg-surface-muted/60 hover:bg-brand-soft/50 hover:text-brand border border-border-soft transition-all flex items-center gap-2.5 text-foreground shadow-xs"
|
||||
>
|
||||
<FolderKanban className="w-4 h-4 text-brand" />
|
||||
<span>Projetos</span>
|
||||
<span>Ver Projetos</span>
|
||||
</Link>
|
||||
<Link
|
||||
to="/analytics"
|
||||
className="p-3 rounded-xl bg-surface-raised hover:bg-amber-500/10 hover:text-amber-400 border border-border transition-colors flex items-center gap-2 text-foreground"
|
||||
className="p-3.5 rounded-xl bg-surface-muted/60 hover:bg-accent-soft/50 hover:text-accent border border-border-soft transition-all flex items-center gap-2.5 text-foreground shadow-xs"
|
||||
>
|
||||
<TrendingUp className="w-4 h-4 text-amber-400" />
|
||||
<span>Analytics</span>
|
||||
<TrendingUp className="w-4 h-4 text-accent" />
|
||||
<span>Analytics da Org</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Tasks List (Right 7 cols) */}
|
||||
{/* Recent Sprint Tasks (Right 7 cols) */}
|
||||
<div className="lg:col-span-7 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-base font-bold text-foreground flex items-center gap-2">
|
||||
<Sparkles className="w-4 h-4 text-accent" />
|
||||
Tarefas Recentes
|
||||
<h2 className="text-lg font-bold text-foreground flex items-center gap-2">
|
||||
<Zap className="w-4 h-4 text-accent" />
|
||||
Tarefas Recentes da Equipe
|
||||
</h2>
|
||||
<Link
|
||||
to="/tasks"
|
||||
|
|
@ -453,22 +468,22 @@ export const DashboardPage: React.FC = () => {
|
|||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-20 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-4"
|
||||
className="h-20 rounded-2xl border border-border-soft bg-surface/60 backdrop-blur-md animate-pulse p-4"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : recentTasks.length === 0 ? (
|
||||
<div className="text-center py-16 rounded-2xl border border-dashed border-border bg-surface/50">
|
||||
<div className="text-center py-20 rounded-3xl border border-dashed border-border-soft bg-surface/60 backdrop-blur-md">
|
||||
<Activity className="w-8 h-8 text-muted mx-auto mb-2" />
|
||||
<p className="text-xs text-muted mb-4">
|
||||
<p className="text-xs text-muted mb-4 font-medium">
|
||||
Nenhuma tarefa encontrada neste workspace.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="inline-flex items-center gap-1.5 px-4 py-2 rounded-xl bg-brand text-white text-xs font-semibold hover:bg-brand-hover shadow-xs"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-brand text-white text-xs font-bold hover:bg-brand-strong shadow-md shadow-brand/20 transition-all"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Criar Primeira Tarefa</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -481,45 +496,62 @@ export const DashboardPage: React.FC = () => {
|
|||
return (
|
||||
<div
|
||||
key={task.id}
|
||||
className={`rounded-2xl border p-4 transition-all duration-200 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 ${
|
||||
className={`rounded-2xl border p-4 transition-all duration-200 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 shadow-panel ${
|
||||
isTracking
|
||||
? 'border-brand/60 bg-brand/5 ring-1 ring-brand/30 shadow-md'
|
||||
: 'border-border bg-surface hover:border-zinc-700 shadow-xs'
|
||||
? 'border-brand/60 bg-brand/5 ring-1 ring-brand/30 shadow-lg'
|
||||
: 'border-border-soft bg-surface/90 backdrop-blur-md hover:border-brand/30 hover:shadow-xl'
|
||||
}`}
|
||||
>
|
||||
<div className="space-y-1.5 flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5 flex-wrap">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{task.project && (
|
||||
<span
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-medium border"
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-semibold border"
|
||||
style={{
|
||||
backgroundColor: task.project.color ? `${task.project.color}15` : 'rgba(255,255,255,0.05)',
|
||||
color: task.project.color || '#94a3b8',
|
||||
borderColor: task.project.color ? `${task.project.color}30` : 'rgba(255,255,255,0.1)',
|
||||
}}
|
||||
>
|
||||
<Folder className="w-2.5 h-2.5" />
|
||||
<Folder className="w-3 h-3" />
|
||||
{task.project.name}
|
||||
</span>
|
||||
)}
|
||||
{task.category && (
|
||||
<span
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-medium border"
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-semibold border"
|
||||
style={{
|
||||
backgroundColor: task.category.color ? `${task.category.color}15` : 'rgba(217,119,6,0.1)',
|
||||
color: task.category.color || '#d97706',
|
||||
borderColor: task.category.color ? `${task.category.color}30` : 'rgba(217,119,6,0.2)',
|
||||
}}
|
||||
>
|
||||
<Tag className="w-2.5 h-2.5" />
|
||||
<Tag className="w-3 h-3" />
|
||||
{task.category.name}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={`text-[10px] font-bold px-2 py-0.5 rounded-md border ${
|
||||
task.status === 'COMPLETED'
|
||||
? 'bg-emerald-500/15 text-emerald-400 border-emerald-500/25'
|
||||
: task.status === 'IN_PROGRESS'
|
||||
? 'bg-teal-500/15 text-teal-400 border-teal-500/25'
|
||||
: 'bg-surface-muted text-muted border-border-soft'
|
||||
}`}
|
||||
>
|
||||
{task.status === 'COMPLETED'
|
||||
? 'Concluído'
|
||||
: task.status === 'IN_PROGRESS'
|
||||
? 'Em Andamento'
|
||||
: task.status === 'CANCELED'
|
||||
? 'Cancelado'
|
||||
: 'A Fazer'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3
|
||||
onClick={() => navigate('/tasks')}
|
||||
className="text-xs font-bold text-foreground hover:text-brand cursor-pointer truncate"
|
||||
className="text-sm font-bold text-foreground hover:text-brand cursor-pointer truncate transition-colors"
|
||||
>
|
||||
{task.title}
|
||||
</h3>
|
||||
|
|
@ -527,13 +559,13 @@ export const DashboardPage: React.FC = () => {
|
|||
|
||||
<div className="flex items-center gap-3 shrink-0 self-end sm:self-center">
|
||||
<div
|
||||
className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-md font-mono text-[11px] ${
|
||||
className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-lg font-mono text-[11px] font-bold ${
|
||||
isTracking
|
||||
? 'bg-teal-500/20 text-teal-300 font-bold'
|
||||
: 'bg-surface-sunken text-muted'
|
||||
? 'bg-teal-500/20 text-teal-300'
|
||||
: 'bg-surface-muted text-muted'
|
||||
}`}
|
||||
>
|
||||
<Clock className={`w-3 h-3 ${isTracking ? 'animate-pulse text-brand' : ''}`} />
|
||||
<Clock className={`w-3.5 h-3.5 ${isTracking ? 'animate-pulse text-brand' : ''}`} />
|
||||
<span>{formatSecondsToTimer(taskSec)}</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -541,10 +573,10 @@ export const DashboardPage: React.FC = () => {
|
|||
type="button"
|
||||
onClick={() => handleToggleTimer(task)}
|
||||
title={isTracking ? 'Pausar cronômetro' : 'Iniciar cronômetro'}
|
||||
className={`p-1.5 rounded-lg transition-colors ${
|
||||
className={`p-2 rounded-xl transition-all shadow-xs ${
|
||||
isTracking
|
||||
? 'bg-rose-500/20 text-rose-300 hover:bg-rose-500/30'
|
||||
: 'bg-brand/15 text-teal-300 hover:bg-brand/25'
|
||||
? 'bg-rose-500/20 text-rose-300 hover:bg-rose-500/30 ring-1 ring-rose-500/40'
|
||||
: 'bg-brand/15 text-teal-300 hover:bg-brand/25 ring-1 ring-brand/30'
|
||||
}`}
|
||||
>
|
||||
{isTracking ? (
|
||||
|
|
|
|||
|
|
@ -153,11 +153,12 @@ export const MembersPage: React.FC = () => {
|
|||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent">
|
||||
Membros & Equipe
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand animate-pulse" />
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -171,7 +172,7 @@ export const MembersPage: React.FC = () => {
|
|||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar dados"
|
||||
className="self-start sm:self-auto p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
className="self-start sm:self-auto p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-panel disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-brand' : ''}`} />
|
||||
</button>
|
||||
|
|
@ -180,7 +181,7 @@ export const MembersPage: React.FC = () => {
|
|||
{/* Feedback Alert */}
|
||||
{feedbackMsg && (
|
||||
<div
|
||||
className={`flex items-center justify-between p-3.5 rounded-xl border text-xs ${
|
||||
className={`flex items-center justify-between p-4 rounded-2xl border text-xs shadow-panel backdrop-blur-md ${
|
||||
feedbackMsg.type === 'success'
|
||||
? 'bg-emerald-500/10 border-emerald-500/25 text-emerald-300'
|
||||
: 'bg-rose-500/10 border-rose-500/25 text-rose-300'
|
||||
|
|
@ -205,19 +206,19 @@ export const MembersPage: React.FC = () => {
|
|||
)}
|
||||
|
||||
{/* Tabs Switcher */}
|
||||
<div className="flex items-center gap-2 border-b border-border pb-1 overflow-x-auto">
|
||||
<div className="flex items-center gap-2 border-b border-border-soft pb-1.5 overflow-x-auto">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('members')}
|
||||
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs font-semibold transition-all whitespace-nowrap ${
|
||||
activeTab === 'members'
|
||||
? 'bg-brand text-white shadow-xs'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised'
|
||||
? 'bg-brand text-white shadow-md shadow-brand/20'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised/80'
|
||||
}`}
|
||||
>
|
||||
<Users className="w-4 h-4" />
|
||||
<span>Membros Ativos</span>
|
||||
<span className="px-1.5 py-0.2 rounded-full text-[10px] bg-white/20">
|
||||
<span className="px-2 py-0.5 rounded-full text-[10px] bg-white/20 font-bold">
|
||||
{members.length}
|
||||
</span>
|
||||
</button>
|
||||
|
|
@ -228,14 +229,14 @@ export const MembersPage: React.FC = () => {
|
|||
onClick={() => setActiveTab('pending')}
|
||||
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs font-semibold transition-all whitespace-nowrap ${
|
||||
activeTab === 'pending'
|
||||
? 'bg-amber-600 text-white shadow-xs'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised'
|
||||
? 'bg-amber-600 text-white shadow-md shadow-amber-600/20'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised/80'
|
||||
}`}
|
||||
>
|
||||
<Clock className="w-4 h-4" />
|
||||
<span>Solicitações Pendentes</span>
|
||||
{pendingRequests.length > 0 && (
|
||||
<span className="px-1.5 py-0.2 rounded-full text-[10px] bg-rose-500 text-white font-bold animate-pulse">
|
||||
<span className="px-2 py-0.5 rounded-full text-[10px] bg-rose-500 text-white font-bold animate-pulse">
|
||||
{pendingRequests.length}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -247,8 +248,8 @@ export const MembersPage: React.FC = () => {
|
|||
onClick={() => setActiveTab('discover')}
|
||||
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs font-semibold transition-all whitespace-nowrap ${
|
||||
activeTab === 'discover'
|
||||
? 'bg-brand text-white shadow-xs'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised'
|
||||
? 'bg-brand text-white shadow-md shadow-brand/20'
|
||||
: 'text-muted hover:text-foreground hover:bg-surface-raised/80'
|
||||
}`}
|
||||
>
|
||||
<UserPlus className="w-4 h-4" />
|
||||
|
|
@ -260,15 +261,15 @@ export const MembersPage: React.FC = () => {
|
|||
{activeTab === 'members' && (
|
||||
<div className="space-y-4">
|
||||
{/* Member Search Bar */}
|
||||
<div className="p-3.5 rounded-2xl border border-border bg-surface flex items-center justify-between gap-3 shadow-xs">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center justify-between gap-3 shadow-panel">
|
||||
<div className="relative w-full sm:w-80">
|
||||
<Search className="w-4 h-4 text-muted absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<Search className="w-4 h-4 text-muted absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<input
|
||||
type="text"
|
||||
value={memberSearchTerm}
|
||||
onChange={(e) => setMemberSearchTerm(e.target.value)}
|
||||
placeholder="Filtrar por nome ou e-mail..."
|
||||
className="w-full pl-9 pr-3 py-2 rounded-xl border border-border bg-surface-raised text-xs text-foreground placeholder-muted/60 focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand"
|
||||
className="w-full pl-9 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs text-muted font-medium">
|
||||
|
|
@ -281,12 +282,12 @@ export const MembersPage: React.FC = () => {
|
|||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-28 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-4"
|
||||
className="h-28 rounded-3xl border border-border-soft bg-surface-sunken/40 animate-pulse p-5"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : filteredMembers.length === 0 ? (
|
||||
<div className="text-center py-16 rounded-2xl border border-dashed border-border bg-surface/50">
|
||||
<div className="text-center py-16 rounded-3xl border border-dashed border-border-soft bg-surface/60 backdrop-blur-md shadow-panel">
|
||||
<Users className="w-8 h-8 text-muted mx-auto mb-2" />
|
||||
<p className="text-xs text-muted">Nenhum membro encontrado com este termo.</p>
|
||||
</div>
|
||||
|
|
@ -297,7 +298,7 @@ export const MembersPage: React.FC = () => {
|
|||
return (
|
||||
<div
|
||||
key={member.userId}
|
||||
className="p-4 rounded-2xl border border-border bg-surface hover:border-zinc-700 transition-all flex items-center justify-between gap-3"
|
||||
className="p-5 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200 flex items-center justify-between gap-3"
|
||||
>
|
||||
<div className="flex items-center gap-3 truncate">
|
||||
<div className="w-10 h-10 rounded-full bg-brand/15 border border-brand/30 flex items-center justify-center text-sm font-bold text-brand uppercase shrink-0">
|
||||
|
|
@ -322,7 +323,7 @@ export const MembersPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className="shrink-0">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[10px] font-medium bg-surface-raised border border-border text-muted">
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-[10px] font-semibold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||
<UserCheck className="w-3 h-3 text-emerald-400" />
|
||||
Ativo
|
||||
</span>
|
||||
|
|
@ -349,7 +350,7 @@ export const MembersPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{pendingRequests.length === 0 ? (
|
||||
<div className="text-center py-16 rounded-2xl border border-dashed border-border bg-surface/50">
|
||||
<div className="text-center py-16 rounded-3xl border border-dashed border-border-soft bg-surface/60 backdrop-blur-md shadow-panel">
|
||||
<CheckCircle2 className="w-8 h-8 text-emerald-400 mx-auto mb-2" />
|
||||
<h4 className="text-sm font-bold text-foreground">Tudo limpo!</h4>
|
||||
<p className="text-xs text-muted">Não há solicitações pendentes de aprovação no momento.</p>
|
||||
|
|
@ -359,7 +360,7 @@ export const MembersPage: React.FC = () => {
|
|||
{pendingRequests.map((req) => (
|
||||
<div
|
||||
key={req.userId}
|
||||
className="p-4 rounded-2xl border border-border bg-surface flex items-center justify-between gap-4"
|
||||
className="p-5 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200 flex items-center justify-between gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-3 truncate">
|
||||
<div className="w-10 h-10 rounded-full bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-sm font-bold text-amber-400 uppercase shrink-0">
|
||||
|
|
@ -376,7 +377,7 @@ export const MembersPage: React.FC = () => {
|
|||
type="button"
|
||||
disabled={actionLoadingId === req.userId}
|
||||
onClick={() => handleReject(req.userId)}
|
||||
className="p-2 rounded-xl border border-rose-500/30 text-rose-400 hover:bg-rose-500/15 transition-colors"
|
||||
className="p-2.5 rounded-xl border border-rose-500/30 text-rose-400 hover:bg-rose-500/15 transition-colors"
|
||||
title="Rejeitar"
|
||||
>
|
||||
<XCircle className="w-4 h-4" />
|
||||
|
|
@ -385,7 +386,7 @@ export const MembersPage: React.FC = () => {
|
|||
type="button"
|
||||
disabled={actionLoadingId === req.userId}
|
||||
onClick={() => handleApprove(req.userId)}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-brand text-white text-xs font-bold hover:bg-brand-hover shadow-xs transition-colors"
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-brand text-white text-xs font-bold hover:bg-brand-hover shadow-md shadow-brand/20 transition-all"
|
||||
>
|
||||
{actionLoadingId === req.userId ? (
|
||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||
|
|
@ -406,7 +407,7 @@ export const MembersPage: React.FC = () => {
|
|||
{activeTab === 'discover' && (
|
||||
<div className="space-y-6">
|
||||
{/* Search other organizations */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface shadow-xs space-y-4">
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel space-y-4">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="w-8 h-8 rounded-lg bg-brand/15 border border-brand/30 flex items-center justify-center text-brand">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
|
|
@ -423,19 +424,19 @@ export const MembersPage: React.FC = () => {
|
|||
|
||||
<form onSubmit={handleSearchOrg} className="flex gap-2.5">
|
||||
<div className="relative flex-1">
|
||||
<Search className="w-4 h-4 text-muted absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<Search className="w-4 h-4 text-muted absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchOrgTerm}
|
||||
onChange={(e) => setSearchOrgTerm(e.target.value)}
|
||||
placeholder="Digite o nome da organização (ex: BeeTech, Squad Core...)"
|
||||
className="w-full pl-9 pr-3 py-2.5 rounded-xl border border-border bg-surface-raised text-xs text-foreground placeholder-muted/60 focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand"
|
||||
className="w-full pl-9 pr-3.5 py-2.5 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={searching || !searchOrgTerm.trim()}
|
||||
className="px-4 py-2.5 rounded-xl bg-brand text-white text-xs font-semibold hover:bg-brand-hover shadow-xs flex items-center gap-1.5 disabled:opacity-50"
|
||||
className="px-4 py-2.5 rounded-xl bg-brand text-white text-xs font-semibold hover:bg-brand-hover shadow-md shadow-brand/20 flex items-center gap-1.5 disabled:opacity-50 transition-all"
|
||||
>
|
||||
{searching ? <Loader2 className="w-4 h-4 animate-spin" /> : <Search className="w-4 h-4" />}
|
||||
<span>Buscar</span>
|
||||
|
|
@ -444,7 +445,7 @@ export const MembersPage: React.FC = () => {
|
|||
|
||||
{/* Results */}
|
||||
{searchResults.length > 0 && (
|
||||
<div className="space-y-2 pt-2 border-t border-border">
|
||||
<div className="space-y-2 pt-2 border-t border-border-soft/60">
|
||||
<p className="text-[11px] font-semibold text-muted uppercase tracking-wider">
|
||||
Resultados da Busca
|
||||
</p>
|
||||
|
|
@ -456,7 +457,7 @@ export const MembersPage: React.FC = () => {
|
|||
return (
|
||||
<div
|
||||
key={org.id}
|
||||
className="p-3.5 rounded-xl border border-border bg-surface-raised flex items-center justify-between gap-3"
|
||||
className="p-4 rounded-2xl border border-border-soft bg-surface-raised/80 flex items-center justify-between gap-3 shadow-xs"
|
||||
>
|
||||
<div className="truncate">
|
||||
<p className="text-xs font-bold text-foreground truncate">{org.name}</p>
|
||||
|
|
@ -491,7 +492,7 @@ export const MembersPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Minhas solicitações enviadas */}
|
||||
<div className="p-5 rounded-2xl border border-border bg-surface shadow-xs space-y-3">
|
||||
<div className="p-6 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel space-y-3">
|
||||
<h3 className="text-xs font-bold uppercase tracking-wider text-muted">
|
||||
Minhas Solicitações de Ingresso
|
||||
</h3>
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
<div className="space-y-6 max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent">
|
||||
Configurações do Workspace
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand animate-pulse" />
|
||||
{activeOrg.name}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -43,8 +44,8 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Main Settings Card */}
|
||||
<div className="rounded-2xl border border-border bg-surface p-6 shadow-xs space-y-6">
|
||||
<div className="flex items-start justify-between gap-4 pb-6 border-b border-border">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-8 shadow-panel space-y-6">
|
||||
<div className="flex items-start justify-between gap-4 pb-6 border-b border-border-soft/60">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-brand to-teal-700 flex items-center justify-center text-white font-bold text-xl shadow-md shadow-brand/20">
|
||||
{activeOrg.name.slice(0, 2).toUpperCase()}
|
||||
|
|
@ -56,7 +57,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<span
|
||||
className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-xl text-xs font-bold border ${
|
||||
className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-bold border ${
|
||||
isOwner
|
||||
? 'bg-amber-500/15 text-amber-300 border-amber-500/30'
|
||||
: 'bg-teal-500/15 text-teal-300 border-teal-500/30'
|
||||
|
|
@ -69,14 +70,14 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
|
||||
{/* Workspace Details List */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-4 rounded-2xl bg-surface-raised/80 border border-border-soft">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Nome da Organização
|
||||
</span>
|
||||
<span className="text-sm font-bold text-foreground">{activeOrg.name}</span>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-4 rounded-2xl bg-surface-raised/80 border border-border-soft">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Seu Perfil Conectado
|
||||
</span>
|
||||
|
|
@ -85,7 +86,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-4 rounded-2xl bg-surface-raised/80 border border-border-soft">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Permissões Ativas
|
||||
</span>
|
||||
|
|
@ -101,7 +102,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-4 rounded-2xl bg-surface-raised/80 border border-border-soft">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Status da Conexão
|
||||
</span>
|
||||
|
|
@ -121,7 +122,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/members')}
|
||||
className="p-3.5 rounded-xl border border-border bg-surface-raised hover:border-brand transition-colors text-left flex items-center justify-between"
|
||||
className="p-4 rounded-2xl border border-border-soft bg-surface-raised/80 hover:border-brand/40 hover:-translate-y-0.5 hover:shadow-md transition-all text-left flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Users className="w-4 h-4 text-brand" />
|
||||
|
|
@ -133,7 +134,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/projects')}
|
||||
className="p-3.5 rounded-xl border border-border bg-surface-raised hover:border-brand transition-colors text-left flex items-center justify-between"
|
||||
className="p-4 rounded-2xl border border-border-soft bg-surface-raised/80 hover:border-brand/40 hover:-translate-y-0.5 hover:shadow-md transition-all text-left flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<FolderKanban className="w-4 h-4 text-brand" />
|
||||
|
|
@ -145,7 +146,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/organizations')}
|
||||
className="p-3.5 rounded-xl border border-border bg-surface-raised hover:border-brand transition-colors text-left flex items-center justify-between"
|
||||
className="p-4 rounded-2xl border border-border-soft bg-surface-raised/80 hover:border-brand/40 hover:-translate-y-0.5 hover:shadow-md transition-all text-left flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Building2 className="w-4 h-4 text-brand" />
|
||||
|
|
@ -157,7 +158,7 @@ export const OrganizationSettingsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Security Note */}
|
||||
<div className="p-4 rounded-xl bg-surface-sunken/60 border border-border flex items-start gap-3 text-xs text-muted">
|
||||
<div className="p-4 rounded-2xl bg-surface-sunken/60 border border-border-soft flex items-start gap-3 text-xs text-muted">
|
||||
<Info className="w-4 h-4 text-brand shrink-0 mt-0.5" />
|
||||
<p>
|
||||
As alterações nos dados cadastrais da organização respeitam a política de isolamento multi-tenant do BeeTask. Apenas os usuários autenticados e aprovados têm acesso aos dados deste workspace.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export const ProfilePage: React.FC = () => {
|
|||
<div className="space-y-6 max-w-4xl mx-auto">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground mb-1">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent mb-1">
|
||||
Meu Perfil & Conta
|
||||
</h1>
|
||||
<p className="text-xs text-muted">
|
||||
|
|
@ -59,8 +59,8 @@ export const ProfilePage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Main Profile Info Card */}
|
||||
<div className="rounded-2xl border border-border bg-surface p-6 shadow-xs space-y-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-6 border-b border-border">
|
||||
<div className="rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-8 shadow-panel space-y-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-6 border-b border-border-soft/60">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-full overflow-hidden border-2 border-brand/40 bg-gradient-to-br from-brand to-teal-700 flex items-center justify-center text-white font-extrabold text-2xl shadow-lg shadow-brand/20 shrink-0">
|
||||
{user.photo ? (
|
||||
|
|
@ -87,7 +87,7 @@ export const ProfilePage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={handleLogout}
|
||||
className="self-start sm:self-auto flex items-center gap-2 px-4 py-2 rounded-xl border border-rose-500/30 text-rose-300 hover:bg-rose-500/10 text-xs font-semibold transition-colors"
|
||||
className="self-start sm:self-auto flex items-center gap-2 px-4 py-2.5 rounded-xl border border-rose-500/30 text-rose-300 hover:bg-rose-500/10 text-xs font-semibold transition-all hover:shadow-xs"
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
<span>Encerrar Sessão</span>
|
||||
|
|
@ -100,13 +100,13 @@ export const ProfilePage: React.FC = () => {
|
|||
Meu Desempenho no Workspace Atual ({activeOrg?.name})
|
||||
</h3>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-8 text-muted gap-2 text-xs rounded-xl bg-surface-raised border border-border">
|
||||
<div className="flex items-center justify-center py-8 text-muted gap-2 text-xs rounded-2xl bg-surface-raised/80 border border-border-soft">
|
||||
<Loader2 className="w-4 h-4 animate-spin text-brand" />
|
||||
<span>Carregando estatísticas pessoais...</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-5 rounded-2xl bg-surface-raised/80 border border-border-soft shadow-xs">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Tempo Registrado
|
||||
</span>
|
||||
|
|
@ -118,7 +118,7 @@ export const ProfilePage: React.FC = () => {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-5 rounded-2xl bg-surface-raised/80 border border-border-soft shadow-xs">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Tarefas Concluídas
|
||||
</span>
|
||||
|
|
@ -130,7 +130,7 @@ export const ProfilePage: React.FC = () => {
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-4 rounded-xl bg-surface-raised border border-border">
|
||||
<div className="p-5 rounded-2xl bg-surface-raised/80 border border-border-soft shadow-xs">
|
||||
<span className="text-[11px] font-semibold text-muted block mb-1">
|
||||
Workspace Ativo
|
||||
</span>
|
||||
|
|
@ -168,18 +168,18 @@ export const ProfilePage: React.FC = () => {
|
|||
<div
|
||||
key={org.id}
|
||||
onClick={() => setActiveOrg(org.id)}
|
||||
className={`p-3.5 rounded-xl border cursor-pointer transition-all flex items-center justify-between gap-3 ${
|
||||
className={`p-4 rounded-2xl border cursor-pointer transition-all flex items-center justify-between gap-3 ${
|
||||
isCurrent
|
||||
? 'border-brand bg-brand/5 ring-1 ring-brand/30'
|
||||
: 'border-border bg-surface-raised hover:border-zinc-700'
|
||||
? 'border-brand bg-brand/10 ring-1 ring-brand/40 shadow-xs'
|
||||
: 'border-border-soft bg-surface-raised/80 hover:border-brand/40 hover:-translate-y-0.5'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`w-9 h-9 rounded-lg flex items-center justify-center font-bold text-xs ${
|
||||
className={`w-9 h-9 rounded-xl flex items-center justify-center font-bold text-xs ${
|
||||
isCurrent
|
||||
? 'bg-brand text-white'
|
||||
: 'bg-surface-sunken text-muted border border-border'
|
||||
? 'bg-brand text-white shadow-xs'
|
||||
: 'bg-surface-sunken text-muted border border-border-soft'
|
||||
}`}
|
||||
>
|
||||
{org.name.slice(0, 2).toUpperCase()}
|
||||
|
|
@ -191,7 +191,7 @@ export const ProfilePage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{isCurrent && (
|
||||
<span className="px-2 py-0.5 rounded-md bg-brand/20 text-brand text-[10px] font-bold">
|
||||
<span className="px-2 py-0.5 rounded-full bg-brand/20 text-brand text-[10px] font-bold">
|
||||
Ativo
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -138,11 +138,12 @@ export const ProjectsPage: React.FC = () => {
|
|||
{/* Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent">
|
||||
Projetos
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand animate-pulse" />
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -157,7 +158,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar projetos"
|
||||
className="p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
className="p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-panel disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-brand' : ''}`} />
|
||||
</button>
|
||||
|
|
@ -165,7 +166,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-brand text-white font-semibold text-xs hover:bg-brand-hover shadow-md shadow-brand/20 transition-all"
|
||||
className="flex items-center gap-1.5 px-4 py-2.5 rounded-xl bg-brand text-white font-semibold text-xs hover:bg-brand-hover shadow-md shadow-brand/25 transition-all hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Novo Projeto</span>
|
||||
|
|
@ -174,7 +175,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-300 text-xs">
|
||||
<div className="flex items-center justify-between p-4 rounded-2xl bg-rose-500/10 border border-rose-500/25 text-rose-300 text-xs shadow-panel backdrop-blur-md">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-rose-400" />
|
||||
<span>{error}</span>
|
||||
|
|
@ -190,15 +191,15 @@ export const ProjectsPage: React.FC = () => {
|
|||
)}
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="p-3.5 rounded-2xl border border-border bg-surface flex items-center justify-between gap-3 shadow-xs">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center justify-between gap-3 shadow-panel">
|
||||
<div className="relative w-full sm:w-80">
|
||||
<Search className="w-4 h-4 text-muted absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<Search className="w-4 h-4 text-muted absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Buscar projeto por nome..."
|
||||
className="w-full pl-9 pr-3 py-2 rounded-xl border border-border bg-surface-raised text-xs text-foreground placeholder-muted/60 focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
className="w-full pl-9 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-muted font-medium">
|
||||
|
|
@ -208,11 +209,11 @@ export const ProjectsPage: React.FC = () => {
|
|||
|
||||
{/* Grid of Projects */}
|
||||
{loading ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-48 rounded-2xl border border-border bg-surface-sunken/40 animate-pulse p-5 space-y-4"
|
||||
className="h-52 rounded-3xl border border-border-soft bg-surface-sunken/40 animate-pulse p-6 space-y-4"
|
||||
>
|
||||
<div className="h-5 bg-surface-raised rounded-md w-1/2" />
|
||||
<div className="h-3 bg-surface-raised rounded-md w-3/4" />
|
||||
|
|
@ -221,7 +222,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
))}
|
||||
</div>
|
||||
) : filteredProjects.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-20 px-4 text-center rounded-2xl border border-dashed border-border bg-surface/50">
|
||||
<div className="flex flex-col items-center justify-center py-20 px-4 text-center rounded-3xl border border-dashed border-border-soft bg-surface/60 backdrop-blur-md shadow-panel">
|
||||
<div className="w-14 h-14 rounded-2xl bg-brand/10 border border-brand/20 flex items-center justify-center text-brand mb-4">
|
||||
<FolderKanban className="w-7 h-7" />
|
||||
</div>
|
||||
|
|
@ -236,7 +237,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-brand text-white text-xs font-semibold hover:bg-brand-hover shadow-md shadow-brand/20 transition-all"
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-brand text-white text-xs font-semibold hover:bg-brand-hover shadow-md shadow-brand/25 transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Criar Primeiro Projeto</span>
|
||||
|
|
@ -258,7 +259,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
return (
|
||||
<div
|
||||
key={project.id}
|
||||
className="group rounded-2xl border border-border bg-surface p-5 hover:border-zinc-700 hover:shadow-lg transition-all duration-200 flex flex-col justify-between"
|
||||
className="group rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md p-6 hover:border-brand/40 shadow-panel hover:shadow-xl hover:-translate-y-1 transition-all duration-200 flex flex-col justify-between"
|
||||
>
|
||||
<div>
|
||||
{/* Top Bar with Icon & Color badge */}
|
||||
|
|
@ -286,7 +287,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Metrics Bar */}
|
||||
<div className="grid grid-cols-2 gap-2 p-3 rounded-xl bg-surface-sunken/60 border border-border/60 mb-4">
|
||||
<div className="grid grid-cols-2 gap-2 p-3.5 rounded-2xl bg-surface-sunken/60 border border-border-soft mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<ListTodo className="w-3.5 h-3.5 text-muted" />
|
||||
<div>
|
||||
|
|
@ -326,7 +327,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{/* Footer Link */}
|
||||
<div className="pt-3 border-t border-border/80 flex items-center justify-between">
|
||||
<div className="pt-3 border-t border-border-soft/60 flex items-center justify-between">
|
||||
<span className="text-[11px] text-muted flex items-center gap-1">
|
||||
<CheckCircle2 className="w-3.5 h-3.5 text-emerald-400" />
|
||||
{stats.completedTasks} entregues
|
||||
|
|
|
|||
|
|
@ -199,11 +199,12 @@ export const TasksPage: React.FC = () => {
|
|||
{/* Page Header */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold tracking-tight bg-gradient-to-r from-brand via-brand-strong to-accent bg-clip-text text-transparent">
|
||||
Quadro de Tarefas
|
||||
</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold bg-brand/15 text-brand border border-brand/25 shadow-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand animate-pulse" />
|
||||
{activeOrg?.name || 'Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -220,19 +221,19 @@ export const TasksPage: React.FC = () => {
|
|||
onClick={loadData}
|
||||
disabled={refreshing}
|
||||
title="Recarregar tarefas"
|
||||
className="p-2 rounded-xl border border-border bg-surface text-muted hover:text-foreground hover:bg-surface-raised transition-colors disabled:opacity-50"
|
||||
className="p-2.5 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md text-muted hover:text-foreground hover:bg-surface-raised transition-all shadow-panel disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin text-brand' : ''}`} />
|
||||
</button>
|
||||
|
||||
{/* Kanban / List Toggle */}
|
||||
<div className="flex items-center p-1 rounded-xl border border-border bg-surface">
|
||||
<div className="flex items-center p-1 rounded-xl border border-border-soft bg-surface/90 backdrop-blur-md shadow-panel">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode('kanban')}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${
|
||||
className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-semibold transition-all ${
|
||||
viewMode === 'kanban'
|
||||
? 'bg-brand text-white shadow-xs'
|
||||
? 'bg-brand text-white shadow-md shadow-brand/20'
|
||||
: 'text-muted hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -242,9 +243,9 @@ export const TasksPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode('list')}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${
|
||||
className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-semibold transition-all ${
|
||||
viewMode === 'list'
|
||||
? 'bg-brand text-white shadow-xs'
|
||||
? 'bg-brand text-white shadow-md shadow-brand/20'
|
||||
: 'text-muted hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -257,7 +258,7 @@ export const TasksPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => handleOpenCreateModal('NOT_STARTED')}
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-brand text-white font-semibold text-xs hover:bg-brand-hover shadow-md shadow-brand/20 transition-all"
|
||||
className="flex items-center gap-1.5 px-4 py-2.5 rounded-xl bg-brand text-white font-semibold text-xs hover:bg-brand-hover shadow-md shadow-brand/25 transition-all hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span>Nova Tarefa</span>
|
||||
|
|
@ -267,7 +268,7 @@ export const TasksPage: React.FC = () => {
|
|||
|
||||
{/* Error alert if any */}
|
||||
{actionError && (
|
||||
<div className="flex items-center justify-between p-3.5 rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-300 text-xs">
|
||||
<div className="flex items-center justify-between p-4 rounded-2xl bg-rose-500/10 border border-rose-500/25 text-rose-300 text-xs shadow-panel backdrop-blur-md">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertCircle className="w-4 h-4 text-rose-400" />
|
||||
<span>{actionError}</span>
|
||||
|
|
@ -283,65 +284,65 @@ export const TasksPage: React.FC = () => {
|
|||
)}
|
||||
|
||||
{/* Quick KPI Bar */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
<div className="p-3.5 rounded-xl border border-border bg-surface flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-zinc-800 border border-zinc-700 flex items-center justify-center text-zinc-300">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3.5">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center gap-3.5 shadow-panel hover:-translate-y-0.5 transition-all">
|
||||
<div className="w-10 h-10 rounded-xl bg-surface-sunken border border-border-soft flex items-center justify-center text-muted">
|
||||
<Filter className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[11px] text-muted">Total de Tarefas</p>
|
||||
<p className="text-base font-bold text-foreground">{tasks.length}</p>
|
||||
<p className="text-[11px] text-muted font-medium">Total de Tarefas</p>
|
||||
<p className="text-lg font-bold text-foreground">{tasks.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3.5 rounded-xl border border-border bg-surface flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center gap-3.5 shadow-panel hover:-translate-y-0.5 transition-all">
|
||||
<div className="w-10 h-10 rounded-xl bg-teal-500/15 border border-teal-500/30 flex items-center justify-center text-teal-400">
|
||||
<Clock className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[11px] text-muted">Em Andamento</p>
|
||||
<p className="text-base font-bold text-teal-400">{inProgressCount}</p>
|
||||
<p className="text-[11px] text-muted font-medium">Em Andamento</p>
|
||||
<p className="text-lg font-bold text-teal-400">{inProgressCount}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3.5 rounded-xl border border-border bg-surface flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-zinc-800 border border-zinc-700 flex items-center justify-center text-muted">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center gap-3.5 shadow-panel hover:-translate-y-0.5 transition-all">
|
||||
<div className="w-10 h-10 rounded-xl bg-amber-500/15 border border-amber-500/30 flex items-center justify-center text-amber-400">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[11px] text-muted">A Fazer</p>
|
||||
<p className="text-base font-bold text-foreground">{notStartedCount}</p>
|
||||
<p className="text-[11px] text-muted font-medium">A Fazer</p>
|
||||
<p className="text-lg font-bold text-foreground">{notStartedCount}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3.5 rounded-xl border border-border bg-surface flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex items-center gap-3.5 shadow-panel hover:-translate-y-0.5 transition-all">
|
||||
<div className="w-10 h-10 rounded-xl bg-emerald-500/15 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[11px] text-muted">Concluídas</p>
|
||||
<p className="text-base font-bold text-emerald-400">{completedCount}</p>
|
||||
<p className="text-[11px] text-muted font-medium">Concluídas</p>
|
||||
<p className="text-lg font-bold text-emerald-400">{completedCount}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filter and Search Bar */}
|
||||
<div className="p-3.5 rounded-2xl border border-border bg-surface flex flex-col md:flex-row items-center justify-between gap-3 shadow-xs">
|
||||
<div className="p-4 rounded-3xl border border-border-soft bg-surface/90 backdrop-blur-md flex flex-col md:flex-row items-center justify-between gap-3 shadow-panel">
|
||||
{/* Search Input */}
|
||||
<div className="relative w-full md:w-80">
|
||||
<Search className="w-4 h-4 text-muted absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<Search className="w-4 h-4 text-muted absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Buscar por título, projeto, categoria..."
|
||||
className="w-full pl-9 pr-3 py-2 rounded-xl border border-border bg-surface-raised text-xs text-foreground placeholder-muted/60 focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
className="w-full pl-9 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground placeholder-muted focus:outline-none focus:ring-2 focus:ring-brand/40 focus:border-brand transition-all"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSearchQuery('')}
|
||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted hover:text-foreground text-xs"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted hover:text-foreground text-xs"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
|
|
@ -354,10 +355,10 @@ export const TasksPage: React.FC = () => {
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setOnlyMyTasks(!onlyMyTasks)}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-medium border transition-colors ${
|
||||
className={`flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-xs font-medium border transition-all ${
|
||||
onlyMyTasks
|
||||
? 'bg-brand/15 text-brand border-brand/40 font-semibold'
|
||||
: 'bg-surface-raised border-border text-muted hover:text-foreground'
|
||||
? 'bg-brand/15 text-brand border-brand/40 font-semibold shadow-xs'
|
||||
: 'bg-surface-raised/80 border-border-soft text-muted hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{onlyMyTasks ? <UserCheck className="w-3.5 h-3.5" /> : <Users className="w-3.5 h-3.5" />}
|
||||
|
|
@ -374,7 +375,7 @@ export const TasksPage: React.FC = () => {
|
|||
e.target.value === 'ALL' ? 'ALL' : Number(e.target.value)
|
||||
)
|
||||
}
|
||||
className="pl-7 pr-3 py-1.5 rounded-xl border border-border bg-surface-raised text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
||||
className="pl-7 pr-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand cursor-pointer"
|
||||
>
|
||||
<option value="ALL">Todos os Projetos</option>
|
||||
{projects.map((p) => (
|
||||
|
|
@ -391,7 +392,7 @@ export const TasksPage: React.FC = () => {
|
|||
<select
|
||||
value={selectedStatus}
|
||||
onChange={(e) => setSelectedStatus(e.target.value as TaskStatus | 'ALL')}
|
||||
className="px-3 py-1.5 rounded-xl border border-border bg-surface-raised text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand"
|
||||
className="px-3.5 py-2 rounded-xl border border-border-soft bg-surface-raised/80 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-brand cursor-pointer"
|
||||
>
|
||||
<option value="ALL">Todos os Status</option>
|
||||
<option value="NOT_STARTED">A Fazer</option>
|
||||
|
|
|
|||
Loading…
Reference in a new issue