From 5d60670bb6c9c7b352f92ad7f7a32f68bd7ba223 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 16 Nov 2023 14:55:56 +0100 Subject: [PATCH 1/2] feat: observatory dashboard Signed-off-by: David Dal Busco --- .../observatory/observatory.did.d.ts | 5 ++ .../observatory/observatory.factory.did.js | 7 ++- .../observatory/observatory.factory.did.mjs | 7 ++- src/frontend/src/lib/api/observatory.api.ts | 11 +++- .../observatory/ObservatoryDashboard.svelte | 57 +++++++++++++++++++ .../ObservatorySettings.svelte} | 2 +- src/frontend/src/lib/i18n/en.json | 7 ++- src/frontend/src/lib/i18n/it.json | 7 ++- src/frontend/src/lib/i18n/zh-cn.json | 7 ++- src/frontend/src/lib/types/i18n.d.ts | 5 +- .../src/routes/(monitor)/+layout.svelte | 5 -- .../routes/(monitor)/analytics/+layout.svelte | 5 ++ .../(monitor)/monitoring/+layout.svelte | 9 +++ .../monitoring/+page.svelte | 23 ++++++-- .../src/routes/(monitor)/monitoring/+page.ts | 5 ++ .../routes/(sub)/monitoring/+layout.svelte | 18 ------ src/observatory/observatory.did | 2 + src/observatory/src/lib.rs | 11 +++- src/observatory/src/store.rs | 9 +++ 19 files changed, 156 insertions(+), 46 deletions(-) create mode 100644 src/frontend/src/lib/components/observatory/ObservatoryDashboard.svelte rename src/frontend/src/lib/components/{mission-control/MissionControlObservatory.svelte => observatory/ObservatorySettings.svelte} (98%) create mode 100644 src/frontend/src/routes/(monitor)/monitoring/+layout.svelte rename src/frontend/src/routes/{(sub) => (monitor)}/monitoring/+page.svelte (54%) create mode 100644 src/frontend/src/routes/(monitor)/monitoring/+page.ts delete mode 100644 src/frontend/src/routes/(sub)/monitoring/+layout.svelte diff --git a/src/declarations/observatory/observatory.did.d.ts b/src/declarations/observatory/observatory.did.d.ts index 549a16a74..d6ab54e2b 100644 --- a/src/declarations/observatory/observatory.did.d.ts +++ b/src/declarations/observatory/observatory.did.d.ts @@ -1,6 +1,10 @@ import type { ActorMethod } from '@dfinity/agent'; import type { Principal } from '@dfinity/principal'; +export interface ArchiveStatuses { + statuses: Result_1; + timestamp: bigint; +} export interface CanisterStatusResponse { status: CanisterStatusType; memory_size: bigint; @@ -79,6 +83,7 @@ export interface SetCronTab { export interface _SERVICE { del_controllers: ActorMethod<[DeleteControllersArgs], undefined>; get_cron_tab: ActorMethod<[], [] | [CronTab]>; + get_statuses: ActorMethod<[], [] | [ArchiveStatuses]>; list_statuses: ActorMethod<[ListStatusesArgs], Array>; set_controllers: ActorMethod<[SetControllersArgs], undefined>; set_cron_tab: ActorMethod<[SetCronTab], CronTab>; diff --git a/src/declarations/observatory/observatory.factory.did.js b/src/declarations/observatory/observatory.factory.did.js index a28b9fe70..73d810ec4 100644 --- a/src/declarations/observatory/observatory.factory.did.js +++ b/src/declarations/observatory/observatory.factory.did.js @@ -24,7 +24,6 @@ export const idlFactory = ({ IDL }) => { mission_control_id: IDL.Principal, created_at: IDL.Nat64 }); - const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) }); const CanisterStatusType = IDL.Variant({ stopped: IDL.Null, stopping: IDL.Null, @@ -57,6 +56,11 @@ export const idlFactory = ({ IDL }) => { mission_control: Result }); const Result_1 = IDL.Variant({ Ok: SegmentsStatuses, Err: IDL.Text }); + const ArchiveStatuses = IDL.Record({ + statuses: Result_1, + timestamp: IDL.Nat64 + }); + const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) }); const ListStatuses = IDL.Record({ cron_jobs: CronJobs, statuses: Result_1, @@ -83,6 +87,7 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ del_controllers: IDL.Func([DeleteControllersArgs], [], []), get_cron_tab: IDL.Func([], [IDL.Opt(CronTab)], ['query']), + get_statuses: IDL.Func([], [IDL.Opt(ArchiveStatuses)], ['query']), list_statuses: IDL.Func([ListStatusesArgs], [IDL.Vec(ListStatuses)], ['query']), set_controllers: IDL.Func([SetControllersArgs], [], []), set_cron_tab: IDL.Func([SetCronTab], [CronTab], []), diff --git a/src/declarations/observatory/observatory.factory.did.mjs b/src/declarations/observatory/observatory.factory.did.mjs index a28b9fe70..73d810ec4 100644 --- a/src/declarations/observatory/observatory.factory.did.mjs +++ b/src/declarations/observatory/observatory.factory.did.mjs @@ -24,7 +24,6 @@ export const idlFactory = ({ IDL }) => { mission_control_id: IDL.Principal, created_at: IDL.Nat64 }); - const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) }); const CanisterStatusType = IDL.Variant({ stopped: IDL.Null, stopping: IDL.Null, @@ -57,6 +56,11 @@ export const idlFactory = ({ IDL }) => { mission_control: Result }); const Result_1 = IDL.Variant({ Ok: SegmentsStatuses, Err: IDL.Text }); + const ArchiveStatuses = IDL.Record({ + statuses: Result_1, + timestamp: IDL.Nat64 + }); + const ListStatusesArgs = IDL.Record({ time_delta: IDL.Opt(IDL.Nat64) }); const ListStatuses = IDL.Record({ cron_jobs: CronJobs, statuses: Result_1, @@ -83,6 +87,7 @@ export const idlFactory = ({ IDL }) => { return IDL.Service({ del_controllers: IDL.Func([DeleteControllersArgs], [], []), get_cron_tab: IDL.Func([], [IDL.Opt(CronTab)], ['query']), + get_statuses: IDL.Func([], [IDL.Opt(ArchiveStatuses)], ['query']), list_statuses: IDL.Func([ListStatusesArgs], [IDL.Vec(ListStatuses)], ['query']), set_controllers: IDL.Func([SetControllersArgs], [], []), set_cron_tab: IDL.Func([SetCronTab], [CronTab], []), diff --git a/src/frontend/src/lib/api/observatory.api.ts b/src/frontend/src/lib/api/observatory.api.ts index 6232a2964..5fbb3beb2 100644 --- a/src/frontend/src/lib/api/observatory.api.ts +++ b/src/frontend/src/lib/api/observatory.api.ts @@ -1,11 +1,16 @@ -import type { CronJobs, CronTab } from '$declarations/observatory/observatory.did'; +import type { ArchiveStatuses, CronJobs, CronTab } from '$declarations/observatory/observatory.did'; import { getObservatoryActor } from '$lib/utils/actor.juno.utils'; import type { Principal } from '@dfinity/principal'; import { toNullable } from '@dfinity/utils'; export const getCronTab = async (): Promise<[] | [CronTab]> => { - const actor = await getObservatoryActor(); - return actor.get_cron_tab(); + const { get_cron_tab } = await getObservatoryActor(); + return get_cron_tab(); +}; + +export const getStatuses = async (): Promise<[] | [ArchiveStatuses]> => { + const { get_statuses } = await getObservatoryActor(); + return get_statuses(); }; export const setCronTab = async ({ diff --git a/src/frontend/src/lib/components/observatory/ObservatoryDashboard.svelte b/src/frontend/src/lib/components/observatory/ObservatoryDashboard.svelte new file mode 100644 index 000000000..1b7761a9e --- /dev/null +++ b/src/frontend/src/lib/components/observatory/ObservatoryDashboard.svelte @@ -0,0 +1,57 @@ + + +
+ {#if loading} + {$i18n.core.loading} + {:else if isNullish(timestamp)} +
+

+ {$i18n.observatory.disabled_go_settings} + +

+
+ {:else} +
+ + {$i18n.observatory.last_data_collection} +

{formatToDate(timestamp)}

+
+
+ {/if} +
diff --git a/src/frontend/src/lib/components/mission-control/MissionControlObservatory.svelte b/src/frontend/src/lib/components/observatory/ObservatorySettings.svelte similarity index 98% rename from src/frontend/src/lib/components/mission-control/MissionControlObservatory.svelte rename to src/frontend/src/lib/components/observatory/ObservatorySettings.svelte index 02a595197..523fa9c00 100644 --- a/src/frontend/src/lib/components/mission-control/MissionControlObservatory.svelte +++ b/src/frontend/src/lib/components/observatory/ObservatorySettings.svelte @@ -98,7 +98,7 @@
{#if loading} - {$i18n.observatory.loading} + {$i18n.core.loading} {:else}
diff --git a/src/frontend/src/lib/i18n/en.json b/src/frontend/src/lib/i18n/en.json index 738f424bf..8071e131c 100644 --- a/src/frontend/src/lib/i18n/en.json +++ b/src/frontend/src/lib/i18n/en.json @@ -385,8 +385,7 @@ }, "observatory": { "title": "Monitoring", - "overview": "Overview", - "loading": "Loading your settings", + "dashboard": "Dashboard", "monitoring": "Monitoring", "enabled": "Enabled", "disabled": "Disabled", @@ -395,7 +394,9 @@ "email_notifications": "Email notifications", "email_notifications_placeholder": "Enter an email to receive notifications", "cycles_threshold": "TCycles threshold (min 0.5)", - "cycles_threshold_placeholder": "A threshold to limit the notifications" + "cycles_threshold_placeholder": "A threshold to limit the notifications", + "last_data_collection": "Last data collection", + "disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the" }, "settings": { "title": "Settings", diff --git a/src/frontend/src/lib/i18n/it.json b/src/frontend/src/lib/i18n/it.json index c7761d4ab..56b94eba9 100644 --- a/src/frontend/src/lib/i18n/it.json +++ b/src/frontend/src/lib/i18n/it.json @@ -385,8 +385,7 @@ }, "observatory": { "title": "Osservatorio", - "overview": "Panoramica", - "loading": "Carica le tue impostazioni", + "dashboard": "Dashboard", "monitoring": "Monitoraggio", "enabled": "Abilitato", "disabled": "Disabilitato", @@ -395,7 +394,9 @@ "email_notifications": "Notifiche email", "email_notifications_placeholder": "Inserisci la tua email per ricevere notifiche", "cycles_threshold": "Soglia TCycles (min 0.5)", - "cycles_threshold_placeholder": "Una soglia per limitare le notifiche" + "cycles_threshold_placeholder": "Una soglia per limitare le notifiche", + "last_data_collection": "Last data collection", + "disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the" }, "settings": { "title": "Impostazioni", diff --git a/src/frontend/src/lib/i18n/zh-cn.json b/src/frontend/src/lib/i18n/zh-cn.json index 47450f6f7..a95feb660 100644 --- a/src/frontend/src/lib/i18n/zh-cn.json +++ b/src/frontend/src/lib/i18n/zh-cn.json @@ -385,8 +385,7 @@ }, "observatory": { "title": "Observatory", - "overview": "开要", - "loading": "加载设置", + "dashboard": "Dashboard", "monitoring": "监控", "enabled": "已启用", "disabled": "已禁用", @@ -395,7 +394,9 @@ "email_notifications": "邮件提醒", "email_notifications_placeholder": "输入用于接受提醒的邮箱", "cycles_threshold": "TCycles 阈值 (最小 0.5)", - "cycles_threshold_placeholder": "阈值设置提醒" + "cycles_threshold_placeholder": "阈值设置提醒", + "last_data_collection": "Last data collection", + "disabled_go_settings": "The monitoring feature appears to be disabled. You can enable it by going to the" }, "settings": { "title": "设置", diff --git a/src/frontend/src/lib/types/i18n.d.ts b/src/frontend/src/lib/types/i18n.d.ts index bfc9ba204..5af032608 100644 --- a/src/frontend/src/lib/types/i18n.d.ts +++ b/src/frontend/src/lib/types/i18n.d.ts @@ -410,8 +410,7 @@ interface I18nUsers { interface I18nObservatory { title: string; - overview: string; - loading: string; + dashboard: string; monitoring: string; enabled: string; disabled: string; @@ -421,6 +420,8 @@ interface I18nObservatory { email_notifications_placeholder: string; cycles_threshold: string; cycles_threshold_placeholder: string; + last_data_collection: string; + disabled_go_settings: string; } interface I18nSettings { diff --git a/src/frontend/src/routes/(monitor)/+layout.svelte b/src/frontend/src/routes/(monitor)/+layout.svelte index 7c105cce0..56921a6cc 100644 --- a/src/frontend/src/routes/(monitor)/+layout.svelte +++ b/src/frontend/src/routes/(monitor)/+layout.svelte @@ -2,11 +2,6 @@ import Layout from '$lib/components/ui/Layout.svelte'; import Footer from '$lib/components/ui/Footer.svelte'; import Navbar from '$lib/components/core/Navbar.svelte'; - import { missionControlStore } from '$lib/stores/mission-control.store'; - import { loadSatellites } from '$lib/services/satellites.services'; - - $: $missionControlStore, - (async () => await loadSatellites({ missionControl: $missionControlStore }))(); diff --git a/src/frontend/src/routes/(monitor)/analytics/+layout.svelte b/src/frontend/src/routes/(monitor)/analytics/+layout.svelte index 2ed97f332..e7052c3b2 100644 --- a/src/frontend/src/routes/(monitor)/analytics/+layout.svelte +++ b/src/frontend/src/routes/(monitor)/analytics/+layout.svelte @@ -2,8 +2,13 @@ import { onMount } from 'svelte'; import { layoutTitle } from '$lib/stores/layout.store'; import { i18n } from '$lib/stores/i18n.store'; + import { missionControlStore } from '$lib/stores/mission-control.store'; + import { loadSatellites } from '$lib/services/satellites.services'; onMount(() => layoutTitle.set($i18n.analytics.title)); + + $: $missionControlStore, + (async () => await loadSatellites({ missionControl: $missionControlStore }))(); diff --git a/src/frontend/src/routes/(monitor)/monitoring/+layout.svelte b/src/frontend/src/routes/(monitor)/monitoring/+layout.svelte new file mode 100644 index 000000000..7871877e4 --- /dev/null +++ b/src/frontend/src/routes/(monitor)/monitoring/+layout.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/frontend/src/routes/(sub)/monitoring/+page.svelte b/src/frontend/src/routes/(monitor)/monitoring/+page.svelte similarity index 54% rename from src/frontend/src/routes/(sub)/monitoring/+page.svelte rename to src/frontend/src/routes/(monitor)/monitoring/+page.svelte index d577e510c..044eccaa8 100644 --- a/src/frontend/src/routes/(sub)/monitoring/+page.svelte +++ b/src/frontend/src/routes/(monitor)/monitoring/+page.svelte @@ -11,29 +11,42 @@ import IdentityGuard from '$lib/components/guards/IdentityGuard.svelte'; import { nonNullish } from '@dfinity/utils'; import { missionControlStore } from '$lib/stores/mission-control.store'; - import MissionControlObservatory from '$lib/components/mission-control/MissionControlObservatory.svelte'; + import ObservatorySettings from '$lib/components/observatory/ObservatorySettings.svelte'; + import ObservatoryDashboard from '$lib/components/observatory/ObservatoryDashboard.svelte'; + import { initTabId } from '$lib/utils/tabs.utils'; const tabs: Tab[] = [ { id: Symbol('1'), - labelKey: 'observatory.overview' + labelKey: 'observatory.dashboard' + }, + { + id: Symbol('2'), + labelKey: 'core.settings' } ]; const store = writable({ - tabId: tabs[0].id, + tabId: initTabId(tabs), tabs }); setContext(TABS_CONTEXT_KEY, { store }); + + $: store.set({ + tabId: initTabId(tabs), + tabs + }); - {#if nonNullish($missionControlStore)} - + {#if $store.tabId === $store.tabs[0].id} + + {:else if $store.tabId === $store.tabs[1].id && nonNullish($missionControlStore)} + {/if} diff --git a/src/frontend/src/routes/(monitor)/monitoring/+page.ts b/src/frontend/src/routes/(monitor)/monitoring/+page.ts new file mode 100644 index 000000000..677692e77 --- /dev/null +++ b/src/frontend/src/routes/(monitor)/monitoring/+page.ts @@ -0,0 +1,5 @@ +import { loadRouteSatellite, type RouteSatellite } from '$lib/utils/nav.utils'; +import type { LoadEvent } from '@sveltejs/kit'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = ($event: LoadEvent): RouteSatellite => loadRouteSatellite($event); diff --git a/src/frontend/src/routes/(sub)/monitoring/+layout.svelte b/src/frontend/src/routes/(sub)/monitoring/+layout.svelte deleted file mode 100644 index 54f0cdff2..000000000 --- a/src/frontend/src/routes/(sub)/monitoring/+layout.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - -