@bardioc/create-bardioc-app 0.5.6 → 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 -1
- package/package.json +1 -1
- package/templates/_base/README.md +1 -1
- package/templates/angular/.env.example +4 -1
- package/templates/angular/package.json +2 -2
- 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 +2 -28
- package/templates/angular/src/app/app.component.ts +5 -5
- package/templates/angular/src/styles.css +4 -0
- package/templates/nextjs/.env.example +4 -1
- package/templates/nextjs/app/page.tsx +11 -8
- package/templates/nextjs/next.config.ts +1 -1
- package/templates/nextjs/package.json +3 -3
- package/templates/nextjs/scripts/dev-env.mjs +41 -0
- package/templates/nextjs/scripts/dev.mjs +27 -0
- package/templates/preact/.env.example +3 -0
- package/templates/preact/package.json +2 -2
- package/templates/preact/src/App.tsx +5 -5
- package/templates/preact/vite.config.ts +4 -1
- package/templates/solid/.env.example +3 -0
- package/templates/solid/package.json +2 -2
- package/templates/solid/src/App.tsx +5 -5
- package/templates/solid/vite.config.ts +4 -1
- package/templates/svelte/.env.example +3 -0
- package/templates/svelte/package.json +2 -2
- package/templates/svelte/src/App.svelte +5 -5
- package/templates/svelte/vite.config.ts +4 -1
- package/templates/vite/.env.example +3 -0
- package/templates/vite/CLAUDE.md +34 -7
- package/templates/vite/package.json +3 -3
- package/templates/vite/src/auth/onboarding.tsx +4 -4
- 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/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/vite.config.ts +4 -1
- package/templates/vue/.env.example +3 -0
- package/templates/vue/package.json +2 -2
- package/templates/vue/src/App.vue +5 -5
- package/templates/vue/vite.config.ts +4 -1
- package/templates/angular/scripts/dev-auth-proxy.mjs +0 -17
- package/templates/nextjs/scripts/dev-auth-proxy.mjs +0 -17
|
@@ -3,9 +3,12 @@ import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
|
3
3
|
import tailwindcss from '@tailwindcss/vite';
|
|
4
4
|
import { defineConfig, loadEnv } from 'vite';
|
|
5
5
|
|
|
6
|
+
const DEFAULT_PORT = {{PORT}};
|
|
7
|
+
|
|
6
8
|
export default defineConfig(({ mode }) => {
|
|
7
9
|
const env = loadEnv(mode, __dirname, '');
|
|
8
10
|
const appName = env.VITE_APP_NAME?.trim();
|
|
11
|
+
const port = Number(env.LOCALHOST_PORT) || DEFAULT_PORT;
|
|
9
12
|
|
|
10
13
|
if (!appName) {
|
|
11
14
|
throw new Error('Missing VITE_APP_NAME for Vite config');
|
|
@@ -15,7 +18,7 @@ export default defineConfig(({ mode }) => {
|
|
|
15
18
|
plugins: [
|
|
16
19
|
bardiocDevAuth({ appName }),
|
|
17
20
|
svelte(),
|
|
18
|
-
bardiocApp({ appName, port
|
|
21
|
+
bardiocApp({ appName, port }),
|
|
19
22
|
tailwindcss(),
|
|
20
23
|
],
|
|
21
24
|
};
|
|
@@ -7,3 +7,6 @@ DEV_SECRET=
|
|
|
7
7
|
|
|
8
8
|
# Base URL of the Bardioc BFF / backend-for-frontend (e.g. https://your-host).
|
|
9
9
|
API_BASE_URL=
|
|
10
|
+
|
|
11
|
+
# By default, the app will start on port {{PORT}}. Feel free to uncomment the following variable and change it to match your local setup preferences.
|
|
12
|
+
#LOCALHOST_PORT={{PORT}}
|
package/templates/vite/CLAUDE.md
CHANGED
|
@@ -35,9 +35,9 @@ src/
|
|
|
35
35
|
auth/
|
|
36
36
|
onboarding.tsx # Empty full-page screen with the DEV_SECRET dev-auth steps
|
|
37
37
|
shell/
|
|
38
|
-
app-shell.tsx # Sidebar + view routing + Toaster + OS top-menu
|
|
38
|
+
app-shell.tsx # Sidebar + Page + view routing + Toaster + OS top-menu (useCommandMenu)
|
|
39
39
|
about-dialog.tsx # "About" Dialog — reads public/app-manifest.json at runtime (manifest example)
|
|
40
|
-
header.tsx # location combobox + language switcher + sync
|
|
40
|
+
header.tsx # Page actions: location combobox + language switcher + sync
|
|
41
41
|
climate/
|
|
42
42
|
types.ts # Station/ClimateState + METRICS config (icon, unit, color)
|
|
43
43
|
climate-data.ts # demo-data generator (replace with your real source)
|
|
@@ -87,9 +87,10 @@ live SDK data comes only when the app is opened inside the host, or when `DEV_SE
|
|
|
87
87
|
|
|
88
88
|
## UI — `@bardioc/ui`
|
|
89
89
|
|
|
90
|
-
Tailwind v4 + the Bardioc design system. **
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
Tailwind v4 + the Bardioc design system. **Before writing any UI, read
|
|
91
|
+
`node_modules/@bardioc/ui/docs/INDEX.md`** — component choice, design tokens, layout and spacing
|
|
92
|
+
patterns, and copy rules ship with the installed package, so they match its version. Theme is wired
|
|
93
|
+
in `src/index.css`:
|
|
93
94
|
|
|
94
95
|
```css
|
|
95
96
|
@import 'tailwindcss';
|
|
@@ -113,6 +114,7 @@ import {
|
|
|
113
114
|
Empty,
|
|
114
115
|
FilterDropdown,
|
|
115
116
|
Kbd,
|
|
117
|
+
Page,
|
|
116
118
|
SidebarBasic,
|
|
117
119
|
SidebarContainedProvider,
|
|
118
120
|
SortableColumnHeader,
|
|
@@ -126,10 +128,33 @@ import type { ChartConfig } from '@bardioc/ui/shadcn/chart';
|
|
|
126
128
|
import type { TReactTable } from '@bardioc/ui/types'; // TReactTable.ColumnDef<Row>
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
sidebar uses `SidebarBasic` inside a `SidebarContainedProvider` (the contained variant for
|
|
131
|
+
The sidebar uses `SidebarBasic` inside a `SidebarContainedProvider` (the contained variant for
|
|
131
132
|
OS windows); the language switcher is `ConnectedLanguageSwitcher useI18n={useI18n}`.
|
|
132
133
|
|
|
134
|
+
### Layout — the container contract
|
|
135
|
+
|
|
136
|
+
The app root opens `@container/app`; `Page` opens its own `@container/page` inside it. Keep both:
|
|
137
|
+
sizing that should follow the whole app reads `@container/app`, and anything inside the page column
|
|
138
|
+
— the page header included — reads `@container/page`, so it responds to the column rather than the
|
|
139
|
+
window.
|
|
140
|
+
|
|
141
|
+
`Page` is the scrolling column, a **sibling of the sidebar** — it owns the header, the scroller and
|
|
142
|
+
the padding that lines them up. Don't compose a header and a `ScrollArea` by hand, and don't wrap
|
|
143
|
+
the sidebar in it:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<div className="bg-background text-foreground @container/app flex h-full min-h-0">
|
|
147
|
+
<SidebarBasic … />
|
|
148
|
+
<Page title={pageTitle} actions={<Header />} actionsClassName="gap-2" sticky className="min-w-0">
|
|
149
|
+
<ViewContent view={view} />
|
|
150
|
+
</Page>
|
|
151
|
+
</div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Collapse your own `actions` against the same container (e.g. `@3xl/page:hidden`) so both sides agree
|
|
155
|
+
on what "wide enough" means. `sticky` keeps the header at the top while the body scrolls under it,
|
|
156
|
+
compacting once it does; drop it if you want the header to scroll away instead.
|
|
157
|
+
|
|
133
158
|
---
|
|
134
159
|
|
|
135
160
|
## i18n — `@bardioc/i18n-core`
|
|
@@ -150,6 +175,8 @@ t('overview.updated', { fallback: 'Updated {time}', params: { time } });
|
|
|
150
175
|
- Always pass a `fallback` so a missing key degrades gracefully.
|
|
151
176
|
- `@bardioc/ui` component strings ship with the `@bardioc/ui` package itself — the app only
|
|
152
177
|
carries its own `en/de.json`.
|
|
178
|
+
- Copy style (Title Case labels, sentence-case messages, American English):
|
|
179
|
+
`node_modules/@bardioc/ui/docs/copy.md`.
|
|
153
180
|
|
|
154
181
|
---
|
|
155
182
|
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
"release:publish": "changeset publish"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@bardioc/app-sdk": "^0.
|
|
21
|
+
"@bardioc/app-sdk": "^0.5.0",
|
|
22
22
|
"@bardioc/i18n-core": "^0.1.1",
|
|
23
|
-
"@bardioc/ui": "^0.
|
|
23
|
+
"@bardioc/ui": "^0.7.29",
|
|
24
24
|
"lucide-react": "^0.544.0",
|
|
25
25
|
"react": "19.2.6",
|
|
26
26
|
"react-dom": "19.2.6"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@bardioc/configs": "
|
|
29
|
+
"@bardioc/configs": "0.2.1",
|
|
30
30
|
"@changesets/cli": "^2.31.0",
|
|
31
31
|
"@tailwindcss/vite": "^4.3.0",
|
|
32
32
|
"@types/node": "^22.19.19",
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -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" />
|