Skip to content

Commit 42ebe79

Browse files
committed
feat: added copy fqdn url menu item to cluster card dropdown
1 parent 3ef4392 commit 42ebe79

File tree

1 file changed

+56
-30
lines changed

1 file changed

+56
-30
lines changed

src/features/clusters/components/ClusterCard.tsx

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { getOperationsUrlForCluster } from '@/lib/urls/getOperationsUrlForCluste
2222
import { Link } from '@tanstack/react-router';
2323
import { Ellipsis } from 'lucide-react';
2424
import { useCallback, useMemo, useState } from 'react';
25+
import { toast } from 'sonner';
2526

2627
const activeClusterStatuses = ['RUNNING'];
2728
const deletedClusterStatuses = ['TERMINATING', 'TERMINATED', 'REMOVED'];
@@ -37,14 +38,17 @@ export function ClusterCard({
3738
const auth = useInstanceAuth(cluster.id);
3839

3940
const isActive = useMemo(() => cluster.status && activeClusterStatuses.includes(cluster.status), [cluster.status]);
40-
const isTerminated = useMemo(() => cluster.status && deletedClusterStatuses.includes(cluster.status), [cluster.status]);
41+
const isTerminated = useMemo(
42+
() => cluster.status && deletedClusterStatuses.includes(cluster.status),
43+
[cluster.status]
44+
);
4145
const operationsUrl = useMemo(() => getOperationsUrlForCluster(cluster), [cluster]);
4246
const instanceClient = useInstanceClient(operationsUrl);
4347
const [signingOut, setSigningOut] = useState(false);
4448

4549
const onSignOutClick = useCallback(async () => {
4650
setSigningOut(true);
47-
const fullCluster = await getClusterInfo(cluster.id).catch(err => {
51+
const fullCluster = await getClusterInfo(cluster.id).catch((err) => {
4852
console.error('Failed to lookup cluster details, proceeding without checking instances.', err);
4953
return null;
5054
});
@@ -61,17 +65,34 @@ export function ClusterCard({
6165
onTerminateClusterModal(cluster);
6266
}, [cluster, onTerminateClusterModal]);
6367

68+
const onCopyFQDNClick = useCallback(() => {
69+
navigator.clipboard.writeText(cluster.fqdn || '');
70+
toast.info('FQDN url copied to clipboard');
71+
}, [cluster.fqdn]);
72+
6473
const menuItems = [
6574
isActive && update && (
66-
<Link to={`${cluster.id}/edit`} disabled={signingOut}><DropdownMenuItem>Edit</DropdownMenuItem></Link>),
75+
<Link to={`${cluster.id}/edit`} disabled={signingOut}>
76+
<DropdownMenuItem>Edit</DropdownMenuItem>
77+
</Link>
78+
),
79+
isActive && view && (
80+
<Link to={`${cluster.id}/instances`} disabled={signingOut}>
81+
<DropdownMenuItem>Instances</DropdownMenuItem>
82+
</Link>
83+
),
6784
isActive && view && (
68-
<Link to={`${cluster.id}/instances`} disabled={signingOut}><DropdownMenuItem>Instances</DropdownMenuItem></Link>),
85+
<DropdownMenuItem onClick={onCopyFQDNClick} disabled={signingOut}>
86+
Copy FQDN Url
87+
</DropdownMenuItem>
88+
),
6989
isActive && view && !!operationsUrl && !auth.isLoading && auth.user && (
70-
<DropdownMenuItem onClick={onSignOutClick} disabled={signingOut}>Sign Out</DropdownMenuItem>),
90+
<DropdownMenuItem onClick={onSignOutClick} disabled={signingOut}>
91+
Sign Out
92+
</DropdownMenuItem>
93+
),
7194
!isTerminated && remove && (
72-
<DropdownMenuItem
73-
className="bg-red focus:bg-red/70 focus:text-white"
74-
onClick={onTerminateClick}>
95+
<DropdownMenuItem className="bg-red focus:bg-red/70 focus:text-white" onClick={onTerminateClick}>
7596
Terminate
7697
</DropdownMenuItem>
7798
),
@@ -82,35 +103,40 @@ export function ClusterCard({
82103
<CardHeader>
83104
<CardDescription className="flex items-center justify-between">
84105
<span className="truncate">{cluster.id}</span>
85-
{!isTerminated && (<DropdownMenu>
86-
<DropdownMenuTrigger>
87-
<Ellipsis aria-label="Cluster options" />
88-
</DropdownMenuTrigger>
89-
<DropdownMenuContent>
90-
<DropdownMenuLabel className="text-gray-600 text-xs">Plans</DropdownMenuLabel>
91-
{cluster.plans?.map(plan => (
92-
<DropdownMenuLabel key={plan.planId}>
93-
{plan.planId} / {plan.regionId}<br />
94-
Auto Renewal {plan.autoRenew
95-
? <Badge variant="success">ON</Badge>
96-
: <Badge variant="warning">OFF</Badge>}
97-
</DropdownMenuLabel>
98-
))}
99-
{menuItems.length > 0 && (<>
100-
<DropdownMenuSeparator />
101-
{...menuItems}
102-
</>)}
103-
</DropdownMenuContent>
104-
</DropdownMenu>)}
106+
{!isTerminated && (
107+
<DropdownMenu>
108+
<DropdownMenuTrigger>
109+
<Ellipsis aria-label="Cluster options" />
110+
</DropdownMenuTrigger>
111+
<DropdownMenuContent>
112+
<DropdownMenuLabel className="text-gray-600 text-xs">Plans</DropdownMenuLabel>
113+
{cluster.plans?.map((plan) => (
114+
<DropdownMenuLabel key={plan.planId}>
115+
{plan.planId} / {plan.regionId}
116+
<br />
117+
Auto Renewal{' '}
118+
{plan.autoRenew ? <Badge variant="success">ON</Badge> : <Badge variant="warning">OFF</Badge>}
119+
</DropdownMenuLabel>
120+
))}
121+
{menuItems.length > 0 && (
122+
<>
123+
<DropdownMenuSeparator />
124+
{...menuItems}
125+
</>
126+
)}
127+
</DropdownMenuContent>
128+
</DropdownMenu>
129+
)}
105130
</CardDescription>
106131
<CardTitle>
107132
<h2>{cluster.name}</h2>
108133
</CardTitle>
109134
</CardHeader>
110135
<CardContent className="flex justify-between">
111136
{cluster.status && (
112-
<Badge variant={renderBadgeStatusVariant(cluster.status)}>{renderBadgeStatusText(cluster.status)}</Badge>)}
113-
{isActive && view && (<ClusterCardAction cluster={cluster} />)}
137+
<Badge variant={renderBadgeStatusVariant(cluster.status)}>{renderBadgeStatusText(cluster.status)}</Badge>
138+
)}
139+
{isActive && view && <ClusterCardAction cluster={cluster} />}
114140
</CardContent>
115141
</Card>
116142
);

0 commit comments

Comments
 (0)