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",
|
||||
"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",
|
||||
"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