ltcai 8.0.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -72
- package/docs/CHANGELOG.md +52 -874
- package/docs/DEVELOPMENT.md +26 -3
- package/docs/LEGACY_COMPATIBILITY.md +3 -1
- package/docs/TRUST_MODEL.md +26 -26
- package/docs/WHY_LATTICE.md +28 -25
- package/docs/kg-schema.md +6 -4
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/embeddings.py +9 -1
- package/lattice_brain/graph/schema.py +6 -0
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/computer_use.py +2 -0
- package/latticeai/api/memory.py +13 -0
- package/latticeai/app_factory.py +43 -162
- package/latticeai/brain/__init__.py +1 -1
- package/latticeai/core/config.py +10 -0
- package/latticeai/core/local_embeddings.py +8 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/mcp_registry.py +140 -1
- package/latticeai/core/tool_registry.py +10 -0
- package/latticeai/core/workspace_graph_trace.py +132 -0
- package/latticeai/core/workspace_memory.py +75 -0
- package/latticeai/core/workspace_os.py +135 -1283
- package/latticeai/core/workspace_os_utils.py +132 -0
- package/latticeai/core/workspace_permissions.py +99 -0
- package/latticeai/core/workspace_plugins.py +97 -0
- package/latticeai/core/workspace_runs.py +612 -0
- package/latticeai/core/workspace_skills.py +114 -0
- package/latticeai/core/workspace_snapshots.py +195 -0
- package/latticeai/core/workspace_timeline.py +107 -0
- package/latticeai/runtime/audit_runtime.py +64 -0
- package/latticeai/runtime/config_runtime.py +4 -0
- package/latticeai/runtime/sso_runtime.py +52 -0
- package/latticeai/services/architecture_readiness.py +6 -5
- package/latticeai/services/memory_service.py +215 -7
- package/latticeai/services/model_capability_registry.py +68 -0
- package/latticeai/services/model_engines.py +603 -0
- package/latticeai/services/model_loading.py +482 -0
- package/latticeai/services/model_recommendation.py +5 -0
- package/latticeai/services/model_runtime.py +146 -837
- package/latticeai/services/product_readiness.py +19 -18
- package/package.json +2 -2
- package/scripts/build_frontend_assets.mjs +11 -2
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +11 -102
- package/static/app/assets/Act-D9jIknFd.js +1 -0
- package/static/app/assets/Brain-CFOtWbPN.js +321 -0
- package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
- package/static/app/assets/Library-C5Q2yWee.js +1 -0
- package/static/app/assets/System-BLbjdr1_.js +1 -0
- package/static/app/assets/core-CwxXejkd.js +1 -2
- package/static/app/assets/index-BqammyNu.js +16 -0
- package/static/app/assets/index-ty1iGgZu.css +2 -0
- package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
- package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
- package/static/app/index.html +3 -10
- package/static/app/theme-boot.js +8 -0
- package/static/sw.js +1 -1
- package/tools/__init__.py +2 -1
- package/tools/computer.py +21 -0
- package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
- package/docs/HANDOVER_v3.6.0.md +0 -46
- package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
- package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
- package/docs/V2_ARCHITECTURE.md +0 -561
- package/docs/V3_2_AUDIT.md +0 -82
- package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
- package/docs/V3_FRONTEND.md +0 -140
- package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
- package/docs/V4_1_VALIDATION_REPORT.md +0 -47
- package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
- package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
- package/docs/V4_2_VALIDATION_REPORT.md +0 -89
- package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
- package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
- package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
- package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
- package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
- package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
- package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
- package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
- package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
- package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
- package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
- package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
- package/docs/V4_3_VALIDATION_REPORT.md +0 -58
- package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
- package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
- package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
- package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
- package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
- package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
- package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
- package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
- package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
- package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
- package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
- package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
- package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
- package/docs/V4_5_1_UX_REPORT.md +0 -45
- package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
- package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
- package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
- package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
- package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
- package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
- package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
- package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
- package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
- package/frontend/index.html +0 -24
- package/frontend/openapi.json +0 -15425
- package/frontend/src/App.tsx +0 -243
- package/frontend/src/api/client.ts +0 -580
- package/frontend/src/api/openapi.ts +0 -17892
- package/frontend/src/components/AdminAccessGate.tsx +0 -70
- package/frontend/src/components/FeedbackState.tsx +0 -45
- package/frontend/src/components/LanguageSwitcher.tsx +0 -23
- package/frontend/src/components/LivingBrain.tsx +0 -220
- package/frontend/src/components/ProductFlow.tsx +0 -171
- package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
- package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
- package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
- package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
- package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
- package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
- package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
- package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
- package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
- package/frontend/src/components/primitives.tsx +0 -392
- package/frontend/src/components/ui/badge.tsx +0 -27
- package/frontend/src/components/ui/button.tsx +0 -37
- package/frontend/src/components/ui/card.tsx +0 -22
- package/frontend/src/components/ui/input.tsx +0 -16
- package/frontend/src/components/ui/textarea.tsx +0 -16
- package/frontend/src/features/admin/AdminConsole.tsx +0 -334
- package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
- package/frontend/src/features/brain/BrainComposer.tsx +0 -85
- package/frontend/src/features/brain/BrainConversation.tsx +0 -688
- package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
- package/frontend/src/features/brain/BrainHome.tsx +0 -624
- package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
- package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
- package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
- package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
- package/frontend/src/features/brain/brainData.ts +0 -246
- package/frontend/src/features/brain/graphLayout.ts +0 -37
- package/frontend/src/features/brain/types.ts +0 -150
- package/frontend/src/features/review/ReviewCard.tsx +0 -100
- package/frontend/src/features/review/ReviewInbox.tsx +0 -127
- package/frontend/src/features/review/reviewHelpers.ts +0 -69
- package/frontend/src/i18n.ts +0 -1303
- package/frontend/src/lib/utils.ts +0 -33
- package/frontend/src/main.tsx +0 -23
- package/frontend/src/pages/Act.tsx +0 -458
- package/frontend/src/pages/Ask.tsx +0 -14
- package/frontend/src/pages/Brain.tsx +0 -914
- package/frontend/src/pages/Capture.tsx +0 -258
- package/frontend/src/pages/Library.tsx +0 -486
- package/frontend/src/pages/System.tsx +0 -621
- package/frontend/src/routes.ts +0 -92
- package/frontend/src/store/appStore.ts +0 -94
- package/frontend/src/styles.css +0 -6579
- package/static/app/assets/Act-DOvf59ru.js +0 -2
- package/static/app/assets/Act-DOvf59ru.js.map +0 -1
- package/static/app/assets/Brain-C7_0mEiI.js +0 -322
- package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
- package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
- package/static/app/assets/Library-BJPEEm5O.js +0 -2
- package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
- package/static/app/assets/System-D6t9jo9V.js +0 -2
- package/static/app/assets/System-D6t9jo9V.js.map +0 -1
- package/static/app/assets/core-CwxXejkd.js.map +0 -1
- package/static/app/assets/index-C7g26IF6.css +0 -2
- package/static/app/assets/index-DbcEYJQ2.js +0 -17
- package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
- package/static/app/assets/primitives-CD38lt4n.js +0 -2
- package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
- package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { RotateCcw } from "lucide-react";
|
|
2
|
-
import type { ApiResult, ReviewItem } from "@/api/client";
|
|
3
|
-
import { ActionButton, KeyValueList } from "@/components/primitives";
|
|
4
|
-
import { Badge } from "@/components/ui/badge";
|
|
5
|
-
import { Button } from "@/components/ui/button";
|
|
6
|
-
import { t } from "@/i18n";
|
|
7
|
-
import { useAppStore } from "@/store/appStore";
|
|
8
|
-
import {
|
|
9
|
-
formatSnoozedUntil,
|
|
10
|
-
hasRunBefore,
|
|
11
|
-
isActionableReview,
|
|
12
|
-
reviewSourceDetail,
|
|
13
|
-
reviewSourceLabel,
|
|
14
|
-
reviewStatusVariant,
|
|
15
|
-
type ReviewAction,
|
|
16
|
-
} from "./reviewHelpers";
|
|
17
|
-
|
|
18
|
-
type ReviewCardProps = {
|
|
19
|
-
item: ReviewItem;
|
|
20
|
-
feedback?: string;
|
|
21
|
-
onAction: (item: ReviewItem, action: ReviewAction, hadRunBefore?: boolean) => Promise<ApiResult<ReviewItem>>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function ReviewCard({ item, feedback, onAction }: ReviewCardProps) {
|
|
25
|
-
const mode = useAppStore((state) => state.mode);
|
|
26
|
-
const language = useAppStore((state) => state.language);
|
|
27
|
-
const provenance = item.provenance || {};
|
|
28
|
-
const payload = item.payload || {};
|
|
29
|
-
const hadRun = hasRunBefore(item);
|
|
30
|
-
const snoozed = item.effective_status === "snoozed";
|
|
31
|
-
const actionable = isActionableReview(item);
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div className="rounded-lg border border-border bg-background/55 p-4">
|
|
35
|
-
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
36
|
-
<div className="min-w-0 flex-1">
|
|
37
|
-
<div className="font-medium">{item.title}</div>
|
|
38
|
-
{item.summary ? <p className="mt-1 text-sm leading-6 text-muted-foreground">{item.summary}</p> : null}
|
|
39
|
-
</div>
|
|
40
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
41
|
-
<Badge variant="muted">{reviewSourceLabel(item.source)}</Badge>
|
|
42
|
-
<Badge variant={reviewStatusVariant(item.effective_status)}>{item.effective_status}</Badge>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
{snoozed ? (
|
|
47
|
-
<div className="mt-3 flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-muted/24 p-3 text-sm">
|
|
48
|
-
<div>
|
|
49
|
-
<div className="font-medium">{formatSnoozedUntil(item.snoozed_until)}</div>
|
|
50
|
-
<p className="mt-1 text-muted-foreground">{t(language, "review.snoozed.detail")}</p>
|
|
51
|
-
</div>
|
|
52
|
-
<Button size="sm" variant="outline" onClick={() => onAction(item, "unsnooze")} disabled={!actionable}>
|
|
53
|
-
<RotateCcw className="h-3.5 w-3.5" /> {t(language, "review.unsnooze")}
|
|
54
|
-
</Button>
|
|
55
|
-
</div>
|
|
56
|
-
) : null}
|
|
57
|
-
|
|
58
|
-
{mode !== "basic" ? (
|
|
59
|
-
<div className="mt-3">
|
|
60
|
-
<KeyValueList
|
|
61
|
-
data={{
|
|
62
|
-
workflow: provenance.workflow_id,
|
|
63
|
-
trigger: provenance.trigger_id,
|
|
64
|
-
run: payload.last_run_id || provenance.run_id,
|
|
65
|
-
source_detail: reviewSourceDetail(provenance, item.source),
|
|
66
|
-
snoozed_until: item.snoozed_until,
|
|
67
|
-
created_at: item.created_at,
|
|
68
|
-
updated_at: item.updated_at,
|
|
69
|
-
}}
|
|
70
|
-
limit={8}
|
|
71
|
-
/>
|
|
72
|
-
</div>
|
|
73
|
-
) : null}
|
|
74
|
-
|
|
75
|
-
{actionable ? (
|
|
76
|
-
<div className="mt-4 grid gap-2">
|
|
77
|
-
<p className="text-xs leading-5 text-muted-foreground">
|
|
78
|
-
{t(language, "review.runNow.detail")}
|
|
79
|
-
</p>
|
|
80
|
-
<div className="flex flex-wrap gap-2" aria-label={t(language, "review.actions.aria")}>
|
|
81
|
-
<ActionButton
|
|
82
|
-
label={t(language, "review.runNow")}
|
|
83
|
-
successLabel={hadRun ? t(language, "review.regenerated") : t(language, "review.executed")}
|
|
84
|
-
action={() => onAction(item, "run_now", hadRun)}
|
|
85
|
-
invalidate={[]}
|
|
86
|
-
/>
|
|
87
|
-
<ActionButton label={t(language, "review.approve")} action={() => onAction(item, "approve")} invalidate={[]} />
|
|
88
|
-
{!snoozed ? <ActionButton label={t(language, "review.snoozeDay")} action={() => onAction(item, "snooze")} invalidate={[]} /> : null}
|
|
89
|
-
<ActionButton label={t(language, "review.dismiss")} action={() => onAction(item, "dismiss")} invalidate={[]} variant="destructive" />
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
) : null}
|
|
93
|
-
{feedback ? (
|
|
94
|
-
<p className={`mt-2 text-xs ${/fail|error|unavailable/i.test(feedback) ? "text-amber-300" : "text-emerald-300"}`}>
|
|
95
|
-
{feedback} - {t(language, "review.feedback.open")}
|
|
96
|
-
</p>
|
|
97
|
-
) : null}
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
-
import { latticeApi, type ReviewItem, type ReviewSourceFilter, type ReviewStatusFilter } from "@/api/client";
|
|
4
|
-
import { EmptyState, LoadingPanel, Tabs } from "@/components/primitives";
|
|
5
|
-
import { Badge } from "@/components/ui/badge";
|
|
6
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
|
-
import { t } from "@/i18n";
|
|
8
|
-
import { useAppStore } from "@/store/appStore";
|
|
9
|
-
import { ReviewCard } from "./ReviewCard";
|
|
10
|
-
import {
|
|
11
|
-
defaultSnoozeUntil,
|
|
12
|
-
reviewSourceFilters,
|
|
13
|
-
reviewStatusFilters,
|
|
14
|
-
type ReviewAction,
|
|
15
|
-
} from "./reviewHelpers";
|
|
16
|
-
|
|
17
|
-
export function ReviewInbox() {
|
|
18
|
-
const language = useAppStore((state) => state.language);
|
|
19
|
-
const qc = useQueryClient();
|
|
20
|
-
const [statusFilter, setStatusFilter] = React.useState<ReviewStatusFilter>("pending");
|
|
21
|
-
const [sourceFilter, setSourceFilter] = React.useState<ReviewSourceFilter>("all");
|
|
22
|
-
const [runFeedback, setRunFeedback] = React.useState<Record<string, string>>({});
|
|
23
|
-
const reviews = useQuery({
|
|
24
|
-
queryKey: ["automationReviews", statusFilter, sourceFilter],
|
|
25
|
-
queryFn: () => latticeApi.automationReviews({
|
|
26
|
-
...(statusFilter !== "all" ? { status: statusFilter } : {}),
|
|
27
|
-
...(sourceFilter !== "all" ? { source: sourceFilter } : {}),
|
|
28
|
-
}),
|
|
29
|
-
});
|
|
30
|
-
const items = reviews.data?.data.items || [];
|
|
31
|
-
|
|
32
|
-
const actOnReview = async (
|
|
33
|
-
item: ReviewItem,
|
|
34
|
-
action: ReviewAction,
|
|
35
|
-
hadRunBefore = false,
|
|
36
|
-
) => {
|
|
37
|
-
const call =
|
|
38
|
-
action === "approve" ? () => latticeApi.approveReviewItem(item.id) :
|
|
39
|
-
action === "dismiss" ? () => latticeApi.dismissReviewItem(item.id) :
|
|
40
|
-
action === "snooze" ? () => latticeApi.snoozeReviewItem(item.id, defaultSnoozeUntil()) :
|
|
41
|
-
action === "unsnooze" ? () => latticeApi.unsnoozeReviewItem(item.id) :
|
|
42
|
-
() => latticeApi.runNowReviewItem(item.id);
|
|
43
|
-
const result = await call();
|
|
44
|
-
if (!result.ok) {
|
|
45
|
-
setRunFeedback((prev) => ({
|
|
46
|
-
...prev,
|
|
47
|
-
[item.id]: result.error || t(language, "review.action.failed", { action }),
|
|
48
|
-
}));
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
if (result.ok) {
|
|
52
|
-
if (action === "run_now") {
|
|
53
|
-
const payload = result.data.payload || {};
|
|
54
|
-
const provenance = result.data.provenance || {};
|
|
55
|
-
const runId = String(payload.last_run_id || provenance.run_id || "");
|
|
56
|
-
setRunFeedback((prev) => ({
|
|
57
|
-
...prev,
|
|
58
|
-
[item.id]: runId
|
|
59
|
-
? `${hadRunBefore ? t(language, "review.regenerated") : t(language, "review.executed")} · ${runId}`
|
|
60
|
-
: hadRunBefore ? t(language, "review.regenerated") : t(language, "review.executed"),
|
|
61
|
-
}));
|
|
62
|
-
} else {
|
|
63
|
-
setRunFeedback((prev) => {
|
|
64
|
-
const next = { ...prev };
|
|
65
|
-
delete next[item.id];
|
|
66
|
-
return next;
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
await qc.invalidateQueries({ queryKey: ["automationReviews"] });
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
if (reviews.isLoading) return <LoadingPanel title={t(language, "review.inbox.title")} />;
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<Card>
|
|
78
|
-
<CardHeader className="gap-3">
|
|
79
|
-
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
80
|
-
<div>
|
|
81
|
-
<CardTitle>{t(language, "review.inbox.title")}</CardTitle>
|
|
82
|
-
<CardDescription>{t(language, "review.inbox.description")}</CardDescription>
|
|
83
|
-
</div>
|
|
84
|
-
{reviews.data ? (
|
|
85
|
-
<Badge variant={reviews.data.ok ? "success" : "warning"}>{reviews.data.ok ? t(language, "review.status.connected") : t(language, "review.status.unavailable")}</Badge>
|
|
86
|
-
) : null}
|
|
87
|
-
</div>
|
|
88
|
-
<div className="grid gap-2">
|
|
89
|
-
<Tabs
|
|
90
|
-
tabs={reviewStatusFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
|
|
91
|
-
value={statusFilter}
|
|
92
|
-
onChange={(id) => setStatusFilter(id as ReviewStatusFilter)}
|
|
93
|
-
/>
|
|
94
|
-
<Tabs
|
|
95
|
-
tabs={reviewSourceFilters.map((filter) => ({ id: filter.id, label: t(language, filter.labelKey) }))}
|
|
96
|
-
value={sourceFilter}
|
|
97
|
-
onChange={(id) => setSourceFilter(id as ReviewSourceFilter)}
|
|
98
|
-
/>
|
|
99
|
-
</div>
|
|
100
|
-
</CardHeader>
|
|
101
|
-
<CardContent>
|
|
102
|
-
{reviews.isError || (reviews.data && !reviews.data.ok) ? (
|
|
103
|
-
<EmptyState
|
|
104
|
-
title={t(language, "review.inbox.loadError")}
|
|
105
|
-
detail={reviews.data?.error || t(language, "review.inbox.unavailable")}
|
|
106
|
-
/>
|
|
107
|
-
) : !items.length ? (
|
|
108
|
-
<EmptyState
|
|
109
|
-
title={t(language, "review.inbox.empty")}
|
|
110
|
-
detail={statusFilter === "snoozed" ? t(language, "review.inbox.emptySnoozed") : t(language, "review.inbox.emptyPending")}
|
|
111
|
-
/>
|
|
112
|
-
) : (
|
|
113
|
-
<div className="grid gap-3">
|
|
114
|
-
{items.map((item) => (
|
|
115
|
-
<ReviewCard
|
|
116
|
-
key={item.id}
|
|
117
|
-
item={item}
|
|
118
|
-
feedback={runFeedback[item.id]}
|
|
119
|
-
onAction={actOnReview}
|
|
120
|
-
/>
|
|
121
|
-
))}
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
</CardContent>
|
|
125
|
-
</Card>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type * as React from "react";
|
|
2
|
-
import type { ReviewItem, ReviewSourceFilter, ReviewStatusFilter } from "@/api/client";
|
|
3
|
-
import type { Badge } from "@/components/ui/badge";
|
|
4
|
-
|
|
5
|
-
export type ReviewAction = "approve" | "dismiss" | "snooze" | "unsnooze" | "run_now";
|
|
6
|
-
|
|
7
|
-
export const reviewStatusFilters: Array<{ id: ReviewStatusFilter; labelKey: string }> = [
|
|
8
|
-
{ id: "pending", labelKey: "review.filter.status.pending" },
|
|
9
|
-
{ id: "snoozed", labelKey: "review.filter.status.snoozed" },
|
|
10
|
-
{ id: "all", labelKey: "review.filter.status.all" },
|
|
11
|
-
{ id: "approved", labelKey: "review.filter.status.approved" },
|
|
12
|
-
{ id: "dismissed", labelKey: "review.filter.status.dismissed" },
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
export const reviewSourceFilters: Array<{ id: ReviewSourceFilter; labelKey: string }> = [
|
|
16
|
-
{ id: "all", labelKey: "review.filter.source.all" },
|
|
17
|
-
{ id: "workflow_run", labelKey: "review.filter.source.workflow_run" },
|
|
18
|
-
{ id: "trigger", labelKey: "review.filter.source.trigger" },
|
|
19
|
-
{ id: "kg_change_digest", labelKey: "review.filter.source.kg_change_digest" },
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
export function reviewStatusVariant(status: string): React.ComponentProps<typeof Badge>["variant"] {
|
|
23
|
-
if (status === "pending") return "warning";
|
|
24
|
-
if (status === "snoozed") return "muted";
|
|
25
|
-
if (status === "approved") return "success";
|
|
26
|
-
if (status === "dismissed") return "danger";
|
|
27
|
-
return "muted";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function reviewSourceLabel(source?: string) {
|
|
31
|
-
if (source === "workflow_run") return "Workflow run";
|
|
32
|
-
if (source === "trigger") return "Trigger";
|
|
33
|
-
if (source === "kg_change_digest") return "KG digest";
|
|
34
|
-
return source || "Automation";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function reviewSourceDetail(provenance: Record<string, unknown>, source?: string) {
|
|
38
|
-
const detail = provenance.source_detail;
|
|
39
|
-
if (detail != null && String(detail).trim()) return String(detail);
|
|
40
|
-
const triggerId = provenance.trigger_id;
|
|
41
|
-
if (triggerId != null && String(triggerId).trim()) return String(triggerId);
|
|
42
|
-
return reviewSourceLabel(source);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function defaultSnoozeUntil() {
|
|
46
|
-
const until = new Date();
|
|
47
|
-
until.setDate(until.getDate() + 1);
|
|
48
|
-
return until.toISOString();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function formatSnoozedUntil(value?: string | null) {
|
|
52
|
-
if (!value) return "Snoozed";
|
|
53
|
-
const date = new Date(value);
|
|
54
|
-
if (Number.isNaN(date.getTime())) return `Snoozed until ${value}`;
|
|
55
|
-
return `Snoozed until ${new Intl.DateTimeFormat(undefined, {
|
|
56
|
-
dateStyle: "medium",
|
|
57
|
-
timeStyle: "short",
|
|
58
|
-
}).format(date)}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function isActionableReview(item: ReviewItem) {
|
|
62
|
-
return item.effective_status === "pending" || item.effective_status === "snoozed";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function hasRunBefore(item: ReviewItem) {
|
|
66
|
-
const payload = item.payload || {};
|
|
67
|
-
const provenance = item.provenance || {};
|
|
68
|
-
return Boolean(payload.last_run_id || provenance.run_id);
|
|
69
|
-
}
|