Skip to content

Commit 9335b3c

Browse files
fix(apollo-vertex): a11y, i18n, and replay handling in dashboard template
Address review findings on the dashboard template: - AutopilotInsight: add aria-label to the icon-only close button, mark its SVG aria-hidden, and make the decorative Autopilot logos alt="". - Route user-facing chrome through t() in AutopilotInsight, DashboardCards, and DashboardLoading; add the new keys to locales/en.json. - DashboardLoading: replace the triggerReplay reset-on-prop-change effect with a key-based remount of an extracted LoadingSequence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e389233 commit 9335b3c

4 files changed

Lines changed: 78 additions & 32 deletions

File tree

apps/apollo-vertex/locales/en.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@
77
"ai_assistant": "AI Assistant",
88
"ai_response_disclaimer": "AI-generated responses should be reviewed for accuracy.",
99
"all": "All",
10+
"amount": "Amount",
1011
"analytics": "Analytics",
1112
"apollo_vertex": "Apollo Vertex",
1213
"apollo_vertex_description": "- UiPath Apollo Vertex",
1314
"asc": "Asc",
1415
"ask_ai": "Ask AI",
1516
"assistant": "Assistant",
1617
"autopilot_empty_description": "Ask me anything about your automation data.",
18+
"autopilot_insight": "Autopilot Insight",
19+
"autopilot_insight_analyzing": "Analyzing {{source}}",
20+
"autopilot_response_area": "Autopilot response area",
21+
"autopilot_response_placeholder": "Chat UX content will appear here",
1722
"bad_response": "Bad response",
1823
"business_user": "Business user",
1924
"cancel": "Cancel",
@@ -30,17 +35,20 @@
3035
"close_sidebar": "Close sidebar",
3136
"collapse_all": "Collapse all",
3237
"collapse_tool_calls": "Collapse tool calls",
38+
"compliance_pass_rates": "Compliance Pass Rates",
3339
"copied": "Copied!",
3440
"copy": "Copy",
3541
"copy_code": "Copy code",
3642
"copy_conversation": "Copy conversation",
3743
"copy_conversation_failed": "Couldn't copy conversation",
3844
"copy_failed": "Couldn't copy",
3945
"copy_payment_id": "Copy payment ID",
46+
"creating_your_overview": "Creating your overview...",
4047
"custom": "Custom",
4148
"dark": "Dark",
4249
"dark_mode": "Dark Mode",
4350
"dashboard": "Dashboard",
51+
"date": "Date",
4452
"desc": "Desc",
4553
"destructive": "Destructive",
4654
"displays_mobile_sidebar": "Displays the mobile sidebar.",
@@ -60,6 +68,7 @@
6068
"feedback_vote_down": "Vote down",
6169
"feedback_vote_up": "Vote up",
6270
"french": "French",
71+
"from_last_week": "from last week",
6372
"german": "German",
6473
"go_to_first_page": "Go to first page",
6574
"go_to_last_page": "Go to last page",
@@ -71,6 +80,7 @@
7180
"image_preview": "Image preview",
7281
"import": "Import",
7382
"info": "Info",
83+
"invoice": "Invoice",
7484
"japanese": "Japanese",
7585
"korean": "Korean",
7686
"language": "Language",
@@ -107,7 +117,11 @@
107117
"previous": "Previous",
108118
"previous_slide": "Previous slide",
109119
"primary_button": "Primary Button",
120+
"processing_activity": "Processing Activity",
121+
"processing_pipeline": "Processing Pipeline",
110122
"projects": "Projects",
123+
"recent_activity": "Recent Activity",
124+
"recent_invoices": "Recent Invoices",
111125
"remove_file": "Remove {{name}}",
112126
"remove_quoted_text": "Remove quoted text",
113127
"reset_to_default": "Reset to Default",
@@ -139,6 +153,7 @@
139153
"spanish": "Spanish",
140154
"spanish-mx": "Spanish (Mexico)",
141155
"start_conversation_with": "Start a conversation with {{name}}",
156+
"status": "Status",
142157
"stop": "Stop",
143158
"success": "Success",
144159
"system": "System",
@@ -163,6 +178,7 @@
163178
"upload_files": "Upload files",
164179
"user_email_placeholder": "user@company.com",
165180
"user_profile": "Profile and settings",
181+
"vendor": "Vendor",
166182
"verifying_access": "Verifying access…",
167183
"view": "View",
168184
"view_customer": "View customer",

apps/apollo-vertex/templates/dashboard/AutopilotInsight.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import { useTranslation } from "react-i18next";
34
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
45

56
interface AutopilotInsightProps {
@@ -11,6 +12,8 @@ export function AutopilotInsight({
1112
onClose,
1213
sourceCardTitle,
1314
}: AutopilotInsightProps) {
15+
const { t } = useTranslation();
16+
1417
return (
1518
<Card
1619
variant="glass"
@@ -20,6 +23,7 @@ export function AutopilotInsight({
2023
<button
2124
type="button"
2225
onClick={onClose}
26+
aria-label={t("close")}
2327
className="absolute top-5 right-5 z-20 size-7 rounded-md flex items-center justify-center text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-all"
2428
>
2529
<svg
@@ -31,6 +35,7 @@ export function AutopilotInsight({
3135
strokeLinecap="round"
3236
strokeLinejoin="round"
3337
className="size-4"
38+
aria-hidden="true"
3439
>
3540
<path d="M18 6 6 18" />
3641
<path d="m6 6 12 12" />
@@ -41,20 +46,20 @@ export function AutopilotInsight({
4146
<div className="flex items-center gap-2">
4247
<img
4348
src="/Autopilot_dark.svg"
44-
alt="Autopilot"
49+
alt=""
4550
className="size-5 block dark:hidden"
4651
/>
4752
<img
4853
src="/Autopilot_light.svg"
49-
alt="Autopilot"
54+
alt=""
5055
className="size-5 hidden dark:block"
5156
/>
5257
<CardTitle className="text-sm font-bold tracking-tight">
53-
Autopilot Insight
58+
{t("autopilot_insight")}
5459
</CardTitle>
5560
</div>
5661
<p className="text-xs text-muted-foreground">
57-
Analyzing {sourceCardTitle}
62+
{t("autopilot_insight_analyzing", { source: sourceCardTitle })}
5863
</p>
5964
</CardHeader>
6065

@@ -63,10 +68,10 @@ export function AutopilotInsight({
6368
<div className="flex-1 border border-dashed border-muted-foreground/15 bg-muted/30 rounded-lg flex items-center justify-center">
6469
<div className="text-center">
6570
<p className="text-sm text-muted-foreground/60">
66-
Autopilot response area
71+
{t("autopilot_response_area")}
6772
</p>
6873
<p className="text-xs text-muted-foreground/40 mt-1">
69-
Chat UX content will appear here
74+
{t("autopilot_response_placeholder")}
7075
</p>
7176
</div>
7277
</div>

apps/apollo-vertex/templates/dashboard/DashboardCards.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
"use client";
2+
13
import { AlertTriangle, CheckCircle, Clock, XCircle } from "lucide-react";
24
import type { LucideIcon } from "lucide-react";
5+
import { useTranslation } from "react-i18next";
36
import { Badge } from "@/components/ui/badge";
47
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
58
import { Progress } from "@/components/ui/progress";
@@ -120,6 +123,8 @@ const complianceChecks = [
120123
// --- Card components ---
121124

122125
export function KpiCards({ kpis }: { kpis: KpiItem[] }) {
126+
const { t } = useTranslation();
127+
123128
return (
124129
<>
125130
{kpis.map((kpi) => (
@@ -135,8 +140,8 @@ export function KpiCards({ kpis }: { kpis: KpiItem[] }) {
135140
<CardContent>
136141
<div className="text-2xl font-bold">{kpi.value}</div>
137142
<p className="text-xs text-muted-foreground mt-1">
138-
<span className="text-emerald-500">{kpi.change}</span> from last
139-
week
143+
<span className="text-emerald-500">{kpi.change}</span>{" "}
144+
{t("from_last_week")}
140145
</p>
141146
</CardContent>
142147
</Card>
@@ -146,20 +151,22 @@ export function KpiCards({ kpis }: { kpis: KpiItem[] }) {
146151
}
147152

148153
export function InvoiceTable() {
154+
const { t } = useTranslation();
155+
149156
return (
150157
<Card variant="glass">
151158
<CardHeader>
152-
<CardTitle>Recent Invoices</CardTitle>
159+
<CardTitle>{t("recent_invoices")}</CardTitle>
153160
</CardHeader>
154161
<CardContent>
155162
<Table>
156163
<TableHeader>
157164
<TableRow>
158-
<TableHead>Invoice</TableHead>
159-
<TableHead>Vendor</TableHead>
160-
<TableHead>Amount</TableHead>
161-
<TableHead>Status</TableHead>
162-
<TableHead>Date</TableHead>
165+
<TableHead>{t("invoice")}</TableHead>
166+
<TableHead>{t("vendor")}</TableHead>
167+
<TableHead>{t("amount")}</TableHead>
168+
<TableHead>{t("status")}</TableHead>
169+
<TableHead>{t("date")}</TableHead>
163170
</TableRow>
164171
</TableHeader>
165172
<TableBody>
@@ -193,10 +200,12 @@ export function InvoiceTable() {
193200
}
194201

195202
export function ActivityBarChart() {
203+
const { t } = useTranslation();
204+
196205
return (
197206
<Card variant="glass">
198207
<CardHeader>
199-
<CardTitle>Processing Activity</CardTitle>
208+
<CardTitle>{t("processing_activity")}</CardTitle>
200209
</CardHeader>
201210
<CardContent>
202211
<div className="flex items-end gap-3 h-32">
@@ -219,10 +228,12 @@ export function ActivityBarChart() {
219228
}
220229

221230
export function ActivityFeed() {
231+
const { t } = useTranslation();
232+
222233
return (
223234
<Card variant="glass">
224235
<CardHeader>
225-
<CardTitle>Recent Activity</CardTitle>
236+
<CardTitle>{t("recent_activity")}</CardTitle>
226237
</CardHeader>
227238
<CardContent>
228239
<div className="space-y-4">
@@ -242,10 +253,12 @@ export function ActivityFeed() {
242253
}
243254

244255
export function PipelineProgress() {
256+
const { t } = useTranslation();
257+
245258
return (
246259
<Card variant="glass">
247260
<CardHeader>
248-
<CardTitle>Processing Pipeline</CardTitle>
261+
<CardTitle>{t("processing_pipeline")}</CardTitle>
249262
</CardHeader>
250263
<CardContent>
251264
<div className="space-y-4">
@@ -265,10 +278,12 @@ export function PipelineProgress() {
265278
}
266279

267280
export function ComplianceProgress() {
281+
const { t } = useTranslation();
282+
268283
return (
269284
<Card variant="glass">
270285
<CardHeader>
271-
<CardTitle>Compliance Pass Rates</CardTitle>
286+
<CardTitle>{t("compliance_pass_rates")}</CardTitle>
272287
</CardHeader>
273288
<CardContent>
274289
<div className="space-y-4">

apps/apollo-vertex/templates/dashboard/DashboardLoading.tsx

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { useEffect, useState } from "react";
4+
import { useTranslation } from "react-i18next";
45

56
type Phase = "logo" | "skeleton" | "done";
67

@@ -10,6 +11,8 @@ interface DashboardLoadingProps {
1011
}
1112

1213
function LogoPhase({ exiting }: { exiting: boolean }) {
14+
const { t } = useTranslation();
15+
1316
return (
1417
<div
1518
className={`absolute inset-0 flex flex-col items-center justify-center transition-all duration-500 ${
@@ -38,7 +41,7 @@ function LogoPhase({ exiting }: { exiting: boolean }) {
3841

3942
{/* Loading text */}
4043
<p className="mt-6 text-sm text-muted-foreground animate-pulse">
41-
Creating your overview...
44+
{t("creating_your_overview")}
4245
</p>
4346

4447
<style>{`
@@ -80,21 +83,13 @@ function SkeletonPhase({ exiting }: { exiting: boolean }) {
8083
);
8184
}
8285

83-
export function DashboardLoading({
84-
children,
85-
triggerReplay,
86-
}: DashboardLoadingProps) {
87-
const [phase, setPhase] = useState<Phase>("done");
86+
// Plays the logo → skeleton → content intro once, then renders children.
87+
// It owns its phase state from mount; to replay, the parent gives it a new
88+
// `key` so it remounts — no resetting state in response to a prop change.
89+
function LoadingSequence({ children }: { children: React.ReactNode }) {
90+
const [phase, setPhase] = useState<Phase>("logo");
8891
const [exiting, setExiting] = useState(false);
8992

90-
useEffect(() => {
91-
if (triggerReplay === 0) return;
92-
if (triggerReplay) {
93-
setExiting(false);
94-
setPhase("logo");
95-
}
96-
}, [triggerReplay]);
97-
9893
useEffect(() => {
9994
if (phase === "done") return;
10095

@@ -133,3 +128,18 @@ export function DashboardLoading({
133128
</div>
134129
);
135130
}
131+
132+
export function DashboardLoading({
133+
children,
134+
triggerReplay,
135+
}: DashboardLoadingProps) {
136+
// No replay requested — render content directly with the same fade-in the
137+
// sequence ends on. Each new `triggerReplay` value remounts the sequence.
138+
if (!triggerReplay) {
139+
return (
140+
<div className="animate-in fade-in duration-500 h-full">{children}</div>
141+
);
142+
}
143+
144+
return <LoadingSequence key={triggerReplay}>{children}</LoadingSequence>;
145+
}

0 commit comments

Comments
 (0)