mirror of https://github.com/OpenVidu/openvidu.git
openvidu-dashboard: the UI library groups its files by component and concern
Components with a template or style file get a folder of their own, related small ones share one (badges, feedback, lock, search, tiles, zone); core services split into auth, http, state, refresh, preferences and ui; the login and settings pages join features and the locked page joins lock. The public API is unchanged.pull/924/head
parent
54006ce300
commit
93afb90b56
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
Angular 22 standalone, zoneless and signals: OnPush is the default (do not set it), `inject()` instead of constructor
|
||||
injection, `@Service()` for root services, Signal Forms for forms. All UI lives in the library
|
||||
`projects/dashboard-ui` (`src/lib/`: `core/` services, `shell/`, `components/` building blocks, `charts/`, `format/`,
|
||||
`features/<resource>/` data services, helpers and pages, `routes.ts`); its README describes composition and the
|
||||
extension points. Behaviour is specified in `openspec/specs/ui-*` and the `*-ui` specs.
|
||||
`projects/dashboard-ui` (`src/lib/`: `core/` services by concern (`auth/`, `http/`, `state/`, `refresh/`,
|
||||
`preferences/`, `ui/`), `shell/`, `components/` building blocks, `charts/`, `format/`, `routing/`, `features/<area>/`
|
||||
pages with their data services and helpers, `routes.ts`); its README describes composition and the extension points.
|
||||
Behaviour is specified in `openspec/specs/ui-*` and the `*-ui` specs.
|
||||
|
||||
A component with a template or style file has a folder of its own, named after it; small components that belong
|
||||
together (badges, feedback, locks, search, tiles, zone) share a folder and its spec.
|
||||
|
||||
## Routes and pages
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { Component, computed, DOCUMENT, inject, input, signal } from '@angular/c
|
|||
import { MatButton } from '@angular/material/button';
|
||||
import { NgxEchartsDirective } from 'ngx-echarts';
|
||||
|
||||
import { ChartColorsService } from './chart-colors.js';
|
||||
import { InfoTip } from '../components/info-tip/info-tip.js';
|
||||
import { formatPercent } from '../format/formatters.js';
|
||||
import { ChartColorsService } from './chart-colors.js';
|
||||
import { type BreakdownEntry, breakdownOption, formatValue, type ValueFormat } from './chart-options.js';
|
||||
import { bindChartOptions, injectEchartsReady, provideLazyEcharts } from './echarts-provider.js';
|
||||
import { InfoTip } from '../components/info-tip.js';
|
||||
|
||||
/** A donut or horizontal bar with direct labels and a fixed colour per entity; more than eight fold into "Other". */
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { computed, DOCUMENT, inject, Service, type Signal } from '@angular/core';
|
||||
|
||||
import { ThemeService } from '../core/theme.js';
|
||||
import { ThemeService } from '../core/preferences/theme.js';
|
||||
|
||||
export const CHART_SLOTS = 8;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
timeSeriesOption
|
||||
} from './chart-options.js';
|
||||
import { bindChartOptions } from './echarts-provider.js';
|
||||
import { TimeSeriesChart } from './time-series-chart.js';
|
||||
import { TimeSeriesChart } from './time-series-chart/time-series-chart.js';
|
||||
|
||||
const COLORS: ChartColors = {
|
||||
slots: ['#0088aa', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#2f4b7c', '#6250d6', '#e34948'],
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { MatIcon } from '@angular/material/icon';
|
|||
import type { ECharts } from 'echarts/core';
|
||||
import { NgxEchartsDirective } from 'ngx-echarts';
|
||||
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import { formatTimestamp } from '../format/formatters.js';
|
||||
import { ChartColorsService } from './chart-colors.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { formatTimestamp } from '../../format/formatters.js';
|
||||
import { ChartColorsService } from '../chart-colors.js';
|
||||
import {
|
||||
brushRange,
|
||||
CHART_GROUP,
|
||||
|
|
@ -18,9 +18,9 @@ import {
|
|||
type SeriesKind,
|
||||
timeSeriesOption,
|
||||
type ValueFormat
|
||||
} from './chart-options.js';
|
||||
import { bindChartOptions, injectEchartsReady, provideLazyEcharts } from './echarts-provider.js';
|
||||
import { InfoTip } from '../components/info-tip.js';
|
||||
} from '../chart-options.js';
|
||||
import { InfoTip } from '../../components/info-tip/info-tip.js';
|
||||
import { bindChartOptions, injectEchartsReady, provideLazyEcharts } from '../echarts-provider.js';
|
||||
|
||||
interface BrushEndEvent {
|
||||
areas?: { coordRange?: [number, number] }[];
|
||||
|
|
@ -36,7 +36,7 @@ interface BrushEndEvent {
|
|||
imports: [InfoTip, MatButton, MatIcon, NgxEchartsDirective],
|
||||
providers: [provideLazyEcharts()],
|
||||
templateUrl: './time-series-chart.html',
|
||||
styleUrl: './chart.scss'
|
||||
styleUrl: '../chart.scss'
|
||||
})
|
||||
export class TimeSeriesChart {
|
||||
private readonly query = inject(QueryStateService);
|
||||
|
|
@ -119,7 +119,7 @@ export class TimeSeriesChart {
|
|||
this.chartOptions.attach(chart);
|
||||
chart.group = CHART_GROUP;
|
||||
|
||||
const { echarts } = await import('./echarts-setup.js');
|
||||
const { echarts } = await import('../echarts-setup.js');
|
||||
|
||||
echarts.connect(CHART_GROUP);
|
||||
|
||||
|
|
@ -6,11 +6,11 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||
import { By } from '@angular/platform-browser';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { statusResponse } from '../../testing/fixtures.js';
|
||||
import { iconNames } from '../../testing/icons.js';
|
||||
import { PreferencesService } from '../core/preferences.js';
|
||||
import { RefreshRegistry, RefreshScheduler } from '../core/refresh.js';
|
||||
import { ServerStatusService } from '../core/server-status.js';
|
||||
import { statusResponse } from '../../../testing/fixtures.js';
|
||||
import { iconNames } from '../../../testing/icons.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { RefreshRegistry, RefreshScheduler } from '../../core/refresh/refresh.js';
|
||||
import { ServerStatusService } from '../../core/refresh/server-status.js';
|
||||
|
||||
import { KindBadge } from './kind-badge.js';
|
||||
import { LiveChip } from './live-chip.js';
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, computed, input } from '@angular/core';
|
||||
|
||||
import { EGRESS_TYPE_LABELS, enumLabel, INGRESS_INPUT_LABELS, PARTICIPANT_KIND_LABELS } from '../format/labels.js';
|
||||
import { EGRESS_TYPE_LABELS, enumLabel, INGRESS_INPUT_LABELS, PARTICIPANT_KIND_LABELS } from '../../format/labels.js';
|
||||
|
||||
/** Labels of the three vocabularies, whose values never collide. */
|
||||
const KIND_LABELS: Readonly<Record<string, string>> = {
|
||||
|
|
@ -2,9 +2,9 @@ import { booleanAttribute, Component, computed, inject, input } from '@angular/c
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
||||
import { RefreshRegistry } from '../core/refresh.js';
|
||||
import { ServerStatusService } from '../core/server-status.js';
|
||||
import { formatTimestamp } from '../format/formatters.js';
|
||||
import { RefreshRegistry } from '../../core/refresh/refresh.js';
|
||||
import { ServerStatusService } from '../../core/refresh/server-status.js';
|
||||
import { formatTimestamp } from '../../format/formatters.js';
|
||||
|
||||
/**
|
||||
* "LIVE" with a pulsing dot; "paused" while the page's auto-refresh is off; "stale" with a clock when the page reports
|
||||
|
|
@ -3,8 +3,8 @@ import { MatChip, MatChipRemove } from '@angular/material/chips';
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
import { EntityPreviewService } from '../core/entity-preview.js';
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import { EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
|
||||
/** The `room` filter of a list: "Room: <name>" (the SID until the name is known), linked to the room, removable. */
|
||||
@Component({
|
||||
|
|
@ -2,7 +2,7 @@ import { booleanAttribute, Component, computed, inject, InjectionToken, input, t
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
||||
import { RECONCILER_ESTIMATE } from '../format/labels.js';
|
||||
import { RECONCILER_ESTIMATE } from '../../format/labels.js';
|
||||
|
||||
/** The CE status domains; an edition registers more with {@link provideStatusStyles}. */
|
||||
export type StatusDomain = 'room' | 'participant' | 'egress' | 'ingress';
|
||||
|
|
@ -2,7 +2,7 @@ import { Component } from '@angular/core';
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { ToastService } from '../core/toast.js';
|
||||
import { ToastService } from '../../core/ui/toast.js';
|
||||
import { CopyButton } from './copy-button.js';
|
||||
|
||||
@Component({
|
||||
|
|
@ -3,8 +3,8 @@ import { MatIconButton } from '@angular/material/button';
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
||||
import { ClipboardService } from '../core/clipboard.js';
|
||||
import { ToastService } from '../core/toast.js';
|
||||
import { ClipboardService } from '../../core/ui/clipboard.js';
|
||||
import { ToastService } from '../../core/ui/toast.js';
|
||||
|
||||
export const COPIED_ICON_MS = 1_000;
|
||||
|
||||
|
|
@ -5,9 +5,9 @@ import { MatIcon } from '@angular/material/icon';
|
|||
import { MatOption, MatSelect } from '@angular/material/select';
|
||||
import { PAGE_SIZES, type PageSize } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { PreferencesService } from '../core/preferences.js';
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import { formatNumber } from '../format/formatters.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { formatNumber } from '../../format/formatters.js';
|
||||
|
||||
/** "Showing x–y of total", or "Showing x–y" when the total is unknown. */
|
||||
export function shownRange(rangeStart: number, rowCount: number, total?: number): string {
|
||||
|
|
@ -4,12 +4,12 @@ import { provideRouter, Router } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { childComponent, routeElement } from '../../testing/harness.js';
|
||||
import { BreakpointService, type ScreenSize } from '../core/breakpoints.js';
|
||||
import { PreferencesService } from '../core/preferences.js';
|
||||
import { childComponent, routeElement } from '../../../testing/harness.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { BreakpointService, type ScreenSize } from '../../core/ui/breakpoints.js';
|
||||
import { SectionCard } from '../tiles/section-card.js';
|
||||
import { CursorPaginator, shownRange } from './cursor-paginator.js';
|
||||
import { DataTable, type SortChange, TableCell, type TableColumn } from './data-table.js';
|
||||
import { SectionCard } from './section-card.js';
|
||||
|
||||
interface Room {
|
||||
sid: string;
|
||||
|
|
@ -21,12 +21,12 @@ import { MatOption, MatSelect } from '@angular/material/select';
|
|||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import type { SortOrder } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { BreakpointService } from '../core/breakpoints.js';
|
||||
import { PreferencesService } from '../core/preferences.js';
|
||||
import { ErrorBanner } from './error-banner.js';
|
||||
import { InfoTip } from './info-tip.js';
|
||||
import { SectionHeaderActions } from './section-card.js';
|
||||
import { Skeleton } from './skeleton.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { BreakpointService } from '../../core/ui/breakpoints.js';
|
||||
import { ErrorBanner } from '../feedback/error-banner.js';
|
||||
import { Skeleton } from '../feedback/skeleton.js';
|
||||
import { InfoTip } from '../info-tip/info-tip.js';
|
||||
import { SectionHeaderActions } from '../tiles/section-card.js';
|
||||
|
||||
export interface TableColumn {
|
||||
key: string;
|
||||
|
|
@ -5,10 +5,10 @@ import { RouterTestingHarness } from '@angular/router/testing';
|
|||
import { RANGE_PRESET_FROM, type RangePreset } from '@openvidu-dashboard/typings';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { childComponent, routeElement } from '../../testing/harness.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { PreferencesService } from '../core/preferences.js';
|
||||
import { SessionZoneService } from '../core/session-zone.js';
|
||||
import { childComponent, routeElement } from '../../../testing/harness.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { SessionZoneService } from '../../core/state/session-zone.js';
|
||||
import {
|
||||
DateTimeRangePicker,
|
||||
instantToWallClock,
|
||||
|
|
@ -26,12 +26,12 @@ import { formatRetentionLength, RANGE_PRESET_FROM, type RangePreset } from '@ope
|
|||
import { format } from 'date-fns';
|
||||
import { enUS } from 'date-fns/locale/en-US';
|
||||
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { browserTimeZone } from '../core/preferences.js';
|
||||
import { QueryStateService, type RangeParams } from '../core/query-state.js';
|
||||
import { resolveRange } from '../core/relative-time.js';
|
||||
import { formatNumber } from '../format/formatters.js';
|
||||
import { ZoneSelect } from './zone-select.js';
|
||||
import { browserTimeZone } from '../../core/preferences/preferences.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { QueryStateService, type RangeParams } from '../../core/state/query-state.js';
|
||||
import { resolveRange } from '../../core/state/relative-time.js';
|
||||
import { formatNumber } from '../../format/formatters.js';
|
||||
import { ZoneSelect } from '../zone/zone-select.js';
|
||||
|
||||
/** Opens the range picker of the header from a page, e.g. its "Widen range" action. */
|
||||
@Service()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, computed, input } from '@angular/core';
|
||||
|
||||
import { injectTicker } from '../core/ticker.js';
|
||||
import { formatDuration } from '../format/formatters.js';
|
||||
import { injectTicker } from '../../core/refresh/ticker.js';
|
||||
import { formatDuration } from '../../format/formatters.js';
|
||||
|
||||
export interface LiveDuration {
|
||||
start?: string | Date | null | undefined;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, computed, input, output } from '@angular/core';
|
||||
import { ApiRequestError, toApiRequestError } from '../core/api-error.js';
|
||||
import { ApiRequestError, toApiRequestError } from '../../core/http/api-error.js';
|
||||
import { Notice } from './notice.js';
|
||||
|
||||
/** Text for an error code; the server message stays for logs. */
|
||||
|
|
@ -2,7 +2,7 @@ import { Component } from '@angular/core';
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { ApiRequestError } from '../core/api-error.js';
|
||||
import { ApiRequestError } from '../../core/http/api-error.js';
|
||||
import { ErrorBanner } from './error-banner.js';
|
||||
import { Notice } from './notice.js';
|
||||
|
||||
|
|
@ -4,7 +4,7 @@ import { By } from '@angular/platform-browser';
|
|||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { settle } from '../../testing/fixtures.js';
|
||||
import { settle } from '../../../testing/fixtures.js';
|
||||
import { InfoTip } from './info-tip.js';
|
||||
|
||||
@Component({
|
||||
|
|
@ -2,7 +2,7 @@ import { NgTemplateOutlet } from '@angular/common';
|
|||
import { Component, computed, contentChild, Directive, inject, input, TemplateRef } from '@angular/core';
|
||||
import type { FeatureKey } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { FeatureService } from '../core/info.js';
|
||||
import { FeatureService } from '../../core/info.js';
|
||||
import { ProLockOverlay } from './pro-lock-overlay.js';
|
||||
|
||||
/** Content of a feature gate, instantiated only when the feature is enabled: `<ng-template ovdFeatureContent>`. */
|
||||
|
|
@ -6,16 +6,16 @@ import { provideRouter } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { routeElement } from '../../testing/harness.js';
|
||||
import { infoResponse } from '../../testing/fixtures.js';
|
||||
import { HttpService } from '../core/http.js';
|
||||
import { InfoService } from '../core/info.js';
|
||||
import { EntityPreviewService } from '../core/entity-preview.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { LockedPage } from '../pages/locked-page.js';
|
||||
import { BreadcrumbService } from '../routing/breadcrumbs.js';
|
||||
import { Breadcrumbs } from './breadcrumbs.js';
|
||||
import { infoResponse } from '../../../testing/fixtures.js';
|
||||
import { routeElement } from '../../../testing/harness.js';
|
||||
import { HttpService } from '../../core/http/http.js';
|
||||
import { InfoService } from '../../core/info.js';
|
||||
import { EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { BreadcrumbService } from '../../routing/breadcrumbs.js';
|
||||
import { Breadcrumbs } from '../../shell/breadcrumbs.js';
|
||||
import { FeatureContent, FeatureGate } from './feature-gate.js';
|
||||
import { LockedPage } from './locked-page.js';
|
||||
|
||||
@Component({ selector: 'ovd-quality-card', template: 'real quality card' })
|
||||
class QualityCard implements OnInit {
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import type { InfoResponse } from '@openvidu-dashboard/typings';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { infoResponse, settle, signIn, statusResponse } from '../../../testing/fixtures.js';
|
||||
import { AuthService } from '../../core/auth/auth.js';
|
||||
import { InfoService } from '../../core/info.js';
|
||||
import { EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { ToastService } from '../../core/ui/toast.js';
|
||||
import { DASHBOARD_ROUTES } from '../../routes.js';
|
||||
import { composeRoutes } from '../../routing/compose-routes.js';
|
||||
|
||||
function setup(): HttpTestingController {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideRouter(composeRoutes(DASHBOARD_ROUTES)),
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
{ provide: ToastService, useValue: { show: vi.fn() } }
|
||||
]
|
||||
});
|
||||
|
||||
return TestBed.inject(HttpTestingController);
|
||||
}
|
||||
|
||||
async function loadInfo(http: HttpTestingController, info: InfoResponse): Promise<void> {
|
||||
const load = TestBed.inject(InfoService).load();
|
||||
|
||||
http.expectOne('api/v1/info').flush(info);
|
||||
await load;
|
||||
}
|
||||
|
||||
describe('LockedPage', () => {
|
||||
it('shows the entity header from list data, the lock and a link back to the list with the range', async () => {
|
||||
const http = setup();
|
||||
|
||||
await signIn(TestBed.inject(AuthService), http);
|
||||
await loadInfo(http, infoResponse());
|
||||
TestBed.inject(EntityPreviewService).set('room', 'RM_ab12cd', {
|
||||
name: 'demo-room',
|
||||
status: 'active',
|
||||
startedAt: '2026-10-13T12:31:05Z'
|
||||
});
|
||||
|
||||
const harness = await RouterTestingHarness.create('/rooms?from=now-6h&to=now&tz=UTC');
|
||||
|
||||
await settle();
|
||||
|
||||
for (const request of http.match((req) => req.url.startsWith('api/v1/rooms'))) {
|
||||
request.flush({});
|
||||
}
|
||||
|
||||
await harness.navigateByUrl('/rooms/RM_ab12cd');
|
||||
|
||||
const element = harness.fixture.nativeElement as HTMLElement;
|
||||
|
||||
expect(element.querySelector('h1')?.textContent).toBe('demo-room');
|
||||
expect(element.querySelector('ovd-locked-page .title ovd-copy-button button')?.getAttribute('aria-label')).toBe(
|
||||
'Copy room name'
|
||||
);
|
||||
expect(element.querySelector('ovd-locked-page ovd-copy-chip')).toBeNull();
|
||||
expect(element.querySelector('ovd-locked-page ovd-status-badge')?.textContent).toContain('active');
|
||||
expect(element.querySelector('ovd-pro-lock-overlay')?.textContent).toContain(
|
||||
'Room detail is available in OpenVidu PRO'
|
||||
);
|
||||
expect(element.querySelector('ovd-pro-lock-overlay a[href^="/rooms"]')?.getAttribute('href')).toBe(
|
||||
'/rooms?from=now-6h&to=now&tz=UTC'
|
||||
);
|
||||
http.expectOne('api/v1/status').flush(statusResponse());
|
||||
http.verify();
|
||||
});
|
||||
});
|
||||
|
|
@ -2,14 +2,14 @@ import { Component, computed, inject } from '@angular/core';
|
|||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import type { FeatureKey, NavigationMeta } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { type EntityKind, EntityPreviewService } from '../core/entity-preview.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { ownData } from '../core/route-state.js';
|
||||
import { CopyButton } from '../components/copy-button.js';
|
||||
import { EmptyState } from '../components/empty-state.js';
|
||||
import { FeatureContent, FeatureGate } from '../components/feature-gate.js';
|
||||
import { StatusBadge, type StatusDomain } from '../components/status-badge.js';
|
||||
import { RECONCILER_ESTIMATE } from '../format/labels.js';
|
||||
import { type EntityKind, EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { ownData } from '../../core/state/route-state.js';
|
||||
import { RECONCILER_ESTIMATE } from '../../format/labels.js';
|
||||
import { StatusBadge, type StatusDomain } from '../badges/status-badge.js';
|
||||
import { CopyButton } from '../copy-button/copy-button.js';
|
||||
import { EmptyState } from '../feedback/empty-state.js';
|
||||
import { FeatureContent, FeatureGate } from './feature-gate.js';
|
||||
|
||||
const STATUS_DOMAINS: Readonly<Record<string, StatusDomain>> = {
|
||||
room: 'room',
|
||||
|
|
@ -2,7 +2,7 @@ import { Component, computed, input } from '@angular/core';
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import type { FeatureKey } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { EXTERNAL_LINKS } from '../core/links.js';
|
||||
import { EXTERNAL_LINKS } from '../../core/links.js';
|
||||
import { FEATURE_COPY } from './features.js';
|
||||
|
||||
/**
|
||||
|
|
@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing';
|
|||
import { provideRouter } from '@angular/router';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { RefreshRegistry, type RefreshScheduler } from '../core/refresh.js';
|
||||
import { RefreshRegistry, type RefreshScheduler } from '../../core/refresh/refresh.js';
|
||||
import { RefreshControl } from './refresh-control.js';
|
||||
|
||||
describe('RefreshControl', () => {
|
||||
|
|
@ -5,9 +5,9 @@ import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
|||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { REFRESH_INTERVALS_MS, type RefreshIntervalMs } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import { RefreshRegistry } from '../core/refresh.js';
|
||||
import { formatTimestamp } from '../format/formatters.js';
|
||||
import { RefreshRegistry } from '../../core/refresh/refresh.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { formatTimestamp } from '../../format/formatters.js';
|
||||
|
||||
export const REFRESH_LABELS: Readonly<Record<RefreshIntervalMs, string>> = {
|
||||
0: 'Off',
|
||||
|
|
@ -6,7 +6,7 @@ import { provideRouter, Router } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { childComponent, routeElement } from '../../testing/harness.js';
|
||||
import { childComponent, routeElement } from '../../../testing/harness.js';
|
||||
import { GlobalSearch, SEARCH_DEBOUNCE_MS, sidTarget } from './global-search.js';
|
||||
|
||||
@Component({ selector: 'ovd-page', template: 'page' })
|
||||
|
|
@ -11,11 +11,11 @@ import { type Params, Router } from '@angular/router';
|
|||
import { entityIdKind, type ListResponse } from '@openvidu-dashboard/typings';
|
||||
import { catchError, debounceTime, distinctUntilChanged, forkJoin, map, of, Subject, switchMap } from 'rxjs';
|
||||
|
||||
import type { EntityKind } from '../core/entity-preview.js';
|
||||
import { GLOBAL_SEARCH_ID } from '../core/focus.js';
|
||||
import { HttpService } from '../core/http.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { resolveRange } from '../core/relative-time.js';
|
||||
import { HttpService } from '../../core/http/http.js';
|
||||
import type { EntityKind } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { resolveRange } from '../../core/state/relative-time.js';
|
||||
import { GLOBAL_SEARCH_ID } from '../../core/ui/focus.js';
|
||||
import { DETAIL_PATHS, SHORT_SEARCH_HINT } from './list-search.js';
|
||||
|
||||
export const SEARCH_DEBOUNCE_MS = 300;
|
||||
|
|
@ -4,7 +4,7 @@ import { provideRouter, Router } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { routeElement } from '../../testing/harness.js';
|
||||
import { routeElement } from '../../../testing/harness.js';
|
||||
import {
|
||||
EXACT_ID_HINT,
|
||||
LIST_SEARCH_DEBOUNCE_MS,
|
||||
|
|
@ -7,9 +7,9 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||
import { Router } from '@angular/router';
|
||||
import { isEntityId } from '@openvidu-dashboard/typings';
|
||||
|
||||
import type { EntityKind } from '../core/entity-preview.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import type { EntityKind } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
|
||||
export const LIST_SEARCH_DEBOUNCE_MS = 300;
|
||||
export const SHORT_SEARCH_HINT = 'Type at least 2 characters';
|
||||
|
|
@ -4,8 +4,8 @@ import { provideRouter } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { routeElement } from '../../testing/harness.js';
|
||||
import { setMediaQuery } from '../../testing/media.js';
|
||||
import { routeElement } from '../../../testing/harness.js';
|
||||
import { setMediaQuery } from '../../../testing/media.js';
|
||||
import { HIGHLIGHT_MS, KpiTile, TICKING_MS } from './kpi-tile.js';
|
||||
import { SectionCard } from './section-card.js';
|
||||
|
||||
|
|
@ -13,8 +13,8 @@ import {
|
|||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { type Params, RouterLink } from '@angular/router';
|
||||
|
||||
import { APPROXIMATE_TEXT } from '../format/info-texts.js';
|
||||
import { InfoTip } from './info-tip.js';
|
||||
import { APPROXIMATE_TEXT } from '../../format/info-texts.js';
|
||||
import { InfoTip } from '../info-tip/info-tip.js';
|
||||
|
||||
export const HIGHLIGHT_MS = 600;
|
||||
/** Values that change more often than this (a duration ticking every second) are never highlighted. */
|
||||
|
|
@ -3,9 +3,9 @@ import { Component, computed, inject, Injectable, input, signal, type TemplateRe
|
|||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
|
||||
import { RouteStateService } from '../core/route-state.js';
|
||||
import { SectionStateService } from '../core/section-state.js';
|
||||
import { InfoTip } from './info-tip.js';
|
||||
import { RouteStateService } from '../../core/state/route-state.js';
|
||||
import { SectionStateService } from '../../core/state/section-state.js';
|
||||
import { InfoTip } from '../info-tip/info-tip.js';
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
|
|
@ -4,10 +4,10 @@ import { provideRouter, Router } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { routeElement } from '../../testing/harness.js';
|
||||
import { ListStateService } from '../core/list-state.js';
|
||||
import { browserTimeZone, PreferencesService } from '../core/preferences.js';
|
||||
import { SessionZoneService } from '../core/session-zone.js';
|
||||
import { routeElement } from '../../../testing/harness.js';
|
||||
import { browserTimeZone, PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { SessionZoneService } from '../../core/state/session-zone.js';
|
||||
import { ZoneChip } from './zone-chip.js';
|
||||
|
||||
@Component({ imports: [ZoneChip], template: '<ovd-zone-chip />' })
|
||||
|
|
@ -4,8 +4,8 @@ import { MatIcon } from '@angular/material/icon';
|
|||
import { MatDivider } from '@angular/material/list';
|
||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
|
||||
import { browserTimeZone } from '../core/preferences.js';
|
||||
import { QueryStateService } from '../core/query-state.js';
|
||||
import { browserTimeZone } from '../../core/preferences/preferences.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { ZoneSelect } from './zone-select.js';
|
||||
|
||||
/**
|
||||
|
|
@ -4,7 +4,7 @@ import { MatOption } from '@angular/material/core';
|
|||
import { MatFormField, MatLabel } from '@angular/material/form-field';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
|
||||
import { isTimeZone } from '../core/relative-time.js';
|
||||
import { isTimeZone } from '../../core/state/relative-time.js';
|
||||
|
||||
const MAX_ZONE_OPTIONS = 50;
|
||||
|
||||
|
|
@ -6,9 +6,9 @@ import { provideRouter, Router } from '@angular/router';
|
|||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { settle } from '../../testing/fixtures.js';
|
||||
import { settle } from '../../../testing/fixtures.js';
|
||||
import { HttpService } from '../http/http.js';
|
||||
import { authGuard, authInterceptor, AuthService } from './auth.js';
|
||||
import { HttpService } from './http.js';
|
||||
|
||||
@Component({ template: 'page' })
|
||||
class Page {}
|
||||
|
|
@ -10,7 +10,7 @@ import { type CanActivateFn, Router } from '@angular/router';
|
|||
import type { AuthTokenResponse, MeResponse } from '@openvidu-dashboard/typings';
|
||||
import { catchError, from, switchMap, throwError } from 'rxjs';
|
||||
|
||||
import { API_BASE, HttpService } from './http.js';
|
||||
import { API_BASE, HttpService } from '../http/http.js';
|
||||
|
||||
/** Requests that must not trigger the refresh-and-replay flow: the auth endpoints themselves. */
|
||||
export const SKIP_SESSION_REFRESH = new HttpContextToken<boolean>(() => false);
|
||||
|
|
@ -5,7 +5,7 @@ import { provideRouter } from '@angular/router';
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { infoResponse } from '../../testing/fixtures.js';
|
||||
import { HttpService } from './http.js';
|
||||
import { HttpService } from './http/http.js';
|
||||
import { FeatureService, InfoService, proFeatureInterceptor } from './info.js';
|
||||
|
||||
describe('InfoService', () => {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { computed, inject, Service, type Signal, signal } from '@angular/core';
|
|||
import type { FeatureKey, InfoPublicResponse, InfoResponse } from '@openvidu-dashboard/typings';
|
||||
import { catchError, throwError } from 'rxjs';
|
||||
|
||||
import { type ApiRequestError, toApiRequestError } from './api-error.js';
|
||||
import { AuthService } from './auth.js';
|
||||
import { API_BASE, HttpService } from './http.js';
|
||||
import { AuthService } from './auth/auth.js';
|
||||
import { type ApiRequestError, toApiRequestError } from './http/api-error.js';
|
||||
import { API_BASE, HttpService } from './http/http.js';
|
||||
|
||||
function isFullInfo(info: InfoResponse | InfoPublicResponse): info is InfoResponse {
|
||||
return 'features' in info;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
type TimeZonePreference
|
||||
} from '@openvidu-dashboard/typings';
|
||||
|
||||
import { isTimeZone } from './relative-time.js';
|
||||
import { isTimeZone } from '../state/relative-time.js';
|
||||
import { StorageService } from './storage.js';
|
||||
|
||||
export const PREFERENCES_KEY = 'ovd.prefs.v1';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { resetMediaQueries, setMediaQuery } from '../../testing/media.js';
|
||||
import { resetMediaQueries, setMediaQuery } from '../../../testing/media.js';
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { ThemeService } from './theme.js';
|
||||
|
||||
|
|
@ -11,10 +11,10 @@ import { MatIconRegistry } from '@angular/material/icon';
|
|||
import { DomSanitizer, Title } from '@angular/platform-browser';
|
||||
import { type RouterStateSnapshot, TitleStrategy } from '@angular/router';
|
||||
|
||||
import { authInterceptor, AuthService } from './auth.js';
|
||||
import { connectivityInterceptor } from './connectivity.js';
|
||||
import { authInterceptor, AuthService } from './auth/auth.js';
|
||||
import { connectivityInterceptor } from './http/connectivity.js';
|
||||
import { InfoService, proFeatureInterceptor } from './info.js';
|
||||
import { ThemeService } from './theme.js';
|
||||
import { ThemeService } from './preferences/theme.js';
|
||||
|
||||
export const PRODUCT_NAME = 'OpenVidu Dashboard';
|
||||
/** Icon sprite built by `scripts/icons.mjs`, relative to the document base. */
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing';
|
|||
import type { RefreshIntervalMs } from '@openvidu-dashboard/typings';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
import { injectRefreshScheduler, RefreshRegistry, RefreshScheduler } from './refresh.js';
|
||||
|
||||
class FakeDocument {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { DestroyRef, DOCUMENT, effect, inject, Service, type Signal, signal } from '@angular/core';
|
||||
import type { RefreshIntervalMs } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
|
||||
/** First retry delay after a failed refresh; it doubles up to `BACKOFF_MAX_MS`. */
|
||||
export const BACKOFF_START_MS = 10_000;
|
||||
|
|
@ -5,9 +5,9 @@ import { TestBed } from '@angular/core/testing';
|
|||
import { provideRouter } from '@angular/router';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { infoResponse, statusResponse } from '../../testing/fixtures.js';
|
||||
import { InfoService } from './info.js';
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { infoResponse, statusResponse } from '../../../testing/fixtures.js';
|
||||
import { InfoService } from '../info.js';
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
import { injectServerStatusPoll, ServerStatusService } from './server-status.js';
|
||||
|
||||
describe('ServerStatusService', () => {
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { computed, effect, inject, Service, signal, untracked } from '@angular/core';
|
||||
import { freshnessWindows, type RefreshIntervalMs, type StatusResponse } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { HttpService } from './http.js';
|
||||
import { InfoService } from './info.js';
|
||||
import { HttpService } from '../http/http.js';
|
||||
import { InfoService } from '../info.js';
|
||||
import { injectRefreshScheduler, type RefreshScheduler } from './refresh.js';
|
||||
|
||||
export const STATUS_POLL_MS: RefreshIntervalMs = 30_000;
|
||||
|
|
@ -4,7 +4,7 @@ import { NavigationEnd, type Params, Router } from '@angular/router';
|
|||
import { PAGE_SIZES, type PageSize, RANGE_PRESET_FROM, SortOrder } from '@openvidu-dashboard/typings';
|
||||
import { filter } from 'rxjs';
|
||||
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
import { QueryStateService, type RangeParams } from './query-state.js';
|
||||
import { isTimeZone } from './relative-time.js';
|
||||
import { RouteStateService } from './route-state.js';
|
||||
|
|
@ -7,8 +7,8 @@ import { RouterTestingHarness } from '@angular/router/testing';
|
|||
import { filter, firstValueFrom } from 'rxjs';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
import { ListStateService } from './list-state.js';
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import { QueryStateService } from './query-state.js';
|
||||
import { SessionZoneService } from './session-zone.js';
|
||||
|
||||
|
|
@ -2,8 +2,8 @@ import { computed, inject, Service } from '@angular/core';
|
|||
import { type Params, Router } from '@angular/router';
|
||||
import { PAGE_SIZES, type PageSize, SortOrder, type TimeExpression } from '@openvidu-dashboard/typings';
|
||||
|
||||
import type { QueryValue } from './http.js';
|
||||
import { PreferencesService } from './preferences.js';
|
||||
import type { QueryValue } from '../http/http.js';
|
||||
import { PreferencesService } from '../preferences/preferences.js';
|
||||
import { isTimeExpression, isTimeZone } from './relative-time.js';
|
||||
import { RouteStateService } from './route-state.js';
|
||||
import { SessionZoneService } from './session-zone.js';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { computed, inject, type Signal } from '@angular/core';
|
||||
import type { InfoLimits } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { InfoService } from './info.js';
|
||||
import { InfoService } from '../info.js';
|
||||
|
||||
const HOUR_MS = 3_600_000;
|
||||
const DAY_MS = 24 * HOUR_MS;
|
||||
|
|
@ -10,7 +10,7 @@ import { describe, expect, it } from 'vitest';
|
|||
import { infoResponse, settle } from '../../../testing/fixtures.js';
|
||||
import { routeElement } from '../../../testing/harness.js';
|
||||
import { InfoService } from '../../core/info.js';
|
||||
import { LiveCountersService } from '../../core/live-counters.js';
|
||||
import { LiveCountersService } from '../../core/refresh/live-counters.js';
|
||||
import { AgentsPage } from './agents-page.js';
|
||||
|
||||
@Component({ selector: 'ovd-blank', template: '' })
|
||||
|
|
|
|||
|
|
@ -10,27 +10,27 @@ import {
|
|||
ParticipantSort,
|
||||
ParticipantStatus
|
||||
} from '@openvidu-dashboard/typings';
|
||||
import { CopyButton } from '../../components/copy-button.js';
|
||||
import { CursorPaginator } from '../../components/cursor-paginator.js';
|
||||
import { DataTable, type SortChange, TableCell, type TableColumn } from '../../components/data-table.js';
|
||||
import { RangePickerService } from '../../components/date-time-range-picker.js';
|
||||
import { Duration } from '../../components/duration.js';
|
||||
import { EmptyState } from '../../components/empty-state.js';
|
||||
import { KpiTile } from '../../components/kpi-tile.js';
|
||||
import { exactListId, ListSearch, listSearchQuery } from '../../components/list-search.js';
|
||||
import { Notice } from '../../components/notice.js';
|
||||
import { RoomChip } from '../../components/room-chip.js';
|
||||
import { SourceIcon } from '../../components/source-icon.js';
|
||||
import { StatusBadge } from '../../components/status-badge.js';
|
||||
import { TileRow } from '../../components/tile-row.js';
|
||||
import { EntityPreviewService } from '../../core/entity-preview.js';
|
||||
import { injectListLoader } from '../../core/list-loader.js';
|
||||
import { ListStateService } from '../../core/list-state.js';
|
||||
import { LiveCountersService } from '../../core/live-counters.js';
|
||||
import { QueryStateService } from '../../core/query-state.js';
|
||||
import { injectRankedSortLimits, rankedSortReasons } from '../../core/ranked-sort.js';
|
||||
import { resolveRange } from '../../core/relative-time.js';
|
||||
import { StorageService } from '../../core/storage.js';
|
||||
import { RoomChip } from '../../components/badges/room-chip.js';
|
||||
import { SourceIcon } from '../../components/badges/source-icon.js';
|
||||
import { StatusBadge } from '../../components/badges/status-badge.js';
|
||||
import { CopyButton } from '../../components/copy-button/copy-button.js';
|
||||
import { CursorPaginator } from '../../components/data-table/cursor-paginator.js';
|
||||
import { DataTable, type SortChange, TableCell, type TableColumn } from '../../components/data-table/data-table.js';
|
||||
import { RangePickerService } from '../../components/date-time-range-picker/date-time-range-picker.js';
|
||||
import { Duration } from '../../components/duration/duration.js';
|
||||
import { EmptyState } from '../../components/feedback/empty-state.js';
|
||||
import { Notice } from '../../components/feedback/notice.js';
|
||||
import { exactListId, ListSearch, listSearchQuery } from '../../components/search/list-search.js';
|
||||
import { KpiTile } from '../../components/tiles/kpi-tile.js';
|
||||
import { TileRow } from '../../components/tiles/tile-row.js';
|
||||
import { StorageService } from '../../core/preferences/storage.js';
|
||||
import { injectListLoader } from '../../core/refresh/list-loader.js';
|
||||
import { LiveCountersService } from '../../core/refresh/live-counters.js';
|
||||
import { EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { injectRankedSortLimits, rankedSortReasons } from '../../core/state/ranked-sort.js';
|
||||
import { resolveRange } from '../../core/state/relative-time.js';
|
||||
import { formatNumber, formatTimestamp } from '../../format/formatters.js';
|
||||
import { injectLiveCounters } from '../overview/overview-data.js';
|
||||
import { participantPreview } from '../participants/participant-display.js';
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import { MatButton } from '@angular/material/button';
|
|||
import { MatIcon } from '@angular/material/icon';
|
||||
import type { FeatureKey } from '@openvidu-dashboard/typings';
|
||||
|
||||
import { EmptyState } from '../../components/empty-state.js';
|
||||
import { ErrorBanner } from '../../components/error-banner.js';
|
||||
import { ProLockOverlay } from '../../components/pro-lock-overlay.js';
|
||||
import { SectionCard } from '../../components/section-card.js';
|
||||
import { Skeleton } from '../../components/skeleton.js';
|
||||
import { EmptyState } from '../../components/feedback/empty-state.js';
|
||||
import { ErrorBanner } from '../../components/feedback/error-banner.js';
|
||||
import { Skeleton } from '../../components/feedback/skeleton.js';
|
||||
import { ProLockOverlay } from '../../components/lock/pro-lock-overlay.js';
|
||||
import { SectionCard } from '../../components/tiles/section-card.js';
|
||||
import { FeatureService } from '../../core/info.js';
|
||||
|
||||
/** Content of an analytics card, instantiated only once it has data: `<ng-template ovdCardContent>`. */
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import {
|
|||
type FeatureKey
|
||||
} from '@openvidu-dashboard/typings';
|
||||
|
||||
import { HttpService } from '../../core/http.js';
|
||||
import type { RangeParams } from '../../core/query-state.js';
|
||||
import { HttpService } from '../../core/http/http.js';
|
||||
import type { RangeParams } from '../../core/state/query-state.js';
|
||||
import { apiRange } from '../rooms/rooms-data.js';
|
||||
import { hourlyGranularity } from './analytics-granularity.js';
|
||||
import type { AnalyticsProCard } from './analytics-seams.js';
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import {
|
|||
seriesRequests
|
||||
} from '../../../testing/analytics.js';
|
||||
import { settle } from '../../../testing/fixtures.js';
|
||||
import { PreferencesService } from '../../core/preferences.js';
|
||||
import { RefreshRegistry } from '../../core/refresh.js';
|
||||
import { ToastService } from '../../core/toast.js';
|
||||
import { PreferencesService } from '../../core/preferences/preferences.js';
|
||||
import { RefreshRegistry } from '../../core/refresh/refresh.js';
|
||||
import { ToastService } from '../../core/ui/toast.js';
|
||||
import { analyticsPlan } from './analytics-data.js';
|
||||
import { autoGranularity, granularityOptions } from './analytics-granularity.js';
|
||||
import { dimensionLabel } from './analytics-page.js';
|
||||
|
|
|
|||
|
|
@ -25,21 +25,21 @@ import {
|
|||
import { BreakdownChart } from '../../charts/breakdown-chart.js';
|
||||
import { seriesNote } from '../../charts/chart-note.js';
|
||||
import type { BreakdownEntry, ChartSeries } from '../../charts/chart-options.js';
|
||||
import { TimeSeriesChart } from '../../charts/time-series-chart.js';
|
||||
import { RangePickerService } from '../../components/date-time-range-picker.js';
|
||||
import { errorMessage } from '../../components/error-banner.js';
|
||||
import { KpiTile } from '../../components/kpi-tile.js';
|
||||
import { Notice } from '../../components/notice.js';
|
||||
import { TileRow } from '../../components/tile-row.js';
|
||||
import { toApiRequestError } from '../../core/api-error.js';
|
||||
import { TimeSeriesChart } from '../../charts/time-series-chart/time-series-chart.js';
|
||||
import { RangePickerService } from '../../components/date-time-range-picker/date-time-range-picker.js';
|
||||
import { errorMessage } from '../../components/feedback/error-banner.js';
|
||||
import { Notice } from '../../components/feedback/notice.js';
|
||||
import { KpiTile } from '../../components/tiles/kpi-tile.js';
|
||||
import { TileRow } from '../../components/tiles/tile-row.js';
|
||||
import { toApiRequestError } from '../../core/http/api-error.js';
|
||||
import { InfoService } from '../../core/info.js';
|
||||
import { injectListLoader } from '../../core/list-loader.js';
|
||||
import { ListStateService } from '../../core/list-state.js';
|
||||
import { QueryStateService } from '../../core/query-state.js';
|
||||
import { resolveRange } from '../../core/relative-time.js';
|
||||
import { ToastService } from '../../core/toast.js';
|
||||
import { INFO_TEXTS } from '../../format/info-texts.js';
|
||||
import { injectListLoader } from '../../core/refresh/list-loader.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { QueryStateService } from '../../core/state/query-state.js';
|
||||
import { resolveRange } from '../../core/state/relative-time.js';
|
||||
import { ToastService } from '../../core/ui/toast.js';
|
||||
import { formatBytes, formatDuration, formatNumber, formatPercent } from '../../format/formatters.js';
|
||||
import { INFO_TEXTS } from '../../format/info-texts.js';
|
||||
import {
|
||||
CONNECTION_TYPE_LABELS,
|
||||
EGRESS_TYPE_LABELS,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import type {
|
|||
SortOrder
|
||||
} from '@openvidu-dashboard/typings';
|
||||
|
||||
import { HttpService } from '../../core/http.js';
|
||||
import type { RangeParams } from '../../core/query-state.js';
|
||||
import { HttpService } from '../../core/http/http.js';
|
||||
import type { RangeParams } from '../../core/state/query-state.js';
|
||||
import { apiRange } from '../rooms/rooms-data.js';
|
||||
|
||||
export interface EgressListRequest {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type EgressListItem, type EgressOutputKind, type EgressStatus, isEntityId } from '@openvidu-dashboard/typings';
|
||||
|
||||
import type { EntityPreview } from '../../core/entity-preview.js';
|
||||
import type { EntityPreview } from '../../core/state/entity-preview.js';
|
||||
|
||||
/** Icon of each output kind. */
|
||||
export const EGRESS_OUTPUT_ICONS: Readonly<Record<EgressOutputKind, string>> = {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { EntityPreviewService } from '../../core/entity-preview.js';
|
||||
import { ListStateService } from '../../core/list-state.js';
|
||||
import { LockedPage } from '../../pages/locked-page.js';
|
||||
import { LockedPage } from '../../components/lock/locked-page.js';
|
||||
import { EntityPreviewService } from '../../core/state/entity-preview.js';
|
||||
import { ListStateService } from '../../core/state/list-state.js';
|
||||
import { EgressDataService } from './egress-data.js';
|
||||
import { egressPreview, isEgressId } from './egress-display.js';
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue