[xc-admin] permission accounts (#494)
* Rework pyth hooks * xc admin/add min pub page (#492) * add min pubs page * add wallet * update connect wallet button * fix header * add ClusterSwitch * fix header css * add update permissions tab * fix default tab * show default value when permission account does not exists * use table and add copy text function * update package-lock.json * update package-lock.json * trigger deployment * remove unused icon * capitalize tabInfo * remove -1 index check * refactor * refactor TabInfo to TAB_INFO * trigger deployment * remove unused icon Co-authored-by: Guillermo Bescos Alapont <gbescos@stanford.edu>
|
@ -1,15 +1,15 @@
|
|||
import { usePythContext } from '../contexts/PythContext'
|
||||
import ClusterSwitch from './ClusterSwitch'
|
||||
import Loadbar from './loaders/Loadbar'
|
||||
import { usePythContext } from '../../contexts/PythContext'
|
||||
import ClusterSwitch from '../ClusterSwitch'
|
||||
import Loadbar from '../loaders/Loadbar'
|
||||
|
||||
function MinPublishers() {
|
||||
const MinPublishers = () => {
|
||||
const { rawConfig, dataIsLoading } = usePythContext()
|
||||
|
||||
return (
|
||||
<div className="pt-15 relative lg:pt-20">
|
||||
<div className="container flex flex-col items-center justify-between pt-32 lg:flex-row ">
|
||||
<div className="mb-10 w-full text-left lg:mb-0">
|
||||
<h1 className="h1 mb-3">Min Publishers</h1>
|
||||
<div className="relative">
|
||||
<div className="container flex flex-col items-center justify-between lg:flex-row">
|
||||
<div className="mb-4 w-full text-left lg:mb-0">
|
||||
<h1 className="h1 mb-4">Min Publishers</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container">
|
|
@ -0,0 +1,96 @@
|
|||
import { PublicKey } from '@solana/web3.js'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import React from 'react'
|
||||
import { usePythContext } from '../../contexts/PythContext'
|
||||
import CopyIcon from '../../images/icons/copy.inline.svg'
|
||||
import ClusterSwitch from '../ClusterSwitch'
|
||||
import Loadbar from '../loaders/Loadbar'
|
||||
|
||||
interface UpdatePermissionsProps {
|
||||
account: string
|
||||
pubkey?: PublicKey
|
||||
}
|
||||
|
||||
const UpdatePermissionsRow: React.FunctionComponent<UpdatePermissionsProps> = ({
|
||||
account,
|
||||
pubkey = new PublicKey(0),
|
||||
}) => {
|
||||
return (
|
||||
<tr key={account} className="border-t border-beige-300">
|
||||
<td className="py-3 pl-4 pr-2 lg:pl-14">{account}</td>
|
||||
<td className="py-3 pl-1 lg:pl-14">
|
||||
<div
|
||||
className="-ml-1 inline-flex cursor-pointer items-center px-1 hover:bg-dark hover:text-white active:bg-darkGray3"
|
||||
onClick={() => {
|
||||
copy(pubkey.toBase58())
|
||||
}}
|
||||
>
|
||||
<span className="mr-2 hidden lg:block">{pubkey.toBase58()}</span>
|
||||
<span className="mr-2 lg:hidden">
|
||||
{pubkey.toBase58().slice(0, 6) +
|
||||
'...' +
|
||||
pubkey.toBase58().slice(-6)}
|
||||
</span>{' '}
|
||||
<CopyIcon className="shrink-0" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
const UpdatePermissions = () => {
|
||||
const { rawConfig, dataIsLoading } = usePythContext()
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="container flex flex-col items-center justify-between lg:flex-row">
|
||||
<div className="mb-4 w-full text-left lg:mb-0">
|
||||
<h1 className="h1 mb-4">Update Permissions</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="mb-4 md:mb-0">
|
||||
<ClusterSwitch />
|
||||
</div>
|
||||
<div className="table-responsive relative mt-6">
|
||||
{dataIsLoading ? (
|
||||
<div className="mt-3">
|
||||
<Loadbar theme="light" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="table-responsive mb-10">
|
||||
<table className="w-full bg-darkGray text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="base16 pt-8 pb-6 pl-4 pr-2 font-semibold opacity-60 lg:pl-14">
|
||||
Account
|
||||
</th>
|
||||
<th className="base16 pt-8 pb-6 pl-1 pr-2 font-semibold opacity-60 lg:pl-14">
|
||||
Public Key
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<UpdatePermissionsRow
|
||||
account="Master Authority"
|
||||
pubkey={rawConfig.permissionAccount?.masterAuthority}
|
||||
/>
|
||||
<UpdatePermissionsRow
|
||||
account="Data Curation Authority"
|
||||
pubkey={rawConfig.permissionAccount?.dataCurationAuthority}
|
||||
/>
|
||||
<UpdatePermissionsRow
|
||||
account="Security Authority"
|
||||
pubkey={rawConfig.permissionAccount?.securityAuthority}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UpdatePermissions
|
|
@ -19,6 +19,7 @@
|
|||
"@types/node": "18.11.18",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"gsap": "^3.11.4",
|
||||
"next": "12.2.5",
|
||||
"next-seo": "^5.15.0",
|
||||
|
|
|
@ -32,6 +32,7 @@ export default function Document() {
|
|||
sizes="16x16"
|
||||
href="/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#242235" />
|
||||
<meta name="msapplication-TileColor" content="#242235" />
|
||||
<meta name="theme-color" content="#242235"></meta>
|
||||
|
|
|
@ -1,13 +1,97 @@
|
|||
import { Tab } from '@headlessui/react'
|
||||
import type { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import Layout from '../components/layout/Layout'
|
||||
import MinPublishers from '../components/MinPublishers'
|
||||
import MinPublishers from '../components/tabs/MinPublishers'
|
||||
import UpdatePermissions from '../components/tabs/UpdatePermissions'
|
||||
import { PythContextProvider } from '../contexts/PythContext'
|
||||
import { classNames } from '../utils/classNames'
|
||||
|
||||
const TAB_INFO = {
|
||||
MinPublishers: {
|
||||
title: 'Min Publishers',
|
||||
description:
|
||||
'Set the minimum number of publishers required to publish a price.',
|
||||
queryString: 'min-publishers',
|
||||
},
|
||||
UpdatePermissions: {
|
||||
title: 'Update Permissions',
|
||||
description: 'Update the permissions of the program.',
|
||||
queryString: 'update-permissions',
|
||||
},
|
||||
}
|
||||
|
||||
const DEFAULT_TAB = 'min-publishers'
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const [currentTabIndex, setCurrentTabIndex] = useState(0)
|
||||
const tabInfoArray = Object.values(TAB_INFO)
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// set current tab value when tab is clicked
|
||||
const handleChangeTab = (index: number) => {
|
||||
router.query.tab = tabInfoArray[index].queryString
|
||||
setCurrentTabIndex(index)
|
||||
router.push(
|
||||
{
|
||||
pathname: router.pathname,
|
||||
query: router.query,
|
||||
},
|
||||
undefined,
|
||||
{ scroll: false }
|
||||
)
|
||||
}
|
||||
|
||||
// set current tab value when page is loaded
|
||||
useEffect(() => {
|
||||
router.query && router.query.tab
|
||||
? setCurrentTabIndex(
|
||||
tabInfoArray.findIndex((v) => v.queryString === router.query.tab)
|
||||
)
|
||||
: setCurrentTabIndex(
|
||||
tabInfoArray.findIndex((v) => v.queryString === DEFAULT_TAB)
|
||||
)
|
||||
}, [router, tabInfoArray])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<PythContextProvider>
|
||||
<MinPublishers />
|
||||
<div className="relative pt-16 md:pt-20">
|
||||
<div className="py-8 md:py-16">
|
||||
<Tab.Group
|
||||
selectedIndex={currentTabIndex}
|
||||
onChange={handleChangeTab}
|
||||
>
|
||||
<Tab.List className="mx-auto max-w-[526px] gap-1 space-x-4 text-center sm:gap-2.5 md:space-x-8">
|
||||
{Object.entries(TAB_INFO).map((tab, idx) => (
|
||||
<Tab
|
||||
key={idx}
|
||||
className={({ selected }) =>
|
||||
classNames(
|
||||
'p-3 text-xs font-semibold uppercase outline-none transition-colors md:text-base',
|
||||
currentTabIndex === idx
|
||||
? 'bg-darkGray3'
|
||||
: 'bg-darkGray2',
|
||||
selected ? 'bg-darkGray3' : 'hover:bg-darkGray3'
|
||||
)
|
||||
}
|
||||
>
|
||||
{tab[1].title}
|
||||
</Tab>
|
||||
))}
|
||||
</Tab.List>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
</div>
|
||||
{tabInfoArray[currentTabIndex].queryString ===
|
||||
TAB_INFO.MinPublishers.queryString ? (
|
||||
<MinPublishers />
|
||||
) : tabInfoArray[currentTabIndex].queryString ===
|
||||
TAB_INFO.UpdatePermissions.queryString ? (
|
||||
<UpdatePermissions />
|
||||
) : null}
|
||||
</PythContextProvider>
|
||||
</Layout>
|
||||
)
|
||||
|
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#242235</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
After Width: | Height: | Size: 965 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 3.6 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="646.000000pt" height="646.000000pt" viewBox="0 0 646.000000 646.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,646.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3039 6455 c-2 -1 -38 -6 -79 -10 -41 -3 -82 -8 -90 -10 -8 -2 -37
|
||||
-7 -64 -10 -27 -3 -51 -7 -55 -9 -4 -3 -25 -7 -47 -10 -22 -4 -71 -15 -108
|
||||
-26 -36 -10 -69 -19 -72 -18 -2 0 -17 -4 -32 -10 -15 -5 -58 -20 -97 -32 -109
|
||||
-36 -253 -96 -315 -131 -8 -5 -26 -14 -39 -19 -43 -19 -229 -131 -241 -145 -7
|
||||
-8 -19 -15 -26 -15 -8 0 -14 -4 -14 -10 0 -5 -5 -10 -11 -10 -21 0 -245 -188
|
||||
-340 -286 -136 -139 -147 -151 -242 -276 -83 -108 -240 -366 -273 -448 -3 -8
|
||||
-14 -33 -24 -55 -35 -81 -89 -224 -106 -285 -20 -72 -65 -251 -69 -275 -3 -16
|
||||
-8 -41 -10 -55 -3 -14 -8 -45 -11 -70 -3 -25 -8 -65 -11 -90 -3 -25 -8 -531
|
||||
-10 -1125 l-4 -1079 318 -318 c175 -175 320 -318 323 -318 3 0 5 600 6 1332 0
|
||||
1149 5 1494 19 1518 1 3 6 27 9 54 4 27 9 53 11 57 3 4 7 22 9 40 5 36 51 192
|
||||
61 209 3 6 8 19 10 30 11 55 123 277 193 383 102 152 127 186 139 187 6 0 14
|
||||
8 17 18 6 16 151 163 226 227 53 46 207 155 218 155 6 0 12 4 14 9 5 15 211
|
||||
118 314 156 96 36 213 72 264 81 14 3 32 7 40 9 8 3 32 7 54 11 21 3 47 7 57
|
||||
9 11 2 56 7 102 11 445 40 883 -71 1252 -318 123 -83 173 -125 291 -242 191
|
||||
-192 311 -369 415 -613 23 -52 42 -100 44 -107 1 -6 5 -19 9 -28 4 -9 9 -25
|
||||
11 -35 2 -10 4 -19 5 -20 2 -2 3 -6 4 -10 2 -5 8 -28 15 -52 63 -221 80 -495
|
||||
47 -776 -7 -57 -36 -189 -60 -270 -2 -5 -6 -23 -11 -40 -4 -16 -10 -34 -14
|
||||
-40 -3 -5 -6 -12 -6 -15 -6 -51 -135 -315 -192 -397 -13 -17 -23 -36 -23 -42
|
||||
0 -6 -4 -11 -8 -11 -4 0 -16 -15 -27 -32 -10 -18 -24 -38 -29 -44 -6 -7 -22
|
||||
-26 -36 -44 -79 -101 -194 -216 -295 -296 -27 -21 -52 -41 -55 -44 -13 -13
|
||||
-129 -90 -136 -90 -4 0 -15 -6 -23 -14 -45 -41 -319 -165 -421 -191 -3 -1 -12
|
||||
-5 -20 -8 -83 -34 -338 -76 -490 -81 l-65 -2 -3 -323 -2 -323 134 6 c73 4 142
|
||||
8 152 10 11 2 44 7 74 11 30 4 64 9 75 11 11 3 36 7 55 10 181 28 462 125 671
|
||||
230 134 67 355 204 372 231 4 7 13 13 18 13 6 0 16 6 23 12 7 7 35 31 61 53
|
||||
157 129 282 258 399 409 66 86 156 213 156 221 0 2 15 28 34 57 42 66 170 330
|
||||
174 361 1 4 9 21 17 37 8 16 16 33 16 37 1 5 3 13 5 18 33 101 44 135 48 156
|
||||
3 13 10 40 16 59 15 51 58 287 66 367 15 145 8 538 -10 633 -3 14 -8 45 -11
|
||||
70 -3 25 -8 53 -11 62 -5 17 -12 47 -20 88 -3 14 -10 43 -16 65 -5 22 -11 47
|
||||
-13 55 -15 71 -123 351 -165 425 -11 19 -18 35 -15 35 8 0 -108 195 -176 295
|
||||
-111 163 -255 328 -410 468 -75 68 -237 193 -294 227 -22 13 -42 27 -45 30
|
||||
-22 26 -271 155 -399 206 -75 31 -272 98 -278 96 -2 -1 -21 5 -43 13 -22 8
|
||||
-40 12 -40 10 0 -3 -12 0 -27 6 -15 6 -46 12 -68 15 -22 3 -47 7 -55 10 -8 3
|
||||
-33 8 -55 10 -22 3 -51 8 -65 10 -14 3 -45 7 -70 10 -55 6 -466 14 -471 9z"/>
|
||||
<path d="M3060 5159 c-25 -4 -61 -9 -80 -13 -82 -13 -216 -59 -317 -108 -343
|
||||
-168 -603 -493 -687 -858 -8 -36 -18 -78 -23 -95 -4 -16 -9 -795 -10 -1730
|
||||
l-4 -1699 318 -318 c175 -175 320 -318 323 -318 3 0 5 888 6 1973 l0 1972 22
|
||||
78 c65 229 219 387 447 458 69 21 180 28 272 16 60 -8 191 -53 214 -73 8 -8
|
||||
20 -14 25 -14 5 0 21 -10 35 -23 15 -12 41 -33 58 -46 76 -59 174 -223 201
|
||||
-338 48 -208 -21 -444 -176 -598 -63 -62 -170 -135 -199 -135 -8 0 -16 -4 -19
|
||||
-9 -9 -14 -131 -42 -196 -45 l-35 -1 -3 -324 -2 -324 77 7 c150 12 242 29 323
|
||||
60 19 8 40 15 45 16 6 1 26 9 45 17 19 8 46 19 58 24 27 10 164 89 172 98 3 3
|
||||
25 20 50 36 110 74 286 275 354 405 16 30 34 58 40 62 6 4 7 8 2 8 -4 0 -2 10
|
||||
6 23 13 20 49 108 54 132 2 6 12 45 23 89 30 113 35 156 35 301 1 166 -6 229
|
||||
-43 365 -32 122 -103 271 -183 387 -51 75 -192 225 -253 271 -16 12 -47 35
|
||||
-69 52 -21 16 -45 30 -52 30 -8 0 -14 4 -14 8 0 10 -144 83 -220 111 -65 24
|
||||
-155 50 -196 56 -16 2 -56 9 -89 14 -69 10 -268 11 -335 0z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#242235",
|
||||
"background_color": "#242235",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,3 @@
|
|||
export const classNames = (...classes: any) => {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|