auth-yes/ui/components/AppLaunchpadPage.tsx

137 lines
5.2 KiB
TypeScript

import { AuthenticatedLayout } from "./AuthenticatedLayout.tsx";
export interface AppCard {
id: string;
name: string;
description: string;
domain: string;
role: string;
}
export const AppLaunchpadPage = ({
apps,
isAdmin,
}: {
apps: AppCard[];
isAdmin: boolean;
}) => {
return (
<AuthenticatedLayout
title="Launchpad"
currentPath="/dashboard"
isAdmin={isAdmin}
>
<div style="margin-bottom: 2rem;">
<h1 style="font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem 0; color: var(--text-primary);">
Application Launchpad
</h1>
<p style="color: var(--text-secondary); margin: 0; font-size: 0.95rem;">
Single sign-on authorized workloads and microservices.
</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1.25rem;">
{apps.length === 0
? (
<div
class="card"
style="grid-column: 1 / -1; text-align: center; padding: 3rem 1.5rem;"
>
<div style="display: inline-flex; width: 48px; height: 48px; background: var(--surface-muted); border-radius: var(--radius-full); align-items: center; justify-content: center; margin-bottom: 1rem; color: var(--text-muted);">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
</div>
<h3 style="margin: 0 0 0.5rem 0; color: var(--text-primary);">
No Authorized Applications
</h3>
<p style="margin: 0; color: var(--text-muted); font-size: 0.9rem;">
You do not have active role grants for any workloads.
</p>
</div>
)
: (
apps.map((app) => {
const isAdminRole = app.role === "Admin" ||
app.role === "Global Admin";
const targetUrl = app.domain.startsWith("http")
? app.domain
: `https://${app.domain}`;
return (
<div
class="card"
key={app.id}
style="display: flex; flex-direction: column; justify-content: space-between; margin-bottom: 0; transition: transform 0.15s ease, box-shadow 0.15s ease;"
>
<div>
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem; gap: 0.5rem;">
<div style="display: flex; align-items: center; gap: 0.65rem;">
<div style="display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; background: var(--primary-light); color: var(--primary); border-radius: var(--radius-md); font-weight: 700; font-size: 1rem;">
{app.name.charAt(0).toUpperCase()}
</div>
<div>
<h3 style="margin: 0; font-size: 1.1rem; color: var(--text-primary);">
{app.name}
</h3>
<span style="font-size: 0.8rem; color: var(--text-muted); font-family: monospace;">
{app.domain}
</span>
</div>
</div>
<span
class={`badge ${
isAdminRole ? "badge-success" : "badge-info"
}`}
>
{app.role}
</span>
</div>
<p style="color: var(--text-secondary); font-size: 0.875rem; line-height: 1.5; margin: 0 0 1.25rem 0; flex: 1;">
{app.description ||
"Zero-trust secured internal application."}
</p>
</div>
<a
href={targetUrl}
class="btn-primary"
style="width: 100%; min-height: 46px; text-decoration: none; justify-content: center;"
>
<span>Launch App</span>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="7" y1="17" x2="17" y2="7"></line>
<polyline points="7 7 17 7 17 17"></polyline>
</svg>
</a>
</div>
);
})
)}
</div>
</AuthenticatedLayout>
);
};