feat: contador en las pestañas de Tareas/Comentarios/Documentos de una actividad
Circulo con el numero al lado del nombre de la pestaña: Tareas muestra las pendientes (sin marcar), Comentarios y Documentos el total. Soporte agregado al componente Tabs reutilizable (tab.count), no solo al detalle de actividad. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "project-linkedcorp-frontend",
|
||||
"private": true,
|
||||
"version": "1.15.2",
|
||||
"version": "1.16.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -4,6 +4,7 @@ export function Tabs({ tabs, active, onChange }) {
|
||||
{tabs.map((tab) => (
|
||||
<button key={tab.key} className={`tab-item ${active === tab.key ? 'active' : ''}`} onClick={() => onChange(tab.key)}>
|
||||
{tab.label}
|
||||
{!!tab.count && <span className="tab-badge">{tab.count}</span>}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,8 @@ import { listTasks, createTask, updateTask, setTaskDone, deleteTask, reorderTask
|
||||
import { promoteTaskToRecurring } from '../../services/recurringTasks.js';
|
||||
import { listCatalog } from '../../services/catalogs.js';
|
||||
import { listUsers } from '../../services/users.js';
|
||||
import { listComments } from '../../services/comments.js';
|
||||
import { listAttachments } from '../../services/attachments.js';
|
||||
import { Button } from '../../components/Button.jsx';
|
||||
import { Field, FieldRow } from '../../components/Field.jsx';
|
||||
import { ComboBox } from '../../components/ComboBox.jsx';
|
||||
@@ -41,6 +43,10 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
});
|
||||
|
||||
const [tab, setTab] = useState('detail');
|
||||
// Recargadas al cambiar de pestaña (listas livianas) para que los contadores de las pestañas
|
||||
// se mantengan al dia sin necesidad de que CommentsPanel/AttachmentsPanel avisen al padre.
|
||||
const { data: comments } = useAsync(() => listComments('activity', activity.id), [activity.id, tab]);
|
||||
const { data: attachments } = useAsync(() => listAttachments('activity', activity.id), [activity.id, tab]);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [form, setForm] = useState(() => toForm(activity));
|
||||
const [newTask, setNewTask] = useState('');
|
||||
@@ -53,6 +59,7 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
useEffect(() => { setForm(toForm(activity)); setEditing(false); }, [activity]);
|
||||
|
||||
const hasTasks = tasks && tasks.length > 0;
|
||||
const pendingTasksCount = (tasks || []).filter((task) => !task.is_done).length;
|
||||
const priorityOptions = (priorities || []).map((p) => ({ value: p.id, label: p.name }));
|
||||
const statusOptions = (statuses || []).map((s) => ({ value: s.id, label: s.name }));
|
||||
const userOptions = (users || []).map((u) => ({ value: u.id, label: u.name }));
|
||||
@@ -141,10 +148,10 @@ export default function ActivityDetailPanel({ activity, projectId, siblingActivi
|
||||
<Tabs
|
||||
tabs={[
|
||||
{ key: 'detail', label: t('tab_info') },
|
||||
{ key: 'tasks', label: t('tasks_title') },
|
||||
{ key: 'tasks', label: t('tasks_title'), count: pendingTasksCount },
|
||||
{ key: 'dependencies', label: t('activity_dependencies') },
|
||||
{ key: 'comments', label: t('tab_comments') },
|
||||
{ key: 'documents', label: t('tab_documents') },
|
||||
{ key: 'comments', label: t('tab_comments'), count: (comments || []).length },
|
||||
{ key: 'documents', label: t('tab_documents'), count: (attachments || []).length },
|
||||
]}
|
||||
active={tab}
|
||||
onChange={setTab}
|
||||
|
||||
@@ -174,11 +174,18 @@ table.list-table { width: 100%; border-collapse: collapse; font-size: .8rem; }
|
||||
/* --- Pestañas reales --- */
|
||||
.tabs { display: flex; gap: 4px; border-bottom: 1px solid rgb(var(--line)); margin-bottom: 16px; overflow-x: auto; }
|
||||
.tab-item {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 14px; font-size: .82rem; font-weight: 600; color: rgb(var(--muted));
|
||||
background: transparent; border: none; border-bottom: 2px solid transparent; cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
.tab-item:hover { color: rgb(var(--body)); }
|
||||
.tab-item.active { color: rgb(var(--accent)); border-bottom-color: rgb(var(--accent)); }
|
||||
.tab-badge {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
|
||||
background: rgb(var(--accent) / .18); color: rgb(var(--accent));
|
||||
font-size: .68rem; font-weight: 700; line-height: 1;
|
||||
}
|
||||
|
||||
/* --- Cards / KPIs --- */
|
||||
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 20px; }
|
||||
|
||||
Reference in New Issue
Block a user