Update connection badge UI and add last update timestamp

- Make connection badge compact with hover animation
- Show small icon (green/gray dot) that expands to show text on hover
- Add smooth 500ms transition animation
- Add last update timestamp to footer with static time display
- Format timestamp as 12-hour time (e.g., "2:34:05 PM")
This commit is contained in:
rcourtman
2025-10-02 14:10:42 +00:00
parent 6149fce74c
commit b678fd898f
+39 -10
View File
@@ -84,12 +84,27 @@ function App() {
const [dataUpdated, setDataUpdated] = createSignal(false);
let updateTimeout: number;
// Last update time formatting
const [lastUpdateText, setLastUpdateText] = createSignal('');
const formatLastUpdate = (timestamp: string) => {
if (!timestamp) return '';
const date = new Date(timestamp);
return date.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true
});
};
// Flash indicator when data updates
createEffect(() => {
// Watch for state changes
const updateTime = state().lastUpdate;
if (updateTime && updateTime !== '') {
setDataUpdated(true);
setLastUpdateText(formatLastUpdate(updateTime));
window.clearTimeout(updateTimeout);
updateTimeout = window.setTimeout(() => setDataUpdated(false), POLLING_INTERVALS.DATA_FLASH);
}
@@ -478,6 +493,7 @@ function App() {
connected={connected}
reconnecting={reconnecting}
dataUpdated={dataUpdated}
lastUpdateText={lastUpdateText}
versionInfo={versionInfo}
darkMode={darkMode}
toggleDarkMode={toggleDarkMode}
@@ -519,6 +535,7 @@ function AppLayout(props: {
connected: () => boolean;
reconnecting: () => boolean;
dataUpdated: () => boolean;
lastUpdateText: () => string;
versionInfo: () => VersionInfo | null;
darkMode: () => boolean;
toggleDarkMode: () => void;
@@ -623,16 +640,16 @@ function AppLayout(props: {
</button>
<div class="flex items-center gap-2">
<div
class={`status text-xs px-2 py-1 rounded-full flex items-center gap-1 ${
class={`group status text-xs rounded-full flex items-center justify-center transition-all duration-500 ease-in-out px-1.5 ${
props.connected()
? 'connected bg-green-200 dark:bg-green-700 text-green-700 dark:text-green-300'
? 'connected bg-green-200 dark:bg-green-700 text-green-700 dark:text-green-300 min-w-6 h-6 group-hover:px-3'
: props.reconnecting()
? 'reconnecting bg-yellow-200 dark:bg-yellow-700 text-yellow-700 dark:text-yellow-300'
: 'disconnected bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300'
? 'reconnecting bg-yellow-200 dark:bg-yellow-700 text-yellow-700 dark:text-yellow-300 py-1'
: 'disconnected bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 min-w-6 h-6 group-hover:px-3'
}`}
>
<Show when={props.reconnecting()}>
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
<svg class="animate-spin h-3 w-3 flex-shrink-0" fill="none" viewBox="0 0 24 24">
<circle
class="opacity-25"
cx="12"
@@ -648,11 +665,19 @@ function AppLayout(props: {
></path>
</svg>
</Show>
{props.connected()
? 'Connected'
: props.reconnecting()
? 'Reconnecting...'
: 'Disconnected'}
<Show when={props.connected()}>
<span class="h-2.5 w-2.5 rounded-full bg-green-600 dark:bg-green-400 flex-shrink-0"></span>
</Show>
<Show when={!props.connected() && !props.reconnecting()}>
<span class="h-2.5 w-2.5 rounded-full bg-gray-600 dark:bg-gray-400 flex-shrink-0"></span>
</Show>
<span class={`whitespace-nowrap overflow-hidden transition-all duration-500 ${props.connected() || (!props.connected() && !props.reconnecting()) ? 'max-w-0 group-hover:max-w-[100px] group-hover:ml-2 group-hover:mr-1 opacity-0 group-hover:opacity-100' : 'max-w-[100px] ml-1 opacity-100'}`}>
{props.connected()
? 'Connected'
: props.reconnecting()
? 'Reconnecting...'
: 'Disconnected'}
</span>
</div>
<Show when={props.hasAuth() && !props.needsAuth()}>
<Show when={props.proxyAuthInfo()?.username}>
@@ -843,6 +868,10 @@ function AppLayout(props: {
</a>
{props.versionInfo()?.isDevelopment && ' (Development)'}
{props.versionInfo()?.isDocker && ' - Docker'}
<Show when={props.lastUpdateText()}>
<span class="mx-2">|</span>
<span>Last updated: {props.lastUpdateText()}</span>
</Show>
</footer>
</>
);