feat(06-01): fix named terminology straggler requirements (TERM-10 through TERM-17)

- TERM-10: Companies.tsx breadcrumb uses VOCAB.companies, loading/delete text uses VOCAB
- TERM-11: InstanceSettings.tsx adds VOCAB import, uses VOCAB.company/companies
- TERM-12: Costs.tsx adds VOCAB import and SCOPE_LABELS map, replaces hardcoded company strings
- TERM-13: CompanyImport.tsx uses VOCAB.appName, VOCAB.company, VOCAB.board throughout
- TERM-17: IssuesList.tsx (component) title='Board view' -> 'Kanban view'
- Dashboard.tsx: 'awaiting board review' -> 'awaiting owner review'
- CompanySettings.tsx: 'No company selected' uses VOCAB.company
- ReportsToPicker.tsx: adds VOCAB import, default label uses VOCAB.ceo not hardcoded 'CEO'
This commit is contained in:
Mikkel Georgsen 2026-03-31 13:25:00 +02:00
parent 02282ae926
commit cb5d14d6f8
8 changed files with 34 additions and 24 deletions

View file

@ -391,7 +391,7 @@ export function IssuesList({
<button
className={`p-1.5 transition-colors ${viewState.viewMode === "board" ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"}`}
onClick={() => updateView({ viewMode: "board" })}
title="Board view"
title="Kanban view"
>
<Columns3 className="h-3.5 w-3.5" />
</button>

View file

@ -1,4 +1,5 @@
import { useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import type { Agent } from "@paperclipai/shared";
import {
Popover,
@ -16,7 +17,7 @@ export function ReportsToPicker({
onChange,
disabled = false,
excludeAgentIds = [],
disabledEmptyLabel = "Reports to: N/A (CEO)",
disabledEmptyLabel,
chooseLabel = "Reports to...",
}: {
agents: Agent[];
@ -27,6 +28,7 @@ export function ReportsToPicker({
disabledEmptyLabel?: string;
chooseLabel?: string;
}) {
const label = disabledEmptyLabel ?? `Reports to: N/A (${VOCAB.ceo})`;
const [open, setOpen] = useState(false);
const exclude = new Set(excludeAgentIds);
const rows = agents.filter(
@ -69,7 +71,7 @@ export function ReportsToPicker({
<>
<User className="h-3 w-3 shrink-0 text-muted-foreground" />
<span className="min-w-0 truncate">
{disabled ? disabledEmptyLabel : chooseLabel}
{disabled ? label : chooseLabel}
</span>
</>
)}

View file

@ -70,7 +70,7 @@ export function Companies() {
});
useEffect(() => {
setBreadcrumbs([{ label: "Companies" }]);
setBreadcrumbs([{ label: VOCAB.companies }]);
}, [setBreadcrumbs]);
function startEdit(companyId: string, currentName: string) {
@ -98,7 +98,7 @@ export function Companies() {
</div>
<div className="h-6">
{loading && <p className="text-sm text-muted-foreground">Loading companies...</p>}
{loading && <p className="text-sm text-muted-foreground">{`Loading ${VOCAB.companies.toLowerCase()}...`}</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}
</div>
@ -267,7 +267,7 @@ export function Companies() {
onClick={(e) => e.stopPropagation()}
>
<p className="text-sm text-destructive font-medium">
Delete this company and all its data? This cannot be undone.
{`Delete this ${VOCAB.company.toLowerCase()} and all its data? This cannot be undone.`}
</p>
<div className="flex items-center gap-2 ml-4 shrink-0">
<Button

View file

@ -704,7 +704,7 @@ export function CompanyImport() {
}, [companyAgents]);
const localZipHelpText =
"Upload a .zip exported directly from Paperclip. Re-zipped archives created by Finder, Explorer, or other zip tools may not import correctly.";
`Upload a .zip exported directly from ${VOCAB.appName}. Re-zipped archives created by Finder, Explorer, or other zip tools may not import correctly.`;
useEffect(() => {
setBreadcrumbs([
@ -1086,7 +1086,7 @@ export function CompanyImport() {
const selectedAction = selectedFile ? (actionMap.get(selectedFile) ?? null) : null;
if (!selectedCompanyId) {
return <EmptyState icon={Download} message="Select a company to import into." />;
return <EmptyState icon={Download} message={`Select a ${VOCAB.company.toLowerCase()} to import into.`} />;
}
return (
@ -1096,7 +1096,7 @@ export function CompanyImport() {
<div>
<h2 className="text-base font-semibold">Import source</h2>
<p className="text-xs text-muted-foreground mt-1">
Choose a GitHub repo or upload a local Paperclip zip package.
{`Choose a GitHub repo or upload a local ${VOCAB.appName} zip package.`}
</p>
</div>
@ -1178,7 +1178,7 @@ export function CompanyImport() {
</Field>
)}
<Field label="Target" hint="Import into this company or create a new one.">
<Field label="Target" hint={`Import into this ${VOCAB.company.toLowerCase()} or create a new one.`}>
<select
className="w-full rounded-md border border-border bg-transparent px-2.5 py-1.5 text-sm outline-none"
value={targetMode}
@ -1187,16 +1187,16 @@ export function CompanyImport() {
setImportPreview(null);
}}
>
<option value="new">Create new company</option>
<option value="new">{`Create new ${VOCAB.company.toLowerCase()}`}</option>
<option value="existing">
Existing company: {selectedCompany?.name}
{`Existing ${VOCAB.company.toLowerCase()}: ${selectedCompany?.name}`}
</option>
</select>
</Field>
{targetMode === "new" && (
<Field
label="New company name"
label={`New ${VOCAB.company.toLowerCase()} name`}
hint="Optional override. Leave blank to use the package name."
>
<input
@ -1211,7 +1211,7 @@ export function CompanyImport() {
<Field
label="Collision strategy"
hint="Board imports can rename, skip, or replace matching company content."
hint={`${VOCAB.board} imports can rename, skip, or replace matching ${VOCAB.company.toLowerCase()} content.`}
>
<select
className="w-full rounded-md border border-border bg-transparent px-2.5 py-1.5 text-sm outline-none"

View file

@ -208,7 +208,7 @@ export function CompanySettings() {
if (!selectedCompany) {
return (
<div className="text-sm text-muted-foreground">
No company selected. Select a company from the switcher above.
{`No ${VOCAB.company.toLowerCase()} selected. Select a ${VOCAB.company.toLowerCase()} from the switcher above.`}
</div>
);
}

View file

@ -1,4 +1,5 @@
import { useEffect, useMemo, useRef, useState, type ComponentType } from "react";
import { VOCAB } from "@paperclipai/branding";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type {
BudgetPolicySummary,
@ -35,6 +36,12 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
const NO_COMPANY = "__none__";
const SCOPE_LABELS: Record<string, string> = {
company: VOCAB.company,
agent: "Agent",
project: "Project",
};
function currentWeekRange(): { from: string; to: string } {
const now = new Date();
const day = now.getDay();
@ -529,7 +536,7 @@ export function Costs() {
}), [budgetPolicies]);
if (!selectedCompanyId) {
return <EmptyState icon={DollarSign} message="Select a company to view costs." />;
return <EmptyState icon={DollarSign} message={`Select a ${VOCAB.company.toLowerCase()} to view costs.`} />;
}
const showCustomPrompt = preset === "custom" && !customReady;
@ -855,7 +862,7 @@ export function Costs() {
<MetricTile
label="Pending approvals"
value={String(budgetData?.pendingApprovalCount ?? 0)}
subtitle="Budget override approvals awaiting board action"
subtitle="Budget override approvals awaiting owner action"
icon={ArrowUpRight}
/>
<MetricTile
@ -907,10 +914,10 @@ export function Costs() {
return (
<section key={scopeType} className="space-y-3">
<div>
<h2 className="text-lg font-semibold capitalize">{scopeType} budgets</h2>
<h2 className="text-lg font-semibold">{SCOPE_LABELS[scopeType] ?? scopeType} budgets</h2>
<p className="text-sm text-muted-foreground">
{scopeType === "company"
? "Company-wide monthly policy."
? `${VOCAB.company}-wide monthly policy.`
: scopeType === "agent"
? "Recurring monthly spend policies for individual agents."
: "Lifetime spend policies for execution-bound projects."}
@ -939,7 +946,7 @@ export function Costs() {
{budgetPolicies.length === 0 ? (
<Card>
<CardContent className="px-5 py-8 text-sm text-muted-foreground">
No budget policies yet. Set agent and project budgets from their detail pages, or use the existing company monthly budget control.
{`No budget policies yet. Set agent and project budgets from their detail pages, or use the existing ${VOCAB.company.toLowerCase()} monthly budget control.`}
</CardContent>
</Card>
) : null}

View file

@ -277,8 +277,8 @@ export function Dashboard() {
description={
<span>
{data.budgets.pendingApprovals > 0
? `${data.budgets.pendingApprovals} budget overrides awaiting board review`
: "Awaiting board review"}
? `${data.budgets.pendingApprovals} budget overrides awaiting owner review`
: "Awaiting owner review"}
</span>
}
/>

View file

@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Clock3, ExternalLink, Settings } from "lucide-react";
import type { InstanceSchedulerHeartbeatAgent } from "@paperclipai/shared";
@ -171,14 +172,14 @@ export function InstanceSettings() {
<h1 className="text-lg font-semibold">Scheduler Heartbeats</h1>
</div>
<p className="text-sm text-muted-foreground">
Agents with a timer heartbeat enabled across all of your companies.
{`Agents with a timer heartbeat enabled across all of your ${VOCAB.companies.toLowerCase()}.`}
</p>
</div>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span><span className="font-semibold text-foreground">{activeCount}</span> active</span>
<span><span className="font-semibold text-foreground">{disabledCount}</span> disabled</span>
<span><span className="font-semibold text-foreground">{grouped.length}</span> {grouped.length === 1 ? "company" : "companies"}</span>
<span><span className="font-semibold text-foreground">{grouped.length}</span> {grouped.length === 1 ? VOCAB.company.toLowerCase() : VOCAB.companies.toLowerCase()}</span>
{anyEnabled && (
<Button
variant="destructive"