mirror of
https://github.com/umami-software/umami.git
synced 2026-02-12 08:37:13 +01:00
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
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:
parent
87bde9da1f
commit
2c7ab2b734
6 changed files with 23 additions and 22 deletions
|
|
@ -255,6 +255,7 @@
|
||||||
"select-date": "Select date",
|
"select-date": "Select date",
|
||||||
"select-filter": "Select filter",
|
"select-filter": "Select filter",
|
||||||
"select-role": "Select role",
|
"select-role": "Select role",
|
||||||
|
"select-component": "Select component",
|
||||||
"select-website": "Select website",
|
"select-website": "Select website",
|
||||||
"session": "Session",
|
"session": "Session",
|
||||||
"session-data": "Session data",
|
"session-data": "Session data",
|
||||||
|
|
@ -371,6 +372,8 @@
|
||||||
"reset-website": "To reset this website, type {confirmation} in the box below to confirm.",
|
"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.",
|
"reset-website-warning": "All statistics for this website will be deleted, but your settings will remain intact.",
|
||||||
"saved": "Saved.",
|
"saved": "Saved.",
|
||||||
|
"select-component-preview": "Select a component to preview",
|
||||||
|
"select-website-first": "Select a website first",
|
||||||
"sever-error": "Server error",
|
"sever-error": "Server error",
|
||||||
"share-url": "Your website stats are publicly available at the following URL:",
|
"share-url": "Your website stats are publicly available at the following URL:",
|
||||||
"team-already-member": "You are already a member of the team.",
|
"team-already-member": "You are already a member of the team.",
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ export function BoardBody() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const websiteId = board?.parameters?.websiteId;
|
||||||
|
const canEdit = editing && !!websiteId;
|
||||||
const rows = board?.parameters?.rows ?? [];
|
const rows = board?.parameters?.rows ?? [];
|
||||||
const minHeight = (rows?.length || 1) * MAX_ROW_HEIGHT + BUTTON_ROW_HEIGHT;
|
const minHeight = (rows?.length || 1) * MAX_ROW_HEIGHT + BUTTON_ROW_HEIGHT;
|
||||||
|
|
||||||
|
|
@ -121,7 +123,7 @@ export function BoardBody() {
|
||||||
rowId={row.id}
|
rowId={row.id}
|
||||||
rowIndex={index}
|
rowIndex={index}
|
||||||
rowCount={rows?.length}
|
rowCount={rows?.length}
|
||||||
editing={editing}
|
editing={canEdit}
|
||||||
onRemove={handleRemoveRow}
|
onRemove={handleRemoveRow}
|
||||||
onMoveUp={handleMoveRowUp}
|
onMoveUp={handleMoveRowUp}
|
||||||
onMoveDown={handleMoveRowDown}
|
onMoveDown={handleMoveRowDown}
|
||||||
|
|
@ -131,7 +133,7 @@ export function BoardBody() {
|
||||||
{index < rows?.length - 1 && <Separator />}
|
{index < rows?.length - 1 && <Separator />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
{editing && (
|
{canEdit && (
|
||||||
<Panel minSize={BUTTON_ROW_HEIGHT}>
|
<Panel minSize={BUTTON_ROW_HEIGHT}>
|
||||||
<Row padding="3">
|
<Row padding="3">
|
||||||
<TooltipTrigger delay={0}>
|
<TooltipTrigger delay={0}>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@umami/react-zen';
|
} from '@umami/react-zen';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useBoard } from '@/components/hooks';
|
import { useBoard, useMessages } from '@/components/hooks';
|
||||||
import { Pencil, Plus, X } from '@/components/icons';
|
import { Pencil, Plus, X } from '@/components/icons';
|
||||||
import type { BoardComponentConfig } from '@/lib/types';
|
import type { BoardComponentConfig } from '@/lib/types';
|
||||||
import { BoardComponentRenderer } from './BoardComponentRenderer';
|
import { BoardComponentRenderer } from './BoardComponentRenderer';
|
||||||
|
|
@ -32,6 +32,7 @@ export function BoardColumn({
|
||||||
}) {
|
}) {
|
||||||
const [showSelect, setShowSelect] = useState(false);
|
const [showSelect, setShowSelect] = useState(false);
|
||||||
const { board } = useBoard();
|
const { board } = useBoard();
|
||||||
|
const { t, labels } = useMessages();
|
||||||
const websiteId = board?.parameters?.websiteId;
|
const websiteId = board?.parameters?.websiteId;
|
||||||
|
|
||||||
const handleSelect = (config: BoardComponentConfig) => {
|
const handleSelect = (config: BoardComponentConfig) => {
|
||||||
|
|
@ -91,7 +92,7 @@ export function BoardColumn({
|
||||||
)}
|
)}
|
||||||
<Modal isOpen={showSelect} onOpenChange={setShowSelect}>
|
<Modal isOpen={showSelect} onOpenChange={setShowSelect}>
|
||||||
<Dialog
|
<Dialog
|
||||||
title="Add component"
|
title={t(labels.selectComponent)}
|
||||||
style={{
|
style={{
|
||||||
width: '750px',
|
width: '750px',
|
||||||
maxWidth: 'calc(100vw - 40px)',
|
maxWidth: 'calc(100vw - 40px)',
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,6 @@
|
||||||
import {
|
import { Box, Button, Column, Focusable, ListItem, Row, Select, Text } from '@umami/react-zen';
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Column,
|
|
||||||
Focusable,
|
|
||||||
Heading,
|
|
||||||
ListItem,
|
|
||||||
Row,
|
|
||||||
Select,
|
|
||||||
Text,
|
|
||||||
} from '@umami/react-zen';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useMessages } from '@/components/hooks';
|
||||||
import type { BoardComponentConfig } from '@/lib/types';
|
import type { BoardComponentConfig } from '@/lib/types';
|
||||||
import {
|
import {
|
||||||
CATEGORIES,
|
CATEGORIES,
|
||||||
|
|
@ -28,6 +19,7 @@ export function BoardComponentSelect({
|
||||||
onSelect: (config: BoardComponentConfig) => void;
|
onSelect: (config: BoardComponentConfig) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
const { t, labels, messages } = useMessages();
|
||||||
const [selectedDef, setSelectedDef] = useState<ComponentDefinition | null>(null);
|
const [selectedDef, setSelectedDef] = useState<ComponentDefinition | null>(null);
|
||||||
const [configValues, setConfigValues] = useState<Record<string, any>>({});
|
const [configValues, setConfigValues] = useState<Record<string, any>>({});
|
||||||
|
|
||||||
|
|
@ -79,13 +71,13 @@ export function BoardComponentSelect({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap="4">
|
<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' }}>
|
<Column gap="1" style={{ width: 200, flexShrink: 0, overflowY: 'auto' }}>
|
||||||
{CATEGORIES.map(cat => {
|
{CATEGORIES.map(cat => {
|
||||||
const components = getComponentsByCategory(cat.key);
|
const components = getComponentsByCategory(cat.key);
|
||||||
return (
|
return (
|
||||||
<Column key={cat.key} gap="1" marginBottom="2">
|
<Column key={cat.key} gap="1" marginBottom="2">
|
||||||
<Heading size="md">{cat.name}</Heading>
|
<Text weight="bold">{cat.name}</Text>
|
||||||
{components.map(def => (
|
{components.map(def => (
|
||||||
<Focusable key={def.type}>
|
<Focusable key={def.type}>
|
||||||
<Row
|
<Row
|
||||||
|
|
@ -155,7 +147,7 @@ export function BoardComponentSelect({
|
||||||
) : (
|
) : (
|
||||||
<Column alignItems="center" justifyContent="center" height="100%">
|
<Column alignItems="center" justifyContent="center" height="100%">
|
||||||
<Text color="muted">
|
<Text color="muted">
|
||||||
{websiteId ? 'Select a component to preview' : 'Select a website first'}
|
{websiteId ? t(messages.selectComponentPreview) : t(messages.selectWebsiteFirst)}
|
||||||
</Text>
|
</Text>
|
||||||
</Column>
|
</Column>
|
||||||
)}
|
)}
|
||||||
|
|
@ -164,10 +156,10 @@ export function BoardComponentSelect({
|
||||||
</Row>
|
</Row>
|
||||||
<Row justifyContent="flex-end" gap="2" paddingTop="4" border="top">
|
<Row justifyContent="flex-end" gap="2" paddingTop="4" border="top">
|
||||||
<Button variant="quiet" onPress={onClose}>
|
<Button variant="quiet" onPress={onClose}>
|
||||||
Cancel
|
{t(labels.cancel)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" onPress={handleAdd} isDisabled={!selectedDef}>
|
<Button variant="primary" onPress={handleAdd} isDisabled={!selectedDef}>
|
||||||
Add
|
{t(labels.add)}
|
||||||
</Button>
|
</Button>
|
||||||
</Row>
|
</Row>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ export function BoardPage({ boardId, editing = false }: { boardId?: string; edit
|
||||||
}
|
}
|
||||||
|
|
||||||
function BoardControls() {
|
function BoardControls() {
|
||||||
const { board, editing } = useBoard();
|
const { board } = useBoard();
|
||||||
const websiteId = board?.parameters?.websiteId;
|
const websiteId = board?.parameters?.websiteId;
|
||||||
|
|
||||||
if (editing || !websiteId) {
|
if (!websiteId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,7 @@ export const labels: Record<string, string> = {
|
||||||
pixel: 'label.pixel',
|
pixel: 'label.pixel',
|
||||||
pixels: 'label.pixels',
|
pixels: 'label.pixels',
|
||||||
addBoard: 'label.add-board',
|
addBoard: 'label.add-board',
|
||||||
|
selectComponent: 'label.select-component',
|
||||||
addLink: 'label.add-link',
|
addLink: 'label.add-link',
|
||||||
addPixel: 'label.add-pixel',
|
addPixel: 'label.add-pixel',
|
||||||
maximize: 'label.maximize',
|
maximize: 'label.maximize',
|
||||||
|
|
@ -367,6 +368,8 @@ export const messages: Record<string, string> = {
|
||||||
noResultsFound: 'message.no-results-found',
|
noResultsFound: 'message.no-results-found',
|
||||||
noWebsitesConfigured: 'message.no-websites-configured',
|
noWebsitesConfigured: 'message.no-websites-configured',
|
||||||
noTeamWebsites: 'message.no-team-websites',
|
noTeamWebsites: 'message.no-team-websites',
|
||||||
|
selectComponentPreview: 'message.select-component-preview',
|
||||||
|
selectWebsiteFirst: 'message.select-website-first',
|
||||||
teamWebsitesInfo: 'message.team-websites-info',
|
teamWebsitesInfo: 'message.team-websites-info',
|
||||||
noMatchPassword: 'message.no-match-password',
|
noMatchPassword: 'message.no-match-password',
|
||||||
goToSettings: 'message.go-to-settings',
|
goToSettings: 'message.go-to-settings',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue