Use i18n for board component strings, show controls in edit mode, require website before editing
Some checks are pending
Node.js CI / build (push) Waiting to run

- Replace raw strings with useMessages hook in BoardColumn and BoardComponentSelect
- Show WebsiteControls on edit screen so users can test filters
- Disable board body editing until a website is selected
- Scope website select to team websites when editing a team board

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mike Cao 2026-02-09 02:46:30 -08:00
parent 87bde9da1f
commit 2c7ab2b734
6 changed files with 23 additions and 22 deletions

View file

@ -255,6 +255,7 @@
"select-date": "Select date",
"select-filter": "Select filter",
"select-role": "Select role",
"select-component": "Select component",
"select-website": "Select website",
"session": "Session",
"session-data": "Session data",
@ -371,6 +372,8 @@
"reset-website": "To reset this website, type {confirmation} in the box below to confirm.",
"reset-website-warning": "All statistics for this website will be deleted, but your settings will remain intact.",
"saved": "Saved.",
"select-component-preview": "Select a component to preview",
"select-website-first": "Select a website first",
"sever-error": "Server error",
"share-url": "Your website stats are publicly available at the following URL:",
"team-already-member": "You are already a member of the team.",

View file

@ -103,6 +103,8 @@ export function BoardBody() {
});
};
const websiteId = board?.parameters?.websiteId;
const canEdit = editing && !!websiteId;
const rows = board?.parameters?.rows ?? [];
const minHeight = (rows?.length || 1) * MAX_ROW_HEIGHT + BUTTON_ROW_HEIGHT;
@ -121,7 +123,7 @@ export function BoardBody() {
rowId={row.id}
rowIndex={index}
rowCount={rows?.length}
editing={editing}
editing={canEdit}
onRemove={handleRemoveRow}
onMoveUp={handleMoveRowUp}
onMoveDown={handleMoveRowDown}
@ -131,7 +133,7 @@ export function BoardBody() {
{index < rows?.length - 1 && <Separator />}
</Fragment>
))}
{editing && (
{canEdit && (
<Panel minSize={BUTTON_ROW_HEIGHT}>
<Row padding="3">
<TooltipTrigger delay={0}>

View file

@ -9,7 +9,7 @@ import {
TooltipTrigger,
} from '@umami/react-zen';
import { useState } from 'react';
import { useBoard } from '@/components/hooks';
import { useBoard, useMessages } from '@/components/hooks';
import { Pencil, Plus, X } from '@/components/icons';
import type { BoardComponentConfig } from '@/lib/types';
import { BoardComponentRenderer } from './BoardComponentRenderer';
@ -32,6 +32,7 @@ export function BoardColumn({
}) {
const [showSelect, setShowSelect] = useState(false);
const { board } = useBoard();
const { t, labels } = useMessages();
const websiteId = board?.parameters?.websiteId;
const handleSelect = (config: BoardComponentConfig) => {
@ -91,7 +92,7 @@ export function BoardColumn({
)}
<Modal isOpen={showSelect} onOpenChange={setShowSelect}>
<Dialog
title="Add component"
title={t(labels.selectComponent)}
style={{
width: '750px',
maxWidth: 'calc(100vw - 40px)',

View file

@ -1,15 +1,6 @@
import {
Box,
Button,
Column,
Focusable,
Heading,
ListItem,
Row,
Select,
Text,
} from '@umami/react-zen';
import { Box, Button, Column, Focusable, ListItem, Row, Select, Text } from '@umami/react-zen';
import { useState } from 'react';
import { useMessages } from '@/components/hooks';
import type { BoardComponentConfig } from '@/lib/types';
import {
CATEGORIES,
@ -28,6 +19,7 @@ export function BoardComponentSelect({
onSelect: (config: BoardComponentConfig) => void;
onClose: () => void;
}) {
const { t, labels, messages } = useMessages();
const [selectedDef, setSelectedDef] = useState<ComponentDefinition | null>(null);
const [configValues, setConfigValues] = useState<Record<string, any>>({});
@ -79,13 +71,13 @@ export function BoardComponentSelect({
return (
<Column gap="4">
<Row gap="4" style={{ height: 500 }}>
<Row gap="4" style={{ height: 600 }}>
<Column gap="1" style={{ width: 200, flexShrink: 0, overflowY: 'auto' }}>
{CATEGORIES.map(cat => {
const components = getComponentsByCategory(cat.key);
return (
<Column key={cat.key} gap="1" marginBottom="2">
<Heading size="md">{cat.name}</Heading>
<Text weight="bold">{cat.name}</Text>
{components.map(def => (
<Focusable key={def.type}>
<Row
@ -155,7 +147,7 @@ export function BoardComponentSelect({
) : (
<Column alignItems="center" justifyContent="center" height="100%">
<Text color="muted">
{websiteId ? 'Select a component to preview' : 'Select a website first'}
{websiteId ? t(messages.selectComponentPreview) : t(messages.selectWebsiteFirst)}
</Text>
</Column>
)}
@ -164,10 +156,10 @@ export function BoardComponentSelect({
</Row>
<Row justifyContent="flex-end" gap="2" paddingTop="4" border="top">
<Button variant="quiet" onPress={onClose}>
Cancel
{t(labels.cancel)}
</Button>
<Button variant="primary" onPress={handleAdd} isDisabled={!selectedDef}>
Add
{t(labels.add)}
</Button>
</Row>
</Column>

View file

@ -22,10 +22,10 @@ export function BoardPage({ boardId, editing = false }: { boardId?: string; edit
}
function BoardControls() {
const { board, editing } = useBoard();
const { board } = useBoard();
const websiteId = board?.parameters?.websiteId;
if (editing || !websiteId) {
if (!websiteId) {
return null;
}

View file

@ -309,6 +309,7 @@ export const labels: Record<string, string> = {
pixel: 'label.pixel',
pixels: 'label.pixels',
addBoard: 'label.add-board',
selectComponent: 'label.select-component',
addLink: 'label.add-link',
addPixel: 'label.add-pixel',
maximize: 'label.maximize',
@ -367,6 +368,8 @@ export const messages: Record<string, string> = {
noResultsFound: 'message.no-results-found',
noWebsitesConfigured: 'message.no-websites-configured',
noTeamWebsites: 'message.no-team-websites',
selectComponentPreview: 'message.select-component-preview',
selectWebsiteFirst: 'message.select-website-first',
teamWebsitesInfo: 'message.team-websites-info',
noMatchPassword: 'message.no-match-password',
goToSettings: 'message.go-to-settings',