Make service/job/program cards fully clickable
Use the stretched-link pattern: the card is relative and the title link gets an after:absolute after:inset-0 overlay so clicking anywhere on the card navigates to the detail page. Interactive controls (action buttons, subdomain/Docs links, ProgramActions) are raised with relative z-10 so they stay independently clickable. Cards also get a hover border for affordance.
This commit is contained in:
@@ -19,11 +19,11 @@ export function JobCard({ job, health }: JobCardProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-5">
|
<div className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-5 hover:border-[var(--primary)] transition-colors">
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<Link
|
<Link
|
||||||
to={`/jobs/${job.id}`}
|
to={`/jobs/${job.id}`}
|
||||||
className="text-base font-semibold hover:text-[var(--primary)] transition-colors"
|
className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0"
|
||||||
>
|
>
|
||||||
{job.id}
|
{job.id}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -59,7 +59,7 @@ export function JobCard({ job, health }: JobCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{job.managed && (
|
{job.managed && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="relative z-10 flex items-center gap-1">
|
||||||
{isDown && (
|
{isDown && (
|
||||||
<button
|
<button
|
||||||
onClick={() => doAction("start")}
|
onClick={() => doAction("start")}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ interface ProgramCardProps {
|
|||||||
|
|
||||||
export function ProgramCard({ program }: ProgramCardProps) {
|
export function ProgramCard({ program }: ProgramCardProps) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-5">
|
<div className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-5 hover:border-[var(--primary)] transition-colors">
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<Link
|
<Link
|
||||||
to={`/programs/${program.id}`}
|
to={`/programs/${program.id}`}
|
||||||
className="text-base font-semibold hover:text-[var(--primary)] transition-colors"
|
className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0"
|
||||||
>
|
>
|
||||||
{program.id}
|
{program.id}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -29,6 +29,7 @@ export function ProgramCard({ program }: ProgramCardProps) {
|
|||||||
<p className="text-sm text-[var(--muted)] mb-3">{program.description}</p>
|
<p className="text-sm text-[var(--muted)] mb-3">{program.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
<ProgramActions
|
<ProgramActions
|
||||||
name={program.id}
|
name={program.id}
|
||||||
actions={program.actions}
|
actions={program.actions}
|
||||||
@@ -38,5 +39,6 @@ export function ProgramCard({ program }: ProgramCardProps) {
|
|||||||
compact
|
compact
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ export function ServiceCard({ service, health }: ServiceCardProps) {
|
|||||||
const isDown = health?.status === "down"
|
const isDown = health?.status === "down"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-5">
|
<div className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-5 hover:border-[var(--primary)] transition-colors">
|
||||||
<div className="flex items-start justify-between mb-2">
|
<div className="flex items-start justify-between mb-2">
|
||||||
<Link
|
<Link
|
||||||
to={`/services/${service.id}`}
|
to={`/services/${service.id}`}
|
||||||
className="text-base font-semibold hover:text-[var(--primary)] transition-colors"
|
className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0"
|
||||||
>
|
>
|
||||||
{service.id}
|
{service.id}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -61,7 +61,7 @@ export function ServiceCard({ service, health }: ServiceCardProps) {
|
|||||||
{service.subdomain && (
|
{service.subdomain && (
|
||||||
<a
|
<a
|
||||||
href={subdomainUrl(service.subdomain) ?? undefined}
|
href={subdomainUrl(service.subdomain) ?? undefined}
|
||||||
className="flex items-center gap-1 text-[var(--primary)] hover:underline"
|
className="relative z-10 flex items-center gap-1 text-[var(--primary)] hover:underline"
|
||||||
>
|
>
|
||||||
<ExternalLink size={12} />
|
<ExternalLink size={12} />
|
||||||
{service.subdomain}
|
{service.subdomain}
|
||||||
@@ -70,7 +70,7 @@ export function ServiceCard({ service, health }: ServiceCardProps) {
|
|||||||
{service.port && (
|
{service.port && (
|
||||||
<a
|
<a
|
||||||
href={`http://localhost:${service.port}/docs`}
|
href={`http://localhost:${service.port}/docs`}
|
||||||
className="text-[var(--primary)] hover:underline"
|
className="relative z-10 text-[var(--primary)] hover:underline"
|
||||||
>
|
>
|
||||||
Docs
|
Docs
|
||||||
</a>
|
</a>
|
||||||
@@ -78,7 +78,7 @@ export function ServiceCard({ service, health }: ServiceCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{service.managed && (
|
{service.managed && (
|
||||||
<div className="flex items-center gap-1">
|
<div className="relative z-10 flex items-center gap-1">
|
||||||
{isDown && (
|
{isDown && (
|
||||||
<button
|
<button
|
||||||
onClick={() => doAction("start")}
|
onClick={() => doAction("start")}
|
||||||
|
|||||||
Reference in New Issue
Block a user