Skip to content

Commit 80f6e4f

Browse files
committed
fix: clean workspace notification copy
1 parent ce810a8 commit 80f6e4f

10 files changed

Lines changed: 154 additions & 112 deletions

File tree

frontend/src/components/dashboard/InsightBanner.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useNavigate } from "react-router";
44
import type { InsightActionParams, InsightRecord, InsightSeverity } from "../../api/insights";
55
import { useInsights, useMarkInsightRead } from "../../hooks/useInsights";
66
import { useI18n } from "../../i18n";
7+
import { getSeverityLabelKey } from "../../utils/severity";
78

89
function getInsightRoute(actionParams: InsightActionParams): string | null {
910
return typeof actionParams.route === "string" && actionParams.route.length > 0
@@ -105,7 +106,7 @@ export function InsightBanner({ projectId }: { projectId?: number }) {
105106
<div className="min-w-0 flex-1">
106107
<div className="flex flex-wrap items-center gap-2">
107108
<span className={`rounded-full px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] ring-1 ${severity.badge}`}>
108-
{insight.severity}
109+
{t(getSeverityLabelKey(insight.severity))}
109110
</span>
110111
<span className="text-[11px] font-medium text-slate-400">
111112
#{insight.project_id}

frontend/src/components/dashboard/WelcomeHero.tsx

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export function WelcomeHero({
4040
const settingsQuery = useSettings();
4141
const keyStatus = getEffectiveKeyStatus(settingsQuery.data);
4242
const keysReady = keyStatus.effective.llm;
43-
const serverModel = settingsQuery.data?.model?.trim() || "gpt-5.4-mini";
4443
void keyRefresh;
4544

4645
useEffect(() => {
@@ -71,14 +70,11 @@ export function WelcomeHero({
7170
return (
7271
<>
7372
<div className="animate-in fade-in slide-in-from-bottom-6 duration-700 ease-out">
74-
<div className="relative overflow-hidden rounded-3xl border border-slate-200/70 bg-[radial-gradient(circle_at_top_left,_rgba(99,102,241,0.14),_transparent_42%),linear-gradient(180deg,#ffffff_0%,#f8fafc_100%)] p-8 shadow-[0_20px_60px_rgba(15,23,42,0.08)] sm:p-10">
75-
<div className="absolute -right-12 -top-12 h-48 w-48 rounded-full bg-indigo-100/40 blur-3xl" />
76-
<div className="absolute -left-8 bottom-0 h-32 w-32 rounded-full bg-sky-100/30 blur-2xl" />
77-
73+
<div className="relative overflow-hidden rounded-3xl border border-slate-200/70 bg-[linear-gradient(180deg,#ffffff_0%,#f8fafc_100%)] p-8 shadow-[0_20px_60px_rgba(15,23,42,0.08)] sm:p-10">
7874
<div className="relative">
7975
<div className="mb-6 inline-flex items-center gap-2 rounded-full bg-slate-900 px-4 py-2 text-sm font-medium text-white">
8076
<Sparkles size={16} />
81-
{t("landing.heroEyebrow")}
77+
{t("welcome.eyebrow")}
8278
</div>
8379

8480
<h1 className="text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl">
@@ -130,27 +126,6 @@ export function WelcomeHero({
130126
<p className={`mt-2 text-xs ${keysReady ? "text-emerald-800/70" : "text-amber-800/70"}`}>
131127
{t("onboarding.privacyHint")}
132128
</p>
133-
<p className={`mt-1 text-xs ${keysReady ? "text-emerald-800/70" : "text-amber-800/70"}`}>
134-
{t("onboarding.serverDefaultModel", { model: serverModel })}
135-
</p>
136-
<div className="mt-3 grid gap-3 sm:grid-cols-2">
137-
<div className="rounded-2xl bg-white/80 px-3 py-3">
138-
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400">
139-
{t(keysReady ? "onboarding.fullModeTitle" : "onboarding.limitedModeWorksTitle")}
140-
</p>
141-
<p className="mt-2 text-sm leading-6 text-slate-700">
142-
{t(keysReady ? "onboarding.fullModeDesc" : "onboarding.limitedModeWorks")}
143-
</p>
144-
</div>
145-
<div className="rounded-2xl bg-white/80 px-3 py-3">
146-
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400">
147-
{t(keysReady ? "onboarding.privacyTitle" : "onboarding.limitedModeNeedsKeyTitle")}
148-
</p>
149-
<p className="mt-2 text-sm leading-6 text-slate-700">
150-
{t(keysReady ? "onboarding.privacyDesc" : "onboarding.limitedModeNeedsKey")}
151-
</p>
152-
</div>
153-
</div>
154129
</div>
155130
</div>
156131

frontend/src/components/layout/NotificationBell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { useLocation, useNavigate } from "react-router";
44
import type { InsightActionParams, InsightSeverity, InsightSummaryItem } from "../../api/insights";
55
import { useInsightsSummary, useMarkInsightRead } from "../../hooks/useInsights";
66
import { useI18n } from "../../i18n";
7+
import { getSeverityLabelKey } from "../../utils/severity";
8+
import { utcDate } from "../../utils/time";
79

810
function getSeverityStyles(severity: InsightSeverity) {
911
switch (severity) {
@@ -31,8 +33,6 @@ function getInsightRoute(actionParams: InsightActionParams): string | null {
3133
: null;
3234
}
3335

34-
import { utcDate } from "../../utils/time";
35-
3636
function formatTimeAgo(value: string, locale: string) {
3737
const timestamp = utcDate(value).getTime();
3838
if (Number.isNaN(timestamp)) {
@@ -188,7 +188,7 @@ export function NotificationBell() {
188188
<p className="text-sm font-semibold text-slate-900">{insight.title}</p>
189189
<div className="mt-1 flex items-center gap-2">
190190
<span className={`rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.16em] ring-1 ${severity.badge}`}>
191-
{insight.severity}
191+
{t(getSeverityLabelKey(insight.severity))}
192192
</span>
193193
<span className="text-[11px] text-slate-400">
194194
{formatTimeAgo(insight.created_at, locale)}

frontend/src/components/monitors/AnalysisDialog.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import type {
3333
TaskArtifactStageCard,
3434
TaskArtifactWatchout,
3535
} from "../../types";
36+
import { getSeverityLabelKey } from "../../utils/severity";
3637

3738
const STAGE_CONFIG: Record<string, { icon: typeof Search; labelKey: TranslationKey }> = {
3839
context_build: { icon: Search, labelKey: "analysis.stageContextBuild" },
@@ -238,9 +239,11 @@ function EvidenceRefs({
238239
function FindingCard({
239240
finding,
240241
evidenceLabel,
242+
severityLabel,
241243
}: {
242244
finding: Finding;
243245
evidenceLabel: string;
246+
severityLabel: string;
244247
}) {
245248
return (
246249
<div className="rounded-2xl border border-slate-200 bg-white p-4 shadow-sm">
@@ -253,7 +256,7 @@ function FindingCard({
253256
SEVERITY_STYLE[finding.severity] ?? SEVERITY_STYLE.info
254257
}`}
255258
>
256-
{finding.severity}
259+
{severityLabel}
257260
</span>
258261
</div>
259262
<p className="text-sm font-semibold text-slate-900">{finding.title}</p>
@@ -966,6 +969,7 @@ export function AnalysisDialog({
966969
key={`${finding.domain}-${finding.title}-${index}`}
967970
finding={finding}
968971
evidenceLabel={t("analysis.evidence")}
972+
severityLabel={t(getSeverityLabelKey(finding.severity))}
969973
/>
970974
))}
971975
</div>

frontend/src/components/monitors/MonitorForm.tsx

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ export function MonitorForm({
7272
}`}
7373
>
7474
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
75-
<div className="flex items-start gap-3">
76-
{keysReady ? (
77-
<CheckCircle2 size={18} className="mt-0.5 text-emerald-600" />
78-
) : (
75+
<div className="flex items-start gap-3">
76+
{keysReady ? (
77+
<CheckCircle2 size={18} className="mt-0.5 text-emerald-600" />
78+
) : (
7979
<KeyRound size={18} className="mt-0.5 text-amber-600" />
8080
)}
8181
<div>
@@ -88,24 +88,6 @@ export function MonitorForm({
8888
<p className={`mt-2 text-xs ${keysReady ? "text-emerald-800/70" : "text-amber-800/70"}`}>
8989
{t("onboarding.privacyHint")}
9090
</p>
91-
<div className="mt-3 grid gap-3 sm:grid-cols-2">
92-
<div className="rounded-2xl bg-white/80 px-3 py-3">
93-
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400">
94-
{t(keysReady ? "onboarding.fullModeTitle" : "onboarding.limitedModeWorksTitle")}
95-
</p>
96-
<p className="mt-2 text-sm leading-6 text-slate-700">
97-
{t(keysReady ? "onboarding.fullModeDesc" : "onboarding.limitedModeWorks")}
98-
</p>
99-
</div>
100-
<div className="rounded-2xl bg-white/80 px-3 py-3">
101-
<p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-400">
102-
{t(keysReady ? "onboarding.privacyTitle" : "onboarding.limitedModeNeedsKeyTitle")}
103-
</p>
104-
<p className="mt-2 text-sm leading-6 text-slate-700">
105-
{t(keysReady ? "onboarding.privacyDesc" : "onboarding.limitedModeNeedsKey")}
106-
</p>
107-
</div>
108-
</div>
10991
</div>
11092
</div>
11193

frontend/src/components/monitors/RunResultsDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useI18n } from "../../i18n";
1414
import type { TranslationKey } from "../../i18n";
1515
import { apiJson } from "../../api/client";
1616
import type { MonitorRun } from "../../types";
17+
import { getSeverityLabelKey } from "../../utils/severity";
1718
import { utcDate } from "../../utils/time";
1819

1920
const SEVERITY_STYLE: Record<string, string> = {
@@ -232,7 +233,7 @@ export function RunResultsDialog({
232233
SEVERITY_STYLE[f.severity] ?? SEVERITY_STYLE.low
233234
}`}
234235
>
235-
{f.severity}
236+
{t(getSeverityLabelKey(f.severity))}
236237
</span>
237238
</div>
238239
<p className="text-sm font-semibold text-slate-900">{f.title}</p>

frontend/src/i18n/locales/en.ts

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,31 @@ export const en = {
2323
"auth.invalidToken": "Invalid token",
2424

2525
// Dashboard
26-
"dashboard.title": "Command Center",
26+
"dashboard.title": "AI CMO Workspace",
2727
"dashboard.newMonitor": "New Monitor",
2828
"dashboard.noProjects": "No projects yet",
2929
"dashboard.noProjectsDesc": "Add a website URL to start monitoring your brand's online presence.",
3030
"dashboard.createMonitor": "Add Website",
31-
"dashboard.subtitle": "See what changed today, what matters now, and what your agents can ship next.",
32-
"dashboard.summaryTitle": "Workspace snapshot",
33-
"dashboard.summarySubtitle": "Track fresh evidence, decision-ready work, and the actions waiting on your team.",
34-
"dashboard.updatedProjects": "{{count}} of {{total}} tracked products had a fresh monitoring update in the last 24 hours.",
35-
"dashboard.findingsReady": "{{count}} findings are active across your latest monitoring runs. {{approvals}} item(s) are waiting in Review & Publish.",
36-
"dashboard.actionsReady": "{{count}} recommended actions are ready to move. {{approvals}} item(s) are waiting for human review.",
31+
"dashboard.subtitle": "Start with the signal that changed, the decision to make, and the action to ship.",
32+
"dashboard.summaryTitle": "Workspace focus",
33+
"dashboard.summarySubtitle": "A demo-ready view of monitoring updates, decisions, and launchable work.",
34+
"dashboard.updatedProjects": "Updated in the last 24 hours: {{count}} / {{total}} products.",
35+
"dashboard.findingsReady": "{{count}} active finding(s). {{approvals}} draft(s) need review.",
36+
"dashboard.actionsReady": "{{count}} ready action(s). {{approvals}} review item(s) open.",
3737
"dashboard.pendingReviews": "Pending Reviews",
38-
"insights.title": "Insights",
39-
"insights.proactive": "What changed today",
40-
"insights.topUnread": "Fresh signals from your agents",
41-
"insights.empty": "No new insights",
42-
"insights.loading": "Loading insights...",
38+
"insights.title": "Signals",
39+
"insights.proactive": "Needs attention",
40+
"insights.topUnread": "Latest agent signals",
41+
"insights.empty": "No signals to review",
42+
"insights.loading": "Loading signals...",
4343
"insights.view": "View",
4444
"insights.dismiss": "Dismiss",
45+
"insights.severityCritical": "Critical",
46+
"insights.severityHigh": "High",
47+
"insights.severityWarning": "Warning",
48+
"insights.severityMedium": "Medium",
49+
"insights.severityLow": "Low",
50+
"insights.severityInfo": "Info",
4551

4652
// Project Tabs
4753
"project.categoryAuto": "Detecting…",
@@ -90,9 +96,9 @@ export const en = {
9096
"overview.competitors": "Competitors",
9197
"overview.campaigns": "Campaigns",
9298
"overview.recentCampaigns": "Recent Campaigns",
93-
"command.changedToday": "What changed today",
94-
"command.whatMattersNow": "What matters now",
95-
"command.readyToShip": "Ready to ship",
99+
"command.changedToday": "Changed",
100+
"command.whatMattersNow": "Decide",
101+
"command.readyToShip": "Ship",
96102
"command.reviewFixes": "Review fixes",
97103
"command.openAiSearch": "Open AI Search",
98104
"command.openOpportunities": "Open opportunities",
@@ -151,9 +157,9 @@ export const en = {
151157

152158
// Monitor Form
153159
"monitorForm.urlPlaceholder": "https://your-website.com",
154-
"monitorForm.subtitle": "Paste a site to run the first scan. OpenCMO checks SEO, AI Search, Google Search, community opportunities, competitor context, and the next actions to ship.",
155-
"monitorForm.analyzing": "Analyzing site...",
156-
"monitorForm.startMonitoring": "Analyze my site",
160+
"monitorForm.subtitle": "Add a site. OpenCMO scans SEO, AI Search, Google Search, community demand, and the next action.",
161+
"monitorForm.analyzing": "Scanning...",
162+
"monitorForm.startMonitoring": "Run scan",
157163

158164
// Monitor List
159165
"monitorList.brand": "Brand",
@@ -379,32 +385,33 @@ export const en = {
379385
"settings.localKeysHint": "Your BYOK credentials stay in your browser unless your self-hosted deployment already provides server defaults.",
380386

381387
// Welcome Hero (onboarding)
382-
"welcome.title": "Analyze your site first",
383-
"welcome.subtitle": "Paste your homepage to get SEO issues, AI Search gaps, competitor context, community opportunities, and the next actions to ship.",
388+
"welcome.eyebrow": "AI CMO workspace",
389+
"welcome.title": "Start with a live audit",
390+
"welcome.subtitle": "Paste a homepage to turn public signals into an AI CMO brief: issues, opportunities, competitors, and next actions.",
384391
"welcome.step1": "Configure API Keys",
385392
"welcome.step1Done": "API keys configured",
386393
"welcome.step2": "Enter your website URL to begin",
387394
"welcome.featureSeo": "Site Health Agent",
388-
"welcome.featureSeoDesc": "Checks crawlability, Core Web Vitals, and technical SEO issues on the pages shaping first impressions.",
395+
"welcome.featureSeoDesc": "Checks crawlability, Core Web Vitals, and technical SEO issues.",
389396
"welcome.featureGeo": "AI Search Agent",
390-
"welcome.featureGeoDesc": "Reviews how ChatGPT, Claude, Gemini, Perplexity, and other AI answers frame your product.",
397+
"welcome.featureGeoDesc": "Reviews how AI answers frame your product and category.",
391398
"welcome.featureCommunity": "Community Agent",
392-
"welcome.featureCommunityDesc": "Finds demand, objections, and outreach opportunities in developer communities.",
399+
"welcome.featureCommunityDesc": "Finds demand, objections, and outreach openings in communities.",
393400
"onboarding.keyReadyTitle": "Full scan ready",
394-
"onboarding.keyReadyDesc": "OpenCMO can use your browser key or a server default for AI-assisted analysis.",
401+
"onboarding.keyReadyDesc": "AI-assisted analysis is available for this workspace.",
395402
"onboarding.keyNeededTitle": "Add an LLM key for full AI coverage",
396-
"onboarding.keyNeededDesc": "You can still create the project now, but keyword extraction, competitor context, and some AI Search checks work best with a configured provider.",
403+
"onboarding.keyNeededDesc": "The scan can start now. Add a key for richer AI Search, competitors, and recommendations.",
397404
"onboarding.configureKeys": "Add API key",
398-
"onboarding.privacyHint": "BYOK stays local in your browser. Self-hosted deployments can also provide server defaults.",
405+
"onboarding.privacyHint": "BYOK stays in this browser unless your deployment provides server defaults.",
399406
"onboarding.serverDefaultModel": "Current server default model: {{model}}.",
400407
"onboarding.fullModeTitle": "Full analysis mode",
401-
"onboarding.fullModeDesc": "AI-assisted context build, broader AI Search coverage, faster competitor discovery, and stronger draft recommendations are ready.",
408+
"onboarding.fullModeDesc": "AI context, AI Search coverage, competitor discovery, and recommendations are ready.",
402409
"onboarding.privacyTitle": "Privacy behavior",
403-
"onboarding.privacyDesc": "BYOK credentials stay in this browser unless your own deployment provides server defaults. You stay in control of the tradeoff.",
410+
"onboarding.privacyDesc": "Credentials stay in this browser unless you choose a server default.",
404411
"onboarding.limitedModeWorksTitle": "Still works now",
405-
"onboarding.limitedModeWorks": "Crawl-based SEO checks, Google Search snapshots, and community discovery still run from the URL-first flow.",
412+
"onboarding.limitedModeWorks": "SEO checks, Google Search snapshots, and community discovery still run.",
406413
"onboarding.limitedModeNeedsKeyTitle": "Works better with a key",
407-
"onboarding.limitedModeNeedsKey": "AI-assisted keyword extraction, automatic competitor context, and broader AI Search checks need a configured provider.",
414+
"onboarding.limitedModeNeedsKey": "Add a provider for keyword extraction, competitor context, and broader AI Search.",
408415
"landing.headerTagline": "Private customization, GitHub source, and deployed trial.",
409416
"landing.metaTitle": "aidCMO — OpenCMO private customization, GitHub, and deployed trial",
410417
"landing.metaDescription": "OpenCMO is an open-source AI CMO workspace. Choose private customization, review the GitHub repo, or try the deployed workspace.",

0 commit comments

Comments
 (0)