- **refactor(main):** migrate static HTML to React components
- **feat(ui):** implement `AcknowledgeButton` component for acknowledging images - **feat(stats):** add dashboard stats for total images, pending updates, and acknowledged status - **chore(deps):** introduce `bun` dependency management and add required libraries - **style(ui):** enhance UI with Tailwind-based components and modularity improvements - **chore:** add drag-and-drop tag assignment using `@dnd-kit/core`
This commit is contained in:
41
frontend/src/lib/serviceIcons.json
Normal file
41
frontend/src/lib/serviceIcons.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"nextcloud": "nextcloud",
|
||||
"postgres": "postgresql",
|
||||
"postgresql": "postgresql",
|
||||
"redis": "redis",
|
||||
"nginx": "nginx",
|
||||
"mysql": "mysql",
|
||||
"mariadb": "mariadb",
|
||||
"grafana": "grafana",
|
||||
"prometheus": "prometheus",
|
||||
"traefik": "traefik",
|
||||
"alpine": "alpinelinux",
|
||||
"ubuntu": "ubuntu",
|
||||
"debian": "debian",
|
||||
"node": "nodedotjs",
|
||||
"python": "python",
|
||||
"golang": "go",
|
||||
"elasticsearch": "elasticsearch",
|
||||
"kibana": "kibana",
|
||||
"portainer": "portainer",
|
||||
"gitea": "gitea",
|
||||
"gitlab": "gitlab",
|
||||
"jellyfin": "jellyfin",
|
||||
"plex": "plex",
|
||||
"rabbitmq": "rabbitmq",
|
||||
"mongodb": "mongodb",
|
||||
"influxdb": "influxdb",
|
||||
"bitwarden": "bitwarden",
|
||||
"vaultwarden": "bitwarden",
|
||||
"wordpress": "wordpress",
|
||||
"homeassistant": "homeassistant",
|
||||
"ghost": "ghost",
|
||||
"caddy": "caddy",
|
||||
"keycloak": "keycloak",
|
||||
"adguard": "adguard",
|
||||
"adguardhome": "adguard",
|
||||
"wireguard": "wireguard",
|
||||
"syncthing": "syncthing",
|
||||
"pihole": "pihole",
|
||||
"sonarqube": "sonarqube"
|
||||
}
|
||||
31
frontend/src/lib/serviceIcons.ts
Normal file
31
frontend/src/lib/serviceIcons.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as SimpleIcons from 'simple-icons'
|
||||
import mapping from './serviceIcons.json'
|
||||
|
||||
// simple-icons exports named icons as siSlugInPascalCase e.g. siPostgresql
|
||||
function slugToKey(slug: string): string {
|
||||
return 'si' + slug.charAt(0).toUpperCase() + slug.slice(1)
|
||||
}
|
||||
|
||||
export interface ServiceIcon {
|
||||
title: string
|
||||
hex: string // e.g. "4169E1" (no leading #)
|
||||
path: string // SVG path data for a 24×24 viewBox
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a Docker image string (e.g. "ghcr.io/linuxserver/nextcloud:28-alpine"),
|
||||
* returns icon data or null for unknown images.
|
||||
*/
|
||||
export function getServiceIcon(image: string): ServiceIcon | null {
|
||||
// Strip tag, take last path segment
|
||||
const base = image.split(':')[0]
|
||||
const name = base.split('/').pop() ?? base
|
||||
// Normalise: lowercase, strip common Docker image suffixes
|
||||
const normalised = name.toLowerCase().replace(/[_-](ce|oss|alpine|fpm|slim|lts)$/, '')
|
||||
|
||||
const slug = (mapping as Record<string, string>)[normalised]
|
||||
if (!slug) return null
|
||||
|
||||
const icon = (SimpleIcons as Record<string, ServiceIcon | undefined>)[slugToKey(slug)]
|
||||
return icon ?? null
|
||||
}
|
||||
17
frontend/src/lib/time.ts
Normal file
17
frontend/src/lib/time.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export function timeAgo(iso: string): string {
|
||||
const now = Date.now()
|
||||
const then = new Date(iso).getTime()
|
||||
const diff = Math.floor((now - then) / 1000)
|
||||
|
||||
if (diff < 60) return 'just now'
|
||||
if (diff < 3600) {
|
||||
const m = Math.floor(diff / 60)
|
||||
return `${m} minute${m !== 1 ? 's' : ''} ago`
|
||||
}
|
||||
if (diff < 86400) {
|
||||
const h = Math.floor(diff / 3600)
|
||||
return `${h} hour${h !== 1 ? 's' : ''} ago`
|
||||
}
|
||||
const d = Math.floor(diff / 86400)
|
||||
return `${d} day${d !== 1 ? 's' : ''} ago`
|
||||
}
|
||||
6
frontend/src/lib/utils.ts
Normal file
6
frontend/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user