diff --git a/front/admin/src/routes/(online)/(app)/+page.svelte b/front/admin/src/routes/(online)/(app)/+page.svelte index b16a9d4a..17740534 100644 --- a/front/admin/src/routes/(online)/(app)/+page.svelte +++ b/front/admin/src/routes/(online)/(app)/+page.svelte @@ -3,6 +3,7 @@ import Page from "$lib/components/Page.svelte"; import { locale } from "$lib/locale"; import Icon from "$share/Icon.svelte"; + import StationSelector from "$share/Map/StationSelector.svelte"; import StatsMap from "$share/Map/StatsMap.svelte"; import { ripple } from "$share/ripple"; import { mdiAccountMultipleOutline, mdiAccountOutline, mdiCurrencyUsd, mdiPoll, mdiRadioTower, mdiShieldAccountOutline } from "@mdi/js"; @@ -15,14 +16,14 @@ let map_view: "now" | "last_24h" | "last_7d" | "last_30d" = "now"; - export const snapshot = { - capture: () => { - return { map_view } - }, - - restore: (ctx: { map_view: typeof map_view }) => { - map_view = ctx.map_view; - } + let map_selector_data: import("$share/Map/StationSelector.svelte").Data = { + all_kind: "all", + kind: "all", + record_id: "", + station: null, + stations: data.stations.items, + stats: data.stats, + storage_public_url: data.config.storage_public_url, } @@ -173,8 +174,13 @@
+ +
+ +
+ export let data: import("./$types").PageData; import StatsMap from "$share/Map/StatsMap.svelte"; - import type { Stats } from "$share/Map/StatsMap.svelte"; - import { click_out } from "$share/actions"; - import { _get, _patch, action } from "$share/net.client"; - import { ripple } from "$share/ripple"; + import { _get, _patch } from "$share/net.client"; - let selector_state: { kind: "account" | "station", record_id: string, data: Stats, station: typeof data.stations.items[number] | null } = { - kind: "account", - record_id: data.account._id, - data: data.stats, - station: null, - }; - import type { View } from "$share/Map/StatsMap.svelte"; import { locale } from "$lib/locale"; - import { logical_fly } from "$share/transition"; - - let view: View = "now"; - - let _token = 0; - - const select = action(async (station: typeof data.stations.items[number] | null) => { - selector_open = false; - if(station?._id === selector_state.station?._id) return; - const token = ++_token; - if(station) { - const { stats }: import("$api/stations/[station]/stream-stats/GET/Output").Output = - await _get(`/api/stations/${station._id}/stream-stats`); - if(token === _token) { - selector_state = { - kind: "station", - record_id: station._id, - data: stats, - station, - } - } - } else { - const { stats }: import("$api/accounts/[account]/stream-stats/GET/Output").Output = - await _get(`/api/accounts/${data.account._id}/stream-stats`); - if(token === _token) { - selector_state = { - kind: "account", - record_id: data.account._id, - data: stats, - station: null, - } - } - } - }) - - let selector_open = false; - - const close_selector = () => { - selector_open = false - } + + import type { Data } from "$share/Map/StationSelector.svelte"; + import StationSelector from "$share/Map/StationSelector.svelte"; - const toggle_selector = () => { - selector_open = !selector_open - } - - const selector_menu_click_out = () => { - setTimeout(close_selector, 2); + let selection_data: Data = { + all_kind: "account", + account_id: data.account._id, + station: null, + kind: "account", + record_id: data.account._id, + stations: data.stations.items, + stats: data.stats, + storage_public_url: data.config.storage_public_url, } - const units = [ "B", "KB", "MB", "GB", "TB" ]; + let view: View = "now"; -
-
-
- -
- {#if selector_open} -
- - {#each data.account_stations as station (station._id)} - - {/each} -
- {/if} -
-
-
- - +
+ +
+
+ + +
diff --git a/front/app/src/routes/(root)/(online)/(app)/accounts/[account]/+page.svelte b/front/app/src/routes/(root)/(online)/(app)/accounts/[account]/+page.svelte index 7402b881..eb002ac1 100644 --- a/front/app/src/routes/(root)/(online)/(app)/accounts/[account]/+page.svelte +++ b/front/app/src/routes/(root)/(online)/(app)/accounts/[account]/+page.svelte @@ -2,17 +2,22 @@ export let data: import("./$types").PageData; import Page from "$lib/components/Page.svelte"; import StatsMap from "$share/Map/StatsMap.svelte"; - import type { Stats } from "$share/Map/StatsMap.svelte"; - import { click_out, intersect } from "$share/actions"; + import { intersect } from "$share/actions"; import { _get, _patch, action } from "$share/net.client"; import { ripple } from "$share/ripple"; - - let selector_state: { kind: "account" | "station", record_id: string, data: Stats, station: typeof data.stations.items[number] | null } = { + + let current_account_stations = data.stations.items.filter(item => item.account_id === data.account._id); + + let map_selector_data: import("$share/Map/StationSelector.svelte").Data = { + all_kind: "account", kind: "account", + account_id: data.account._id, record_id: data.account._id, - data: data.stats, station: null, - }; + stations: current_account_stations, + storage_public_url: data.config.storage_public_url, + stats: data.stats, + } import type { View } from "$share/Map/StatsMap.svelte"; import { default_logger } from "$share/logger"; @@ -29,60 +34,10 @@ import { _string } from "$share/formy/validate"; import AccountStationItem from "./account-station-item.svelte"; import { locale } from "$lib/locale"; - import { logical_fly } from "$share/transition"; + import StationSelector from "$share/Map/StationSelector.svelte"; - $: current_account_stations = data.stations.items.filter(item => item.account_id === data.account._id); - let view: View = "now"; - let _token = 0; - - const select = action(async (station: typeof data.stations.items[number] | null) => { - selector_open = false; - if(station?._id === selector_state.station?._id) return; - const token = ++_token; - if(station) { - const { stats }: import("$api/stations/[station]/stream-stats/GET/Output").Output = - await _get(`/api/stations/${station._id}/stream-stats`); - if(token === _token) { - selector_state = { - kind: "station", - record_id: station._id, - data: stats, - station, - } - } - } else { - const { stats }: import("$api/accounts/[account]/stream-stats/GET/Output").Output = - await _get(`/api/accounts/${data.account._id}/stream-stats`); - if(token === _token) { - selector_state = { - kind: "account", - record_id: data.account._id, - data: stats, - station: null, - } - } - } - }) - - $: account_stations = data.stations.items.filter(item => item.account_id === data.account._id); - - let selector_open = false; - - const close_selector = () => { - selector_open = false - } - - const toggle_selector = () => { - selector_open = !selector_open - } - - const selector_menu_click_out = () => { - setTimeout(close_selector, 2); - } - - const units = [ "B", "KB", "MB", "GB", "TB" ]; const to_fixed_2 = (v: number): number => Math.round(v * 100) / 100; @@ -183,97 +138,12 @@ .edit-btn:hover { background: rgba(0,0,0,0.05); } - - - .stats { margin-top: 2rem; background: #fff; border-radius: 0.5rem; box-shadow: 0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04); } - - .stats-selector-out { - padding: 0.5rem; - margin-bottom: -1rem; - } - - .stats-selector { - position: relative; - display: flex; - flex-direction: column; - align-items: flex-start; - } - - .stats-selector-btn, .stats-selector-item { - display: flex; - flex-direction: row; - align-items: center; - padding: 0 1rem; - height: 3rem; - border-radius: 0.25rem; - transition: background-color 200ms ease; - } - - .stats-selector-btn:hover, .stats-selector-btn.open, .stats-selector-item:hover { - background: rgba(0,0,0,0.025); - } - - .stats-selector-btn-text { - margin-inline-end: 0.35rem; - } - - .stats-selector-btn-chevron { - display: flex; - align-items: center; - justify-content: center; - } - - .stats-selector-item.current { - background: rgba(var(--blue-rgb), 0.1); - } - - .stats-selector-menu { - min-width: min(80vw, 20rem); - } - - .stats-selector-btn-icon, .stats-selector-icon { - width: 1.75rem; - height: 1.75rem; - border-radius: 0.25rem; - background-position: center; - background-size: contain; - background-repeat: no-repeat; - flex: none; - margin-inline-start: -0.5rem; - margin-inline-end: 0.75rem; - } - - .stats-selector-anchor { - position: absolute; - inset-block-end: 0; - inset-inline-start: 0; - width: 0; - height: 0; - z-index: 1; - } - - .stats-selector-menu { - display: flex; - flex-direction: column; - box-shadow: 0 5px 25px 0 rgb(0 0 0 / 10%); - background: #fff; - padding: 0.5rem; - border-radius: 0.5rem; - } - - .stats-selector-item { - display: flex; - flex-direction: row; - align-items: center; - } - - .meters { --spacing: 1.5rem; display: flex; @@ -399,60 +269,17 @@ {/if}
-
-
- -
- {#if selector_open} -
- - {#each account_stations as station (station._id)} - - {/each} -
- {/if} -
-
+
+
diff --git a/front/server/src/api/admin-api.ts b/front/server/src/api/admin-api.ts index 63f87967..26f2153b 100644 --- a/front/server/src/api/admin-api.ts +++ b/front/server/src/api/admin-api.ts @@ -130,6 +130,11 @@ export const admin_api = ({ return await client.get_stream_stats(ip(req), ua(req), admin_token(req)); })) + api.route("/stream-stats/now") + .get(json(async req => { + return await client.get_stream_stats_item_now(ip(req), ua(req), admin_token(req)); + })) + api.use(shared_api({ client, get_token: admin_token, diff --git a/front/share/src/Map/StationSelector.svelte b/front/share/src/Map/StationSelector.svelte new file mode 100644 index 00000000..ed6da0e3 --- /dev/null +++ b/front/share/src/Map/StationSelector.svelte @@ -0,0 +1,203 @@ + + + + + + +
+
+ +
+ {#if selector_open} +
+ + {#each data.stations as station (station._id)} + + {/each} +
+ {/if} +
+
+
diff --git a/front/share/src/Map/StatsMapInternal.svelte b/front/share/src/Map/StatsMapInternal.svelte index ffc312dd..77b2048b 100644 --- a/front/share/src/Map/StatsMapInternal.svelte +++ b/front/share/src/Map/StatsMapInternal.svelte @@ -63,9 +63,14 @@ break; } - const url = kind === "account" ? `/api/accounts/${record_id}/stream-stats/now` : `/api/stations/${record_id}/stream-stats/now`; + const url = + kind === "all" ? `/api/stream-stats/now` : + kind === "account" ? `/api/accounts/${record_id}/stream-stats/now` : + `/api/stations/${record_id}/stream-stats/now`; + let output: - import("$api/stations/[station]/stream-stats/now/GET/Output").Output | + import("$api/stream-stats/now/GET/Output").Output | + import("$api/accounts/[account]/stream-stats/now/GET/Output").Output | import("$api/stations/[station]/stream-stats/now/GET/Output").Output; try {