From 112247351066447412cc24a1c1fe96840db7e61b Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Fri, 5 Jun 2026 17:50:50 +0800 Subject: [PATCH 1/7] Refactor developer dashboard widgets --- addon/components/widget/api-traffic.hbs | 1 + addon/components/widget/api-traffic.js | 5 + .../components/widget/developer-activity.hbs | 30 ++ addon/components/widget/developer-activity.js | 46 +++ addon/components/widget/developers-chart.hbs | 22 ++ addon/components/widget/developers-chart.js | 77 +++++ .../components/widget/developers-kpi-tile.hbs | 24 ++ .../components/widget/developers-kpi-tile.js | 79 +++++ addon/components/widget/endpoint-health.hbs | 30 ++ addon/components/widget/endpoint-health.js | 42 +++ addon/components/widget/event-stream.hbs | 35 ++ addon/components/widget/event-stream.js | 38 +++ .../components/widget/kpi-active-api-keys.hbs | 1 + .../components/widget/kpi-active-api-keys.js | 5 + .../components/widget/kpi-active-webhooks.hbs | 1 + .../components/widget/kpi-active-webhooks.js | 5 + .../components/widget/kpi-api-error-rate.hbs | 1 + addon/components/widget/kpi-api-error-rate.js | 5 + addon/components/widget/kpi-api-latency.hbs | 1 + addon/components/widget/kpi-api-latency.js | 5 + addon/components/widget/kpi-api-requests.hbs | 1 + addon/components/widget/kpi-api-requests.js | 5 + .../components/widget/kpi-events-emitted.hbs | 1 + addon/components/widget/kpi-events-emitted.js | 5 + .../widget/kpi-webhook-failures.hbs | 1 + .../components/widget/kpi-webhook-failures.js | 5 + .../components/widget/kpi-webhook-success.hbs | 1 + .../components/widget/kpi-webhook-success.js | 5 + addon/components/widget/quick-resources.hbs | 17 + addon/components/widget/quick-resources.js | 11 + addon/components/widget/webhook-delivery.hbs | 1 + addon/components/widget/webhook-delivery.js | 5 + addon/extension.js | 169 ++++++++- addon/styles/dev-engine.css | 322 +++++++++++++++++- addon/templates/home/index.hbs | 17 +- app/components/widget/api-traffic.js | 1 + app/components/widget/developer-activity.js | 1 + app/components/widget/developers-chart.js | 1 + app/components/widget/developers-kpi-tile.js | 1 + app/components/widget/endpoint-health.js | 1 + app/components/widget/event-stream.js | 1 + app/components/widget/kpi-active-api-keys.js | 1 + app/components/widget/kpi-active-webhooks.js | 1 + app/components/widget/kpi-api-error-rate.js | 1 + app/components/widget/kpi-api-latency.js | 1 + app/components/widget/kpi-api-requests.js | 1 + app/components/widget/kpi-events-emitted.js | 1 + app/components/widget/kpi-webhook-failures.js | 1 + app/components/widget/kpi-webhook-success.js | 1 + app/components/widget/quick-resources.js | 1 + app/components/widget/webhook-delivery.js | 1 + .../widget/developers-kpi-tile-test.js | 36 ++ .../components/widget/quick-resources-test.js | 17 + translations/ar-ae.yaml | 33 ++ translations/bg-bg.yaml | 33 ++ translations/en-us.yaml | 35 +- translations/es-es.yaml | 33 ++ translations/fr-fr.yaml | 33 ++ translations/mn-mn.yaml | 33 ++ translations/pt-br.yaml | 33 ++ translations/ru-ru.yaml | 33 ++ translations/vi-vn.yaml | 33 ++ translations/zh-cn.yaml | 33 ++ 63 files changed, 1411 insertions(+), 9 deletions(-) create mode 100644 addon/components/widget/api-traffic.hbs create mode 100644 addon/components/widget/api-traffic.js create mode 100644 addon/components/widget/developer-activity.hbs create mode 100644 addon/components/widget/developer-activity.js create mode 100644 addon/components/widget/developers-chart.hbs create mode 100644 addon/components/widget/developers-chart.js create mode 100644 addon/components/widget/developers-kpi-tile.hbs create mode 100644 addon/components/widget/developers-kpi-tile.js create mode 100644 addon/components/widget/endpoint-health.hbs create mode 100644 addon/components/widget/endpoint-health.js create mode 100644 addon/components/widget/event-stream.hbs create mode 100644 addon/components/widget/event-stream.js create mode 100644 addon/components/widget/kpi-active-api-keys.hbs create mode 100644 addon/components/widget/kpi-active-api-keys.js create mode 100644 addon/components/widget/kpi-active-webhooks.hbs create mode 100644 addon/components/widget/kpi-active-webhooks.js create mode 100644 addon/components/widget/kpi-api-error-rate.hbs create mode 100644 addon/components/widget/kpi-api-error-rate.js create mode 100644 addon/components/widget/kpi-api-latency.hbs create mode 100644 addon/components/widget/kpi-api-latency.js create mode 100644 addon/components/widget/kpi-api-requests.hbs create mode 100644 addon/components/widget/kpi-api-requests.js create mode 100644 addon/components/widget/kpi-events-emitted.hbs create mode 100644 addon/components/widget/kpi-events-emitted.js create mode 100644 addon/components/widget/kpi-webhook-failures.hbs create mode 100644 addon/components/widget/kpi-webhook-failures.js create mode 100644 addon/components/widget/kpi-webhook-success.hbs create mode 100644 addon/components/widget/kpi-webhook-success.js create mode 100644 addon/components/widget/quick-resources.hbs create mode 100644 addon/components/widget/quick-resources.js create mode 100644 addon/components/widget/webhook-delivery.hbs create mode 100644 addon/components/widget/webhook-delivery.js create mode 100644 app/components/widget/api-traffic.js create mode 100644 app/components/widget/developer-activity.js create mode 100644 app/components/widget/developers-chart.js create mode 100644 app/components/widget/developers-kpi-tile.js create mode 100644 app/components/widget/endpoint-health.js create mode 100644 app/components/widget/event-stream.js create mode 100644 app/components/widget/kpi-active-api-keys.js create mode 100644 app/components/widget/kpi-active-webhooks.js create mode 100644 app/components/widget/kpi-api-error-rate.js create mode 100644 app/components/widget/kpi-api-latency.js create mode 100644 app/components/widget/kpi-api-requests.js create mode 100644 app/components/widget/kpi-events-emitted.js create mode 100644 app/components/widget/kpi-webhook-failures.js create mode 100644 app/components/widget/kpi-webhook-success.js create mode 100644 app/components/widget/quick-resources.js create mode 100644 app/components/widget/webhook-delivery.js create mode 100644 tests/integration/components/widget/developers-kpi-tile-test.js create mode 100644 tests/integration/components/widget/quick-resources-test.js diff --git a/addon/components/widget/api-traffic.hbs b/addon/components/widget/api-traffic.hbs new file mode 100644 index 0000000..c73c1ca --- /dev/null +++ b/addon/components/widget/api-traffic.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/api-traffic.js b/addon/components/widget/api-traffic.js new file mode 100644 index 0000000..815054a --- /dev/null +++ b/addon/components/widget/api-traffic.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetApiTrafficComponent extends Component { + widget = 'api-traffic'; +} diff --git a/addon/components/widget/developer-activity.hbs b/addon/components/widget/developer-activity.hbs new file mode 100644 index 0000000..3805512 --- /dev/null +++ b/addon/components/widget/developer-activity.hbs @@ -0,0 +1,30 @@ +
+
+
+
{{t "developers.component.widget.dashboard.developer-activity.title"}}
+
{{t "developers.component.widget.dashboard.developer-activity.subtitle"}}
+
+ +
+
+ {{#if this.load.isRunning}} +
+ {{else if this.error}} +
{{this.error}}
+ {{else if this.items.length}} + {{#each this.items as |item|}} +
+
+
+
{{item.label}}
+
{{item.type}} / {{item.status}}
+
+
+ {{/each}} + {{else}} +
{{t "developers.component.widget.dashboard.empty"}}
+ {{/if}} +
+
diff --git a/addon/components/widget/developer-activity.js b/addon/components/widget/developer-activity.js new file mode 100644 index 0000000..6a9cced --- /dev/null +++ b/addon/components/widget/developer-activity.js @@ -0,0 +1,46 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { task } from 'ember-concurrency'; + +export default class WidgetDeveloperActivityComponent extends Component { + @service fetch; + + @tracked data = null; + @tracked error = null; + + constructor() { + super(...arguments); + this.load.perform(); + } + + get items() { + return this.data?.items ?? []; + } + + iconFor(type) { + if (type === 'webhook') { + return 'webhook'; + } + + if (type === 'event') { + return 'bolt'; + } + + return 'terminal'; + } + + @task *load() { + try { + this.data = yield this.fetch.get('metrics/dev/activity', { limit: 14 }); + this.error = null; + } catch (error) { + this.error = error?.message ?? 'Unable to load activity'; + } + } + + @action refresh() { + this.load.perform(); + } +} diff --git a/addon/components/widget/developers-chart.hbs b/addon/components/widget/developers-chart.hbs new file mode 100644 index 0000000..4194167 --- /dev/null +++ b/addon/components/widget/developers-chart.hbs @@ -0,0 +1,22 @@ +
+
+
+
{{@title}}
+
{{@subtitle}}
+
+ +
+
+ {{#if this.load.isRunning}} +
+ {{else if this.error}} +
{{this.error}}
+ {{else}} +
+ +
+ {{/if}} +
+
diff --git a/addon/components/widget/developers-chart.js b/addon/components/widget/developers-chart.js new file mode 100644 index 0000000..1f0891b --- /dev/null +++ b/addon/components/widget/developers-chart.js @@ -0,0 +1,77 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { task } from 'ember-concurrency'; + +const COLORS = [ + ['#2563eb', 'rgba(37, 99, 235, 0.15)'], + ['#10b981', 'rgba(16, 185, 129, 0.15)'], + ['#ef4444', 'rgba(239, 68, 68, 0.15)'], + ['#f59e0b', 'rgba(245, 158, 11, 0.15)'], +]; + +export default class WidgetDevelopersChartComponent extends Component { + @service fetch; + + @tracked data = null; + @tracked error = null; + + constructor() { + super(...arguments); + this.load.perform(); + } + + get labels() { + return this.data?.labels ?? []; + } + + get datasets() { + return (this.data?.datasets ?? []).map((dataset, index) => { + const color = COLORS[index % COLORS.length]; + + return { + ...dataset, + borderColor: color[0], + backgroundColor: color[1], + borderWidth: 2, + fill: true, + tension: 0.35, + pointRadius: 0, + pointHoverRadius: 4, + }; + }); + } + + get chartOptions() { + return { + responsive: true, + maintainAspectRatio: false, + interaction: { mode: 'index', intersect: false }, + plugins: { + legend: { + display: true, + position: 'top', + labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8 }, + }, + }, + scales: { + x: { grid: { display: false }, ticks: { maxRotation: 0 } }, + y: { beginAtZero: true, grid: { color: 'rgba(148, 163, 184, 0.16)' } }, + }, + }; + } + + @task *load() { + try { + this.data = yield this.fetch.get(`metrics/dev/${this.args.endpoint}`, { period: this.args.period ?? '30d' }); + this.error = null; + } catch (error) { + this.error = error?.message ?? 'Unable to load chart'; + } + } + + @action refresh() { + this.load.perform(); + } +} diff --git a/addon/components/widget/developers-kpi-tile.hbs b/addon/components/widget/developers-kpi-tile.hbs new file mode 100644 index 0000000..b77d8fd --- /dev/null +++ b/addon/components/widget/developers-kpi-tile.hbs @@ -0,0 +1,24 @@ +
+
+
+
+
{{this.title}}
+ {{#if this.error}} +
{{this.error}}
+ {{else if this.load.isRunning}} +
+ {{else}} +
{{this.value}}
+ {{/if}} +
+ +
+ +
+ {{or @footnote (t "developers.component.widget.dashboard.period")}} + +
+
+
diff --git a/addon/components/widget/developers-kpi-tile.js b/addon/components/widget/developers-kpi-tile.js new file mode 100644 index 0000000..1e6628b --- /dev/null +++ b/addon/components/widget/developers-kpi-tile.js @@ -0,0 +1,79 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { task } from 'ember-concurrency'; + +export default class WidgetDevelopersKpiTileComponent extends Component { + @service fetch; + + @tracked data = null; + @tracked error = null; + + constructor() { + super(...arguments); + this.load.perform(); + } + + get metric() { + return this.data?.metrics?.[this.args.metric] ?? {}; + } + + get title() { + return this.args.title ?? this.metric.label ?? 'Metric'; + } + + get value() { + const value = this.metric.value ?? 0; + + if (this.metric.format === 'percent') { + return `${value}%`; + } + + if (this.metric.format === 'duration') { + return `${Number(value).toLocaleString()}ms`; + } + + return Number(value).toLocaleString(); + } + + get deltaText() { + const delta = this.metric.delta_percent; + + if (typeof delta !== 'number') { + return 'Current'; + } + + return `${delta > 0 ? '+' : ''}${delta}%`; + } + + get deltaStatus() { + const delta = this.metric.delta_percent; + + if (typeof delta !== 'number' || delta === 0) { + return 'info'; + } + + const positive = delta > 0; + const isGood = this.metric.inverse ? !positive : positive; + + return isGood ? 'success' : 'danger'; + } + + get accentClass() { + return `developers-kpi-accent-${this.args.accent ?? 'blue'}`; + } + + @task *load() { + try { + this.data = yield this.fetch.get('metrics/dev/kpis', { period: this.args.period ?? '30d' }); + this.error = null; + } catch (error) { + this.error = error?.message ?? 'Unable to load metric'; + } + } + + @action refresh() { + this.load.perform(); + } +} diff --git a/addon/components/widget/endpoint-health.hbs b/addon/components/widget/endpoint-health.hbs new file mode 100644 index 0000000..4db9ccd --- /dev/null +++ b/addon/components/widget/endpoint-health.hbs @@ -0,0 +1,30 @@ +
+
+
+
{{t "developers.component.widget.dashboard.endpoint-health.title"}}
+
{{t "developers.component.widget.dashboard.endpoint-health.subtitle"}}
+
+ +
+
+ {{#if this.load.isRunning}} +
+ {{else if this.error}} +
{{this.error}}
+ {{else if this.items.length}} + {{#each this.items as |item|}} +
+
+
{{item.url}}
+
{{item.status}} / {{item.mode}} / {{item.deliveries}} deliveries
+
+
{{item.success_rate}}%
+
+ {{/each}} + {{else}} +
{{t "developers.component.widget.dashboard.empty"}}
+ {{/if}} +
+
diff --git a/addon/components/widget/endpoint-health.js b/addon/components/widget/endpoint-health.js new file mode 100644 index 0000000..8f65eac --- /dev/null +++ b/addon/components/widget/endpoint-health.js @@ -0,0 +1,42 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { task } from 'ember-concurrency'; + +export default class WidgetEndpointHealthComponent extends Component { + @service fetch; + + @tracked data = null; + @tracked error = null; + + constructor() { + super(...arguments); + this.load.perform(); + } + + get items() { + return this.data?.items ?? []; + } + + statusClass(item) { + if (item.failures > 0 || item.success_rate < 95) { + return 'text-rose-600 dark:text-rose-300'; + } + + return 'text-emerald-600 dark:text-emerald-300'; + } + + @task *load() { + try { + this.data = yield this.fetch.get('metrics/dev/endpoint-health', { period: this.args.period ?? '30d' }); + this.error = null; + } catch (error) { + this.error = error?.message ?? 'Unable to load endpoints'; + } + } + + @action refresh() { + this.load.perform(); + } +} diff --git a/addon/components/widget/event-stream.hbs b/addon/components/widget/event-stream.hbs new file mode 100644 index 0000000..b7207be --- /dev/null +++ b/addon/components/widget/event-stream.hbs @@ -0,0 +1,35 @@ +
+
+
+
{{t "developers.component.widget.dashboard.event-stream.title"}}
+
{{t "developers.component.widget.dashboard.event-stream.subtitle"}}
+
+ +
+
+ {{#if this.load.isRunning}} +
+ {{else if this.error}} +
{{this.error}}
+ {{else}} +
{{t "developers.component.widget.dashboard.event-stream.types"}}
+ {{#each this.types as |item|}} +
+
{{item.label}}
+
{{item.value}}
+
+ {{else}} +
{{t "developers.component.widget.dashboard.empty"}}
+ {{/each}} +
{{t "developers.component.widget.dashboard.event-stream.sources"}}
+ {{#each this.sources as |item|}} +
+
{{item.label}}
+
{{item.value}}
+
+ {{/each}} + {{/if}} +
+
diff --git a/addon/components/widget/event-stream.js b/addon/components/widget/event-stream.js new file mode 100644 index 0000000..360cea6 --- /dev/null +++ b/addon/components/widget/event-stream.js @@ -0,0 +1,38 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { task } from 'ember-concurrency'; + +export default class WidgetEventStreamComponent extends Component { + @service fetch; + + @tracked data = null; + @tracked error = null; + + constructor() { + super(...arguments); + this.load.perform(); + } + + get types() { + return this.data?.types ?? []; + } + + get sources() { + return this.data?.sources ?? []; + } + + @task *load() { + try { + this.data = yield this.fetch.get('metrics/dev/events', { period: this.args.period ?? '30d' }); + this.error = null; + } catch (error) { + this.error = error?.message ?? 'Unable to load events'; + } + } + + @action refresh() { + this.load.perform(); + } +} diff --git a/addon/components/widget/kpi-active-api-keys.hbs b/addon/components/widget/kpi-active-api-keys.hbs new file mode 100644 index 0000000..43aa579 --- /dev/null +++ b/addon/components/widget/kpi-active-api-keys.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-active-api-keys.js b/addon/components/widget/kpi-active-api-keys.js new file mode 100644 index 0000000..94bd09a --- /dev/null +++ b/addon/components/widget/kpi-active-api-keys.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiActiveApiKeysComponent extends Component { + widget = 'active-api-keys'; +} diff --git a/addon/components/widget/kpi-active-webhooks.hbs b/addon/components/widget/kpi-active-webhooks.hbs new file mode 100644 index 0000000..72b67c0 --- /dev/null +++ b/addon/components/widget/kpi-active-webhooks.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-active-webhooks.js b/addon/components/widget/kpi-active-webhooks.js new file mode 100644 index 0000000..0d6b2d5 --- /dev/null +++ b/addon/components/widget/kpi-active-webhooks.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiActiveWebhooksComponent extends Component { + widget = 'active-webhooks'; +} diff --git a/addon/components/widget/kpi-api-error-rate.hbs b/addon/components/widget/kpi-api-error-rate.hbs new file mode 100644 index 0000000..eb08c2b --- /dev/null +++ b/addon/components/widget/kpi-api-error-rate.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-api-error-rate.js b/addon/components/widget/kpi-api-error-rate.js new file mode 100644 index 0000000..e7a9a4f --- /dev/null +++ b/addon/components/widget/kpi-api-error-rate.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiApiErrorRateComponent extends Component { + widget = 'api-error-rate'; +} diff --git a/addon/components/widget/kpi-api-latency.hbs b/addon/components/widget/kpi-api-latency.hbs new file mode 100644 index 0000000..e64160e --- /dev/null +++ b/addon/components/widget/kpi-api-latency.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-api-latency.js b/addon/components/widget/kpi-api-latency.js new file mode 100644 index 0000000..d015781 --- /dev/null +++ b/addon/components/widget/kpi-api-latency.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiApiLatencyComponent extends Component { + widget = 'api-latency'; +} diff --git a/addon/components/widget/kpi-api-requests.hbs b/addon/components/widget/kpi-api-requests.hbs new file mode 100644 index 0000000..ba1c2b2 --- /dev/null +++ b/addon/components/widget/kpi-api-requests.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-api-requests.js b/addon/components/widget/kpi-api-requests.js new file mode 100644 index 0000000..fdfadac --- /dev/null +++ b/addon/components/widget/kpi-api-requests.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiApiRequestsComponent extends Component { + widget = 'api-requests'; +} diff --git a/addon/components/widget/kpi-events-emitted.hbs b/addon/components/widget/kpi-events-emitted.hbs new file mode 100644 index 0000000..875244c --- /dev/null +++ b/addon/components/widget/kpi-events-emitted.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-events-emitted.js b/addon/components/widget/kpi-events-emitted.js new file mode 100644 index 0000000..654c23b --- /dev/null +++ b/addon/components/widget/kpi-events-emitted.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiEventsEmittedComponent extends Component { + widget = 'events-emitted'; +} diff --git a/addon/components/widget/kpi-webhook-failures.hbs b/addon/components/widget/kpi-webhook-failures.hbs new file mode 100644 index 0000000..816a4b2 --- /dev/null +++ b/addon/components/widget/kpi-webhook-failures.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-webhook-failures.js b/addon/components/widget/kpi-webhook-failures.js new file mode 100644 index 0000000..b8b991f --- /dev/null +++ b/addon/components/widget/kpi-webhook-failures.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiWebhookFailuresComponent extends Component { + widget = 'webhook-failures'; +} diff --git a/addon/components/widget/kpi-webhook-success.hbs b/addon/components/widget/kpi-webhook-success.hbs new file mode 100644 index 0000000..ec8c427 --- /dev/null +++ b/addon/components/widget/kpi-webhook-success.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/kpi-webhook-success.js b/addon/components/widget/kpi-webhook-success.js new file mode 100644 index 0000000..e59987c --- /dev/null +++ b/addon/components/widget/kpi-webhook-success.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetKpiWebhookSuccessComponent extends Component { + widget = 'webhook-success'; +} diff --git a/addon/components/widget/quick-resources.hbs b/addon/components/widget/quick-resources.hbs new file mode 100644 index 0000000..dfac2ea --- /dev/null +++ b/addon/components/widget/quick-resources.hbs @@ -0,0 +1,17 @@ +
+
+
+
{{t "developers.component.widget.dashboard.quick-resources.title"}}
+
{{t "developers.component.widget.dashboard.quick-resources.subtitle"}}
+
+
+
+ {{#each this.resources as |resource|}} + + + {{resource.label}} + + + {{/each}} +
+
diff --git a/addon/components/widget/quick-resources.js b/addon/components/widget/quick-resources.js new file mode 100644 index 0000000..a953adc --- /dev/null +++ b/addon/components/widget/quick-resources.js @@ -0,0 +1,11 @@ +import Component from '@glimmer/component'; + +export default class WidgetQuickResourcesComponent extends Component { + resources = [ + { label: 'API Keys', route: 'api-keys.index', icon: 'key' }, + { label: 'Webhooks', route: 'webhooks.index', icon: 'webhook' }, + { label: 'Logs', route: 'logs.index', icon: 'terminal' }, + { label: 'Events', route: 'events.index', icon: 'bolt' }, + { label: 'Sockets', route: 'sockets.index', icon: 'plug' }, + ]; +} diff --git a/addon/components/widget/webhook-delivery.hbs b/addon/components/widget/webhook-delivery.hbs new file mode 100644 index 0000000..31c604a --- /dev/null +++ b/addon/components/widget/webhook-delivery.hbs @@ -0,0 +1 @@ + diff --git a/addon/components/widget/webhook-delivery.js b/addon/components/widget/webhook-delivery.js new file mode 100644 index 0000000..0a2de4e --- /dev/null +++ b/addon/components/widget/webhook-delivery.js @@ -0,0 +1,5 @@ +import Component from '@glimmer/component'; + +export default class WidgetWebhookDeliveryComponent extends Component { + widget = 'webhook-delivery'; +} diff --git a/addon/extension.js b/addon/extension.js index 96f9627..9b57219 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -44,19 +44,180 @@ export default { ], }); - // Register widgets + // Register dashboard and widgets const widgets = [ + new Widget({ + id: 'developers-kpi-api-requests', + name: 'API Requests', + description: 'Total API requests for the current period.', + icon: 'code', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-api-requests'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-api-error-rate', + name: 'API Error Rate', + description: 'Percentage of API requests returning an error.', + icon: 'triangle-exclamation', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-api-error-rate'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-api-latency', + name: 'Avg API Latency', + description: 'Average API response duration for the current period.', + icon: 'gauge-high', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-api-latency'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-webhook-success', + name: 'Webhook Success Rate', + description: 'Percentage of webhook deliveries returning a 2xx response.', + icon: 'webhook', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-webhook-success'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-active-api-keys', + name: 'Active API Keys', + description: 'Active API credentials in this organization.', + icon: 'key', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-active-api-keys'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-active-webhooks', + name: 'Active Webhooks', + description: 'Enabled webhook endpoints in this organization.', + icon: 'plug-circle-check', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-active-webhooks'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-webhook-failures', + name: 'Webhook Failures', + description: 'Failed webhook deliveries for the current period.', + icon: 'webhook', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-webhook-failures'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-kpi-events-emitted', + name: 'Events Emitted', + description: 'Platform events emitted in the current period.', + icon: 'bolt', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/kpi-events-emitted'), + grid_options: { w: 3, h: 4, minW: 3, minH: 4 }, + category: 'KPI Tiles', + default: true, + }), + new Widget({ + id: 'developers-api-traffic', + name: 'API Traffic', + description: 'API request volume, successes, and errors over time.', + icon: 'chart-line', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/api-traffic'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Analytics', + default: true, + }), + new Widget({ + id: 'developers-webhook-delivery', + name: 'Webhook Delivery Health', + description: 'Webhook delivery volume, success, failure, and retry health.', + icon: 'chart-column', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/webhook-delivery'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Analytics', + default: true, + }), + new Widget({ + id: 'developers-endpoint-health', + name: 'Endpoint Health', + description: 'Webhook endpoint delivery health and recent failures.', + icon: 'heart-pulse', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/endpoint-health'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Operations', + default: true, + }), + new Widget({ + id: 'developers-event-stream', + name: 'Event Stream', + description: 'Top event types and event sources in the selected period.', + icon: 'bolt', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/event-stream'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Operations', + default: true, + }), + new Widget({ + id: 'developers-activity', + name: 'Developer Activity', + description: 'Recent API requests, webhook deliveries, and events.', + icon: 'timeline', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/developer-activity'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Operations', + default: true, + }), + new Widget({ + id: 'developers-quick-resources', + name: 'Quick Resources', + description: 'Shortcuts into API keys, webhooks, logs, events, and sockets.', + icon: 'link', + component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/quick-resources'), + grid_options: { w: 6, h: 8, minW: 5, minH: 7 }, + category: 'Resources', + default: true, + }), new Widget({ id: 'dev-api-metrics-widget', - name: 'Developer API Metrics', - description: 'Key metrics from API Usage.', + name: 'Developer API Metrics (Legacy)', + description: 'Legacy grouped monitoring widget. Replaced by individual developer dashboard widgets.', icon: 'code', component: new ExtensionComponent('@fleetbase/dev-engine', 'widget/api-metrics'), grid_options: { w: 12, h: 12, minW: 8, minH: 12 }, options: { title: 'API Metrics' }, + category: 'Legacy', + default: false, }), ]; - widgetService.registerWidgets('dashboard', widgets); + const getWidgetById = (id = null, mutate = null) => { + if (!id) return null; + const widget = widgets.find((w) => w.id === id); + const clone = new Widget(widget.toObject()); + if (typeof mutate === 'function') { + mutate(clone); + } + return clone; + }; + + widgetService.registerDashboard('developers'); + widgetService.registerWidgets('developers', widgets); + widgetService.registerWidgets('dashboard', [ + getWidgetById('developers-kpi-api-error-rate'), + getWidgetById('developers-kpi-api-latency'), + getWidgetById('developers-kpi-webhook-success'), + getWidgetById('developers-api-traffic', (widget) => { + widget.withGridOptions({ w: 6, h: 7, minW: 5, minH: 6 }); + }), + ]); }, }; diff --git a/addon/styles/dev-engine.css b/addon/styles/dev-engine.css index 99f2e9e..4a66599 100644 --- a/addon/styles/dev-engine.css +++ b/addon/styles/dev-engine.css @@ -49,4 +49,324 @@ body[data-theme='dark'] .webhook-attempts-date-filter-container > .date-filter-l position: relative; height: 100%; width: 100%; -} \ No newline at end of file +} + +.developers-dashboard-widget { + min-width: 0; + border-color: #e5e7eb; + background-color: #fff; + animation: developers-widget-fade-up 320ms cubic-bezier(0.16, 1, 0.3, 1) both; + transition: + border-color 180ms ease, + box-shadow 180ms ease; +} + +.developers-dashboard-widget:hover { + border-color: #cbd5e1; + box-shadow: 0 10px 28px -22px rgb(15 23 42 / 45%); +} + +body[data-theme='dark'] .developers-dashboard-widget { + border-color: #374151; + background-color: #1f2937; +} + +@keyframes developers-widget-fade-up { + from { + opacity: 0; + transform: translateY(8px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.developers-dashboard-page { + display: flex; + width: 100%; + align-items: center; + justify-content: space-between; + gap: 1rem; + border-bottom: 1px solid #e5e7eb; + background-color: #fff; + padding: 1rem; + box-shadow: 0 1px 2px rgb(15 23 42 / 6%); +} + +body[data-theme='dark'] .developers-dashboard-page { + border-bottom-color: #1f2937; + background-color: #111827; + box-shadow: 0 1px 2px rgb(2 6 23 / 30%); +} + +.developers-dashboard-title h1 { + color: #111827; + font-size: 1.05rem; + font-weight: 800; + line-height: 1.2; +} + +body[data-theme='dark'] .developers-dashboard-title h1 { + color: #f9fafb; +} + +.developers-dashboard-actions { + justify-content: flex-end; +} + +.developers-dashboard-create-wrapper { + padding: 1rem; +} + +.developers-widget-header { + display: flex; + min-height: 3.25rem; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + border-bottom: 1px solid #e5e7eb; + padding: 0.75rem; +} + +body[data-theme='dark'] .developers-widget-header { + border-bottom-color: #374151; +} + +.developers-widget-title { + overflow: hidden; + color: #111827; + font-size: 0.82rem; + font-weight: 800; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; +} + +body[data-theme='dark'] .developers-widget-title { + color: #f9fafb; +} + +.developers-widget-subtitle, +.developers-list-subtitle { + overflow: hidden; + color: #6b7280; + font-size: 0.69rem; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +body[data-theme='dark'] .developers-widget-subtitle, +body[data-theme='dark'] .developers-list-subtitle { + color: #9ca3af; +} + +.developers-widget-icon-button, +.developers-kpi-icon { + width: 1.75rem; + height: 1.75rem; + flex: 0 0 1.75rem; + color: #64748b; +} + +.developers-widget-icon-button:hover, +.developers-kpi-icon:hover { + color: #111827; +} + +body[data-theme='dark'] .developers-widget-icon-button:hover, +body[data-theme='dark'] .developers-kpi-icon:hover { + color: #f9fafb; +} + +.developers-kpi-label { + color: #6b7280; + font-size: 0.625rem; + font-weight: 800; + line-height: 1.15; + text-transform: uppercase; +} + +body[data-theme='dark'] .developers-kpi-label { + color: #9ca3af; +} + +.developers-kpi-value { + color: #111827; + font-size: 1.45rem; + font-weight: 800; + line-height: 1; +} + +body[data-theme='dark'] .developers-kpi-value { + color: #f9fafb; +} + +.developers-kpi-delta { + max-width: 5.5rem; +} + +.developers-kpi-accent-blue { + background-image: linear-gradient(135deg, rgb(37 99 235 / 12%) 0%, rgb(37 99 235 / 0%) 64%); +} + +.developers-kpi-accent-rose { + background-image: linear-gradient(135deg, rgb(225 29 72 / 12%) 0%, rgb(225 29 72 / 0%) 64%); +} + +.developers-kpi-accent-amber { + background-image: linear-gradient(135deg, rgb(245 158 11 / 14%) 0%, rgb(245 158 11 / 0%) 64%); +} + +.developers-kpi-accent-emerald { + background-image: linear-gradient(135deg, rgb(16 185 129 / 12%) 0%, rgb(16 185 129 / 0%) 64%); +} + +.developers-kpi-accent-indigo { + background-image: linear-gradient(135deg, rgb(79 70 229 / 12%) 0%, rgb(79 70 229 / 0%) 64%); +} + +.developers-kpi-accent-cyan { + background-image: linear-gradient(135deg, rgb(6 182 212 / 12%) 0%, rgb(6 182 212 / 0%) 64%); +} + +.developers-chart-widget { + overflow: hidden; +} + +.developers-chart-body { + display: flex; + min-height: 0; + min-width: 0; + flex: 1 1 auto; + align-items: stretch; + justify-content: stretch; + padding: 0.75rem; +} + +.developers-chart-frame { + position: relative; + min-height: 0; + width: 100%; + flex: 1 1 auto; +} + +.developers-dashboard-widget .ui-chart { + position: relative; + width: 100%; + height: 100%; + min-height: 0; +} + +.developers-dashboard-widget .ui-chart > canvas { + position: absolute; + inset: 0; + width: 100% !important; + height: 100% !important; +} + +.developers-widget-scroll-body { + min-height: 0; + flex: 1 1 auto; + overflow-y: auto; + padding: 0.5rem; +} + +.developers-list-row, +.developers-activity-row, +.developers-resource-link { + display: flex; + min-width: 0; + align-items: center; + gap: 0.65rem; + border-radius: 0.375rem; + padding: 0.5rem; +} + +.developers-resource-link { + color: #111827; + font-size: 0.78rem; + font-weight: 700; +} + +.developers-list-row + .developers-list-row, +.developers-activity-row + .developers-activity-row, +.developers-resource-link + .developers-resource-link { + margin-top: 0.25rem; +} + +.developers-list-row:hover, +.developers-activity-row:hover, +.developers-resource-link:hover { + background-color: #f8fafc; +} + +body[data-theme='dark'] .developers-resource-link { + color: #f9fafb; +} + +body[data-theme='dark'] .developers-list-row:hover, +body[data-theme='dark'] .developers-activity-row:hover, +body[data-theme='dark'] .developers-resource-link:hover { + background-color: #111827; +} + +.developers-list-title { + min-width: 0; + overflow: hidden; + color: #111827; + font-size: 0.75rem; + font-weight: 700; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +body[data-theme='dark'] .developers-list-title { + color: #f9fafb; +} + +.developers-list-metric { + margin-left: auto; + flex: 0 0 auto; + font-size: 0.78rem; + font-weight: 800; +} + +.developers-activity-icon { + display: flex; + width: 1.75rem; + height: 1.75rem; + flex: 0 0 1.75rem; + align-items: center; + justify-content: center; + border-radius: 0.375rem; + background-color: #eff6ff; + color: #2563eb; + font-size: 0.75rem; +} + +body[data-theme='dark'] .developers-activity-icon { + background-color: rgb(37 99 235 / 16%); + color: #93c5fd; +} + +.developers-mini-section { + padding: 0.25rem 0.5rem; + color: #64748b; + font-size: 0.62rem; + font-weight: 800; + text-transform: uppercase; +} + +.developers-empty-state { + display: flex; + min-height: 5rem; + align-items: center; + justify-content: center; + color: #64748b; + font-size: 0.75rem; + font-weight: 600; +} diff --git a/addon/templates/home/index.hbs b/addon/templates/home/index.hbs index 8cfcee8..1f2674f 100644 --- a/addon/templates/home/index.hbs +++ b/addon/templates/home/index.hbs @@ -1,3 +1,14 @@ - - - \ No newline at end of file + + + + {{outlet}} + diff --git a/app/components/widget/api-traffic.js b/app/components/widget/api-traffic.js new file mode 100644 index 0000000..5783957 --- /dev/null +++ b/app/components/widget/api-traffic.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/api-traffic'; diff --git a/app/components/widget/developer-activity.js b/app/components/widget/developer-activity.js new file mode 100644 index 0000000..0682b8e --- /dev/null +++ b/app/components/widget/developer-activity.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/developer-activity'; diff --git a/app/components/widget/developers-chart.js b/app/components/widget/developers-chart.js new file mode 100644 index 0000000..8f41481 --- /dev/null +++ b/app/components/widget/developers-chart.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/developers-chart'; diff --git a/app/components/widget/developers-kpi-tile.js b/app/components/widget/developers-kpi-tile.js new file mode 100644 index 0000000..7d7fcd8 --- /dev/null +++ b/app/components/widget/developers-kpi-tile.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/developers-kpi-tile'; diff --git a/app/components/widget/endpoint-health.js b/app/components/widget/endpoint-health.js new file mode 100644 index 0000000..28c5c53 --- /dev/null +++ b/app/components/widget/endpoint-health.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/endpoint-health'; diff --git a/app/components/widget/event-stream.js b/app/components/widget/event-stream.js new file mode 100644 index 0000000..6f1cf54 --- /dev/null +++ b/app/components/widget/event-stream.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/event-stream'; diff --git a/app/components/widget/kpi-active-api-keys.js b/app/components/widget/kpi-active-api-keys.js new file mode 100644 index 0000000..1c56d7e --- /dev/null +++ b/app/components/widget/kpi-active-api-keys.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-active-api-keys'; diff --git a/app/components/widget/kpi-active-webhooks.js b/app/components/widget/kpi-active-webhooks.js new file mode 100644 index 0000000..eebe9f2 --- /dev/null +++ b/app/components/widget/kpi-active-webhooks.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-active-webhooks'; diff --git a/app/components/widget/kpi-api-error-rate.js b/app/components/widget/kpi-api-error-rate.js new file mode 100644 index 0000000..822aae7 --- /dev/null +++ b/app/components/widget/kpi-api-error-rate.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-api-error-rate'; diff --git a/app/components/widget/kpi-api-latency.js b/app/components/widget/kpi-api-latency.js new file mode 100644 index 0000000..4ab224a --- /dev/null +++ b/app/components/widget/kpi-api-latency.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-api-latency'; diff --git a/app/components/widget/kpi-api-requests.js b/app/components/widget/kpi-api-requests.js new file mode 100644 index 0000000..c2435bd --- /dev/null +++ b/app/components/widget/kpi-api-requests.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-api-requests'; diff --git a/app/components/widget/kpi-events-emitted.js b/app/components/widget/kpi-events-emitted.js new file mode 100644 index 0000000..3cf0b0a --- /dev/null +++ b/app/components/widget/kpi-events-emitted.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-events-emitted'; diff --git a/app/components/widget/kpi-webhook-failures.js b/app/components/widget/kpi-webhook-failures.js new file mode 100644 index 0000000..176b96d --- /dev/null +++ b/app/components/widget/kpi-webhook-failures.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-webhook-failures'; diff --git a/app/components/widget/kpi-webhook-success.js b/app/components/widget/kpi-webhook-success.js new file mode 100644 index 0000000..81e46f6 --- /dev/null +++ b/app/components/widget/kpi-webhook-success.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/kpi-webhook-success'; diff --git a/app/components/widget/quick-resources.js b/app/components/widget/quick-resources.js new file mode 100644 index 0000000..c54a738 --- /dev/null +++ b/app/components/widget/quick-resources.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/quick-resources'; diff --git a/app/components/widget/webhook-delivery.js b/app/components/widget/webhook-delivery.js new file mode 100644 index 0000000..6f0d8dc --- /dev/null +++ b/app/components/widget/webhook-delivery.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/components/widget/webhook-delivery'; diff --git a/tests/integration/components/widget/developers-kpi-tile-test.js b/tests/integration/components/widget/developers-kpi-tile-test.js new file mode 100644 index 0000000..1f17b45 --- /dev/null +++ b/tests/integration/components/widget/developers-kpi-tile-test.js @@ -0,0 +1,36 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import Service from '@ember/service'; +import { hbs } from 'ember-cli-htmlbars'; + +class FetchStub extends Service { + get() { + return Promise.resolve({ + metrics: { + api_requests: { + label: 'API Requests', + value: 42, + format: 'count', + delta_percent: 12, + }, + }, + }); + } +} + +module('Integration | Component | widget/developers-kpi-tile', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.owner.register('service:fetch', FetchStub); + }); + + test('it renders a dashboard KPI metric', async function (assert) { + await render(hbs``); + + assert.dom('.developers-kpi-tile').exists(); + assert.dom('.developers-kpi-label').hasText('API Requests'); + assert.dom('.developers-kpi-value').hasText('42'); + }); +}); diff --git a/tests/integration/components/widget/quick-resources-test.js b/tests/integration/components/widget/quick-resources-test.js new file mode 100644 index 0000000..c1a5824 --- /dev/null +++ b/tests/integration/components/widget/quick-resources-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'dummy/tests/helpers'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | widget/quick-resources', function (hooks) { + setupRenderingTest(hooks); + + test('it renders developer resource shortcuts', async function (assert) { + await render(hbs``); + + assert.dom('.developers-dashboard-widget').exists(); + assert.dom('.developers-resource-link').exists({ count: 5 }); + assert.dom('.developers-resource-link').includesText('API Keys'); + assert.dom('.developers-resource-link').includesText('Webhooks'); + }); +}); diff --git a/translations/ar-ae.yaml b/translations/ar-ae.yaml index 0a18a1b..2f15983 100644 --- a/translations/ar-ae.yaml +++ b/translations/ar-ae.yaml @@ -148,6 +148,39 @@ developers: metrics: date-created: تاريخ الإنشاء widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: المراقبة api-requests: طلبات API diff --git a/translations/bg-bg.yaml b/translations/bg-bg.yaml index b465094..84db0bf 100644 --- a/translations/bg-bg.yaml +++ b/translations/bg-bg.yaml @@ -156,6 +156,39 @@ developers: metrics: date-created: Дата на създаване widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Мониторинг api-requests: API заявки diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 32bdc6f..505aed4 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -138,6 +138,39 @@ developers: metrics: date-created: Date Created widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Monitoring api-requests: API Requests @@ -249,4 +282,4 @@ developers: webhooks: Webhooks websockets: WebSockets logs: Logs - events: Events \ No newline at end of file + events: Events diff --git a/translations/es-es.yaml b/translations/es-es.yaml index a57e7f5..d1bf532 100644 --- a/translations/es-es.yaml +++ b/translations/es-es.yaml @@ -160,6 +160,39 @@ developers: metrics: date-created: Fecha de creación widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Monitorización api-requests: Solicitudes API diff --git a/translations/fr-fr.yaml b/translations/fr-fr.yaml index eb808a3..3715803 100644 --- a/translations/fr-fr.yaml +++ b/translations/fr-fr.yaml @@ -160,6 +160,39 @@ developers: metrics: date-created: Date de création widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Surveillance api-requests: Requêtes API diff --git a/translations/mn-mn.yaml b/translations/mn-mn.yaml index 2a00aee..31ef24c 100644 --- a/translations/mn-mn.yaml +++ b/translations/mn-mn.yaml @@ -152,6 +152,39 @@ developers: metrics: date-created: Үүсгэсэн огноо widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Хянах api-requests: API Хүсэлтүүд diff --git a/translations/pt-br.yaml b/translations/pt-br.yaml index 93e5515..c9a50c0 100644 --- a/translations/pt-br.yaml +++ b/translations/pt-br.yaml @@ -155,6 +155,39 @@ developers: metrics: date-created: Data de Criação widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Monitoramento api-requests: Requisições API diff --git a/translations/ru-ru.yaml b/translations/ru-ru.yaml index e7597d3..7832d35 100644 --- a/translations/ru-ru.yaml +++ b/translations/ru-ru.yaml @@ -153,6 +153,39 @@ developers: metrics: date-created: Дата создания widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Мониторинг api-requests: API запросы diff --git a/translations/vi-vn.yaml b/translations/vi-vn.yaml index afa7fca..2cbbdf5 100644 --- a/translations/vi-vn.yaml +++ b/translations/vi-vn.yaml @@ -152,6 +152,39 @@ developers: metrics: date-created: Ngày tạo widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: Giám sát api-requests: Yêu cầu API diff --git a/translations/zh-cn.yaml b/translations/zh-cn.yaml index 232de3a..d6f5944 100644 --- a/translations/zh-cn.yaml +++ b/translations/zh-cn.yaml @@ -138,6 +138,39 @@ developers: metrics: date-created: 创建日期 widget: + dashboard: + name: Developers Dashboard + period: 30d + empty: No data available + kpi: + api-requests: API Requests + api-error-rate: API Error Rate + api-latency: Avg API Latency + webhook-success: Webhook Success + active-api-keys: Active API Keys + active-webhooks: Active Webhooks + webhook-failures: Webhook Failures + events-emitted: Events Emitted + api-traffic: + title: API Traffic + subtitle: Request volume, successes, and errors + webhook-delivery: + title: Webhook Delivery Health + subtitle: Delivery volume, retries, and failures + endpoint-health: + title: Endpoint Health + subtitle: Recent webhook endpoint reliability + event-stream: + title: Event Stream + subtitle: Top event types and sources + types: Event types + sources: Sources + developer-activity: + title: Developer Activity + subtitle: Recent logs, webhooks, and events + quick-resources: + title: Quick Resources + subtitle: Jump into developer tools api-metrics: title: 监控 api-requests: API请求 From 361058f7458ada44760ff6624722ff680b8a44a3 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 10 Jun 2026 16:03:24 +0800 Subject: [PATCH 2/7] update webhook icon --- addon/components/widget/quick-resources.js | 2 +- addon/extension.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addon/components/widget/quick-resources.js b/addon/components/widget/quick-resources.js index a953adc..6d75b8b 100644 --- a/addon/components/widget/quick-resources.js +++ b/addon/components/widget/quick-resources.js @@ -3,7 +3,7 @@ import Component from '@glimmer/component'; export default class WidgetQuickResourcesComponent extends Component { resources = [ { label: 'API Keys', route: 'api-keys.index', icon: 'key' }, - { label: 'Webhooks', route: 'webhooks.index', icon: 'webhook' }, + { label: 'Webhooks', route: 'webhooks.index', icon: 'globe' }, { label: 'Logs', route: 'logs.index', icon: 'terminal' }, { label: 'Events', route: 'events.index', icon: 'bolt' }, { label: 'Sockets', route: 'sockets.index', icon: 'plug' }, diff --git a/addon/extension.js b/addon/extension.js index 9b57219..a29aca3 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -20,7 +20,7 @@ export default { { title: 'Webhooks', description: 'Configure webhook endpoints to receive real-time event notifications.', - icon: 'webhook', + icon: 'globe', route: 'console.developers.webhooks', }, { @@ -211,13 +211,13 @@ export default { widgetService.registerDashboard('developers'); widgetService.registerWidgets('developers', widgets); - widgetService.registerWidgets('dashboard', [ - getWidgetById('developers-kpi-api-error-rate'), - getWidgetById('developers-kpi-api-latency'), - getWidgetById('developers-kpi-webhook-success'), - getWidgetById('developers-api-traffic', (widget) => { - widget.withGridOptions({ w: 6, h: 7, minW: 5, minH: 6 }); - }), - ]); + // widgetService.registerWidgets('dashboard', [ + // getWidgetById('developers-kpi-api-error-rate'), + // getWidgetById('developers-kpi-api-latency'), + // getWidgetById('developers-kpi-webhook-success'), + // getWidgetById('developers-api-traffic', (widget) => { + // widget.withGridOptions({ w: 6, h: 7, minW: 5, minH: 6 }); + // }), + // ]); }, }; From c3afb7e559d9d335531067e35998135092e9e51d Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 10 Jun 2026 16:05:38 +0800 Subject: [PATCH 3/7] upgrade to node 22.x and pnpm 11 --- .github/workflows/ci.yml | 20 +++++++++----------- pnpm-lock.yaml | 8 ++++---- pnpm-workspace.yaml | 8 ++++++++ 3 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e11201b..fa2f721 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,21 +8,19 @@ on: pull_request: branches: [ main ] +env: + NODE_VERSION: 22.x + jobs: build: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x] # Build on Node.js 18 - steps: - uses: actions/checkout@v2 - - name: Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v2 with: - node-version: ${{ matrix.node-version }} + node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm uses: pnpm/action-setup@v2.0.1 @@ -42,10 +40,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Node.js 18.x + - name: Setup Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v2 with: - node-version: 18.x + node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm uses: pnpm/action-setup@v2.0.1 @@ -71,10 +69,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Node.js 18.x + - name: Setup Node.js ${{ env.NODE_VERSION }} uses: actions/setup-node@v2 with: - node-version: 18.x + node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm uses: pnpm/action-setup@v2.0.1 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70ad693..3e27283 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9421,7 +9421,7 @@ snapshots: dependencies: postcss: 8.5.6 - '@ember-data/adapter@4.12.8(@ember-data/store@4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))': + '@ember-data/adapter@4.12.8(@ember-data/store@4.12.8)(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))': dependencies: '@ember-data/private-build-infra': 4.12.8 '@ember-data/store': 4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) @@ -9552,7 +9552,7 @@ snapshots: '@ember-data/rfc395-data@0.0.4': {} - '@ember-data/serializer@4.12.8(@ember-data/store@4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))': + '@ember-data/serializer@4.12.8(@ember-data/store@4.12.8)(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))': dependencies: '@ember-data/private-build-infra': 4.12.8 '@ember-data/store': 4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) @@ -13698,7 +13698,7 @@ snapshots: ember-data@4.12.8(@babel/core@7.28.5)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0): dependencies: - '@ember-data/adapter': 4.12.8(@ember-data/store@4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))) + '@ember-data/adapter': 4.12.8(@ember-data/store@4.12.8)(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))) '@ember-data/debug': 4.12.8(@ember-data/store@4.12.8)(@ember/string@3.1.1)(webpack@5.103.0) '@ember-data/graph': 4.12.8(@ember-data/store@4.12.8) '@ember-data/json-api': 4.12.8(@ember-data/graph@4.12.8)(@ember-data/store@4.12.8) @@ -13706,7 +13706,7 @@ snapshots: '@ember-data/model': 4.12.8(@babel/core@7.28.5)(@ember-data/debug@4.12.8)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/store@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) '@ember-data/private-build-infra': 4.12.8 '@ember-data/request': 4.12.8 - '@ember-data/serializer': 4.12.8(@ember-data/store@4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))) + '@ember-data/serializer': 4.12.8(@ember-data/store@4.12.8)(@ember/string@3.1.1)(ember-inflector@4.0.3(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))) '@ember-data/store': 4.12.8(@babel/core@7.28.5)(@ember-data/graph@4.12.8)(@ember-data/json-api@4.12.8)(@ember-data/legacy-compat@4.12.8)(@ember-data/model@4.12.8)(@ember-data/tracking@4.12.8)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) '@ember-data/tracking': 4.12.8 '@ember/edition-utils': 1.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..bf39b1a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,8 @@ +allowBuilds: + '@fortawesome/fontawesome-common-types': false + '@fortawesome/fontawesome-svg-core': false + '@fortawesome/free-brands-svg-icons': false + '@fortawesome/free-solid-svg-icons': false + core-js: false + fsevents: false +minimumReleaseAge: 0 \ No newline at end of file From 6f23dd6b86591e7cf7a5fbd8e9f18cb01f9c45bc Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 11 Jun 2026 20:11:16 +0800 Subject: [PATCH 4/7] latest changes --- addon/controllers/api-keys/index.js | 37 +++++- addon/controllers/application.js | 94 ++++++++++++++ addon/routes/api-keys/index.js | 11 +- addon/templates/application.hbs | 12 +- app/controllers/application.js | 1 + tests/unit/controllers/api-keys/index-test.js | 115 +++++++++++++++++- tests/unit/controllers/application-test.js | 114 +++++++++++++++++ 7 files changed, 371 insertions(+), 13 deletions(-) create mode 100644 addon/controllers/application.js create mode 100644 app/controllers/application.js create mode 100644 tests/unit/controllers/application-test.js diff --git a/addon/controllers/api-keys/index.js b/addon/controllers/api-keys/index.js index 5035964..9e52881 100644 --- a/addon/controllers/api-keys/index.js +++ b/addon/controllers/api-keys/index.js @@ -28,7 +28,7 @@ export default class ApiKeysIndexController extends Controller { * * @var {Array} */ - queryParams = ['page', 'limit', 'sort']; + queryParams = ['page', 'limit', 'sort', 'query', 'view_api_key']; /** * Expiration options for api keys @@ -72,6 +72,13 @@ export default class ApiKeysIndexController extends Controller { */ @tracked query; + /** + * Deep-linked API key to open in the edit modal. + * + * @var {String} + */ + @tracked view_api_key; + /** * Checks if console environment is in live mode * @@ -509,4 +516,32 @@ export default class ApiKeysIndexController extends Controller { @action reload() { return this.hostRouter.refresh(); } + + @action async openDeepLinkedApiKey() { + const apiKeyId = this.view_api_key; + + if (!apiKeyId) { + return; + } + + try { + const apiKey = this.store.peekRecord('api-credential', apiKeyId) ?? (await this.store.findRecord('api-credential', apiKeyId)); + this.editApiKey(apiKey, { + onDecline: this.clearDeepLinkedApiKey, + onFinish: this.clearDeepLinkedApiKey, + }); + } catch (_) { + this.notifications.warning('Unable to open the selected API key.'); + this.clearDeepLinkedApiKey(); + } + } + + @action clearDeepLinkedApiKey() { + if (!this.view_api_key) { + return; + } + + this.view_api_key = null; + this.hostRouter.transitionTo({ queryParams: { view_api_key: null } }); + } } diff --git a/addon/controllers/application.js b/addon/controllers/application.js new file mode 100644 index 0000000..b4a16cc --- /dev/null +++ b/addon/controllers/application.js @@ -0,0 +1,94 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ApplicationController extends Controller { + @service intl; + @service abilities; + @service fetch; + + get navigationItems() { + return [ + { + label: this.intl.t('developers.application.sidebar.items.home'), + description: 'Developer dashboard and API health overview.', + icon: 'home', + route: 'console.developers.home', + keywords: ['dashboard', 'overview', 'metrics'], + }, + { + label: this.intl.t('developers.application.sidebar.items.api-keys'), + description: 'Create and manage API credentials.', + icon: 'key', + route: 'console.developers.api-keys', + permission: 'developers list api-key', + visible: this.can('developers see api-key'), + keywords: ['credentials', 'access keys', 'sandbox', 'live keys'], + }, + { + label: this.intl.t('developers.application.sidebar.items.webhooks'), + description: 'Configure webhook endpoints.', + icon: 'globe-asia', + route: 'console.developers.webhooks', + permission: 'developers list webhook', + visible: this.can('developers see webhook'), + keywords: ['endpoints', 'callbacks', 'notifications'], + }, + { + label: this.intl.t('developers.application.sidebar.items.websockets'), + description: 'Inspect websocket channels.', + icon: 'plug', + route: 'console.developers.sockets', + permission: 'developers list socket', + visible: this.can('developers see socket'), + keywords: ['sockets', 'channels', 'realtime'], + }, + { + label: this.intl.t('developers.application.sidebar.items.logs'), + description: 'Review API request logs.', + icon: 'file-lines', + route: 'console.developers.logs', + permission: 'developers list log', + visible: this.can('developers see log'), + keywords: ['requests', 'traffic', 'debugging'], + }, + { + label: this.intl.t('developers.application.sidebar.items.events'), + description: 'Browse platform events.', + icon: 'calendar-day', + route: 'console.developers.events', + permission: 'developers list event', + visible: this.can('developers see event'), + keywords: ['event stream', 'activity', 'webhook events'], + }, + ]; + } + + can(permission) { + try { + return this.abilities.can(permission); + } catch (_) { + return true; + } + } + + @action + async searchNavigation({ query, limit = 12 }) { + const trimmedQuery = query?.trim(); + + if (!trimmedQuery) { + return []; + } + + try { + const response = await this.fetch.get('developers/search', { + query: trimmedQuery, + limit, + }); + + return response.results ?? []; + } catch (_) { + return []; + } + } +} diff --git a/addon/routes/api-keys/index.js b/addon/routes/api-keys/index.js index 327ab8b..91dacb8 100644 --- a/addon/routes/api-keys/index.js +++ b/addon/routes/api-keys/index.js @@ -1,6 +1,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; +import { later } from '@ember/runloop'; export default class ApiKeysIndexRoute extends Route { @service store; @@ -21,6 +22,9 @@ export default class ApiKeysIndexRoute extends Route { query: { refreshModel: true, }, + view_api_key: { + refreshModel: false, + }, sort: { refreshModel: true, }, @@ -38,10 +42,15 @@ export default class ApiKeysIndexRoute extends Route { } model(params) { - return this.store.query('api-credential', { ...params }); + const queryParams = { ...params }; + delete queryParams.view_api_key; + + return this.store.query('api-credential', { ...queryParams }); } setupController(controller) { + super.setupController(...arguments); controller.testMode = this.currentUser.getOption('sandbox', false); + later(controller, controller.openDeepLinkedApiKey); } } diff --git a/addon/templates/application.hbs b/addon/templates/application.hbs index c35e643..c4cc71f 100644 --- a/addon/templates/application.hbs +++ b/addon/templates/application.hbs @@ -1,15 +1,7 @@ - - {{t "developers.application.sidebar.items.home"}} - {{t "developers.application.sidebar.items.api-keys"}} - {{t "developers.application.sidebar.items.webhooks"}} - {{t "developers.application.sidebar.items.websockets"}} - {{t "developers.application.sidebar.items.logs"}} - {{t "developers.application.sidebar.items.events"}} - - + {{outlet}} - \ No newline at end of file + diff --git a/app/controllers/application.js b/app/controllers/application.js new file mode 100644 index 0000000..09b6f58 --- /dev/null +++ b/app/controllers/application.js @@ -0,0 +1 @@ +export { default } from '@fleetbase/dev-engine/controllers/application'; diff --git a/tests/unit/controllers/api-keys/index-test.js b/tests/unit/controllers/api-keys/index-test.js index efd7283..382d956 100644 --- a/tests/unit/controllers/api-keys/index-test.js +++ b/tests/unit/controllers/api-keys/index-test.js @@ -1,12 +1,125 @@ import { module, test } from 'qunit'; import { setupTest } from 'dummy/tests/helpers'; +class IntlStub { + t(key) { + return key; + } +} + +class CurrentUserStub { + options = {}; + user = {}; + + getOption(key, defaultValue = null) { + return key in this.options ? this.options[key] : defaultValue; + } + + setOption(key, value) { + this.options[key] = value; + } +} + +class AbilitiesStub { + cannot() { + return false; + } +} + +class StoreStub { + record = { id: 'api_key_uuid', name: 'Live API Key' }; + findRequests = []; + + peekRecord() { + return null; + } + + findRecord(modelName, id) { + this.findRequests.push({ modelName, id }); + return Promise.resolve(this.record); + } +} + +class HostRouterStub { + transitions = []; + + transitionTo(...args) { + this.transitions.push(args); + return Promise.resolve(); + } + + refresh() { + return Promise.resolve(); + } +} + +class ModalsManagerStub { + show() {} + confirm() {} +} + +class NotificationsStub { + warnings = []; + + warning(message) { + this.warnings.push(message); + } + + serverError() {} + success() {} +} + +class EmptyServiceStub {} + module('Unit | Controller | api-keys/index', function (hooks) { setupTest(hooks); - // TODO: Replace this with your real tests. + hooks.beforeEach(function () { + this.owner.register('service:intl', IntlStub); + this.owner.register('service:current-user', CurrentUserStub); + this.owner.register('service:abilities', AbilitiesStub); + this.owner.register('service:store', StoreStub); + this.owner.register('service:host-router', HostRouterStub); + this.owner.register('service:modals-manager', ModalsManagerStub); + this.owner.register('service:notifications', NotificationsStub); + this.owner.register('service:crud', EmptyServiceStub); + this.owner.register('service:fetch', EmptyServiceStub); + this.owner.register('service:theme', EmptyServiceStub); + this.owner.register('service:universe', EmptyServiceStub); + }); + test('it exists', function (assert) { let controller = this.owner.lookup('controller:api-keys/index'); assert.ok(controller); }); + + test('it opens a deep-linked API key in the existing edit modal', async function (assert) { + const controller = this.owner.lookup('controller:api-keys/index'); + const store = this.owner.lookup('service:store'); + + controller.view_api_key = 'api_key_uuid'; + controller.editApiKey = (apiKey, options) => { + assert.strictEqual(apiKey, store.record); + assert.strictEqual(typeof options.onDecline, 'function'); + assert.strictEqual(typeof options.onFinish, 'function'); + }; + + await controller.openDeepLinkedApiKey(); + + assert.deepEqual(store.findRequests, [{ modelName: 'api-credential', id: 'api_key_uuid' }]); + }); + + test('it clears only the API key deep-link query param', function (assert) { + const controller = this.owner.lookup('controller:api-keys/index'); + const hostRouter = this.owner.lookup('service:host-router'); + + controller.query = 'live'; + controller.view_api_key = 'api_key_uuid'; + + controller.clearDeepLinkedApiKey(); + + assert.strictEqual(controller.view_api_key, null); + assert.strictEqual(controller.query, 'live', 'table query is preserved'); + assert.deepEqual(hostRouter.transitions, [[{ queryParams: { view_api_key: null } }]]); + }); }); diff --git a/tests/unit/controllers/application-test.js b/tests/unit/controllers/application-test.js new file mode 100644 index 0000000..83cd3b8 --- /dev/null +++ b/tests/unit/controllers/application-test.js @@ -0,0 +1,114 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'dummy/tests/helpers'; + +class IntlStub { + translations = { + 'developers.application.sidebar.items.home': 'Dashboard', + 'developers.application.sidebar.items.api-keys': 'API Keys', + 'developers.application.sidebar.items.webhooks': 'Webhooks', + 'developers.application.sidebar.items.websockets': 'WebSockets', + 'developers.application.sidebar.items.logs': 'Logs', + 'developers.application.sidebar.items.events': 'Events', + }; + + t(key) { + return this.translations[key] ?? key; + } +} + +class AbilitiesStub { + denied = new Set(); + + can(permission) { + return !this.denied.has(permission); + } +} + +class FetchStub { + requests = []; + response = { + results: [ + { + label: 'Live API Key', + description: 'flb_live_123', + icon: 'key', + type: 'API Key', + route: 'console.developers.api-keys.index', + breadcrumb: 'Developers > API Keys', + queryParams: { query: 'live', view_api_key: 'api_key_uuid' }, + }, + ], + }; + + get(url, params) { + this.requests.push({ url, params }); + return Promise.resolve(this.response); + } +} + +module('Unit | Controller | application', function (hooks) { + setupTest(hooks); + + hooks.beforeEach(function () { + this.owner.register('service:intl', IntlStub); + this.owner.register('service:abilities', AbilitiesStub); + this.owner.register('service:fetch', FetchStub); + }); + + test('it builds developer sidebar navigator items with host routes', function (assert) { + const controller = this.owner.lookup('controller:application'); + const items = controller.navigationItems; + + assert.deepEqual( + items.map((item) => item.label), + ['Dashboard', 'API Keys', 'Webhooks', 'WebSockets', 'Logs', 'Events'], + 'root items keep the developer labels' + ); + assert.deepEqual( + items.map((item) => item.route), + ['console.developers.home', 'console.developers.api-keys', 'console.developers.webhooks', 'console.developers.sockets', 'console.developers.logs', 'console.developers.events'], + 'root items keep the console host route names' + ); + assert.strictEqual(items[1].permission, 'developers list api-key'); + assert.true(items[1].visible, 'api keys item is visible when the see permission is allowed'); + }); + + test('it marks developer navigator items hidden when see permissions are denied', function (assert) { + const abilities = this.owner.lookup('service:abilities'); + abilities.denied.add('developers see webhook'); + + const controller = this.owner.lookup('controller:application'); + const webhooks = controller.navigationItems.find((item) => item.route === 'console.developers.webhooks'); + + assert.false(webhooks.visible); + }); + + test('it fetches developer resource search results for the sidebar navigator', async function (assert) { + const controller = this.owner.lookup('controller:application'); + const fetch = this.owner.lookup('service:fetch'); + const results = await controller.searchNavigation({ query: ' live ', limit: 12 }); + + assert.deepEqual(fetch.requests, [{ url: 'developers/search', params: { query: 'live', limit: 12 } }], 'calls the developer search endpoint with the trimmed query'); + assert.deepEqual(results, fetch.response.results, 'returns navigator-ready endpoint results'); + }); + + test('it does not fetch developer resource search results for blank queries', async function (assert) { + const controller = this.owner.lookup('controller:application'); + const fetch = this.owner.lookup('service:fetch'); + const results = await controller.searchNavigation({ query: ' ', limit: 12 }); + + assert.deepEqual(results, []); + assert.deepEqual(fetch.requests, [], 'blank queries do not call the adapter'); + }); + + test('it returns empty developer search results when the adapter fails', async function (assert) { + const controller = this.owner.lookup('controller:application'); + const fetch = this.owner.lookup('service:fetch'); + + fetch.get = () => Promise.reject(new Error('adapter failed')); + + const results = await controller.searchNavigation({ query: 'live', limit: 12 }); + + assert.deepEqual(results, []); + }); +}); From cbbf63139761b513f91b7527e17f5df52be1f1ea Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 11 Jun 2026 20:39:13 +0800 Subject: [PATCH 5/7] v0.2.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d45ad7c..836fdd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/dev-engine", - "version": "0.2.13", + "version": "0.2.14", "description": "Fleetbase Developers extension provides a module for managing developer resources such as API keys, webhooks, sockets, events and logs.", "fleetbase": { "route": "developers" From ad01fd4d850495121843d77b2354fd5de765bf2a Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Fri, 12 Jun 2026 18:12:39 +0800 Subject: [PATCH 6/7] latest changes --- addon/helpers/format-milliseconds.js | 9 --------- app/helpers/format-milliseconds.js | 1 - .../helpers/format-milliseconds-test.js | 17 ----------------- 3 files changed, 27 deletions(-) delete mode 100644 addon/helpers/format-milliseconds.js delete mode 100644 app/helpers/format-milliseconds.js delete mode 100644 tests/integration/helpers/format-milliseconds-test.js diff --git a/addon/helpers/format-milliseconds.js b/addon/helpers/format-milliseconds.js deleted file mode 100644 index 01aef2a..0000000 --- a/addon/helpers/format-milliseconds.js +++ /dev/null @@ -1,9 +0,0 @@ -import { helper } from '@ember/component/helper'; - -export default helper(function formatMilliseconds([milliseconds]) { - if (!milliseconds || typeof milliseconds !== 'number') { - return '-'; - } - - return milliseconds.toString().startsWith(0) ? `${milliseconds.toFixed(3).substring(2)}ms` : `${milliseconds.toFixed(3)}s`; -}); diff --git a/app/helpers/format-milliseconds.js b/app/helpers/format-milliseconds.js deleted file mode 100644 index ef7b3a9..0000000 --- a/app/helpers/format-milliseconds.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@fleetbase/dev-engine/helpers/format-milliseconds'; diff --git a/tests/integration/helpers/format-milliseconds-test.js b/tests/integration/helpers/format-milliseconds-test.js deleted file mode 100644 index bb928c9..0000000 --- a/tests/integration/helpers/format-milliseconds-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'dummy/tests/helpers'; -import { render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Helper | format-milliseconds', function (hooks) { - setupRenderingTest(hooks); - - // TODO: Replace this with your real tests. - test('it renders', async function (assert) { - this.set('inputValue', '1234'); - - await render(hbs`{{format-milliseconds this.inputValue}}`); - - assert.dom(this.element).hasText('1234'); - }); -}); From c86a1056467d48f70763074082d38151d7ed18d8 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Fri, 12 Jun 2026 19:21:32 +0800 Subject: [PATCH 7/7] upgraded dependencies --- package.json | 4 +- pnpm-lock.yaml | 266 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 228 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 836fdd9..0ee9458 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ }, "dependencies": { "@babel/core": "^7.23.2", - "@fleetbase/ember-core": "^0.3.17", - "@fleetbase/ember-ui": "^0.3.25", + "@fleetbase/ember-core": "^0.3.22", + "@fleetbase/ember-ui": "^0.3.34", "@fortawesome/ember-fontawesome": "^2.0.0", "@fortawesome/fontawesome-svg-core": "6.4.0", "@fortawesome/free-brands-svg-icons": "6.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e27283..d0dc0e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,11 +12,11 @@ importers: specifier: ^7.23.2 version: 7.28.5 '@fleetbase/ember-core': - specifier: ^0.3.17 - version: 0.3.17(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(eslint@8.57.1)(webpack@5.103.0) + specifier: ^0.3.22 + version: 0.3.22(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(eslint@8.57.1)(webpack@5.103.0) '@fleetbase/ember-ui': - specifier: ^0.3.25 - version: 0.3.25(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.103.0) + specifier: ^0.3.34 + version: 0.3.34(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.103.0) '@fortawesome/ember-fontawesome': specifier: ^2.0.0 version: 2.0.0(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(rollup@2.79.2)(webpack@5.103.0) @@ -255,8 +255,8 @@ packages: resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} '@babel/helper-remap-async-to-generator@7.27.1': @@ -369,8 +369,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-decorators@7.28.6': - resolution: {integrity: sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==} + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1168,6 +1168,10 @@ packages: resolution: {integrity: sha512-EfI9cJ5/3QSUJtwm7x1MXrx3TEa2p7RNgSHefy7fvGm8/DP1xUFL25nST1NaHbHcqR1UhMlrTtv5iUIDoVzeQQ==} engines: {node: 12.* || 14.* || >= 16} + '@embroider/addon-shim@1.10.3': + resolution: {integrity: sha512-GYbaiC1v9inbiwVg5s+Sd14Jc66NYxg23mEOocgWAZFCtOfhMnRLaLAA6SytW76myVVYImGHX5PFK4PVuH2yng==} + engines: {node: 12.* || 14.* || >= 16} + '@embroider/addon@0.30.0': resolution: {integrity: sha512-hBgskhX38RMIyHcnUpRt+KbddLMPLVOFdLp4qhv7Vs881SPDwsbo7pir4KpILmnoqcvBMh1MCVY970tYaAyMcQ==} engines: {node: 10.* || >= 12} @@ -1197,6 +1201,10 @@ packages: resolution: {integrity: sha512-d7RQwDwqqHo7YvjE9t1rtIrCCYtbSoO0uRq2ikVhRh4hGS5OojZNu2ZtS0Wqrg+V72CRtMFr/hibTvHNsRM2Lg==} engines: {node: 12.* || 14.* || >= 16} + '@embroider/shared-internals@3.1.1': + resolution: {integrity: sha512-IlxD8okTt9cRUFpJKD8gTuQUBuEflrhCUju1xZFdYvmGm5XVqHPfG4I6+bEdKb8NO92aqHxr9SYuYibDmpMM9w==} + engines: {node: 12.* || 14.* || >= 16} + '@embroider/test-setup@3.0.3': resolution: {integrity: sha512-3K5KSyTdnxAkZQill6+TdC/XTRr6226LNwZMsrhRbBM0FFZXw2D8qmJSHPvZLheQx3A1jnF9t1lyrAzrKlg6Yw==} engines: {node: 12.* || 14.* || >= 16} @@ -1243,18 +1251,21 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@event-calendar/core@5.7.1': + resolution: {integrity: sha512-ms9MQagthrmRO+ytZD5dDQHjBb+r/6mwld4/psZRaBI6bciIEsmphCrxFaF9HnGvLW0htSa973TVbYMbDsDtVA==} + '@fleetbase/ember-accounting@0.0.1': resolution: {integrity: sha512-61WGQ/VtmkEloBfdNEd83C9E57axiBXbBPdXAbaS3dsCBpKmqwPo1CkrYUN7vVa3oUP9ZRouVVVffbE0YDnAng==} engines: {node: '>= 18'} peerDependencies: ember-source: '>= 4.0.0' - '@fleetbase/ember-core@0.3.17': - resolution: {integrity: sha512-fFyorS6Ir/lW2u1y/d46U/0PoIhz4JKSVJZJddveIPK3v/0shpHRRsI4gnW+EtIzE3Dgq6Z7p6pQvrPBpPw/YQ==} + '@fleetbase/ember-core@0.3.22': + resolution: {integrity: sha512-tCYgxJoemUXgjsUztzeJZSwU2ejDp5x2WYI3FsBO55AlcC+mfWm0FsYtuPd9xd9PoO1jZ3T3VyvQQ06HRPtqWw==} engines: {node: '>= 18'} - '@fleetbase/ember-ui@0.3.25': - resolution: {integrity: sha512-CM0dXMlFe3VyIGFgmbRDEK+e/Y79Ezu4T57geJF2cHr+/1f3oLvhLqPaZIs1J1TTSgcvCl3E+owcYWw2mWxLlg==} + '@fleetbase/ember-ui@0.3.34': + resolution: {integrity: sha512-l67lrbpeh0JRLDHr1sJ3QnxP6vPKwVW614CVWEASs6ZImGCBr6CK3DjsT3h+1uslQa51dpixN/stiQ+C/Z1etA==} engines: {node: '>= 18'} '@fleetbase/intl-lint@0.0.1': @@ -1584,6 +1595,11 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@sveltejs/acorn-typescript@1.0.10': + resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==} + peerDependencies: + acorn: ^8.9.0 + '@szmarczak/http-timer@1.1.2': resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} engines: {node: '>=6'} @@ -1809,6 +1825,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/express-serve-static-core@4.19.7': resolution: {integrity: sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==} @@ -1892,6 +1911,9 @@ packages: '@types/symlink-or-copy@1.2.2': resolution: {integrity: sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -2037,6 +2059,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + ag-channel@5.0.0: resolution: {integrity: sha512-bArHkdqQxynim981t8FLZM5TfA0v7p081OlFdOxs6clB79GSGcGlOQMDa31DT9F5VMjzqNiJmhfGwinvfU/3Zg==} @@ -2173,6 +2200,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -2294,6 +2325,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + babel-code-frame@6.26.0: resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} @@ -2502,6 +2537,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -2970,6 +3008,10 @@ packages: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + collection-visit@1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} @@ -3477,8 +3519,8 @@ packages: decorator-transforms@2.3.0: resolution: {integrity: sha512-jo8c1ss9yFPudHuYYcrJ9jpkDZIoi+lOGvt+Uyp9B+dz32i50icRMx9Bfa8hEt7TnX1FyKWKkjV+cUdT/ep2kA==} - decorator-transforms@2.3.1: - resolution: {integrity: sha512-PDOk74Zqqy0946Lx4ckXxbgG6uhPScOICtrxL/pXmfznxchqNee0TaJISClGJQe6FeT8ohGqsOgdjfahm4FwEw==} + decorator-transforms@2.3.2: + resolution: {integrity: sha512-XcErcjlmCzG5ODgYjt6ZTXwd6S8fPKln/sJmw15ZXkWG2JpoQNwszis+AwF6XSGlOoG7g8MCEO97g+Yw3fk5OQ==} deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} @@ -3551,6 +3593,9 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + devalue@5.8.1: + resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -4128,8 +4173,8 @@ packages: engines: {node: 12.* || 14.* || >= 16.*} hasBin: true - ember-tracked-storage-polyfill@1.0.0: - resolution: {integrity: sha512-eL7lZat68E6P/D7b9UoTB5bB5Oh/0aju0Z7PCMi3aTwhaydRaxloE7TGrTRYU+NdJuyNVZXeGyxFxn2frvd3TA==} + ember-tracked-storage-polyfill@1.0.1: + resolution: {integrity: sha512-lr66R+1H9qMXIUXxwzpixS/qTwsMEpJXS5s2nOdvQP9U/JYuZT9MexpvLktSUQ1uWEhGQA8DDeeVh4R1CvLDFQ==} engines: {node: 12.* || >= 14} ember-truth-helpers@3.1.1: @@ -4349,6 +4394,9 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} @@ -4371,6 +4419,14 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} + esrap@2.2.11: + resolution: {integrity: sha512-gPdx+I+BjYEinNMQaBXFjbaJVyoPMU4ZODg5mE+M4DqVG9VusAVHHjcBX+zqyITlI0DIARwDMMzZwAWj36dRoQ==} + peerDependencies: + '@typescript-eslint/types': ^8.2.0 + peerDependenciesMeta: + '@typescript-eslint/types': + optional: true + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -5378,6 +5434,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -5651,6 +5710,9 @@ packages: loader.js@4.7.0: resolution: {integrity: sha512-9M2KvGT6duzGMgkOcTkWb+PR/Q2Oe54df/tLgHGVmFpAmtqJ553xJh6N63iFYI2yjo2PeJXbS5skHi/QpJq4vA==} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -5726,6 +5788,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-symbols@2.2.0: resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} engines: {node: '>=4'} @@ -5952,6 +6017,9 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -6470,6 +6538,10 @@ packages: pkg-entry-points@1.1.1: resolution: {integrity: sha512-BhZa7iaPmB4b3vKIACoppyUoYn8/sFs17VJJtzrzPZvEnN2nqrgg911tdL65lA2m1ml6UI3iPeYbZQ4VXpn1mA==} + pkg-entry-points@1.1.2: + resolution: {integrity: sha512-bmmM+SdLXNNetFr4o53QiiZRZicls2apmzj8HRpo4bU+3nJHiPO/omv8TXHIOzTcirua3YBAwTlKE+7zkICh4g==} + engines: {node: '>=20.19.5'} + pkg-up@2.0.0: resolution: {integrity: sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==} engines: {node: '>=4'} @@ -7338,6 +7410,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.0: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} @@ -7770,6 +7847,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svelte@5.56.3: + resolution: {integrity: sha512-w7JvrM5IFl5cmfbY0TLik9o7mjRUJmRMhOR51tBPu708Gr/MjbGs7VnJnr/B0CaXeI4vtnOh7RKxDr0cwhMdDA==} + engines: {node: '>=18'} + svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} @@ -8405,6 +8486,9 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -8527,7 +8611,7 @@ snapshots: '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: @@ -8656,10 +8740,10 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': dependencies: @@ -9679,6 +9763,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@embroider/addon-shim@1.10.3': + dependencies: + '@embroider/shared-internals': 3.1.1 + broccoli-funnel: 3.0.8 + common-ancestor-path: 1.0.1 + semver: 7.8.4 + transitivePeerDependencies: + - supports-color + '@embroider/addon@0.30.0': dependencies: ember-cli-babel: 7.26.11 @@ -9749,6 +9842,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@embroider/shared-internals@3.1.1': + dependencies: + babel-import-util: 3.0.1 + debug: 4.4.3 + ember-rfc176-data: 0.3.18 + fs-extra: 9.1.0 + is-subdir: 1.2.0 + js-string-escape: 1.0.1 + lodash: 4.18.1 + minimatch: 3.1.5 + pkg-entry-points: 1.1.2 + resolve-package-path: 4.0.3 + resolve.exports: 2.0.3 + semver: 7.8.4 + typescript-memoize: 1.1.1 + transitivePeerDependencies: + - supports-color + '@embroider/test-setup@3.0.3': dependencies: lodash: 4.17.21 @@ -9786,6 +9897,12 @@ snapshots: '@eslint/js@8.57.1': {} + '@event-calendar/core@5.7.1': + dependencies: + svelte: 5.56.3 + transitivePeerDependencies: + - '@typescript-eslint/types' + '@fleetbase/ember-accounting@0.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))': dependencies: '@babel/core': 7.28.5 @@ -9795,7 +9912,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@fleetbase/ember-core@0.3.17(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(eslint@8.57.1)(webpack@5.103.0)': + '@fleetbase/ember-core@0.3.22(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(eslint@8.57.1)(webpack@5.103.0)': dependencies: '@babel/core': 7.28.5 compress-json: 3.4.0 @@ -9828,13 +9945,14 @@ snapshots: - utf-8-validate - webpack - '@fleetbase/ember-ui@0.3.25(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.103.0)': + '@fleetbase/ember-ui@0.3.34(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(webpack@5.103.0))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.103.0)': dependencies: '@babel/core': 7.28.5 '@ember/render-modifiers': 2.1.0(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) '@ember/string': 3.1.1 '@embroider/addon': 0.30.0 '@embroider/macros': 1.19.5 + '@event-calendar/core': 5.7.1 '@fleetbase/ember-accounting': 0.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0)) '@floating-ui/dom': 1.7.4 '@fortawesome/ember-fontawesome': 2.0.0(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.103.0))(rollup@2.79.2)(webpack@5.103.0) @@ -9918,6 +10036,7 @@ snapshots: - '@glimmer/tracking' - '@glint/environment-ember-loose' - '@glint/template' + - '@typescript-eslint/types' - ember-cli-mirage - ember-resolver - ember-source @@ -10410,6 +10529,10 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} + '@sveltejs/acorn-typescript@1.0.10(acorn@8.17.0)': + dependencies: + acorn: 8.17.0 + '@szmarczak/http-timer@1.1.2': dependencies: defer-to-connect: 1.1.3 @@ -10637,7 +10760,7 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/eslint@8.56.12': dependencies: @@ -10646,11 +10769,13 @@ snapshots: '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + '@types/express-serve-static-core@4.19.7': dependencies: '@types/node': 24.10.1 @@ -10747,6 +10872,8 @@ snapshots: '@types/symlink-or-copy@1.2.2': {} + '@types/trusted-types@2.0.7': {} + '@ungap/structured-clone@1.3.0': {} '@webassemblyjs/ast@1.14.1': @@ -10929,13 +11056,13 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-phases@1.0.4(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.17.0): dependencies: - acorn: 8.15.0 + acorn: 8.17.0 - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-jsx@5.3.2(acorn@8.17.0): dependencies: - acorn: 8.15.0 + acorn: 8.17.0 acorn@5.7.4: {} @@ -10943,6 +11070,8 @@ snapshots: acorn@8.15.0: {} + acorn@8.17.0: {} + ag-channel@5.0.0: dependencies: consumable-stream: 2.0.0 @@ -11062,6 +11191,8 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.1: {} + aria-query@5.3.2: {} arr-diff@4.0.0: {} @@ -11188,6 +11319,8 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axobject-query@4.1.0: {} + babel-code-frame@6.26.0: dependencies: chalk: 1.1.3 @@ -11498,6 +11631,11 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@1.1.15: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -12425,6 +12563,8 @@ snapshots: clone@2.1.2: {} + clsx@2.1.1: {} + collection-visit@1.0.0: dependencies: map-visit: 1.0.0 @@ -12789,9 +12929,9 @@ snapshots: transitivePeerDependencies: - '@babel/core' - decorator-transforms@2.3.1(@babel/core@7.28.5): + decorator-transforms@2.3.2(@babel/core@7.28.5): dependencies: - '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.28.5) babel-import-util: 3.0.1 transitivePeerDependencies: - '@babel/core' @@ -12856,6 +12996,8 @@ snapshots: detect-newline@3.1.0: {} + devalue@5.8.1: {} + didyoumean@1.2.2: {} diff@5.2.0: {} @@ -14289,10 +14431,9 @@ snapshots: transitivePeerDependencies: - supports-color - ember-tracked-storage-polyfill@1.0.0: + ember-tracked-storage-polyfill@1.0.1: dependencies: ember-cli-babel: 7.26.11 - ember-cli-htmlbars: 5.7.2 transitivePeerDependencies: - supports-color @@ -14641,12 +14782,14 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + esm@3.2.25: {} espree@9.6.1: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) eslint-visitor-keys: 3.4.3 esprima@3.0.0: {} @@ -14657,6 +14800,10 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@2.2.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -15916,6 +16063,10 @@ snapshots: is-plain-object@5.0.0: {} + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.9 + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -16166,6 +16317,8 @@ snapshots: loader.js@4.7.0: {} + locate-character@3.0.0: {} + locate-path@2.0.0: dependencies: p-locate: 2.0.0 @@ -16232,6 +16385,8 @@ snapshots: lodash@4.17.21: {} + lodash@4.18.1: {} + log-symbols@2.2.0: dependencies: chalk: 2.4.2 @@ -16482,6 +16637,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.15 + minimatch@5.1.6: dependencies: brace-expansion: 2.0.2 @@ -17006,6 +17165,8 @@ snapshots: pkg-entry-points@1.1.1: {} + pkg-entry-points@1.1.2: {} + pkg-up@2.0.0: dependencies: find-up: 2.1.0 @@ -17979,6 +18140,8 @@ snapshots: semver@7.7.3: {} + semver@7.8.4: {} + send@0.19.0: dependencies: debug: 2.6.9 @@ -18558,6 +18721,27 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svelte@5.56.3: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@types/estree': 1.0.9 + '@types/trusted-types': 2.0.7 + acorn: 8.17.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.8.1 + esm-env: 1.2.2 + esrap: 2.2.11 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + transitivePeerDependencies: + - '@typescript-eslint/types' + svg-tags@1.0.0: {} symlink-or-copy@1.3.1: {} @@ -18671,7 +18855,7 @@ snapshots: terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 + acorn: 8.17.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -18862,9 +19046,9 @@ snapshots: tracked-built-ins@3.4.0(@babel/core@7.28.5): dependencies: - '@embroider/addon-shim': 1.10.2 - decorator-transforms: 2.3.1(@babel/core@7.28.5) - ember-tracked-storage-polyfill: 1.0.0 + '@embroider/addon-shim': 1.10.3 + decorator-transforms: 2.3.2(@babel/core@7.28.5) + ember-tracked-storage-polyfill: 1.0.1 transitivePeerDependencies: - '@babel/core' - supports-color @@ -19204,13 +19388,13 @@ snapshots: webpack@5.103.0: dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) + acorn: 8.17.0 + acorn-import-phases: 1.0.4(acorn@8.17.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 @@ -19393,3 +19577,5 @@ snapshots: yocto-queue@1.2.2: {} yoctocolors-cjs@2.1.3: {} + + zimmerframe@1.1.4: {}