feat: version 3.2.0 -- Integracion IA: configuracion y plan de actividades

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-23 15:55:54 -05:00
parent c64a16064f
commit 89dec6da7e
25 changed files with 538 additions and 10 deletions
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { Plus, Save, Trash2, ArrowRightLeft, FolderUp, Archive, RotateCcw } from 'lucide-react';
import { Plus, Save, Trash2, ArrowRightLeft, FolderUp, Archive, RotateCcw, Sparkles } from 'lucide-react';
import { useUi } from '../../context/ui-context.jsx';
import { useAuth } from '../../context/auth-context.jsx';
import { useAsync, useAction } from '../../hooks/useAsync.js';
@@ -20,6 +20,8 @@ import { StatusBadge, ProgressBar, DueBadge } from '../../components/StatusBadge
import { dueStatus } from '../../utils/dueStatus.js';
import { DetailPopup } from '../../components/DetailPopup.jsx';
import ActivityDetailPanel from './ActivityDetailPanel.jsx';
import { AiPlanDialog } from './AiPlanDialog.jsx';
import { getAiStatus } from '../../services/ai.js';
import { DraftNotice } from '../../components/DraftNotice.jsx';
import { DraftMark, useGuardedClose } from '../../components/NavigationGuard.jsx';
import { useDraftForm, useUserDrafts, hasDraftFor } from '../../hooks/useDraftForm.js';
@@ -32,6 +34,9 @@ export default function ProjectActivitiesTab({ project, projectId }) {
const navigate = useNavigate();
const runAction = useAction();
const { data: activities, reload } = useAsync(() => listActivitiesByProject(projectId), [projectId]);
// Boton de IA solo si el superadmin configuro la clave; el backend valida permisos al usarlo.
const { data: aiStatus } = useAsync(() => getAiStatus().catch(() => ({ enabled: false })), []);
const [aiPlanOpen, setAiPlanOpen] = useState(false);
const [trashOpen, setTrashOpen] = useState(false);
const { data: trashed, reload: reloadTrash } = useAsync(
() => (trashOpen ? listActivitiesTrash(projectId) : Promise.resolve([])),
@@ -272,9 +277,14 @@ export default function ProjectActivitiesTab({ project, projectId }) {
{project?.isOwner && (
<Button variant="standard" icon={Archive} onClick={() => setTrashOpen(true)}>{t('activities_trash')}</Button>
)}
{aiStatus?.enabled && (
<Button variant="standard" icon={Sparkles} onClick={() => setAiPlanOpen(true)}>{t('ai_plan_button')}</Button>
)}
<Button variant="accent" icon={Plus} onClick={() => setCreating(true)}>{t('activity_new')}{pendingNew && !creating && <DraftMark />}</Button>
</div>
<AiPlanDialog open={aiPlanOpen} projectId={projectId} onClose={() => setAiPlanOpen(false)} onCreated={reload} />
<DetailPopup open={trashOpen} title={t('activities_trash')} onClose={() => setTrashOpen(false)} width={480} height={420}>
{(trashed || []).length === 0 && <div className="text-muted" style={{ fontSize: '.82rem' }}>{t('activities_trash_empty')}</div>}
{(trashed || []).map((a) => (