feat: refine launcher admin and showcase UI
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { Activity, Bot, Boxes, ChartNoAxesColumnIncreasing, KeyRound, Network, Sparkles } from "lucide-react";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import type { LauncherServiceView } from "../../entities/service/types";
|
||||
import { cn } from "../../shared/lib/cn";
|
||||
import { ServiceStatusBadge } from "../../shared/ui/StatusBadge";
|
||||
|
||||
const DEFAULT_RAIL_MEDIA = "/storage/default.gif";
|
||||
|
||||
export function ServiceRail({
|
||||
services,
|
||||
@@ -12,35 +13,63 @@ export function ServiceRail({
|
||||
selectedServiceId?: string;
|
||||
onSelect: (serviceId: string) => void;
|
||||
}) {
|
||||
const selectedService = services.find((service) => service.id === selectedServiceId) ?? services[0];
|
||||
const railMediaSrc = selectedService?.media.ambientVideo ?? selectedService?.media.coverImage ?? DEFAULT_RAIL_MEDIA;
|
||||
const railMediaKind = selectedService?.media.ambientKind ?? selectedService?.media.coverKind;
|
||||
|
||||
return (
|
||||
<div className="service-rail" aria-label="Доступные сервисы">
|
||||
{services.map((service) => (
|
||||
<button
|
||||
key={service.id}
|
||||
className={cn("service-tile", selectedServiceId === service.id && "service-tile--active")}
|
||||
onClick={() => onSelect(service.id)}
|
||||
type="button"
|
||||
>
|
||||
<span className="service-tile__media" style={{ "--tile-accent": service.accentColor ?? "#B5FF5A" } as React.CSSProperties}>
|
||||
<ServiceIcon slug={service.slug} />
|
||||
</span>
|
||||
<span className="service-tile__content">
|
||||
<strong>{service.title}</strong>
|
||||
<small>{service.subtitle}</small>
|
||||
</span>
|
||||
<ServiceStatusBadge status={service.status} />
|
||||
</button>
|
||||
))}
|
||||
<RailMedia className="service-rail__backdrop-media" src={railMediaSrc} kind={railMediaKind} />
|
||||
<div className="service-rail__glass" />
|
||||
<div className="service-rail__scroll">
|
||||
<div className="service-rail__track">
|
||||
{services.map((service) => (
|
||||
<button
|
||||
key={service.id}
|
||||
className={cn("service-tile", selectedServiceId === service.id && "service-tile--active")}
|
||||
onClick={() => onSelect(service.id)}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="service-tile__media"
|
||||
style={{ "--tile-accent": service.accentColor ?? "#B5FF5A" } as React.CSSProperties}
|
||||
>
|
||||
<RailMedia
|
||||
className="service-tile__media-asset"
|
||||
src={service.media.coverImage ?? service.media.ambientVideo ?? DEFAULT_RAIL_MEDIA}
|
||||
kind={service.media.coverKind ?? service.media.ambientKind}
|
||||
/>
|
||||
</span>
|
||||
<span className="service-tile__content">
|
||||
<strong>{service.title}</strong>
|
||||
</span>
|
||||
<span className="service-tile__arrow" aria-hidden="true">
|
||||
<ChevronRight size={18} strokeWidth={2.1} />
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ServiceIcon({ slug }: { slug: string }) {
|
||||
if (slug.includes("task")) return <ChartNoAxesColumnIncreasing size={18} />;
|
||||
if (slug.includes("1c")) return <Boxes size={18} />;
|
||||
if (slug.includes("tender")) return <KeyRound size={18} />;
|
||||
if (slug.includes("twin")) return <Activity size={18} />;
|
||||
if (slug.includes("digital-modules")) return <Sparkles size={18} />;
|
||||
if (slug.includes("internal")) return <Network size={18} />;
|
||||
return <Bot size={18} />;
|
||||
function RailMedia({
|
||||
src,
|
||||
kind,
|
||||
className,
|
||||
}: {
|
||||
src: string;
|
||||
kind?: LauncherServiceView["media"]["coverKind"];
|
||||
className: string;
|
||||
}) {
|
||||
if (kind === "video" || isVideoSource(src)) {
|
||||
return <video className={className} src={src} autoPlay loop muted playsInline />;
|
||||
}
|
||||
|
||||
return <img className={className} src={src} alt="" />;
|
||||
}
|
||||
|
||||
function isVideoSource(src: string) {
|
||||
return /\.(mp4|webm|mov|m4v|avi|mkv)(\?.*)?$/i.test(src);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user