@scalio-cloud/ui 0.6.2 → 0.8.0
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/CHANGELOG.md +14 -0
- package/README.md +34 -2
- package/nav.css +119 -0
- package/package.json +1 -1
- package/react/icons.js +26 -1
- package/react/index.js +1 -1
- package/react/labels.js +223 -0
- package/react/license.js +287 -0
- package/react/nav.js +8 -4
- package/react/security.js +327 -0
- package/react/support.js +336 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
- New `SecurityFactors` (profile: authenticator app, backup codes, passkeys) with `securityApi(api, webauthn)`, and
|
|
5
|
+
`LicensePanel` (status, usage, add-ons, key) with `licenseApi(api)` and `mapScalioLicense`. They replace the copies
|
|
6
|
+
in Citadel, Foundry, Rosetta and Nexus. `LABELS` get `mfa` and `license` texts, `Icons` get Smartphone, KeyRound,
|
|
7
|
+
Trash, Copy, Puzzle, Plus, Alert, and `.scalio-btn` gets the variants `is-primary`, `is-ghost`, `is-danger`.
|
|
8
|
+
**No visible change** until an app uses the new blocks; then they look the same in every app.
|
|
9
|
+
|
|
10
|
+
## 0.7.0
|
|
11
|
+
- `SupportDrawer` takes `client` (from the new `supportApi(api)`) and `page`: tabs "Neue Anfrage" (category,
|
|
12
|
+
subject, description, technical details) and "Meine Tickets" (list, answers from support, reply, rating) for the
|
|
13
|
+
central support desk (ADR 0022). New settings card `SupportConnection` (portal address and one-time code, status,
|
|
14
|
+
check, disconnect). `LABELS` get the support texts. **Visible change only for apps that pass `client`**; without
|
|
15
|
+
it the drawer keeps its single form.
|
|
16
|
+
|
|
3
17
|
## 0.6.2
|
|
4
18
|
- **Visible change for apps using `SettingsLayout`:** the settings area uses the full width of the window (was at most
|
|
5
19
|
64rem), so wide screens show more cards side by side. No change for other apps.
|
package/README.md
CHANGED
|
@@ -106,11 +106,16 @@ const apps = useScalioApps(() => api.get("/apps").then((r) => r.data)); // onc
|
|
|
106
106
|
>
|
|
107
107
|
<Routes>...</Routes>
|
|
108
108
|
</AppShell>
|
|
109
|
-
<SupportDrawer open={supportOpen} onClose={...} lang={lang}
|
|
110
|
-
onSubmit={(d) => api.post("/support/tickets", { ...d, url: location.pathname }).then(() => toast.success(...))}
|
|
109
|
+
<SupportDrawer open={supportOpen} onClose={...} lang={lang} client={supportApi(api)} page={location.pathname}
|
|
111
110
|
context={[["URL", location.pathname], [L.tenant, current?.name]]} />
|
|
112
111
|
```
|
|
113
112
|
|
|
113
|
+
With `client` the drawer has the tabs "Neue Anfrage" (category, subject, description, technical details) and
|
|
114
|
+
"Meine Tickets" (list, answers, reply, rating); tickets go to the central support desk in Compass (scalio-docs ADR
|
|
115
|
+
0022), the backend routes come from scalio-connect `scalio_connect.support.web`. `supportApi(api)` takes the app's
|
|
116
|
+
axios instance. The settings card `<SupportConnection client={supportApi(api)} lang={lang} />` connects the
|
|
117
|
+
installation with a one-time code (admins). Without `client` the drawer keeps the old single form with `onSubmit`.
|
|
118
|
+
|
|
114
119
|
`link` is the router's `NavLink` (it gets `to`, `end`, a `className` function and `onClick`); without it the
|
|
115
120
|
sidebar renders plain links and marks the item for `activePath`. Item test ids default to `nav-<path>`
|
|
116
121
|
(`nav-overview` for `/`), or set `testId`. Page blocks: `KpiTile` (tone `error` = red number),
|
|
@@ -184,6 +189,33 @@ import { AuthLayout, AuthHeader, MicrosoftButton, AuthDivider, AuthMessage, Prof
|
|
|
184
189
|
|
|
185
190
|
Form fields and buttons stay the app's own (shadcn), so they match the rest of the app.
|
|
186
191
|
|
|
192
|
+
## Two-factor card and license panel (React)
|
|
193
|
+
|
|
194
|
+
The same backend routes in every app, so the apps pass only their axios instance and a few helpers.
|
|
195
|
+
|
|
196
|
+
```jsx
|
|
197
|
+
import { SecurityFactors, securityApi, LicensePanel, licenseApi } from "@scalio-cloud/ui/react";
|
|
198
|
+
import { QRCodeSVG } from "qrcode.react";
|
|
199
|
+
import { startRegistration, browserSupportsWebAuthn } from "@simplewebauthn/browser";
|
|
200
|
+
|
|
201
|
+
const security = securityApi(api, { startRegistration, browserSupportsWebAuthn }); // once, outside the component
|
|
202
|
+
const license = licenseApi(api);
|
|
203
|
+
|
|
204
|
+
// Profile -> Sicherheit: authenticator app, backup codes, passkeys. Hidden for accounts without a password.
|
|
205
|
+
<SecurityFactors client={security} lang={lang} product="Scalio Citadel" email={user.email}
|
|
206
|
+
qr={(uri) => <QRCodeSVG value={uri} size={160} />} notify={(text, tone) => toast[tone](text)}
|
|
207
|
+
confirm={confirm} onMfaChange={(on) => setUser({ ...user, mfa_enabled: on })} />
|
|
208
|
+
|
|
209
|
+
// Einstellungen -> System -> Lizenz (scalio-license backend: /license, /license/addons)
|
|
210
|
+
<LicensePanel client={license} lang={lang} product="Citadel" kindLabel={licenseKindLabel}
|
|
211
|
+
featureLabel={licenseFeatureLabel} noFeatures="Nur Drift-Erkennung" notify={...} confirm={confirm} />
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`securityApi` reads snake_case and camelCase answers. `notify(text, tone)` gets `"success"` or `"error"`; without it
|
|
215
|
+
the blocks show the message themselves. `confirm` is the app's dialog (`{title, action, danger}` -> Promise<boolean>),
|
|
216
|
+
`window.confirm` otherwise. `formatError(err)` lets the app phrase API errors (for example license limit codes). A license backend with other routes passes its own `client` and a `map(license, {L})`
|
|
217
|
+
that returns the view model described in `react/license.js`.
|
|
218
|
+
|
|
187
219
|
## Installing
|
|
188
220
|
|
|
189
221
|
Public on npmjs.com, no token needed: `yarn add @scalio-cloud/ui` (pin the exact version).
|
package/nav.css
CHANGED
|
@@ -144,6 +144,33 @@
|
|
|
144
144
|
.scalio-support-title { margin: 0; font-family: 'Chivo', sans-serif; font-weight: 700; font-size: 1.125rem; text-transform: uppercase; }
|
|
145
145
|
.scalio-support-context { margin-bottom: 1rem; padding: 0.75rem; border: 1px solid hsl(var(--border)); border-radius: 2px; background: hsl(var(--muted)); font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; overflow-wrap: anywhere; }
|
|
146
146
|
.scalio-support-form { display: flex; flex-direction: column; gap: 0.375rem; }
|
|
147
|
+
.scalio-support-tabs { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
|
|
148
|
+
.scalio-support-tab { flex: 1; padding: 0 0 0.5rem; border: 0; border-bottom: 2px solid transparent; background: none; color: hsl(var(--muted-foreground)); font: inherit; font-size: 0.875rem; cursor: pointer; }
|
|
149
|
+
.scalio-support-tab.is-active { border-bottom-color: hsl(var(--primary)); color: hsl(var(--foreground)); font-weight: 600; }
|
|
150
|
+
.scalio-support-check { display: flex; align-items: flex-start; gap: 0.5rem; margin-top: 0.5rem; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
|
|
151
|
+
.scalio-support-check input { margin-top: 0.125rem; }
|
|
152
|
+
.scalio-support-list, .scalio-support-detail, .scalio-support-conn { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
153
|
+
.scalio-support-item { display: block; width: 100%; padding: 0.75rem; border: 1px solid hsl(var(--border)); border-radius: 2px; background: none; color: inherit; font: inherit; font-size: 0.875rem; text-align: left; cursor: pointer; }
|
|
154
|
+
.scalio-support-item:hover { background: hsl(var(--muted)); }
|
|
155
|
+
.scalio-support-item.is-waiting { border-style: dashed; cursor: default; }
|
|
156
|
+
.scalio-support-item-head { display: flex; justify-content: space-between; gap: 0.5rem; }
|
|
157
|
+
.scalio-support-item-title { margin: 0; font-weight: 600; }
|
|
158
|
+
.scalio-support-number { flex-shrink: 0; font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; color: hsl(var(--primary)); }
|
|
159
|
+
.scalio-support-meta { margin: 0; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
|
|
160
|
+
.scalio-support-empty { padding: 2rem 0; text-align: center; font-size: 0.875rem; color: hsl(var(--muted-foreground)); }
|
|
161
|
+
.scalio-support-back { align-self: flex-start; padding: 0; border: 0; background: none; color: hsl(var(--muted-foreground)); font: inherit; font-size: 0.75rem; cursor: pointer; }
|
|
162
|
+
.scalio-support-back:hover { color: hsl(var(--foreground)); }
|
|
163
|
+
.scalio-support-msg { margin: 0; padding: 0.75rem; border: 1px solid hsl(var(--border)); border-radius: 2px; font-size: 0.875rem; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
164
|
+
.scalio-support-msg.is-support { border-color: hsl(var(--primary) / 0.3); background: hsl(var(--primary) / 0.05); }
|
|
165
|
+
.scalio-support-stars { display: flex; gap: 0.25rem; }
|
|
166
|
+
.scalio-support-stars button { border: 0; background: none; font-size: 1.25rem; line-height: 1; color: #f59e0b; cursor: pointer; }
|
|
167
|
+
.scalio-support-conn { font-size: 0.875rem; }
|
|
168
|
+
.scalio-support-conn-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
169
|
+
.scalio-support-conn-facts { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 1rem; margin: 0; font-size: 0.75rem; }
|
|
170
|
+
.scalio-support-conn-facts dt { color: hsl(var(--muted-foreground)); }
|
|
171
|
+
.scalio-support-conn-facts dd { margin: 0; font-family: 'JetBrains Mono', monospace; overflow-wrap: anywhere; }
|
|
172
|
+
.scalio-support-conn-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
|
173
|
+
.scalio-btn-danger { color: hsl(var(--destructive)); }
|
|
147
174
|
.scalio-field-label { margin-top: 0.5rem; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: hsl(var(--muted-foreground)); }
|
|
148
175
|
.scalio-input {
|
|
149
176
|
width: 100%; height: 2.5rem; padding: 0 0.75rem; border: 1px solid hsl(var(--input)); border-radius: 2px;
|
|
@@ -243,3 +270,95 @@
|
|
|
243
270
|
.scalio-settings-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1rem; margin-bottom: 1.5rem; }
|
|
244
271
|
.scalio-settings-body { display: flex; flex-direction: column; }
|
|
245
272
|
.scalio-settings-section-footer { display: flex; justify-content: flex-end; gap: 0.5rem; padding-top: 0.25rem; }
|
|
273
|
+
|
|
274
|
+
/* Button variants for page blocks (base: .scalio-btn in shell.css) */
|
|
275
|
+
.scalio-btn.is-primary { border-color: hsl(var(--primary)); background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
|
|
276
|
+
.scalio-btn.is-primary:hover { background: hsl(var(--primary) / 0.9); }
|
|
277
|
+
.scalio-btn.is-ghost { border-color: transparent; background: transparent; color: hsl(var(--muted-foreground)); }
|
|
278
|
+
.scalio-btn.is-ghost:hover { color: hsl(var(--foreground)); background: transparent; }
|
|
279
|
+
.scalio-btn.is-danger { color: hsl(var(--destructive)); }
|
|
280
|
+
.scalio-btn.is-ghost.is-danger { color: hsl(var(--muted-foreground)); }
|
|
281
|
+
.scalio-btn.is-ghost.is-danger:hover { color: hsl(var(--destructive)); }
|
|
282
|
+
.scalio-btn:disabled { opacity: 0.6; cursor: default; }
|
|
283
|
+
|
|
284
|
+
/* Two-factor card (SecurityFactors) */
|
|
285
|
+
.scalio-security { display: flex; flex-direction: column; gap: 1.5rem; font-size: 0.875rem; }
|
|
286
|
+
.scalio-security-block { display: flex; flex-direction: column; gap: 0.75rem; }
|
|
287
|
+
.scalio-security-head { display: flex; align-items: flex-start; gap: 0.75rem; }
|
|
288
|
+
.scalio-security-head > .scalio-icon { margin-top: 0.125rem; flex-shrink: 0; color: hsl(var(--muted-foreground)); }
|
|
289
|
+
.scalio-security-title { font-weight: 600; font-size: 0.875rem; }
|
|
290
|
+
.scalio-security-hint { margin: 0; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
|
|
291
|
+
.scalio-security-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
|
292
|
+
.scalio-security .is-indented { padding-left: 1.75rem; }
|
|
293
|
+
.scalio-security-form, .scalio-security-confirm { display: flex; flex-direction: column; gap: 0.75rem; }
|
|
294
|
+
.scalio-security-confirm, .scalio-security-codes {
|
|
295
|
+
max-width: 28rem; padding: 1rem; border: 1px solid hsl(var(--border)); border-radius: 2px; background: hsl(var(--card));
|
|
296
|
+
display: flex; flex-direction: column; gap: 0.75rem;
|
|
297
|
+
}
|
|
298
|
+
.scalio-security-field { display: flex; flex-direction: column; gap: 0.25rem; max-width: 20rem; }
|
|
299
|
+
.scalio-security-field .scalio-field-label { margin-top: 0; }
|
|
300
|
+
.scalio-security-confirm .scalio-security-field { max-width: none; }
|
|
301
|
+
.scalio-security-code { font-family: 'JetBrains Mono', monospace; letter-spacing: 0.2em; }
|
|
302
|
+
.scalio-security-qr { display: inline-block; align-self: flex-start; padding: 0.75rem; border-radius: 2px; background: #fff; }
|
|
303
|
+
.scalio-security-secret { font-family: 'JetBrains Mono', monospace; overflow-wrap: anywhere; color: hsl(var(--foreground)); }
|
|
304
|
+
.scalio-security-code-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.25rem 1.5rem; font-family: 'JetBrains Mono', monospace; }
|
|
305
|
+
.scalio-security-keys { margin: 0; list-style: none; border-top: 1px solid hsl(var(--border)); border-bottom: 1px solid hsl(var(--border)); }
|
|
306
|
+
.scalio-security-keys li { display: flex; align-items: center; gap: 0.75rem; padding: 0.375rem 0; }
|
|
307
|
+
.scalio-security-keys li + li { border-top: 1px solid hsl(var(--border)); }
|
|
308
|
+
.scalio-security-key-name { flex: 1 1 auto; min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
|
|
309
|
+
.scalio-security-key-date { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: hsl(var(--muted-foreground)); }
|
|
310
|
+
.scalio-security-keys .scalio-btn { width: 2rem; height: 2rem; padding: 0; }
|
|
311
|
+
|
|
312
|
+
/* License panel (LicensePanel) */
|
|
313
|
+
.scalio-license { border: 1px solid hsl(var(--border)); border-radius: 2px; background: hsl(var(--card)); color: hsl(var(--card-foreground)); font-size: 0.875rem; }
|
|
314
|
+
.scalio-license-part { padding: 1.25rem; }
|
|
315
|
+
.scalio-license-part + .scalio-license-part { border-top: 1px solid hsl(var(--border)); }
|
|
316
|
+
.scalio-license-title { display: flex; align-items: center; gap: 0.5rem; font-family: 'Chivo', sans-serif; font-weight: 700; }
|
|
317
|
+
.scalio-license-title .scalio-icon { color: hsl(var(--primary)); }
|
|
318
|
+
.scalio-license-hint { margin: 0.25rem 0 0; color: hsl(var(--muted-foreground)); }
|
|
319
|
+
.scalio-license-messages { display: flex; flex-direction: column; gap: 0.25rem; margin: 1.25rem 1.25rem 0; }
|
|
320
|
+
.scalio-license-messages div { display: flex; gap: 0.5rem; }
|
|
321
|
+
.scalio-license-messages .scalio-icon { flex-shrink: 0; margin-top: 0.125rem; }
|
|
322
|
+
.scalio-license-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
|
|
323
|
+
@media (min-width: 768px) { .scalio-license-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
324
|
+
.scalio-license-status { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
|
|
325
|
+
.scalio-license-edition { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
|
|
326
|
+
.scalio-license-customer { font-weight: 600; }
|
|
327
|
+
.scalio-license-rows { display: flex; flex-direction: column; gap: 0.375rem; margin: 0; }
|
|
328
|
+
.scalio-license-rows > div { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
329
|
+
.scalio-license-rows dt { color: hsl(var(--muted-foreground)); white-space: nowrap; }
|
|
330
|
+
.scalio-license-rows dd { margin: 0; text-align: right; font-weight: 500; overflow-wrap: anywhere; min-width: 0; }
|
|
331
|
+
.scalio-license-rows dd.is-mono, .scalio-license-copy { font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; font-weight: 400; }
|
|
332
|
+
.scalio-license-copy { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0; border: 0; background: none; color: hsl(var(--muted-foreground)); cursor: pointer; text-align: right; }
|
|
333
|
+
.scalio-license-copy:hover { color: hsl(var(--primary)); }
|
|
334
|
+
.scalio-license .is-ok { color: #15803d; }
|
|
335
|
+
.scalio-license .is-warn { color: #b45309; }
|
|
336
|
+
.scalio-license .is-error { color: hsl(var(--destructive)); }
|
|
337
|
+
.dark .scalio-license .is-ok { color: #4ade80; }
|
|
338
|
+
.dark .scalio-license .is-warn { color: #fbbf24; }
|
|
339
|
+
.scalio-license-sub { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: hsl(var(--muted-foreground)); }
|
|
340
|
+
.scalio-license-features { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 0.125rem; }
|
|
341
|
+
.scalio-license-features li { display: flex; align-items: center; gap: 0.375rem; }
|
|
342
|
+
.scalio-license-features .scalio-icon { width: 14px; height: 14px; color: #16a34a; }
|
|
343
|
+
.scalio-license-usage { display: flex; flex-direction: column; gap: 1rem; padding: 1rem; border: 1px solid hsl(var(--border)); border-radius: 2px; background: hsl(var(--muted) / 0.4); }
|
|
344
|
+
.scalio-license-usage .scalio-license-sub { margin-bottom: 0; }
|
|
345
|
+
.scalio-license-bar-head { display: flex; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.25rem; font-size: 0.75rem; }
|
|
346
|
+
.scalio-license-bar-label { font-weight: 600; color: hsl(var(--muted-foreground)); }
|
|
347
|
+
.scalio-license-bar-label::first-letter { text-transform: uppercase; }
|
|
348
|
+
.scalio-license-bar-value { font-family: 'JetBrains Mono', monospace; color: hsl(var(--muted-foreground)); }
|
|
349
|
+
.scalio-license-bar-value.is-error { color: hsl(var(--destructive)); }
|
|
350
|
+
.scalio-license-bar { height: 6px; overflow: hidden; border-radius: 999px; background: hsl(var(--muted)); }
|
|
351
|
+
.scalio-license-bar > div { height: 100%; background: hsl(var(--primary)); }
|
|
352
|
+
.scalio-license-bar > div.is-unlimited { background: #10b981; }
|
|
353
|
+
.scalio-license-bar > div.is-full { background: hsl(var(--destructive)); }
|
|
354
|
+
.scalio-license-addons { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.5rem; margin-bottom: 1rem; }
|
|
355
|
+
@media (min-width: 640px) { .scalio-license-addons { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
|
356
|
+
.scalio-license-addon { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; padding: 0.625rem 0.75rem; border: 1px solid hsl(var(--border)); border-radius: 2px; }
|
|
357
|
+
.scalio-license-addon-name { font-weight: 600; }
|
|
358
|
+
.scalio-license-addon-detail { margin: 0.125rem 0 0; font-size: 0.75rem; color: hsl(var(--muted-foreground)); }
|
|
359
|
+
.scalio-license-addon .scalio-btn { width: 2rem; height: 2rem; padding: 0; flex-shrink: 0; }
|
|
360
|
+
.scalio-license-empty { margin: 0 0 1rem; color: hsl(var(--muted-foreground)); }
|
|
361
|
+
.scalio-license-form { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
362
|
+
.scalio-license-form textarea { font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; }
|
|
363
|
+
.scalio-license-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
|
364
|
+
.scalio-license-addon-error { margin-bottom: 0.5rem; font-size: 0.75rem; color: hsl(var(--destructive)); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalio-cloud/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Scalio Control Room design tokens, Tailwind preset, self-hosted fonts, the shared app header, sign-in layout, sidebar and page blocks",
|
|
5
5
|
"license": "SEE LICENSE IN README.md",
|
|
6
6
|
"private": false,
|
package/react/icons.js
CHANGED
|
@@ -70,4 +70,29 @@ const Activity = icon([
|
|
|
70
70
|
["path", { d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" }],
|
|
71
71
|
]);
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
const Smartphone = icon([["rect", { x: 5, y: 2, width: 14, height: 20, rx: 2, ry: 2 }], ["path", { d: "M12 18h.01" }]]);
|
|
74
|
+
const KeyRound = icon([
|
|
75
|
+
["path", { d: "M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" }],
|
|
76
|
+
["circle", { cx: 16.5, cy: 7.5, r: 0.5, fill: "currentColor" }],
|
|
77
|
+
]);
|
|
78
|
+
const Trash = icon([
|
|
79
|
+
["path", { d: "M3 6h18" }],
|
|
80
|
+
["path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }],
|
|
81
|
+
["path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }],
|
|
82
|
+
["path", { d: "M10 11v6M14 11v6" }],
|
|
83
|
+
]);
|
|
84
|
+
const Copy = icon([
|
|
85
|
+
["rect", { width: 14, height: 14, x: 8, y: 8, rx: 2, ry: 2 }],
|
|
86
|
+
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }],
|
|
87
|
+
]);
|
|
88
|
+
const Puzzle = icon([
|
|
89
|
+
["path", { d: "M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z" }],
|
|
90
|
+
]);
|
|
91
|
+
const Plus = icon([["path", { d: "M5 12h14M12 5v14" }]]);
|
|
92
|
+
const Alert = icon([
|
|
93
|
+
["path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }],
|
|
94
|
+
["path", { d: "M12 9v4M12 17h.01" }],
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
module.exports = { Sun, Moon, Monitor, LifeBuoy, LogOut, Menu, Check, X, Search, ChevronDown, Fingerprint, FileStack, ShieldCheck, Hammer, Activity,
|
|
98
|
+
Smartphone, KeyRound, Trash, Copy, Puzzle, Plus, Alert };
|
package/react/index.js
CHANGED
|
@@ -333,6 +333,6 @@ module.exports = {
|
|
|
333
333
|
TEST_IDS,
|
|
334
334
|
initials,
|
|
335
335
|
};
|
|
336
|
-
Object.assign(module.exports, Nav, Blocks, require("./settings"));
|
|
336
|
+
Object.assign(module.exports, Nav, Blocks, require("./settings"), require("./support"), require("./security"), require("./license"));
|
|
337
337
|
// After the exports above: auth.js uses LanguageToggle and ThemeToggle from this module.
|
|
338
338
|
Object.assign(module.exports, require("./auth"));
|
package/react/labels.js
CHANGED
|
@@ -59,6 +59,117 @@ const LABELS = {
|
|
|
59
59
|
send: "Anfrage senden",
|
|
60
60
|
sending: "Wird gesendet …",
|
|
61
61
|
close: "Schliessen",
|
|
62
|
+
supportNew: "Neue Anfrage",
|
|
63
|
+
supportMine: "Meine Tickets",
|
|
64
|
+
supportCategory: "Kategorie",
|
|
65
|
+
supportCategories: { question: "Frage", bug: "Fehler", wish: "Wunsch", license: "Lizenz", billing: "Rechnung" },
|
|
66
|
+
supportDiagnostics: "Technische Angaben mitsenden (Version, Lizenzstufe, Browser, letzte Fehler; keine Personendaten)",
|
|
67
|
+
supportSentAs: "Gesendet als {n}. Die Antwort sehen Sie unter «Meine Tickets» und per E-Mail.",
|
|
68
|
+
supportQueued: "Gespeichert. Die Anfrage wird gesendet, sobald das Support-Portal erreichbar ist.",
|
|
69
|
+
supportWaiting: "Wartet auf Versand",
|
|
70
|
+
supportNotSent: "Nicht gesendet. Bitte schreiben Sie an den Support.",
|
|
71
|
+
supportNotConnected: "Diese Installation ist noch nicht mit dem Support verbunden. Anfragen werden bis zu 3 Tage aufbewahrt.",
|
|
72
|
+
supportUnreachable: "Das Support-Portal ist gerade nicht erreichbar.",
|
|
73
|
+
supportNone: "Noch keine Tickets.",
|
|
74
|
+
supportFromSupport: "Scalio Support",
|
|
75
|
+
supportReply: "Antworten",
|
|
76
|
+
supportReplyPlaceholder: "Ihre Antwort",
|
|
77
|
+
supportReplySent: "Antwort gesendet.",
|
|
78
|
+
supportRate: "Wie zufrieden sind Sie mit der Lösung?",
|
|
79
|
+
supportThanks: "Danke für Ihre Bewertung.",
|
|
80
|
+
supportStatus: { new: "Neu", open: "Offen", in_progress: "In Bearbeitung", waiting: "Wartet auf Sie", waiting_customer: "Wartet auf Sie", resolved: "Gelöst", closed: "Geschlossen" },
|
|
81
|
+
supportConnStatus: { unpaired: "Nicht verbunden", pending: "Wartet auf Bestätigung", active: "Verbunden", blocked: "Gesperrt" },
|
|
82
|
+
supportConnHelp: "Tickets aus dieser Installation gehen an den Scalio Support. Den einmaligen Code erhalten Sie im Scalio-Portal unter Support -> Verbundene Apps.",
|
|
83
|
+
supportConnHelpConnected: "Tickets aus dieser Installation gehen an den Scalio Support.",
|
|
84
|
+
supportConnPending: "Ein Administrator Ihrer Organisation muss die Verbindung im Scalio-Portal bestätigen.",
|
|
85
|
+
supportConnBlocked: "Diese Installation ist für den Support gesperrt. Bitte wenden Sie sich an Scalio.",
|
|
86
|
+
supportConnKeyLost: "Der Schlüssel dieser Verbindung kann nicht mehr gelesen werden. Bitte verbinden Sie neu.",
|
|
87
|
+
supportPortal: "Support-Portal",
|
|
88
|
+
supportCode: "Einmaliger Code",
|
|
89
|
+
supportInstance: "Instanz",
|
|
90
|
+
supportSince: "Verbunden seit",
|
|
91
|
+
supportOutbox: "Warteschlange: {pending} wartend, {failed} nicht gesendet",
|
|
92
|
+
supportConnect: "Verbinden",
|
|
93
|
+
supportConnected: "Verbindung hergestellt.",
|
|
94
|
+
supportCheck: "Verbindung prüfen",
|
|
95
|
+
supportChecked: "Verbindung geprüft.",
|
|
96
|
+
supportRemove: "Trennen",
|
|
97
|
+
supportRemoveConfirm: "Verbindung zum Support trennen? Neue Tickets warten dann, bis wieder verbunden ist.",
|
|
98
|
+
supportRemoved: "Verbindung getrennt.",
|
|
99
|
+
// Two-factor card (SecurityFactors) and license panel (LicensePanel)
|
|
100
|
+
mfa: {
|
|
101
|
+
backupTitle: "Ihre Backup-Codes",
|
|
102
|
+
backupHint: "Jeder Code funktioniert einmal, falls Sie Ihr Telefon nicht zur Hand haben. Bewahren Sie die Codes sicher auf, zum Beispiel im Passwort-Manager. Sie werden nur jetzt angezeigt.",
|
|
103
|
+
copy: "Kopieren",
|
|
104
|
+
copied: "Kopiert",
|
|
105
|
+
download: "Herunterladen",
|
|
106
|
+
saved: "Ich habe die Codes gespeichert",
|
|
107
|
+
app: "Authenticator-App",
|
|
108
|
+
on: "Aktiv. Die Anmeldung fragt nach einem Code. Noch {n} Backup-Codes.",
|
|
109
|
+
off: "Aus. Mit einer App wie Microsoft Authenticator fragt die Anmeldung zusätzlich nach einem 6-stelligen Code.",
|
|
110
|
+
newCodes: "Neue Backup-Codes",
|
|
111
|
+
turnOff: "Ausschalten",
|
|
112
|
+
turnOffTitle: "Authenticator-App ausschalten",
|
|
113
|
+
turnedOff: "Authenticator-App ausgeschaltet",
|
|
114
|
+
setUp: "Einrichten",
|
|
115
|
+
setUpTitle: "Authenticator-App einrichten",
|
|
116
|
+
scan: "Scannen Sie den QR-Code mit Ihrer Authenticator-App und geben Sie dann den angezeigten Code ein.",
|
|
117
|
+
key: "Oder geben Sie diesen Schlüssel ein:",
|
|
118
|
+
code: "Code",
|
|
119
|
+
activate: "Aktivieren",
|
|
120
|
+
passkeys: "Passkeys",
|
|
121
|
+
passkeysHint: "Anmelden mit Face ID, Touch ID, Windows Hello oder einem Sicherheitsschlüssel, ohne Passwort.",
|
|
122
|
+
addPasskey: "Passkey hinzufügen",
|
|
123
|
+
passkeyAdded: "Passkey hinzugefügt",
|
|
124
|
+
device: "Name des Geräts",
|
|
125
|
+
deviceExample: "z. B. MacBook",
|
|
126
|
+
used: "genutzt {date}",
|
|
127
|
+
added: "hinzugefügt {date}",
|
|
128
|
+
remove: "Entfernen",
|
|
129
|
+
removePasskey: "Passkey {name} entfernen?",
|
|
130
|
+
currentPassword: "Aktuelles Passwort",
|
|
131
|
+
verifying: "Wird geprüft …",
|
|
132
|
+
continue: "Weiter",
|
|
133
|
+
cancel: "Abbrechen",
|
|
134
|
+
},
|
|
135
|
+
license: {
|
|
136
|
+
title: "Lizenz",
|
|
137
|
+
hint: "Lizenzen kaufen und verlängern Sie in Scalio Compass. Der Schlüssel gilt nur für die Installations-ID unten.",
|
|
138
|
+
managed: "Die Lizenz dieser Instanz wird von Ihrem Anbieter verwaltet.",
|
|
139
|
+
status: "Status",
|
|
140
|
+
active: "Aktiv",
|
|
141
|
+
limited: "Eingeschränkt",
|
|
142
|
+
trialEnds: "Test endet",
|
|
143
|
+
maintenanceUntil: "Wartung bis",
|
|
144
|
+
never: "nie",
|
|
145
|
+
installationId: "Installations-ID",
|
|
146
|
+
idCopied: "Installations-ID kopiert",
|
|
147
|
+
copy: "Kopieren",
|
|
148
|
+
copyFailed: "Kopieren fehlgeschlagen",
|
|
149
|
+
domains: "Domains",
|
|
150
|
+
coverage: "Abdeckung dieser Version",
|
|
151
|
+
covered: "Abgedeckt",
|
|
152
|
+
grace: "Kulanzfrist bis {date}",
|
|
153
|
+
notCovered: "Nicht abgedeckt (schreibgeschützt)",
|
|
154
|
+
buildDate: "Build-Datum",
|
|
155
|
+
features: "Funktionen",
|
|
156
|
+
noFeatures: "Nur die Grundfunktionen",
|
|
157
|
+
usage: "Nutzung",
|
|
158
|
+
addons: "Add-ons",
|
|
159
|
+
included: "Enthalten",
|
|
160
|
+
noAddons: "Keine Add-ons.",
|
|
161
|
+
addonPlaceholder: "Add-on-Schlüssel einfügen",
|
|
162
|
+
add: "Hinzufügen",
|
|
163
|
+
addonActivated: "Add-on aktiviert",
|
|
164
|
+
addonRemoved: "Add-on entfernt",
|
|
165
|
+
key: "Lizenzschlüssel",
|
|
166
|
+
keyPlaceholder: "Lizenzschlüssel einfügen",
|
|
167
|
+
activate: "Aktivieren",
|
|
168
|
+
activated: "Lizenz aktiviert",
|
|
169
|
+
remove: "Entfernen",
|
|
170
|
+
removed: "Lizenzschlüssel entfernt",
|
|
171
|
+
removeConfirm: "Lizenzschlüssel entfernen? {product} läuft danach als Community-Edition.",
|
|
172
|
+
},
|
|
62
173
|
// States
|
|
63
174
|
loading: "Wird geladen …",
|
|
64
175
|
empty: "Noch keine Einträge.",
|
|
@@ -130,6 +241,116 @@ const LABELS = {
|
|
|
130
241
|
send: "Send request",
|
|
131
242
|
sending: "Sending …",
|
|
132
243
|
close: "Close",
|
|
244
|
+
supportNew: "New request",
|
|
245
|
+
supportMine: "My tickets",
|
|
246
|
+
supportCategory: "Category",
|
|
247
|
+
supportCategories: { question: "Question", bug: "Bug", wish: "Wish", license: "License", billing: "Billing" },
|
|
248
|
+
supportDiagnostics: "Include technical details (version, license tier, browser, recent errors; no personal data)",
|
|
249
|
+
supportSentAs: "Sent as {n}. You will see the answer under “My tickets” and by email.",
|
|
250
|
+
supportQueued: "Saved. The request will be sent as soon as the support portal is reachable.",
|
|
251
|
+
supportWaiting: "Waiting to be sent",
|
|
252
|
+
supportNotSent: "Not sent. Please write to support.",
|
|
253
|
+
supportNotConnected: "This installation is not connected to support yet. Requests are kept for up to 3 days.",
|
|
254
|
+
supportUnreachable: "The support portal is not reachable right now.",
|
|
255
|
+
supportNone: "No tickets yet.",
|
|
256
|
+
supportFromSupport: "Scalio Support",
|
|
257
|
+
supportReply: "Reply",
|
|
258
|
+
supportReplyPlaceholder: "Your reply",
|
|
259
|
+
supportReplySent: "Reply sent.",
|
|
260
|
+
supportRate: "How satisfied are you with the solution?",
|
|
261
|
+
supportThanks: "Thank you for your rating.",
|
|
262
|
+
supportStatus: { new: "New", open: "Open", in_progress: "In progress", waiting: "Waiting for you", waiting_customer: "Waiting for you", resolved: "Resolved", closed: "Closed" },
|
|
263
|
+
supportConnStatus: { unpaired: "Not connected", pending: "Waiting for confirmation", active: "Connected", blocked: "Blocked" },
|
|
264
|
+
supportConnHelp: "Tickets from this installation go to Scalio Support. You get the one-time code in the Scalio portal under Support -> Connected apps.",
|
|
265
|
+
supportConnHelpConnected: "Tickets from this installation go to Scalio Support.",
|
|
266
|
+
supportConnPending: "An administrator of your organisation must confirm the connection in the Scalio portal.",
|
|
267
|
+
supportConnBlocked: "This installation is blocked for support. Please contact Scalio.",
|
|
268
|
+
supportConnKeyLost: "The key of this connection can no longer be read. Please connect again.",
|
|
269
|
+
supportPortal: "Support portal",
|
|
270
|
+
supportCode: "One-time code",
|
|
271
|
+
supportInstance: "Instance",
|
|
272
|
+
supportSince: "Connected since",
|
|
273
|
+
supportOutbox: "Queue: {pending} waiting, {failed} not sent",
|
|
274
|
+
supportConnect: "Connect",
|
|
275
|
+
supportConnected: "Connected.",
|
|
276
|
+
supportCheck: "Check connection",
|
|
277
|
+
supportChecked: "Connection checked.",
|
|
278
|
+
supportRemove: "Disconnect",
|
|
279
|
+
supportRemoveConfirm: "Disconnect from support? New tickets then wait until the instance is connected again.",
|
|
280
|
+
supportRemoved: "Disconnected.",
|
|
281
|
+
mfa: {
|
|
282
|
+
backupTitle: "Your backup codes",
|
|
283
|
+
backupHint: "Each code works once, if you do not have your phone. Keep them in a safe place, for example your password manager. They are shown only now.",
|
|
284
|
+
copy: "Copy",
|
|
285
|
+
copied: "Copied",
|
|
286
|
+
download: "Download",
|
|
287
|
+
saved: "I have saved the codes",
|
|
288
|
+
app: "Authenticator app",
|
|
289
|
+
on: "On. Sign-in asks for a code. {n} backup codes left.",
|
|
290
|
+
off: "Off. With an app such as Microsoft Authenticator, sign-in also asks for a 6-digit code.",
|
|
291
|
+
newCodes: "New backup codes",
|
|
292
|
+
turnOff: "Turn off",
|
|
293
|
+
turnOffTitle: "Turn off authenticator app",
|
|
294
|
+
turnedOff: "Authenticator app turned off",
|
|
295
|
+
setUp: "Set up",
|
|
296
|
+
setUpTitle: "Set up authenticator app",
|
|
297
|
+
scan: "Scan the QR code with your authenticator app, then enter the code it shows.",
|
|
298
|
+
key: "Or enter this key:",
|
|
299
|
+
code: "Code",
|
|
300
|
+
activate: "Activate",
|
|
301
|
+
passkeys: "Passkeys",
|
|
302
|
+
passkeysHint: "Sign in with Face ID, Touch ID, Windows Hello or a security key, without a password.",
|
|
303
|
+
addPasskey: "Add passkey",
|
|
304
|
+
passkeyAdded: "Passkey added",
|
|
305
|
+
device: "Name of the device",
|
|
306
|
+
deviceExample: "e.g. MacBook",
|
|
307
|
+
used: "used {date}",
|
|
308
|
+
added: "added {date}",
|
|
309
|
+
remove: "Remove",
|
|
310
|
+
removePasskey: "Remove passkey {name}?",
|
|
311
|
+
currentPassword: "Current password",
|
|
312
|
+
verifying: "Verifying …",
|
|
313
|
+
continue: "Continue",
|
|
314
|
+
cancel: "Cancel",
|
|
315
|
+
},
|
|
316
|
+
license: {
|
|
317
|
+
title: "License",
|
|
318
|
+
hint: "Buy or extend a license in Scalio Compass. The key is bound to the installation ID below.",
|
|
319
|
+
managed: "The license of this instance is managed by your provider.",
|
|
320
|
+
status: "Status",
|
|
321
|
+
active: "Active",
|
|
322
|
+
limited: "Limited",
|
|
323
|
+
trialEnds: "Trial ends",
|
|
324
|
+
maintenanceUntil: "Maintenance until",
|
|
325
|
+
never: "never",
|
|
326
|
+
installationId: "Installation ID",
|
|
327
|
+
idCopied: "Installation ID copied",
|
|
328
|
+
copy: "Copy",
|
|
329
|
+
copyFailed: "Copy failed",
|
|
330
|
+
domains: "Domains",
|
|
331
|
+
coverage: "Build coverage",
|
|
332
|
+
covered: "Covered",
|
|
333
|
+
grace: "Grace period until {date}",
|
|
334
|
+
notCovered: "Not covered (read-only)",
|
|
335
|
+
buildDate: "Build date",
|
|
336
|
+
features: "Features",
|
|
337
|
+
noFeatures: "Basic features only",
|
|
338
|
+
usage: "Usage",
|
|
339
|
+
addons: "Add-ons",
|
|
340
|
+
included: "Included",
|
|
341
|
+
noAddons: "No add-ons.",
|
|
342
|
+
addonPlaceholder: "Paste an add-on key",
|
|
343
|
+
add: "Add",
|
|
344
|
+
addonActivated: "Add-on activated",
|
|
345
|
+
addonRemoved: "Add-on removed",
|
|
346
|
+
key: "License key",
|
|
347
|
+
keyPlaceholder: "Paste the license key",
|
|
348
|
+
activate: "Activate",
|
|
349
|
+
activated: "License activated",
|
|
350
|
+
remove: "Remove",
|
|
351
|
+
removed: "License key removed",
|
|
352
|
+
removeConfirm: "Remove the license key? {product} then runs as Community edition.",
|
|
353
|
+
},
|
|
133
354
|
loading: "Loading …",
|
|
134
355
|
empty: "No entries yet.",
|
|
135
356
|
sections: {
|
|
@@ -156,6 +377,8 @@ function labelsFor(lang, overrides) {
|
|
|
156
377
|
sections: Object.assign({}, base.sections, o.sections),
|
|
157
378
|
sectionHints: Object.assign({}, base.sectionHints, o.sectionHints),
|
|
158
379
|
settingsGroups: Object.assign({}, base.settingsGroups, o.settingsGroups),
|
|
380
|
+
mfa: Object.assign({}, base.mfa, o.mfa),
|
|
381
|
+
license: Object.assign({}, base.license, o.license),
|
|
159
382
|
});
|
|
160
383
|
}
|
|
161
384
|
|