@bardioc/create-bardioc-app 0.5.5 → 0.5.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/bin/create.mjs +2 -4
- package/package.json +1 -1
- package/templates/_base/README.md +17 -3
- package/templates/_base/scripts/stamp-manifest.mjs +1 -1
- package/templates/_opt-link/scripts/link-source.mjs +7 -5
- package/templates/angular/.env.example +7 -2
- package/templates/angular/_gitignore +1 -0
- package/templates/angular/package.json +2 -4
- package/templates/angular/scripts/dev-env.mjs +41 -0
- package/templates/angular/scripts/dev.mjs +27 -0
- package/templates/angular/scripts/sync-runtime-env.mjs +3 -48
- package/templates/angular/src/app/app.component.ts +6 -6
- package/templates/angular/src/styles.css +4 -0
- package/templates/angular/tsconfig.json +1 -0
- package/templates/nextjs/.env.example +7 -2
- package/templates/nextjs/_gitignore +1 -0
- package/templates/nextjs/app/layout.tsx +2 -0
- package/templates/nextjs/app/page.tsx +13 -7
- package/templates/nextjs/eslint.config.mjs +4 -0
- package/templates/nextjs/next.config.ts +2 -21
- package/templates/nextjs/package.json +8 -3
- package/templates/nextjs/prettier.config.mjs +1 -0
- package/templates/nextjs/scripts/dev-env.mjs +41 -0
- package/templates/nextjs/scripts/dev.mjs +27 -0
- package/templates/nextjs/tsconfig.json +3 -18
- package/templates/preact/.env.example +3 -0
- package/templates/preact/eslint.config.mjs +3 -0
- package/templates/preact/package.json +8 -3
- package/templates/preact/prettier.config.mjs +1 -0
- package/templates/preact/src/App.tsx +6 -6
- package/templates/preact/tsconfig.json +4 -10
- package/templates/preact/vite.config.ts +9 -1
- package/templates/solid/.env.example +3 -0
- package/templates/solid/eslint.config.mjs +3 -0
- package/templates/solid/package.json +8 -4
- package/templates/solid/prettier.config.mjs +1 -0
- package/templates/solid/src/App.tsx +7 -7
- package/templates/solid/tsconfig.json +4 -9
- package/templates/solid/vite.config.ts +10 -2
- package/templates/svelte/.env.example +3 -0
- package/templates/svelte/_gitignore +1 -0
- package/templates/svelte/eslint.config.mjs +3 -0
- package/templates/svelte/package.json +8 -3
- package/templates/svelte/prettier.config.mjs +1 -0
- package/templates/svelte/src/App.svelte +6 -6
- package/templates/svelte/tsconfig.json +4 -4
- package/templates/svelte/vite.config.ts +9 -1
- package/templates/vite/.env.example +3 -0
- package/templates/vite/CLAUDE.md +40 -8
- package/templates/vite/eslint.config.mjs +3 -0
- package/templates/vite/package.json +9 -3
- package/templates/vite/prettier.config.mjs +1 -0
- package/templates/vite/src/auth/onboarding.tsx +4 -4
- package/templates/vite/src/climate/climate-data.ts +13 -6
- package/templates/vite/src/climate/conditions-chart.tsx +2 -2
- package/templates/vite/src/climate/overview-view.tsx +1 -1
- package/templates/vite/src/climate/stations-view.tsx +3 -3
- package/templates/vite/src/climate/use-climate.ts +2 -2
- package/templates/vite/src/events/live-events-view.tsx +2 -2
- package/templates/vite/src/graph/explorer-view.tsx +4 -4
- package/templates/vite/src/graph/requests.ts +2 -1
- package/templates/vite/src/locales/de.json +4 -4
- package/templates/vite/src/locales/en.json +44 -44
- package/templates/vite/src/profile/profile-view.tsx +5 -5
- package/templates/vite/src/shell/about-dialog.tsx +3 -3
- package/templates/vite/src/shell/app-shell.tsx +16 -12
- package/templates/vite/src/shell/header.tsx +24 -24
- package/templates/vite/tsconfig.json +4 -10
- package/templates/vite/vite.config.ts +10 -2
- package/templates/vue/.env.example +3 -0
- package/templates/vue/_gitignore +1 -0
- package/templates/vue/eslint.config.mjs +3 -0
- package/templates/vue/package.json +8 -3
- package/templates/vue/prettier.config.mjs +1 -0
- package/templates/vue/src/App.vue +6 -6
- package/templates/vue/tsconfig.json +5 -9
- package/templates/vue/vite.config.ts +10 -2
- package/templates/angular/scripts/dev-auth-proxy.mjs +0 -91
|
@@ -25,7 +25,7 @@ export function Onboarding() {
|
|
|
25
25
|
icon={PlugZap}
|
|
26
26
|
iconVariant="chip"
|
|
27
27
|
titleSize="lg"
|
|
28
|
-
title={t('auth.title', { fallback: 'Connect to Bardioc
|
|
28
|
+
title={t('auth.title', { fallback: 'Connect to Bardioc' })}
|
|
29
29
|
description={t('auth.description', {
|
|
30
30
|
fallback: 'This dashboard reads live data through the Bardioc SDK transport.',
|
|
31
31
|
})}
|
|
@@ -42,7 +42,7 @@ export function Onboarding() {
|
|
|
42
42
|
<ol className="text-muted-foreground list-decimal space-y-2 pl-4">
|
|
43
43
|
<li>
|
|
44
44
|
{t('auth.step1', {
|
|
45
|
-
fallback: 'Set DEV_SECRET=<your-secret> and API_BASE_URL=<your-host> in .env',
|
|
45
|
+
fallback: 'Set DEV_SECRET=<your-secret> and API_BASE_URL=<your-host> in .env.',
|
|
46
46
|
})}
|
|
47
47
|
</li>
|
|
48
48
|
<li>
|
|
@@ -52,10 +52,10 @@ export function Onboarding() {
|
|
|
52
52
|
</ol>
|
|
53
53
|
<div className="border-border mt-3 space-y-1 border-t pt-3">
|
|
54
54
|
<p className="text-foreground font-medium">
|
|
55
|
-
{t('auth.detectedTitle', { fallback: 'Detected
|
|
55
|
+
{t('auth.detectedTitle', { fallback: 'Detected Environment' })}
|
|
56
56
|
</p>
|
|
57
57
|
<DetectedRow
|
|
58
|
-
label={t('auth.insideHost', { fallback: 'Inside
|
|
58
|
+
label={t('auth.insideHost', { fallback: 'Inside Host Iframe' })}
|
|
59
59
|
ok={insideHost}
|
|
60
60
|
/>
|
|
61
61
|
</div>
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import type { ClimateReading, ClimateState, Condition, ForecastDay, Station } from './types';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type StationBase = Pick<Station, 'id' | 'name' | 'region'>;
|
|
4
|
+
|
|
5
|
+
// Non-empty tuples, so indexing them stays type-safe under noUncheckedIndexedAccess.
|
|
6
|
+
const STATIONS: [StationBase, ...StationBase[]] = [
|
|
4
7
|
{ id: 'lagoon', name: 'Lagoon Station', region: 'Equatorial Belt' },
|
|
5
8
|
{ id: 'verdant', name: 'Verdant Ridge', region: 'Highlands' },
|
|
6
9
|
{ id: 'aurora', name: 'Aurora Flats', region: 'Polar Rim' },
|
|
7
10
|
{ id: 'cinder', name: 'Cinder Basin', region: 'Volcanic Plains' },
|
|
8
11
|
{ id: 'mistral', name: 'Mistral Cove', region: 'Coastal Shelf' },
|
|
9
12
|
];
|
|
10
|
-
const CONDITIONS: Condition[] = ['sunny', 'cloudy', 'rainy'];
|
|
11
|
-
const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
13
|
+
const CONDITIONS: [Condition, ...Condition[]] = ['sunny', 'cloudy', 'rainy'];
|
|
14
|
+
const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as const;
|
|
12
15
|
|
|
13
|
-
function pick<T>(items: readonly T[]): T {
|
|
14
|
-
return items[Math.floor(Math.random() * items.length)];
|
|
16
|
+
function pick<T>(items: readonly [T, ...T[]]): T {
|
|
17
|
+
return items[Math.floor(Math.random() * items.length)] ?? items[0];
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function round(value: number, decimals: number): number {
|
|
@@ -63,7 +66,11 @@ export function generateState(
|
|
|
63
66
|
activeStationId?: string,
|
|
64
67
|
source: ClimateState['source'] = 'mock'
|
|
65
68
|
): ClimateState {
|
|
66
|
-
const
|
|
69
|
+
const [firstBase, ...restBases] = STATIONS;
|
|
70
|
+
const stations: [Station, ...Station[]] = [
|
|
71
|
+
buildStation(firstBase),
|
|
72
|
+
...restBases.map(buildStation),
|
|
73
|
+
];
|
|
67
74
|
const active = stations.find(station => station.id === activeStationId) ?? stations[0];
|
|
68
75
|
return {
|
|
69
76
|
stations,
|
|
@@ -28,8 +28,8 @@ export function ConditionsChart({ forecast }: Readonly<ConditionsChartProps>) {
|
|
|
28
28
|
seriesData={seriesData}
|
|
29
29
|
xKey="day"
|
|
30
30
|
chartConfig={chartConfig}
|
|
31
|
-
title={t('chart.title', { fallback: '7-
|
|
32
|
-
description={t('chart.description', { fallback: 'Daylight hours by condition' })}
|
|
31
|
+
title={t('chart.title', { fallback: '7-Day Outlook' })}
|
|
32
|
+
description={t('chart.description', { fallback: 'Daylight hours by condition.' })}
|
|
33
33
|
heightClass="h-[220px]"
|
|
34
34
|
/>
|
|
35
35
|
);
|
|
@@ -122,7 +122,7 @@ export function StationsView() {
|
|
|
122
122
|
<div className="flex flex-col gap-3">
|
|
123
123
|
<div>
|
|
124
124
|
<h2 className="text-lg font-semibold">
|
|
125
|
-
{t('stations.title', { fallback: 'Monitoring
|
|
125
|
+
{t('stations.title', { fallback: 'Monitoring Stations' })}
|
|
126
126
|
</h2>
|
|
127
127
|
<p className="text-muted-foreground text-xs">
|
|
128
128
|
{t('stations.description', { fallback: '' })}
|
|
@@ -134,13 +134,13 @@ export function StationsView() {
|
|
|
134
134
|
columns={columns}
|
|
135
135
|
filterKey={['name', 'region']}
|
|
136
136
|
showSearchBar
|
|
137
|
-
filterPlaceholder={t('stations.search', { fallback: 'Search
|
|
137
|
+
filterPlaceholder={t('stations.search', { fallback: 'Search Stations…' })}
|
|
138
138
|
itemLabel={t('stations.itemLabel', { fallback: 'stations' })}
|
|
139
139
|
onRowClick={station => setActiveStation(station.id)}
|
|
140
140
|
filterSlot={<ConditionFilter selected={conditions} onToggle={onToggle} />}
|
|
141
141
|
emptyState={
|
|
142
142
|
<p className="text-muted-foreground p-6 text-center text-sm">
|
|
143
|
-
{t('stations.empty', { fallback: 'No stations match your filters' })}
|
|
143
|
+
{t('stations.empty', { fallback: 'No stations match your filters.' })}
|
|
144
144
|
</p>
|
|
145
145
|
}
|
|
146
146
|
/>
|
|
@@ -89,7 +89,7 @@ export function useClimate() {
|
|
|
89
89
|
|
|
90
90
|
notify(
|
|
91
91
|
t('notify.synced', {
|
|
92
|
-
fallback: 'Synced {count} stations',
|
|
92
|
+
fallback: 'Synced {count} stations.',
|
|
93
93
|
params: { count: next.stations.length },
|
|
94
94
|
}),
|
|
95
95
|
source === 'graph' ? 'success' : 'warning'
|
|
@@ -108,7 +108,7 @@ export function useClimate() {
|
|
|
108
108
|
const station = next.stations.find(item => item.id === id);
|
|
109
109
|
notify(
|
|
110
110
|
t('notify.stationSelected', {
|
|
111
|
-
fallback: 'Active station: {name}',
|
|
111
|
+
fallback: 'Active station: {name}.',
|
|
112
112
|
params: { name: station?.name ?? id },
|
|
113
113
|
}),
|
|
114
114
|
'info'
|
|
@@ -37,14 +37,14 @@ export function LiveEventsView() {
|
|
|
37
37
|
<Empty
|
|
38
38
|
icon={Activity}
|
|
39
39
|
iconVariant="chip"
|
|
40
|
-
title={t('events.emptyTitle', { fallback: 'Waiting for
|
|
40
|
+
title={t('events.emptyTitle', { fallback: 'Waiting for Events' })}
|
|
41
41
|
description={
|
|
42
42
|
isInsideIframe()
|
|
43
43
|
? t('events.emptyHost', {
|
|
44
44
|
fallback: 'No graph events yet — activity will appear here as it happens.',
|
|
45
45
|
})
|
|
46
46
|
: t('events.emptyStandalone', {
|
|
47
|
-
fallback: 'Open inside Bardioc
|
|
47
|
+
fallback: 'Open inside Bardioc (or a dev session) to receive live events.',
|
|
48
48
|
})
|
|
49
49
|
}
|
|
50
50
|
/>
|
|
@@ -28,12 +28,12 @@ export function ExplorerView() {
|
|
|
28
28
|
const data = await request.run(bridge);
|
|
29
29
|
const ms = Date.now() - started;
|
|
30
30
|
setResult({ status: 'done', live: true, ms, data });
|
|
31
|
-
notify(t('notify.queryOk', { fallback: 'Request ok ({ms} ms)', params: { ms } }), 'success');
|
|
31
|
+
notify(t('notify.queryOk', { fallback: 'Request ok ({ms} ms).', params: { ms } }), 'success');
|
|
32
32
|
} catch {
|
|
33
33
|
const ms = Date.now() - started;
|
|
34
34
|
setResult({ status: 'done', live: false, ms, data: request.mock() });
|
|
35
35
|
notify(
|
|
36
|
-
t('notify.queryFailed', { fallback: 'Request failed — showing simulated data' }),
|
|
36
|
+
t('notify.queryFailed', { fallback: 'Request failed — showing simulated data.' }),
|
|
37
37
|
'warning'
|
|
38
38
|
);
|
|
39
39
|
}
|
|
@@ -85,7 +85,7 @@ export function ExplorerView() {
|
|
|
85
85
|
>
|
|
86
86
|
{result.status === 'running'
|
|
87
87
|
? t('explorer.running', { fallback: 'Running…' })
|
|
88
|
-
: t('explorer.run', { fallback: 'Run
|
|
88
|
+
: t('explorer.run', { fallback: 'Run Request' })}
|
|
89
89
|
</Button>
|
|
90
90
|
</div>
|
|
91
91
|
|
|
@@ -107,7 +107,7 @@ export function ExplorerView() {
|
|
|
107
107
|
{result.status === 'done'
|
|
108
108
|
? JSON.stringify(result.data, null, 2)
|
|
109
109
|
: t('explorer.empty', {
|
|
110
|
-
fallback: 'Select a request and run it to see the response',
|
|
110
|
+
fallback: 'Select a request and run it to see the response.',
|
|
111
111
|
})}
|
|
112
112
|
</pre>
|
|
113
113
|
</div>
|
|
@@ -42,7 +42,8 @@ async function sampleNodes(bridge: HostBridge, limit: number): Promise<GraphNode
|
|
|
42
42
|
// query by a type that actually exists, a node lookup, and a Gremlin neighbor traversal — the
|
|
43
43
|
// lookup/traversal presets discover a real node from the wildcard query first, so nothing is
|
|
44
44
|
// hard-coded to a specific instance.
|
|
45
|
-
|
|
45
|
+
// Non-empty tuple, so the Explorer can index the first preset as its default.
|
|
46
|
+
export const SDK_REQUESTS: [SdkRequest, ...SdkRequest[]] = [
|
|
46
47
|
{
|
|
47
48
|
id: 'graph.query.all',
|
|
48
49
|
category: 'graph',
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"neverSaved": "Noch nicht gespeichert"
|
|
40
40
|
},
|
|
41
41
|
"auth": {
|
|
42
|
-
"title": "Mit Bardioc
|
|
43
|
-
"description": "Dieses Dashboard liest Live-Daten über den Bardioc-SDK-Transport. Es läuft in Bardioc
|
|
42
|
+
"title": "Mit Bardioc verbinden",
|
|
43
|
+
"description": "Dieses Dashboard liest Live-Daten über den Bardioc-SDK-Transport. Es läuft in Bardioc oder eigenständig mit einer CLI-Dev-Session.",
|
|
44
44
|
"stepsTitle": "Eigenständig mit Dev-Session ausführen:",
|
|
45
45
|
"step1": "Setze DEV_SECRET=<dein-secret> und API_BASE_URL=<dein-host> in .env",
|
|
46
46
|
"step2": "Dev-Server starten:",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"noOperator": "Betreiber nicht verfügbar",
|
|
57
57
|
"updated": "Aktualisiert {time}",
|
|
58
58
|
"persistence": "In IndexedDB gespeichert",
|
|
59
|
-
"notPersisted": "Vorschau — nicht gespeichert (in Bardioc
|
|
59
|
+
"notPersisted": "Vorschau — nicht gespeichert (in Bardioc öffnen zum Speichern)",
|
|
60
60
|
"historyOne": "{count} Snapshot im Verlauf",
|
|
61
61
|
"historyOther": "{count} Snapshots im Verlauf",
|
|
62
62
|
"source": "Quelle"
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"description": "Live-Graph-Ereignisse, vom OS über SSE gestreamt (Berechtigung \"events\").",
|
|
128
128
|
"emptyTitle": "Warte auf Ereignisse",
|
|
129
129
|
"emptyHost": "Noch keine Graph-Ereignisse — Aktivität erscheint hier, sobald sie auftritt.",
|
|
130
|
-
"emptyStandalone": "In Bardioc
|
|
130
|
+
"emptyStandalone": "In Bardioc (oder einer Dev-Session) öffnen, um Live-Ereignisse zu empfangen."
|
|
131
131
|
},
|
|
132
132
|
"notify": {
|
|
133
133
|
"synced": "{count} Stationen synchronisiert",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"app": { "title": "Weather Dashboard", "tagline": "Live
|
|
2
|
+
"app": { "title": "Weather Dashboard", "tagline": "Live Planetary Readings" },
|
|
3
3
|
"nav": {
|
|
4
4
|
"overview": "Overview",
|
|
5
5
|
"stations": "Stations",
|
|
@@ -9,77 +9,77 @@
|
|
|
9
9
|
},
|
|
10
10
|
"menu": {
|
|
11
11
|
"view": "View",
|
|
12
|
-
"about": "About
|
|
12
|
+
"about": "About This App"
|
|
13
13
|
},
|
|
14
14
|
"about": {
|
|
15
|
-
"title": "About
|
|
15
|
+
"title": "About This App",
|
|
16
16
|
"version": "Version",
|
|
17
17
|
"id": "App ID",
|
|
18
18
|
"description": "Description",
|
|
19
19
|
"permissions": "Permissions",
|
|
20
|
-
"error": "Could not read app-manifest.json",
|
|
21
|
-
"source": "Read live from public/app-manifest.json"
|
|
20
|
+
"error": "Could not read app-manifest.json.",
|
|
21
|
+
"source": "Read live from public/app-manifest.json."
|
|
22
22
|
},
|
|
23
23
|
"genericTable": {
|
|
24
|
-
"filterPlaceholder": "Filter
|
|
24
|
+
"filterPlaceholder": "Filter Results...",
|
|
25
25
|
"itemLabel": "rows",
|
|
26
26
|
"showing": "{from} to {to} of {total}",
|
|
27
27
|
"columns": "Columns",
|
|
28
28
|
"noResults": "No results found.",
|
|
29
|
-
"goToPreviousPage": "Go to
|
|
30
|
-
"goToNextPage": "Go to
|
|
29
|
+
"goToPreviousPage": "Go to Previous Page",
|
|
30
|
+
"goToNextPage": "Go to Next Page"
|
|
31
31
|
},
|
|
32
32
|
"common": { "button": { "previous": "Previous", "next": "Next" } },
|
|
33
33
|
"header": {
|
|
34
|
-
"search": "Search
|
|
34
|
+
"search": "Search Stations…",
|
|
35
35
|
"sync": "Sync",
|
|
36
36
|
"syncing": "Syncing…",
|
|
37
37
|
"location": "Station",
|
|
38
38
|
"savedAt": "Saved {time}",
|
|
39
|
-
"neverSaved": "Not
|
|
39
|
+
"neverSaved": "Not Yet Saved"
|
|
40
40
|
},
|
|
41
41
|
"auth": {
|
|
42
|
-
"title": "Connect to Bardioc
|
|
43
|
-
"description": "This dashboard reads live data through the Bardioc SDK transport. It runs when opened inside Bardioc
|
|
42
|
+
"title": "Connect to Bardioc",
|
|
43
|
+
"description": "This dashboard reads live data through the Bardioc SDK transport. It runs when opened inside Bardioc, or standalone with a CLI dev session.",
|
|
44
44
|
"stepsTitle": "Run standalone with a dev session:",
|
|
45
|
-
"step1": "Set DEV_SECRET=<your-secret> and API_BASE_URL=<your-host> in .env",
|
|
45
|
+
"step1": "Set DEV_SECRET=<your-secret> and API_BASE_URL=<your-host> in .env.",
|
|
46
46
|
"step2": "Start the dev server:",
|
|
47
|
-
"detectedTitle": "Detected
|
|
48
|
-
"insideHost": "Inside
|
|
49
|
-
"devAuth": "Dev
|
|
47
|
+
"detectedTitle": "Detected Environment",
|
|
48
|
+
"insideHost": "Inside Host Iframe",
|
|
49
|
+
"devAuth": "Dev Auth Enabled",
|
|
50
50
|
"yes": "yes",
|
|
51
51
|
"no": "no",
|
|
52
52
|
"retry": "Reload"
|
|
53
53
|
},
|
|
54
54
|
"overview": {
|
|
55
55
|
"operator": "Operator",
|
|
56
|
-
"noOperator": "Operator
|
|
56
|
+
"noOperator": "Operator Unavailable",
|
|
57
57
|
"updated": "Updated {time}",
|
|
58
58
|
"persistence": "Persisted to IndexedDB",
|
|
59
|
-
"notPersisted": "Preview — not persisted (open inside Bardioc
|
|
59
|
+
"notPersisted": "Preview — not persisted (open inside Bardioc to persist)",
|
|
60
60
|
"historyOne": "{count} snapshot in history",
|
|
61
61
|
"historyOther": "{count} snapshots in history",
|
|
62
62
|
"source": "Source"
|
|
63
63
|
},
|
|
64
64
|
"conditions": { "sunny": "Sunny", "cloudy": "Cloudy", "rainy": "Rainy" },
|
|
65
|
-
"source": { "graph": "Bardioc
|
|
65
|
+
"source": { "graph": "Bardioc Graph", "mock": "Simulated" },
|
|
66
66
|
"metrics": {
|
|
67
67
|
"oxygen": "Oxygen",
|
|
68
|
-
"temperature": "Surface
|
|
68
|
+
"temperature": "Surface Temp",
|
|
69
69
|
"humidity": "Humidity",
|
|
70
|
-
"windSpeed": "Wind
|
|
70
|
+
"windSpeed": "Wind Speed",
|
|
71
71
|
"vegetation": "Vegetation",
|
|
72
|
-
"oxygenHint": "Atmospheric
|
|
73
|
-
"temperatureHint": "Ground
|
|
72
|
+
"oxygenHint": "Atmospheric Mix",
|
|
73
|
+
"temperatureHint": "Ground Level",
|
|
74
74
|
"humidityHint": "Relative",
|
|
75
75
|
"windSpeedHint": "Sustained",
|
|
76
|
-
"vegetationHint": "Surface
|
|
76
|
+
"vegetationHint": "Surface Cover"
|
|
77
77
|
},
|
|
78
|
-
"chart": { "title": "7-
|
|
78
|
+
"chart": { "title": "7-Day Outlook", "description": "Daylight hours by condition." },
|
|
79
79
|
"stations": {
|
|
80
|
-
"title": "Monitoring
|
|
80
|
+
"title": "Monitoring Stations",
|
|
81
81
|
"description": "All stations reporting to this instance. Search, filter, or select one.",
|
|
82
|
-
"search": "Search
|
|
82
|
+
"search": "Search Stations…",
|
|
83
83
|
"filter": "Condition",
|
|
84
84
|
"colName": "Station",
|
|
85
85
|
"colRegion": "Region",
|
|
@@ -88,16 +88,16 @@
|
|
|
88
88
|
"colHumidity": "Humidity",
|
|
89
89
|
"colVegetation": "Vegetation",
|
|
90
90
|
"itemLabel": "stations",
|
|
91
|
-
"empty": "No stations match your filters",
|
|
91
|
+
"empty": "No stations match your filters.",
|
|
92
92
|
"apply": "Apply",
|
|
93
93
|
"clear": "Clear"
|
|
94
94
|
},
|
|
95
95
|
"explorer": {
|
|
96
96
|
"title": "Graph Explorer",
|
|
97
97
|
"description": "Run real Bardioc SDK transport requests. Without a session, each request falls back to simulated data so you can still explore the shape.",
|
|
98
|
-
"run": "Run
|
|
98
|
+
"run": "Run Request",
|
|
99
99
|
"running": "Running…",
|
|
100
|
-
"empty": "Select a request and run it to see the response",
|
|
100
|
+
"empty": "Select a request and run it to see the response.",
|
|
101
101
|
"live": "Live",
|
|
102
102
|
"mock": "Simulated",
|
|
103
103
|
"categoryGraph": "Graph",
|
|
@@ -106,35 +106,35 @@
|
|
|
106
106
|
"durationMs": "{ms} ms"
|
|
107
107
|
},
|
|
108
108
|
"profile": {
|
|
109
|
-
"title": "Operator
|
|
109
|
+
"title": "Operator Profile",
|
|
110
110
|
"description": "Pulled live from the OS transport (os.profile).",
|
|
111
111
|
"name": "Name",
|
|
112
|
-
"displayName": "Display
|
|
113
|
-
"firstName": "First
|
|
114
|
-
"lastName": "Last
|
|
112
|
+
"displayName": "Display Name",
|
|
113
|
+
"firstName": "First Name",
|
|
114
|
+
"lastName": "Last Name",
|
|
115
115
|
"email": "Email",
|
|
116
116
|
"id": "ID",
|
|
117
117
|
"organization": "Organization",
|
|
118
118
|
"units": "Units",
|
|
119
119
|
"members": "Members",
|
|
120
|
-
"raw": "Raw
|
|
121
|
-
"errorTitle": "Could
|
|
120
|
+
"raw": "Raw Response",
|
|
121
|
+
"errorTitle": "Could Not Load Profile",
|
|
122
122
|
"errorHint": "The transport returned an error. Authorize a session, then reload.",
|
|
123
123
|
"unknown": "Unknown"
|
|
124
124
|
},
|
|
125
125
|
"events": {
|
|
126
126
|
"title": "Live Events",
|
|
127
127
|
"description": "Live graph events streamed from the OS over SSE (permission \"events\").",
|
|
128
|
-
"emptyTitle": "Waiting for
|
|
128
|
+
"emptyTitle": "Waiting for Events",
|
|
129
129
|
"emptyHost": "No graph events yet — activity will appear here as it happens.",
|
|
130
|
-
"emptyStandalone": "Open inside Bardioc
|
|
130
|
+
"emptyStandalone": "Open inside Bardioc (or a dev session) to receive live events."
|
|
131
131
|
},
|
|
132
132
|
"notify": {
|
|
133
|
-
"synced": "Synced {count} stations",
|
|
134
|
-
"saved": "Saved to IndexedDB",
|
|
135
|
-
"stationSelected": "Active station: {name}",
|
|
136
|
-
"localeChanged": "Language switched to {locale}",
|
|
137
|
-
"queryOk": "Request ok ({ms} ms)",
|
|
138
|
-
"queryFailed": "Request failed — showing simulated data"
|
|
133
|
+
"synced": "Synced {count} stations.",
|
|
134
|
+
"saved": "Saved to IndexedDB.",
|
|
135
|
+
"stationSelected": "Active station: {name}.",
|
|
136
|
+
"localeChanged": "Language switched to {locale}.",
|
|
137
|
+
"queryOk": "Request ok ({ms} ms).",
|
|
138
|
+
"queryFailed": "Request failed — showing simulated data."
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -31,7 +31,7 @@ export function ProfileView() {
|
|
|
31
31
|
<Empty
|
|
32
32
|
icon={UserX}
|
|
33
33
|
iconVariant="chip"
|
|
34
|
-
title={t('profile.errorTitle', { fallback: 'Could
|
|
34
|
+
title={t('profile.errorTitle', { fallback: 'Could Not Load Profile' })}
|
|
35
35
|
description={profile.error ?? t('profile.errorHint', { fallback: '' })}
|
|
36
36
|
/>
|
|
37
37
|
);
|
|
@@ -44,7 +44,7 @@ export function ProfileView() {
|
|
|
44
44
|
<div className="flex flex-col gap-4">
|
|
45
45
|
<div>
|
|
46
46
|
<h2 className="text-lg font-semibold">
|
|
47
|
-
{t('profile.title', { fallback: 'Operator
|
|
47
|
+
{t('profile.title', { fallback: 'Operator Profile' })}
|
|
48
48
|
</h2>
|
|
49
49
|
<p className="text-muted-foreground text-xs">
|
|
50
50
|
{t('profile.description', { fallback: '' })}
|
|
@@ -56,15 +56,15 @@ export function ProfileView() {
|
|
|
56
56
|
<Avatar firstName={name} size="lg" />
|
|
57
57
|
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
58
58
|
<Row
|
|
59
|
-
label={t('profile.displayName', { fallback: 'Display
|
|
59
|
+
label={t('profile.displayName', { fallback: 'Display Name' })}
|
|
60
60
|
value={asString(data.display_name, unknown)}
|
|
61
61
|
/>
|
|
62
62
|
<Row
|
|
63
|
-
label={t('profile.firstName', { fallback: 'First
|
|
63
|
+
label={t('profile.firstName', { fallback: 'First Name' })}
|
|
64
64
|
value={asString(data.first_name, unknown)}
|
|
65
65
|
/>
|
|
66
66
|
<Row
|
|
67
|
-
label={t('profile.lastName', { fallback: 'Last
|
|
67
|
+
label={t('profile.lastName', { fallback: 'Last Name' })}
|
|
68
68
|
value={asString(data.last_name, unknown)}
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
@@ -81,7 +81,7 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
|
|
|
81
81
|
modal
|
|
82
82
|
showCloseOnHeader
|
|
83
83
|
width={440}
|
|
84
|
-
title={t('about.title', { fallback: 'About
|
|
84
|
+
title={t('about.title', { fallback: 'About This App' })}
|
|
85
85
|
>
|
|
86
86
|
<div className="flex flex-col items-center gap-4 px-2 pb-2">
|
|
87
87
|
<div className="flex flex-col items-center gap-2 py-2">
|
|
@@ -93,7 +93,7 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
|
|
|
93
93
|
|
|
94
94
|
{failed ? (
|
|
95
95
|
<p className="text-destructive text-sm">
|
|
96
|
-
{t('about.error', { fallback: 'Could not read app-manifest.json' })}
|
|
96
|
+
{t('about.error', { fallback: 'Could not read app-manifest.json.' })}
|
|
97
97
|
</p>
|
|
98
98
|
) : (
|
|
99
99
|
<div className="w-full">
|
|
@@ -121,7 +121,7 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
|
|
|
121
121
|
)}
|
|
122
122
|
|
|
123
123
|
<p className="text-muted-foreground text-center text-xs">
|
|
124
|
-
{t('about.source', { fallback: 'Read live from public/app-manifest.json' })}
|
|
124
|
+
{t('about.source', { fallback: 'Read live from public/app-manifest.json.' })}
|
|
125
125
|
</p>
|
|
126
126
|
</div>
|
|
127
127
|
</Dialog>
|
|
@@ -2,11 +2,11 @@ import type { SdkMenuRoot } from '@bardioc/app-sdk';
|
|
|
2
2
|
import { useCommandMenu, type AppCommand } from '@bardioc/app-sdk/react';
|
|
3
3
|
import {
|
|
4
4
|
BardiocLogoIcon,
|
|
5
|
+
Page,
|
|
5
6
|
SidebarBasic,
|
|
6
7
|
SidebarContainedProvider,
|
|
7
8
|
Toaster,
|
|
8
9
|
} from '@bardioc/ui/components';
|
|
9
|
-
import { ScrollArea } from '@bardioc/ui/shadcn';
|
|
10
10
|
import {
|
|
11
11
|
Activity,
|
|
12
12
|
LayoutDashboard,
|
|
@@ -68,7 +68,7 @@ export function AppShell() {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
notify(
|
|
71
|
-
t('notify.localeChanged', { fallback: 'Language switched to {locale}', params: { locale } }),
|
|
71
|
+
t('notify.localeChanged', { fallback: 'Language switched to {locale}.', params: { locale } }),
|
|
72
72
|
'info'
|
|
73
73
|
);
|
|
74
74
|
}, [locale, notify, t]);
|
|
@@ -86,7 +86,7 @@ export function AppShell() {
|
|
|
86
86
|
() => [
|
|
87
87
|
{
|
|
88
88
|
id: 'app.about',
|
|
89
|
-
title: t('menu.about', { fallback: 'About
|
|
89
|
+
title: t('menu.about', { fallback: 'About This App' }),
|
|
90
90
|
handler: () => setAboutOpen(true),
|
|
91
91
|
},
|
|
92
92
|
...NAV.map(entry => ({
|
|
@@ -121,6 +121,9 @@ export function AppShell() {
|
|
|
121
121
|
|
|
122
122
|
useCommandMenu(commands, menu);
|
|
123
123
|
|
|
124
|
+
const activeNav = NAV.find(entry => entry.id === view);
|
|
125
|
+
const pageTitle = activeNav ? t(activeNav.labelKey, { fallback: activeNav.id }) : undefined;
|
|
126
|
+
|
|
124
127
|
const header = (
|
|
125
128
|
<div className="flex items-center gap-2 px-2 py-1">
|
|
126
129
|
<BardiocLogoIcon className="size-5" />
|
|
@@ -132,7 +135,7 @@ export function AppShell() {
|
|
|
132
135
|
|
|
133
136
|
return (
|
|
134
137
|
<SidebarContainedProvider defaultCollapsed={false}>
|
|
135
|
-
<div className="bg-background text-foreground flex h-full min-h-0">
|
|
138
|
+
<div className="bg-background text-foreground @container/app flex h-full min-h-0">
|
|
136
139
|
<SidebarBasic
|
|
137
140
|
header={header}
|
|
138
141
|
rail={false}
|
|
@@ -144,14 +147,15 @@ export function AppShell() {
|
|
|
144
147
|
}}
|
|
145
148
|
className="border-r"
|
|
146
149
|
/>
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
<Page
|
|
151
|
+
title={pageTitle}
|
|
152
|
+
actions={<Header />}
|
|
153
|
+
actionsClassName="gap-2"
|
|
154
|
+
sticky
|
|
155
|
+
className="min-w-0"
|
|
156
|
+
>
|
|
157
|
+
<ViewContent view={view} />
|
|
158
|
+
</Page>
|
|
155
159
|
</div>
|
|
156
160
|
<AboutDialog open={aboutOpen} onOpenChange={setAboutOpen} />
|
|
157
161
|
<Toaster position="bottom-right" />
|
|
@@ -4,35 +4,35 @@ import { LocationSelect } from '../climate/location-select';
|
|
|
4
4
|
import { useDashboard } from '../dashboard-provider';
|
|
5
5
|
import { SUPPORTED_LOCALES, useI18n, useTranslation } from '../i18n';
|
|
6
6
|
|
|
7
|
+
// Goes into Page's `actions` slot, so it renders bare controls: Page owns the header row, its
|
|
8
|
+
// padding and its border, and lines all three up with the body below.
|
|
7
9
|
export function Header() {
|
|
8
10
|
const { t } = useTranslation();
|
|
9
11
|
const { state, syncing, refresh, setActiveStation } = useDashboard();
|
|
10
12
|
|
|
11
13
|
return (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onSelect={setActiveStation}
|
|
19
|
-
/>
|
|
20
|
-
)}
|
|
21
|
-
<ConnectedLanguageSwitcher
|
|
22
|
-
useI18n={useI18n}
|
|
23
|
-
supportedLocales={[...SUPPORTED_LOCALES]}
|
|
24
|
-
flags={{ en: 'EN', de: 'DE' }}
|
|
14
|
+
<>
|
|
15
|
+
{state?.stations && (
|
|
16
|
+
<LocationSelect
|
|
17
|
+
stations={state.stations}
|
|
18
|
+
value={state.activeStationId}
|
|
19
|
+
onSelect={setActiveStation}
|
|
25
20
|
/>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
)}
|
|
22
|
+
<ConnectedLanguageSwitcher
|
|
23
|
+
useI18n={useI18n}
|
|
24
|
+
supportedLocales={[...SUPPORTED_LOCALES]}
|
|
25
|
+
flags={{ en: 'EN', de: 'DE' }}
|
|
26
|
+
/>
|
|
27
|
+
<Button
|
|
28
|
+
className="gap-2"
|
|
29
|
+
size="sm"
|
|
30
|
+
onClick={() => void refresh()}
|
|
31
|
+
disabled={syncing || !state}
|
|
32
|
+
>
|
|
33
|
+
<RefreshCw className={`size-4 ${syncing ? 'animate-spin' : ''}`} />
|
|
34
|
+
{t('header.sync', { fallback: 'Sync' })}
|
|
35
|
+
</Button>
|
|
36
|
+
</>
|
|
37
37
|
);
|
|
38
38
|
}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "@bardioc/configs/typescript/react-library.json",
|
|
2
3
|
"compilerOptions": {
|
|
3
|
-
"
|
|
4
|
+
"declaration": false,
|
|
5
|
+
"declarationMap": false,
|
|
4
6
|
"module": "ESNext",
|
|
5
7
|
"moduleResolution": "Bundler",
|
|
6
|
-
"
|
|
7
|
-
"jsx": "react-jsx",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"noEmit": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"isolatedModules": true
|
|
8
|
+
"noEmit": true
|
|
15
9
|
},
|
|
16
10
|
"include": ["src"],
|
|
17
11
|
"exclude": ["node_modules", "dist"]
|