Skip to content

Commit

Permalink
feat: Smaller improvements to the frontend ui
Browse files Browse the repository at this point in the history
synchronized time-ago, apps can be sorted by status and name, added last seen column
  • Loading branch information
stmh committed Oct 21, 2024
1 parent cdf4aad commit 7fb4f89
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 50 deletions.
24 changes: 24 additions & 0 deletions frontend/src/components/last-started.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts">
import type { AppService } from '../types';
import TimeAgo from './time-ago.svelte';
export let services: AppService[] = [];
let lastStarted: string | null = null;
$: {
if (services.length > 0) {
lastStarted =
services.reduce(
(oldest, service) => {
if (!oldest || new Date(service.started_at) < new Date(oldest.started_at)) {
return service;
}
return oldest;
},
null as AppService | null
)?.started_at ?? null;
}
}
</script>

<TimeAgo dateString={lastStarted}></TimeAgo>
20 changes: 20 additions & 0 deletions frontend/src/components/table-header-sort.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script lang="ts">
import Icon from '@iconify/svelte';
export let name: string;
export let key: string;
export let sortBy: string;
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function setSort(sortValue: string) {
dispatch('setSort', { sortBy: sortValue });
}
</script>

<button class="link" on:click={() => setSort(key)}
>{name}
{#if sortBy == key}<Icon class="float-right" icon="heroicons:arrow-small-up-solid" />
{/if}</button
>
44 changes: 8 additions & 36 deletions frontend/src/components/time-ago.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { time } from '../stores/time';
export let dateString: string | null = null;
let timeAgoText: string = '';
let interval: number;
function getTimeAgo(dateString: string) {
const now = new Date();
function getTimeAgo(now: Date, dateString: string) {
const past = new Date(dateString);
const diffSeconds = Math.floor((now.getTime() - past.getTime()) / 1000);
Expand All @@ -16,43 +13,18 @@
} else if (diffSeconds < 3600) {
const diffMinutes = Math.floor(diffSeconds / 60);
return `${diffMinutes} minutes ago`;
} else {
} else if (diffSeconds < 86400) {
const diffHours = Math.floor(diffSeconds / 3600);
return `${diffHours} hours ago`;
}
}
function updateTime() {
timeAgoText = dateString ? getTimeAgo(dateString) : '';
scheduleNextUpdate(dateString);
}
function scheduleNextUpdate(dateString: string | null) {
let milliseconds: number;
if (dateString == null) {
milliseconds = 1000;
} else {
const now = new Date();
const past = new Date(dateString);
const diffSeconds = Math.floor((now.getTime() - past.getTime()) / 1000);
if (past == null || isNaN(diffSeconds) || diffSeconds < 60) {
milliseconds = 1000;
} else {
milliseconds = 60000;
}
const diffDays = Math.floor(diffSeconds / 86400);
return `${diffDays} days ago`;
}
if (interval) clearInterval(interval);
interval = setInterval(updateTime, milliseconds);
}
onMount(() => {
updateTime();
});
onDestroy(() => {
if (interval) clearInterval(interval);
});
$: {
timeAgoText = dateString ? getTimeAgo($time, dateString) : '';
}
</script>

<span>{timeAgoText}</span>
62 changes: 53 additions & 9 deletions frontend/src/routes/dashboard/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { onMount } from 'svelte';
import { apps } from '../../stores/appsStore';
import { loadApps } from '../../stores/appsStore';
Expand All @@ -10,11 +10,49 @@
import AppStatusPill from '../../components/app-status-pill.svelte';
import Icon from '@iconify/svelte';
import PageHeader from '../../components/page-header.svelte';
import type { App } from '../../types';
import TableHeaderSort from '../../components/table-header-sort.svelte';
import LastStarted from '../../components/last-started.svelte';
const filter = writable('');
const sortBy = writable('status');
onMount(() => {
loadApps();
function setSort(event: CustomEvent<string>) {
sortBy.set(event.detail.sortBy);
}
function doSort(a: App, b: App): number {
if ($sortBy === 'name') {
return a.name.localeCompare(b.name);
}
if ($sortBy === 'status') {
const result = a.status.localeCompare(b.status);
if (result === 0) {
return a.name.localeCompare(b.name);
} else {
return result;
}
}
return 0;
}
const filteredAndSortedApps = writable([] as App[]);
function updateAppList() {
$filteredAndSortedApps = $apps
.filter((app) => app.name.toLowerCase().includes($filter.toLowerCase()))
.sort((a, b) => doSort(a, b));
}
sortBy.subscribe(() => {
updateAppList();
});
filter.subscribe(() => {
updateAppList();
});
onMount(async () => {
await loadApps();
updateAppList();
});
</script>

Expand All @@ -30,15 +68,20 @@
<thead>
<tr>
<th class="w-1"></th>
<th>Name</th>
<th
><TableHeaderSort on:setSort={setSort} sortBy={$sortBy} key="name" name="Name"
></TableHeaderSort></th
>
<th>Services</th>
<th>Status</th>
<th
><TableHeaderSort on:setSort={setSort} sortBy={$sortBy} key="status" name="Status"
></TableHeaderSort></th
>
<th>Last time started</th>
</tr>
</thead>
<tbody>
{#each $apps
.filter((app) => app.name.toLowerCase().includes($filter.toLowerCase()))
.sort((a, b) => a.name.localeCompare(b.name)) as app}
{#each $filteredAndSortedApps as app}
<tr>
<td><StartStopAppAction name={app.name} status={app.status} /></td>
<td><a class="link-primary" href="/dashboard/{app.name}">{app.name}</a></td>
Expand All @@ -50,7 +93,8 @@
{/each}</td
>
<td><AppStatusPill status={app.status} /></td>
</tr>
<td><LastStarted services={app.services}></LastStarted> </td></tr
>
{/each}
</tbody>
</table>
11 changes: 11 additions & 0 deletions frontend/src/stores/time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { readable } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000); // update every second

return function stop() {
clearInterval(interval);
};
});
24 changes: 19 additions & 5 deletions src/stop_flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,25 @@ pub fn register_signal_handler(stop_flag: &StopFlag) {
let stop_flag = stop_flag.clone();

tokio::spawn(async move {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
info!("Terminate signal received, initiating graceful shutdown...");
let mut terminate = signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler");
let mut interrupt = signal::unix::signal(signal::unix::SignalKind::interrupt())
.expect("failed to install SIGINT handler");
let mut hangup = signal::unix::signal(signal::unix::SignalKind::hangup())
.expect("failed to install SIGHUP handler");

tokio::select! {
_ = terminate.recv() => {
info!("SIGTERM received, initiating graceful shutdown...");
}
_ = interrupt.recv() => {
info!("SIGINT received, initiating graceful shutdown...");
}
_ = hangup.recv() => {
info!("SIGHUP received, initiating graceful shutdown...");
}
}

stop_flag.stop();
});
}
Expand Down

0 comments on commit 7fb4f89

Please sign in to comment.