Skip to content

Commit 0524155

Browse files
committed
Add resource description, add single resource for acl, add icons for group badges, add inactivity expiration
1 parent c8e3b50 commit 0524155

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1396
-467
lines changed

src/app/(dashboard)/peer/page.tsx

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import LoginExpiredBadge from "@components/ui/LoginExpiredBadge";
2525
import TextWithTooltip from "@components/ui/TextWithTooltip";
2626
import { getOperatingSystem } from "@hooks/useOperatingSystem";
2727
import useRedirect from "@hooks/useRedirect";
28-
import { IconCloudLock, IconInfoCircle } from "@tabler/icons-react";
2928
import useFetchApi from "@utils/api";
29+
import { cn } from "@utils/helpers";
3030
import dayjs from "dayjs";
3131
import { isEmpty, trim } from "lodash";
3232
import {
@@ -41,6 +41,7 @@ import {
4141
NetworkIcon,
4242
PencilIcon,
4343
TerminalSquare,
44+
TimerResetIcon,
4445
} from "lucide-react";
4546
import { useRouter, useSearchParams } from "next/navigation";
4647
import { toASCII } from "punycode";
@@ -61,6 +62,7 @@ import type { Peer } from "@/interfaces/Peer";
6162
import PageContainer from "@/layouts/PageContainer";
6263
import useGroupHelper from "@/modules/groups/useGroupHelper";
6364
import { AccessiblePeersSection } from "@/modules/peer/AccessiblePeersSection";
65+
import { PeerExpirationToggle } from "@/modules/peer/PeerExpirationToggle";
6466
import { PeerNetworkRoutesSection } from "@/modules/peer/PeerNetworkRoutesSection";
6567

6668
export default function PeerPage() {
@@ -70,9 +72,16 @@ export default function PeerPage() {
7072

7173
useRedirect("/peers", false, !peerId);
7274

75+
const peerKey = useMemo(() => {
76+
let id = peer?.id ?? "";
77+
let ssh = peer?.ssh_enabled ? "1" : "0";
78+
let expiration = peer?.login_expiration_enabled ? "1" : "0";
79+
return `${id}-${ssh}-${expiration}`;
80+
}, [peer]);
81+
7382
return peer && !isLoading ? (
7483
<PeerProvider peer={peer} key={peerId}>
75-
<PeerOverview />
84+
<PeerOverview key={peerKey} />
7685
</PeerProvider>
7786
) : (
7887
<FullScreenLoading />
@@ -89,6 +98,9 @@ function PeerOverview() {
8998
const [loginExpiration, setLoginExpiration] = useState(
9099
peer.login_expiration_enabled,
91100
);
101+
const [inactivityExpiration, setInactivityExpiration] = useState(
102+
peer.inactivity_expiration_enabled,
103+
);
92104
const [selectedGroups, setSelectedGroups, { getAllGroupCalls }] =
93105
useGroupHelper({
94106
initial: peerGroups,
@@ -111,10 +123,16 @@ function PeerOverview() {
111123
ssh,
112124
selectedGroups,
113125
loginExpiration,
126+
inactivityExpiration,
114127
]);
115128

116129
const updatePeer = async () => {
117-
const updateRequest = update(name, ssh, loginExpiration);
130+
const updateRequest = update({
131+
name,
132+
ssh,
133+
loginExpiration,
134+
inactivityExpiration,
135+
});
118136
const groupCalls = getAllGroupCalls();
119137
const batchCall = groupCalls
120138
? [...groupCalls, updateRequest]
@@ -125,13 +143,19 @@ function PeerOverview() {
125143
promise: Promise.all(batchCall).then(() => {
126144
mutate("/peers/" + peer.id);
127145
mutate("/groups");
128-
updateHasChangedRef([name, ssh, selectedGroups, loginExpiration]);
146+
updateHasChangedRef([
147+
name,
148+
ssh,
149+
selectedGroups,
150+
loginExpiration,
151+
inactivityExpiration,
152+
]);
129153
}),
130154
loadingMessage: "Saving the peer...",
131155
});
132156
};
133157

134-
const { isUser } = useLoggedInUser();
158+
const { isUser, isOwnerOrAdmin } = useLoggedInUser();
135159

136160
return (
137161
<PageContainer>
@@ -213,53 +237,43 @@ function PeerOverview() {
213237
<div className={"flex gap-10 w-full mt-5 max-w-6xl"}>
214238
<PeerInformationCard peer={peer} />
215239

216-
<div className={"flex flex-col gap-6 w-1/2"}>
217-
<FullTooltip
218-
content={
240+
<div className={"flex flex-col gap-6 w-1/2 transition-all"}>
241+
<div>
242+
<PeerExpirationToggle
243+
peer={peer}
244+
value={loginExpiration}
245+
icon={<TimerResetIcon size={16} />}
246+
onChange={(state) => {
247+
setLoginExpiration(state);
248+
!state && setInactivityExpiration(false);
249+
}}
250+
/>
251+
{isOwnerOrAdmin && !!peer?.user_id && (
219252
<div
220-
className={
221-
"flex gap-2 items-center !text-nb-gray-300 text-xs"
222-
}
223-
>
224-
{!peer.user_id ? (
225-
<>
226-
<>
227-
<IconInfoCircle size={14} />
228-
<span>
229-
Login expiration is disabled for all peers added
230-
with an setup-key.
231-
</span>
232-
</>
233-
</>
234-
) : (
235-
<>
236-
<LockIcon size={14} />
237-
<span>
238-
{`You don't have the required permissions to update this
239-
setting.`}
240-
</span>
241-
</>
253+
className={cn(
254+
"border border-nb-gray-900 border-t-0 rounded-b-md bg-nb-gray-940 px-[1.28rem] pt-3 pb-5 flex flex-col gap-4 mx-[0.25rem]",
255+
!loginExpiration
256+
? "opacity-50 pointer-events-none"
257+
: "bg-nb-gray-930/80",
242258
)}
259+
>
260+
<PeerExpirationToggle
261+
peer={peer}
262+
variant={"blank"}
263+
value={inactivityExpiration}
264+
onChange={setInactivityExpiration}
265+
title={"Require login after disconnect"}
266+
description={
267+
"Enable to require authentication after users disconnect from management for 10 minutes."
268+
}
269+
className={
270+
!loginExpiration ? "opacity-40 pointer-events-none" : ""
271+
}
272+
/>
243273
</div>
244-
}
245-
className={"w-full block"}
246-
disabled={!!peer.user_id && !isUser}
247-
>
248-
<FancyToggleSwitch
249-
disabled={!peer.user_id || isUser}
250-
value={loginExpiration}
251-
onChange={setLoginExpiration}
252-
label={
253-
<>
254-
<IconCloudLock size={16} />
255-
Login Expiration
256-
</>
257-
}
258-
helpText={
259-
"Enable to require SSO login peers to re-authenticate when their login expires."
260-
}
261-
/>
262-
</FullTooltip>
274+
)}
275+
</div>
276+
263277
<FullTooltip
264278
content={
265279
<div
@@ -335,7 +349,7 @@ function PeerOverview() {
335349
);
336350
}
337351

338-
function PeerInformationCard({ peer }: { peer: Peer }) {
352+
function PeerInformationCard({ peer }: Readonly<{ peer: Peer }>) {
339353
const { isLoading, getRegionByPeer } = useCountries();
340354

341355
const countryText = useMemo(() => {
@@ -489,7 +503,7 @@ interface ModalProps {
489503
peer: Peer;
490504
initialName: string;
491505
}
492-
function EditNameModal({ onSuccess, peer, initialName }: ModalProps) {
506+
function EditNameModal({ onSuccess, peer, initialName }: Readonly<ModalProps>) {
493507
const [name, setName] = useState(initialName);
494508

495509
const isDisabled = useMemo(() => {

src/app/(dashboard)/team/user/page.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function UserPage() {
4040
const { data: users, isLoading } = useFetchApi<User[]>(
4141
`/users?service_user=${isServiceUser}`,
4242
);
43+
const { isOwnerOrAdmin } = useLoggedInUser();
4344

4445
const user = useMemo(() => {
4546
return users?.find((u) => u.id === userId);
@@ -49,11 +50,15 @@ export default function UserPage() {
4950

5051
const userGroups = useGroupIdsToGroups(user?.auto_groups);
5152

52-
return !isLoading && user && userGroups !== undefined ? (
53-
<UserOverview user={user} initialGroups={userGroups} />
54-
) : (
55-
<FullScreenLoading />
56-
);
53+
if (!isOwnerOrAdmin && user && !isLoading) {
54+
return <UserOverview user={user} initialGroups={[]} />;
55+
}
56+
57+
if (isOwnerOrAdmin && user && !isLoading && userGroups) {
58+
return <UserOverview user={user} initialGroups={userGroups} />;
59+
}
60+
61+
return <FullScreenLoading />;
5762
}
5863

5964
type Props = {
@@ -195,7 +200,7 @@ function UserOverview({ user, initialGroups }: Readonly<Props>) {
195200
<div className={"flex gap-10 w-full mt-8 max-w-6xl items-start"}>
196201
<UserInformationCard user={user} />
197202
<div className={"flex flex-col gap-8 w-1/2 "}>
198-
{!user.is_service_user && (
203+
{!user.is_service_user && isOwnerOrAdmin && (
199204
<div>
200205
<Label>Auto-assigned groups</Label>
201206
<HelpText>

src/assets/icons/CircleIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function CircleIcon({
1212
size = 11,
1313
inactiveDot = "gray",
1414
className,
15-
}: Props) {
15+
}: Readonly<Props>) {
1616
return (
1717
<span
1818
style={{ width: size + "px", height: size + "px" }}

src/assets/icons/EntraIcon.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function EntraIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
width="231"
7+
height="231"
8+
viewBox="0 0 231 231"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
{...iconProperties(props)}
12+
>
13+
<path
14+
d="M48.7923 180.077C53.7717 183.183 62.0492 186.635 70.8015 186.635C78.771 186.635 86.1758 184.325 92.3102 180.385C92.3102 180.385 92.323 180.385 92.3358 180.373L115.5 165.896V218.167C111.83 218.167 108.134 217.166 104.925 215.164L48.7923 180.077Z"
15+
fill="#225086"
16+
/>
17+
<path
18+
d="M100.78 19.3398L4.53017 127.91C-2.90033 136.303 -0.962501 148.982 8.67533 155.001C8.67533 155.001 44.3007 177.267 48.7923 180.077C53.7717 183.183 62.0492 186.635 70.8015 186.635C78.771 186.635 86.1758 184.325 92.3102 180.385C92.3102 180.385 92.323 180.385 92.3358 180.373L115.5 165.896L59.4953 130.887L115.513 67.6958V12.8333C110.072 12.8333 104.63 15.0022 100.78 19.3398Z"
19+
fill="#66DDFF"
20+
/>
21+
<path
22+
d="M59.4953 130.887L60.1627 131.298L115.5 165.896H115.513V67.7087L115.5 67.6958L59.4953 130.887Z"
23+
fill="#CBF8FF"
24+
/>
25+
<path
26+
d="M222.325 155.001C231.963 148.982 233.9 136.303 226.47 127.91L163.317 56.672C158.222 54.2978 152.511 52.9375 146.467 52.9375C134.596 52.9375 123.983 58.058 116.925 66.1045L115.526 67.683L171.53 130.874L115.513 165.884V218.154C119.196 218.154 122.866 217.153 126.075 215.151L222.325 154.988V155.001Z"
27+
fill="#074793"
28+
/>
29+
<path
30+
d="M115.513 12.8333V67.6958L116.912 66.1173C123.97 58.0708 134.583 52.9503 146.454 52.9503C152.511 52.9503 158.209 54.3235 163.304 56.6848L130.207 19.3527C126.37 15.015 120.929 12.8462 115.5 12.8462L115.513 12.8333Z"
31+
fill="#0294E4"
32+
/>
33+
<path
34+
d="M171.518 130.887L115.513 67.7087V165.884L171.518 130.887Z"
35+
fill="#96BCC2"
36+
/>
37+
</svg>
38+
);
39+
}

src/assets/icons/GoogleIcon.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function GoogleIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
xmlns="http://www.w3.org/2000/svg"
7+
height="24"
8+
viewBox="0 0 24 24"
9+
width="24"
10+
{...iconProperties(props)}
11+
>
12+
<path
13+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
14+
fill="#4285F4"
15+
/>
16+
<path
17+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
18+
fill="#34A853"
19+
/>
20+
<path
21+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
22+
fill="#FBBC05"
23+
/>
24+
<path
25+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
26+
fill="#EA4335"
27+
/>
28+
<path d="M1 1h22v22H1z" fill="none" />
29+
</svg>
30+
);
31+
}

src/assets/icons/JWTIcon.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function JWTIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
height="2500"
7+
viewBox=".4 .3 99.7 100"
8+
width="2500"
9+
xmlns="http://www.w3.org/2000/svg"
10+
{...iconProperties(props)}
11+
>
12+
<g fill="none">
13+
<path
14+
d="m57.8 27.2-.1-26.9h-15l.1 26.9 7.5 10.3zm-15 46.1v27h15v-27l-7.5-10.3z"
15+
fill="#fff"
16+
/>
17+
<path
18+
d="m57.8 73.3 15.8 21.8 12.1-8.8-15.8-21.8-12.1-3.9zm-15-46.1-15.9-21.8-12.1 8.8 15.8 21.8 12.2 3.9z"
19+
fill="#00f2e6"
20+
/>
21+
<path
22+
d="m30.6 36-25.6-8.3-4.6 14.2 25.6 8.4 12.1-4zm31.8 18.2 7.5 10.3 25.6 8.3 4.6-14.2-25.6-8.3z"
23+
fill="#00b9f1"
24+
/>
25+
<path
26+
d="m74.5 50.3 25.6-8.4-4.6-14.2-25.6 8.3-7.5 10.3zm-48.5 0-25.6 8.3 4.6 14.2 25.6-8.3 7.5-10.3z"
27+
fill="#d63aff"
28+
/>
29+
<path
30+
d="m30.6 64.5-15.8 21.8 12.1 8.8 15.9-21.8v-12.7zm39.3-28.5 15.8-21.8-12.1-8.8-15.8 21.8v12.7z"
31+
fill="#fb015b"
32+
/>
33+
</g>
34+
</svg>
35+
);
36+
}

src/assets/icons/OktaIcon.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { iconProperties, IconProps } from "@/assets/icons/IconProperties";
2+
3+
export default function OktaIcon(props: Readonly<IconProps>) {
4+
return (
5+
<svg
6+
width="215"
7+
height="215"
8+
viewBox="0 0 215 215"
9+
xmlns="http://www.w3.org/2000/svg"
10+
{...iconProperties(props)}
11+
>
12+
<path
13+
d="M118.287 1.49306L113.865 55.9597C111.773 55.7208 109.681 55.6014 107.53 55.6014C104.841 55.6014 102.211 55.7806 99.6412 56.1986L97.1312 29.8014C97.0714 28.9653 97.7288 28.2486 98.5655 28.2486H103.048L100.896 1.55278C100.836 0.716667 101.494 0 102.271 0H116.913C117.749 0 118.407 0.716667 118.287 1.55278V1.49306ZM81.3538 4.18056C81.1147 3.40417 80.278 2.92639 79.5011 3.225L65.7557 8.24167C64.9788 8.54028 64.6202 9.43611 64.9788 10.1528L76.1544 34.5194L71.9112 36.0722C71.1343 36.3708 70.7758 37.2667 71.1343 37.9833L82.549 61.9319C86.6727 59.6625 91.1549 57.9306 95.8761 56.8556L81.4135 4.18056H81.3538ZM47.6475 19.35L79.2621 63.9028C75.258 66.5306 71.6124 69.6361 68.5048 73.2194L49.56 54.5861C48.9623 53.9889 49.0221 53.0333 49.6197 52.4958L53.086 49.6292L34.2607 30.5778C33.663 29.9806 33.7228 29.025 34.3802 28.4875L45.5558 19.1111C46.2132 18.5736 47.1097 18.6931 47.5878 19.35H47.6475ZM21.1129 45.15C20.4555 44.6722 19.4993 44.9111 19.0809 45.6278L11.7899 58.2889C11.3715 59.0056 11.6703 59.9014 12.3875 60.2597L36.6512 71.7264L34.3802 75.6083C33.9619 76.325 34.2607 77.2806 35.0376 77.5792L59.1818 88.6278C60.9149 84.1486 63.2456 79.9681 66.1143 76.2056L21.1129 45.15ZM3.24377 79.55C3.3633 78.7139 4.19998 78.2361 4.97689 78.4153L57.867 92.2111C56.4924 96.6903 55.7155 101.408 55.596 106.306L29.1211 104.156C28.2844 104.096 27.6868 103.319 27.8661 102.483L28.643 98.0639L1.92899 95.5556C1.09231 95.4958 0.554444 94.7194 0.67397 93.8833L3.18401 79.4903L3.24377 79.55ZM1.2716 115.264C0.434919 115.324 -0.102947 116.1 0.0165789 116.936L2.58638 131.329C2.70591 132.165 3.54259 132.643 4.3195 132.464L30.2566 125.715L31.0335 130.135C31.153 130.971 31.9897 131.449 32.7666 131.269L58.3451 124.222C56.851 119.803 55.8948 115.085 55.6558 110.247L1.21184 115.264H1.2716ZM9.75792 153.128C9.33958 152.411 9.63839 151.515 10.3555 151.157L59.7196 127.746C61.5723 132.165 64.0823 136.286 67.0705 139.989L45.4363 155.397C44.7789 155.875 43.8227 155.696 43.4044 154.979L41.1334 151.037L19.0809 166.267C18.4235 166.744 17.4673 166.506 17.049 165.789L9.69816 153.128H9.75792ZM69.5805 142.856L31.153 181.735C30.5554 182.332 30.6151 183.288 31.2725 183.825L42.5079 193.201C43.1653 193.739 44.0618 193.619 44.5399 192.963L60.0782 171.104L63.5445 174.031C64.2018 174.568 65.158 174.449 65.6361 173.732L80.6964 151.874C76.6325 149.365 72.8675 146.319 69.6403 142.856H69.5805ZM61.9906 205.265C61.2137 204.967 60.8551 204.071 61.2137 203.354L83.9833 153.665C88.1667 155.815 92.7087 157.428 97.43 158.324L90.7365 184.004C90.5572 184.781 89.6608 185.258 88.8839 184.96L84.6407 183.407L77.5289 209.267C77.2899 210.043 76.4532 210.521 75.6763 210.222L61.9309 205.206L61.9906 205.265ZM101.195 158.981L96.7726 213.447C96.7128 214.283 97.3702 215 98.1471 215H112.789C113.626 215 114.283 214.283 114.164 213.447L112.012 186.751H116.494C117.331 186.751 117.988 186.035 117.929 185.199L115.419 158.801C112.849 159.219 110.219 159.399 107.53 159.399C105.378 159.399 103.287 159.279 101.195 158.981ZM153.906 11.5264C154.264 10.75 153.906 9.91389 153.129 9.61528L139.383 4.59861C138.607 4.3 137.77 4.77778 137.531 5.55417L130.419 31.4139L126.176 29.8611C125.399 29.5625 124.562 30.0403 124.323 30.8167L117.63 56.4972C122.411 57.4528 126.893 59.0653 131.076 61.1556L153.906 11.5264ZM183.907 33.2056L145.479 72.0847C142.252 68.6208 138.547 65.575 134.423 63.0667L149.483 41.2083C149.961 40.5514 150.918 40.3722 151.575 40.9097L155.041 43.8361L170.58 21.9778C171.058 21.3208 172.014 21.2014 172.612 21.7389L183.847 31.1153C184.504 31.6528 184.504 32.6083 183.967 33.2056H183.907ZM204.704 63.7833C205.481 63.425 205.72 62.5292 205.302 61.8125L197.951 49.1514C197.533 48.4347 196.576 48.2556 195.919 48.6736L173.867 63.9028L171.596 60.0208C171.177 59.3042 170.221 59.0653 169.564 59.6028L147.93 75.0111C150.918 78.7139 153.368 82.8347 155.28 87.2542L204.644 63.8431L204.704 63.7833ZM212.473 83.6111L214.983 98.0042C215.103 98.8403 214.565 99.5569 213.728 99.6764L159.284 104.753C159.045 99.8556 158.089 95.1972 156.595 90.7778L182.174 83.7306C182.951 83.4917 183.787 84.0292 183.907 84.8653L184.684 89.2847L210.621 82.5361C211.398 82.3569 212.234 82.8347 212.354 83.6708L212.473 83.6111ZM210.023 136.465C210.8 136.644 211.637 136.167 211.756 135.331L214.266 120.938C214.386 120.101 213.848 119.385 213.011 119.265L186.297 116.757L187.074 112.337C187.194 111.501 186.656 110.785 185.819 110.665L159.344 108.515C159.225 113.413 158.448 118.131 157.073 122.61L209.963 136.406L210.023 136.465ZM195.919 169.313C195.501 170.029 194.545 170.208 193.887 169.79L148.886 138.735C151.754 134.972 154.085 130.792 155.818 126.312L179.962 137.361C180.739 137.719 181.038 138.615 180.62 139.332L178.349 143.214L202.613 154.681C203.33 155.039 203.628 155.935 203.21 156.651L195.919 169.313ZM135.738 150.978L167.352 195.531C167.831 196.188 168.787 196.307 169.384 195.769L180.56 186.393C181.217 185.856 181.217 184.9 180.68 184.303L161.854 165.251L165.321 162.385C165.978 161.847 165.978 160.892 165.38 160.294L146.435 141.661C143.268 145.244 139.682 148.41 135.678 150.978H135.738ZM135.439 211.656C134.662 211.954 133.826 211.476 133.586 210.7L119.124 158.025C123.845 156.95 128.327 155.218 132.451 152.949L143.866 176.897C144.224 177.674 143.866 178.569 143.089 178.808L138.846 180.361L150.021 204.728C150.38 205.504 150.021 206.34 149.244 206.639L135.499 211.656H135.439Z"
14+
fill="white"
15+
/>
16+
</svg>
17+
);
18+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
import { cn } from "@utils/helpers";
12
import * as React from "react";
23

34
type Props = {
45
children: React.ReactNode;
6+
className?: string;
57
};
68

7-
export const DropdownInfoText = ({ children }: Props) => {
9+
export const DropdownInfoText = ({ children, className }: Props) => {
810
return (
9-
<div className={"text-center pt-2 mb-6 text-nb-gray-400"}>{children}</div>
11+
<div className={cn("text-center pt-2 mb-6 text-nb-gray-400", className)}>
12+
{children}
13+
</div>
1014
);
1115
};

0 commit comments

Comments
 (0)