fix: version 4.0.3 -- Indicador de proceso en consultas a la IA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# Changelog - project.linkedcorp.net
|
# Changelog - project.linkedcorp.net
|
||||||
|
|
||||||
|
## [4.0.3] - 2026-10-23
|
||||||
|
### Fixed
|
||||||
|
- Las consultas a la IA ("Nuevo con IA" y "Plan con IA") muestran un indicador de proceso con
|
||||||
|
spinner, mensaje y segundos transcurridos (componente compartido `BusyPanel`); antes parecía
|
||||||
|
que el botón no hacía nada mientras la IA respondía.
|
||||||
|
|
||||||
## [4.0.2] - 2026-10-23
|
## [4.0.2] - 2026-10-23
|
||||||
### Changed
|
### Changed
|
||||||
- Detección de proyectos duplicados en "Nuevo con IA" ahora la decide la IA, dentro de la misma
|
- Detección de proyectos duplicados en "Nuevo con IA" ahora la decide la IA, dentro de la misma
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "project-linkedcorp-backend",
|
"name": "project-linkedcorp-backend",
|
||||||
"version": "4.0.2",
|
"version": "4.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "project-linkedcorp-frontend",
|
"name": "project-linkedcorp-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "4.0.2",
|
"version": "4.0.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
// Indicador de proceso largo (ej. consultas a la IA): spinner, mensaje y segundos transcurridos,
|
||||||
|
// para que el usuario sepa que el sistema esta trabajando y no colgado.
|
||||||
|
export function BusyPanel({ message, hint }) {
|
||||||
|
const [seconds, setSeconds] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => setSeconds((s) => s + 1), 1000);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className="busy-panel" role="status" aria-live="polite">
|
||||||
|
<div className="busy-spinner" aria-hidden="true" />
|
||||||
|
<div>
|
||||||
|
<div className="busy-message">{message} <span className="text-muted">({seconds}s)</span></div>
|
||||||
|
{hint && <div className="text-muted busy-hint">{hint}</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -26,6 +26,9 @@ export const messages = {
|
|||||||
ai_plan_generating: 'Generando...',
|
ai_plan_generating: 'Generando...',
|
||||||
ai_plan_review: 'Desmarca lo que no quieras. Las actividades se crean encadenadas desde hoy, con prioridad media; luego puedes editarlas.',
|
ai_plan_review: 'Desmarca lo que no quieras. Las actividades se crean encadenadas desde hoy, con prioridad media; luego puedes editarlas.',
|
||||||
ai_plan_days: 'días',
|
ai_plan_days: 'días',
|
||||||
|
ai_busy_generating: 'La IA está preparando la propuesta...',
|
||||||
|
ai_busy_hint: 'Suele tardar entre 5 y 30 segundos. No cierres esta ventana.',
|
||||||
|
ai_busy_creating: 'Creando actividades y tareas...',
|
||||||
ai_plan_day: 'día',
|
ai_plan_day: 'día',
|
||||||
ai_plan_apply: 'Crear seleccionadas',
|
ai_plan_apply: 'Crear seleccionadas',
|
||||||
ai_plan_creating: 'Creando...',
|
ai_plan_creating: 'Creando...',
|
||||||
@@ -588,6 +591,9 @@ export const messages = {
|
|||||||
ai_plan_generating: 'Generating...',
|
ai_plan_generating: 'Generating...',
|
||||||
ai_plan_review: 'Uncheck what you do not want. Activities are created back to back from today with medium priority; you can edit them later.',
|
ai_plan_review: 'Uncheck what you do not want. Activities are created back to back from today with medium priority; you can edit them later.',
|
||||||
ai_plan_days: 'days',
|
ai_plan_days: 'days',
|
||||||
|
ai_busy_generating: 'AI is preparing the proposal...',
|
||||||
|
ai_busy_hint: 'It usually takes 5 to 30 seconds. Do not close this window.',
|
||||||
|
ai_busy_creating: 'Creating activities and tasks...',
|
||||||
ai_plan_day: 'day',
|
ai_plan_day: 'day',
|
||||||
ai_plan_apply: 'Create selected',
|
ai_plan_apply: 'Create selected',
|
||||||
ai_plan_creating: 'Creating...',
|
ai_plan_creating: 'Creating...',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { DetailPopup } from '../../components/DetailPopup.jsx';
|
|||||||
import { Button } from '../../components/Button.jsx';
|
import { Button } from '../../components/Button.jsx';
|
||||||
import { Field } from '../../components/Field.jsx';
|
import { Field } from '../../components/Field.jsx';
|
||||||
import { InfoBar } from '../../components/InfoBar.jsx';
|
import { InfoBar } from '../../components/InfoBar.jsx';
|
||||||
|
import { BusyPanel } from '../../components/BusyPanel.jsx';
|
||||||
import { useUi } from '../../context/ui-context.jsx';
|
import { useUi } from '../../context/ui-context.jsx';
|
||||||
import { generateProjectPlan, applyProjectPlan } from '../../services/ai.js';
|
import { generateProjectPlan, applyProjectPlan } from '../../services/ai.js';
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ export function AiPlanDialog({ open, projectId, onClose, onCreated }) {
|
|||||||
return (
|
return (
|
||||||
<DetailPopup open={open} title={t('ai_plan_title')} onClose={busy ? () => {} : close} width={720} height={620}>
|
<DetailPopup open={open} title={t('ai_plan_title')} onClose={busy ? () => {} : close} width={720} height={620}>
|
||||||
{error && <InfoBar tone="error">{error}</InfoBar>}
|
{error && <InfoBar tone="error">{error}</InfoBar>}
|
||||||
|
{busy && <BusyPanel message={plan ? t('ai_busy_creating') : t('ai_busy_generating')} hint={plan ? undefined : t('ai_busy_hint')} />}
|
||||||
{!plan ? (
|
{!plan ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-muted" style={{ marginTop: 0 }}>{t('ai_plan_hint')}</p>
|
<p className="text-muted" style={{ marginTop: 0 }}>{t('ai_plan_hint')}</p>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Button } from '../../components/Button.jsx';
|
|||||||
import { Field, FieldRow } from '../../components/Field.jsx';
|
import { Field, FieldRow } from '../../components/Field.jsx';
|
||||||
import { ComboBox } from '../../components/ComboBox.jsx';
|
import { ComboBox } from '../../components/ComboBox.jsx';
|
||||||
import { InfoBar } from '../../components/InfoBar.jsx';
|
import { InfoBar } from '../../components/InfoBar.jsx';
|
||||||
|
import { BusyPanel } from '../../components/BusyPanel.jsx';
|
||||||
import { useUi } from '../../context/ui-context.jsx';
|
import { useUi } from '../../context/ui-context.jsx';
|
||||||
import { useAuth } from '../../context/auth-context.jsx';
|
import { useAuth } from '../../context/auth-context.jsx';
|
||||||
import { proposeNewProject, createNewProject } from '../../services/ai.js';
|
import { proposeNewProject, createNewProject } from '../../services/ai.js';
|
||||||
@@ -71,6 +72,7 @@ export function NewProjectAiDialog({ open, deptOptions, minLength = 20, onClose,
|
|||||||
return (
|
return (
|
||||||
<DetailPopup open={open} title={t('ai_new_project_title')} onClose={close} width={760} height={680}>
|
<DetailPopup open={open} title={t('ai_new_project_title')} onClose={close} width={760} height={680}>
|
||||||
{error && <InfoBar tone="error">{error}</InfoBar>}
|
{error && <InfoBar tone="error">{error}</InfoBar>}
|
||||||
|
{busy && <BusyPanel message={proposal ? t('ai_busy_creating') : t('ai_busy_generating')} hint={proposal ? undefined : t('ai_busy_hint')} />}
|
||||||
{!proposal ? (
|
{!proposal ? (
|
||||||
<>
|
<>
|
||||||
<Field label={t('ai_new_project_objective')} hint={`${objective.trim().length}/${minLength} ${t('ai_new_project_min_chars')}`}>
|
<Field label={t('ai_new_project_objective')} hint={`${objective.trim().length}/${minLength} ${t('ai_new_project_min_chars')}`}>
|
||||||
|
|||||||
@@ -575,3 +575,9 @@ table.list-table { width: 100%; border-collapse: collapse; font-size: .8rem; }
|
|||||||
.text-muted { color: rgb(var(--muted)); }
|
.text-muted { color: rgb(var(--muted)); }
|
||||||
.mt-0 { margin-top: 0; }
|
.mt-0 { margin-top: 0; }
|
||||||
.empty-state { text-align: center; padding: 40px 16px; color: rgb(var(--muted)); }
|
.empty-state { text-align: center; padding: 40px 16px; color: rgb(var(--muted)); }
|
||||||
|
|
||||||
|
/* BusyPanel: proceso largo en curso (consultas a la IA) */
|
||||||
|
.busy-panel { display: flex; align-items: center; gap: 14px; padding: 16px; margin: 12px 0; border-radius: 8px; background: rgb(var(--accent) / .08); border: 1px solid rgb(var(--accent) / .3); }
|
||||||
|
.busy-spinner { width: 28px; height: 28px; flex: none; border-radius: 50%; border: 3px solid rgb(var(--accent) / .25); border-top-color: rgb(var(--accent)); animation: update-spin .9s linear infinite; }
|
||||||
|
.busy-message { font-weight: 600; }
|
||||||
|
.busy-hint { font-size: .78rem; margin-top: 2px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user