feat(J): J-UI frontend — ImprovementCenter, ProposalCard, PatternInsight, API client, i18n, tsc clean
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { TrendingUp, Lightbulb } from 'lucide-react';
|
||||||
|
import { Card } from '@/components/ui/Card';
|
||||||
|
import { EmptyState } from '@/components/ui/EmptyState';
|
||||||
|
import { Skeleton } from '@/components/ui/Skeleton';
|
||||||
|
import { ProposalCard } from './ProposalCard';
|
||||||
|
import { PatternInsight } from './PatternInsight';
|
||||||
|
import {
|
||||||
|
fetchProposals,
|
||||||
|
fetchPatterns,
|
||||||
|
approveProposal,
|
||||||
|
rejectProposal,
|
||||||
|
rollbackProposal,
|
||||||
|
type ImprovementProposal,
|
||||||
|
type DetectedPattern,
|
||||||
|
} from '@/api/improvement';
|
||||||
|
|
||||||
|
export interface ImprovementCenterProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImprovementCenter({ className }: ImprovementCenterProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const { data: proposals = [], isLoading: loadingProposals } = useQuery<ImprovementProposal[]>({
|
||||||
|
queryKey: ['improvement', 'proposals'],
|
||||||
|
queryFn: () => fetchProposals(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: patterns = [], isLoading: loadingPatterns } = useQuery<DetectedPattern[]>({
|
||||||
|
queryKey: ['improvement', 'patterns'],
|
||||||
|
queryFn: fetchPatterns,
|
||||||
|
});
|
||||||
|
|
||||||
|
const invalidate = () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['improvement', 'proposals'] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['improvement', 'patterns'] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const approveMutation = useMutation({
|
||||||
|
mutationFn: approveProposal,
|
||||||
|
onSuccess: invalidate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rejectMutation = useMutation({
|
||||||
|
mutationFn: rejectProposal,
|
||||||
|
onSuccess: invalidate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rollbackMutation = useMutation({
|
||||||
|
mutationFn: rollbackProposal,
|
||||||
|
onSuccess: invalidate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const busy = approveMutation.isPending || rejectMutation.isPending || rollbackMutation.isPending;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} data-testid="improvement-center">
|
||||||
|
<div className="mb-6">
|
||||||
|
<h2 className="flex items-center gap-2 text-xl font-semibold text-secondary-900">
|
||||||
|
<TrendingUp className="w-5 h-5 text-primary-600" aria-hidden="true" />
|
||||||
|
{t('improvement.title')}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-secondary-500 mt-1">{t('improvement.subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Patterns / Bottlenecks */}
|
||||||
|
<section className="mb-8" aria-labelledby="improvement-patterns-heading">
|
||||||
|
<h3 id="improvement-patterns-heading" className="text-base font-semibold text-secondary-800 mb-3">
|
||||||
|
{t('improvement.patterns')}
|
||||||
|
</h3>
|
||||||
|
{loadingPatterns ? (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<Skeleton className="h-40" />
|
||||||
|
<Skeleton className="h-40" />
|
||||||
|
</div>
|
||||||
|
) : patterns.length === 0 ? (
|
||||||
|
<EmptyState
|
||||||
|
icon={<Lightbulb className="w-6 h-6" />}
|
||||||
|
title={t('improvement.noPatterns')}
|
||||||
|
description={t('improvement.noPatternsDesc')}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{patterns.map((pattern) => (
|
||||||
|
<PatternInsight key={pattern.id} pattern={pattern} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Proposals */}
|
||||||
|
<section aria-labelledby="improvement-proposals-heading">
|
||||||
|
<h3 id="improvement-proposals-heading" className="text-base font-semibold text-secondary-800 mb-3">
|
||||||
|
{t('improvement.proposals')}
|
||||||
|
</h3>
|
||||||
|
{loadingProposals ? (
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
<Skeleton className="h-56" />
|
||||||
|
<Skeleton className="h-56" />
|
||||||
|
</div>
|
||||||
|
) : proposals.length === 0 ? (
|
||||||
|
<EmptyState
|
||||||
|
icon={<Lightbulb className="w-6 h-6" />}
|
||||||
|
title={t('improvement.noProposals')}
|
||||||
|
description={t('improvement.noProposalsDesc')}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
{proposals.map((proposal) => (
|
||||||
|
<ProposalCard
|
||||||
|
key={proposal.id}
|
||||||
|
proposal={proposal}
|
||||||
|
busy={busy}
|
||||||
|
onApprove={(id) => approveMutation.mutate(id)}
|
||||||
|
onReject={(id) => rejectMutation.mutate(id)}
|
||||||
|
onRollback={(id) => rollbackMutation.mutate(id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1353,5 +1353,43 @@
|
|||||||
"title": "Knowledge & Workstream aktivieren",
|
"title": "Knowledge & Workstream aktivieren",
|
||||||
"description": "Erstellen Sie Wiki-Artikel und verbinden Sie Menschen, Agenten und Workflows in einem einheitlichen Stream."
|
"description": "Erstellen Sie Wiki-Artikel und verbinden Sie Menschen, Agenten und Workflows in einem einheitlichen Stream."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"improvement": {
|
||||||
|
"title": "Improvement Center",
|
||||||
|
"subtitle": "Kontrollierte Selbstverbesserung — Vorschläge, Muster und Wirkungsmessung.",
|
||||||
|
"proposals": "Verbesserungsvorschläge",
|
||||||
|
"patterns": "Erkannte Muster & Engpässe",
|
||||||
|
"evidence": "Evidenz",
|
||||||
|
"approve": "Genehmigen",
|
||||||
|
"reject": "Ablehnen",
|
||||||
|
"rollback": "Zurücksetzen",
|
||||||
|
"score": "Bewertung",
|
||||||
|
"confidence": "Konfidenz",
|
||||||
|
"occurrences": "Vorkommen",
|
||||||
|
"bottleneck": "Engpass",
|
||||||
|
"rationale": "Begründung",
|
||||||
|
"expectedBenefit": "Erwarteter Nutzen",
|
||||||
|
"riskAssessment": "Risikobewertung",
|
||||||
|
"noPatterns": "Keine Muster erkannt",
|
||||||
|
"noPatternsDesc": "Es wurden noch keine Verbesserungsmuster oder Engpässe erkannt.",
|
||||||
|
"noProposals": "Keine Vorschläge",
|
||||||
|
"noProposalsDesc": "Es liegen noch keine Verbesserungsvorschläge vor.",
|
||||||
|
"status": {
|
||||||
|
"draft": "Entwurf",
|
||||||
|
"evaluating": "Wird bewertet",
|
||||||
|
"pending_approval": "Freigabe ausstehend",
|
||||||
|
"approved": "Genehmigt",
|
||||||
|
"rejected": "Abgelehnt",
|
||||||
|
"active": "Aktiv",
|
||||||
|
"rolled_back": "Zurückgesetzt",
|
||||||
|
"expired": "Abgelaufen"
|
||||||
|
},
|
||||||
|
"patternTypes": {
|
||||||
|
"repetitive_sequence": "Wiederkehrende Sequenz",
|
||||||
|
"frequent_corrections": "Häufige Korrekturen",
|
||||||
|
"rejected_suggestions": "Abgelehnte Vorschläge",
|
||||||
|
"error_retries": "Fehler & Wiederholungen",
|
||||||
|
"repetitive_handoffs": "Wiederkehrende Übergaben"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1353,5 +1353,43 @@
|
|||||||
"title": "Enable Knowledge & Workstream",
|
"title": "Enable Knowledge & Workstream",
|
||||||
"description": "Create wiki articles and connect humans, agents, and workflows in a unified stream."
|
"description": "Create wiki articles and connect humans, agents, and workflows in a unified stream."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"improvement": {
|
||||||
|
"title": "Improvement Center",
|
||||||
|
"subtitle": "Controlled self-improvement — proposals, patterns and impact measurement.",
|
||||||
|
"proposals": "Improvement Proposals",
|
||||||
|
"patterns": "Detected Patterns & Bottlenecks",
|
||||||
|
"evidence": "Evidence",
|
||||||
|
"approve": "Approve",
|
||||||
|
"reject": "Reject",
|
||||||
|
"rollback": "Rollback",
|
||||||
|
"score": "Score",
|
||||||
|
"confidence": "Confidence",
|
||||||
|
"occurrences": "Occurrences",
|
||||||
|
"bottleneck": "Bottleneck",
|
||||||
|
"rationale": "Rationale",
|
||||||
|
"expectedBenefit": "Expected Benefit",
|
||||||
|
"riskAssessment": "Risk Assessment",
|
||||||
|
"noPatterns": "No patterns detected",
|
||||||
|
"noPatternsDesc": "No improvement patterns or bottlenecks have been detected yet.",
|
||||||
|
"noProposals": "No proposals",
|
||||||
|
"noProposalsDesc": "There are no improvement proposals yet.",
|
||||||
|
"status": {
|
||||||
|
"draft": "Draft",
|
||||||
|
"evaluating": "Evaluating",
|
||||||
|
"pending_approval": "Pending Approval",
|
||||||
|
"approved": "Approved",
|
||||||
|
"rejected": "Rejected",
|
||||||
|
"active": "Active",
|
||||||
|
"rolled_back": "Rolled Back",
|
||||||
|
"expired": "Expired"
|
||||||
|
},
|
||||||
|
"patternTypes": {
|
||||||
|
"repetitive_sequence": "Repetitive Sequence",
|
||||||
|
"frequent_corrections": "Frequent Corrections",
|
||||||
|
"rejected_suggestions": "Rejected Suggestions",
|
||||||
|
"error_retries": "Errors & Retries",
|
||||||
|
"repetitive_handoffs": "Repetitive Handoffs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user