128 lines
3.2 KiB
TypeScript
128 lines
3.2 KiB
TypeScript
import { ArrowUpRight, Box, Globe2 } from "lucide-react";
|
|
import type { DockApp, Favorite } from "../../shared/contracts";
|
|
import { effectiveAppUrl, favoriteIconUrl } from "../../shared/domain";
|
|
import { ServiceIcon } from "./ServiceIcon";
|
|
|
|
const serviceDescriptions: Array<[RegExp, string]> = [
|
|
[/plex/i, "Media server & streaming"],
|
|
[/jellyfin/i, "De vrije software mediaserver"],
|
|
[/sonarr/i, "PVR voor tv-series"],
|
|
[/radarr/i, "PVR voor films"],
|
|
[/home.?assistant|ha-core/i, "Centrale automatisering"],
|
|
[/adguard|pihole|pi-hole/i, "Netwerkbeveiliging & filter"],
|
|
[/nextcloud/i, "Bestandshosting"],
|
|
[/gitea|github|gitlab/i, "Code en samenwerking"],
|
|
[/immich/i, "Foto's en herinneringen"],
|
|
[/paperless/i, "Documenten en archief"],
|
|
];
|
|
|
|
function describeService(app: DockApp, categoryName: string) {
|
|
const haystack = `${app.displayName} ${app.technicalName}`;
|
|
return (
|
|
serviceDescriptions.find(([pattern]) => pattern.test(haystack))?.[1] ??
|
|
`${categoryName} service`
|
|
);
|
|
}
|
|
|
|
function statusLabel(status: DockApp["status"]) {
|
|
return status === "online"
|
|
? "Online"
|
|
: status === "offline"
|
|
? "Offline"
|
|
: "Status onbekend";
|
|
}
|
|
|
|
export function AppTile({
|
|
app,
|
|
externalAccess,
|
|
showStatus,
|
|
index,
|
|
categoryName,
|
|
variant = "standard",
|
|
}: {
|
|
app: DockApp;
|
|
externalAccess: boolean;
|
|
showStatus: boolean;
|
|
index: number;
|
|
categoryName: string;
|
|
variant?: "standard" | "feature";
|
|
}) {
|
|
const url = effectiveAppUrl(app, externalAccess);
|
|
const content = (
|
|
<>
|
|
<ServiceIcon
|
|
name={app.displayName}
|
|
icon={app.icon}
|
|
hint={`${app.technicalName} ${app.image ?? ""}`}
|
|
index={index}
|
|
/>
|
|
<span className="tile-copy">
|
|
<strong>{app.displayName}</strong>
|
|
<small>{describeService(app, categoryName)}</small>
|
|
</span>
|
|
{showStatus && (
|
|
<span className={`tile-status ${app.status}`}>
|
|
<span /> {statusLabel(app.status)}
|
|
</span>
|
|
)}
|
|
<ArrowUpRight className="tile-arrow" size={17} aria-hidden="true" />
|
|
</>
|
|
);
|
|
return url ? (
|
|
<a
|
|
className={`app-tile ${variant}`}
|
|
href={url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
data-testid="app-tile"
|
|
>
|
|
{content}
|
|
</a>
|
|
) : (
|
|
<div
|
|
className={`app-tile ${variant} disabled`}
|
|
aria-label={`${app.displayName} heeft geen URL`}
|
|
>
|
|
{content}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function FavoriteTile({
|
|
favorite,
|
|
index,
|
|
}: {
|
|
favorite: Favorite;
|
|
index: number;
|
|
}) {
|
|
return (
|
|
<a
|
|
className="app-tile"
|
|
href={favorite.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
data-testid="favorite-tile"
|
|
>
|
|
<ServiceIcon
|
|
name={favorite.name}
|
|
icon={favoriteIconUrl(favorite)}
|
|
index={index + 2}
|
|
/>
|
|
<span className="tile-copy">
|
|
<strong>{favorite.name}</strong>
|
|
<small>Favoriet</small>
|
|
</span>
|
|
<Globe2 className="status-icon" size={15} aria-hidden="true" />
|
|
<ArrowUpRight className="tile-arrow" size={17} aria-hidden="true" />
|
|
</a>
|
|
);
|
|
}
|
|
|
|
export function EmptyIcon() {
|
|
return (
|
|
<div className="empty-icon" aria-hidden="true">
|
|
<Box size={28} />
|
|
</div>
|
|
);
|
|
}
|