Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
REACT_APP_DEBUG=
REACT_APP_API_BASE_URL=$API_BASE_URL
REACT_APP_LINK_SSV_WEBAPP=$LINK_SSV_WEBAPP
REACT_APP_GOOGLE_TAG_SECRET=$GOOGLE_TAG_SECRET
REACT_APP_GOOGLE_TAG_URL=https://www.googletagmanager.com/gtm.js?id=
REACT_APP_ANNOUNCEMENT=$ANNOUNCEMENT
REACT_APP_MIXPANEL_TOKEN=$MIXPANEL_TOKEN
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
Binary file modified public/favicon.ico
Binary file not shown.
4 changes: 2 additions & 2 deletions public/site.webmanifest
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Shadcn Table",
"short_name": "Shadcn Table",
"name": "SSV Network Explorer",
"short_name": "SSV Network Explorer",
"icons": [
{
"src": "/icon.png",
Expand Down
6 changes: 3 additions & 3 deletions src/api/clusters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export const searchClusters = async <
filtered as unknown as Record<string, string>
)

return await api.get<PaginatedClustersResponse<T>>(
endpoint(params.network, "clusters", `?${searchParams}`)
)
const e = endpoint(params.network, "clusters", `?${searchParams}`)
console.log("e:", e)
return await api.get<PaginatedClustersResponse<T>>(e)
},
[JSON.stringify(stringifyBigints(params))],
{
Expand Down
25 changes: 17 additions & 8 deletions src/app/_components/clusters/clusters-table-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import { formatSSV } from "@/lib/utils/number"
import { remove0x, shortenAddress } from "@/lib/utils/strings"
import { CopyBtn } from "@/components/ui/copy-btn"
import { Text } from "@/components/ui/text"
import { Tooltip } from "@/components/ui/tooltip"
import { ClusterStatusBadge } from "@/components/clusters/cluster-status-badge"
import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header"
import { OperatorAvatar } from "@/components/operators/operator-avatar"
import { OperatorInfo } from "@/components/tooltip/operator-info"

export const clustersTableColumns = [
{
Expand Down Expand Up @@ -59,13 +62,19 @@ export const clustersTableColumns = [
<div className="flex gap-1">
{row.original.operators.map((operator) => {
return (
<Link href={`/operator/${operator.id}`} key={operator.id}>
<OperatorAvatar
size="base"
src={operator.logo}
isPrivate={operator.is_private}
/>
</Link>
<Tooltip
asChild
key={operator.id}
className="w-[240px] p-4"
content={<OperatorInfo operator={operator} />}
>
<Link href={`/operator/${operator.id}`} key={operator.id}>
<OperatorAvatar
src={operator.logo}
isPrivate={operator.is_private}
/>
</Link>
</Tooltip>
)
})}
</div>
Expand All @@ -91,7 +100,7 @@ export const clustersTableColumns = [
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Active" />
),
cell: ({ row }) => <div>{row.original.active ? "Yes" : "No"}</div>,
cell: ({ row }) => <ClusterStatusBadge active={row.original.active} />,
},
] satisfies ColumnDef<Cluster>[]

Expand Down
47 changes: 27 additions & 20 deletions src/app/_components/clusters/filters/cluster-id-filter.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"use client"

import { useState } from "react"
import { FC, useState } from "react"
import { searchClusters } from "@/api/clusters"
import { useQuery } from "@tanstack/react-query"
import { CommandLoading } from "cmdk"
import { xor } from "lodash-es"
import { Loader2, X } from "lucide-react"
import { useQueryState } from "nuqs"

import { clustersSearchFilters } from "@/lib/search-parsers/clusters-search-parsers"
import { cn } from "@/lib/utils"
import { shortenAddress } from "@/lib/utils/strings"
import { useClustersSearchParams } from "@/hooks/search/use-clusters-search-params"
import { useNetworkQuery } from "@/hooks/search/use-network-query"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
Expand All @@ -22,10 +25,22 @@ import {
import { Text } from "@/components/ui/text"
import { FilterButton } from "@/components/filter/filter-button"

export function ClusterIdFilter() {
type ClusterIdFilterProps = {
searchQueryKey?: string
}

export const ClusterIdFilter: FC<ClusterIdFilterProps> = ({
searchQueryKey = "cluster",
}) => {
const [open, setOpen] = useState(false)
const [search, setSearch] = useState<string>("")
const { network, filters, setFilters } = useClustersSearchParams()

const network = useNetworkQuery().query.value
const [clusterIds, setClusterIds] = useQueryState(
searchQueryKey,
clustersSearchFilters.clusterId
)

const query = useQuery({
queryKey: ["clusters", "ids", search, network],
queryFn: async () => {
Expand All @@ -46,8 +61,8 @@ export function ClusterIdFilter() {
return (
<FilterButton
name="Cluster ID"
activeFiltersCount={filters.clusterId?.length ?? 0}
onClear={() => setFilters((prev) => ({ ...prev, clusterId: [] }))}
activeFiltersCount={clusterIds?.length ?? 0}
onClear={() => setClusterIds([])}
popover={{
root: {
open,
Expand All @@ -66,20 +81,15 @@ export function ClusterIdFilter() {
onValueChange={(value) => setSearch(value)}
/>
</div>
{Boolean(filters.clusterId?.length) && (
{Boolean(clusterIds?.length) && (
<div className="flex flex-wrap gap-1 border-y border-gray-200 p-2">
{filters.clusterId?.map((id) => (
{clusterIds?.map((id) => (
<Button
size="sm"
key={id}
className="h-6 gap-0.5 rounded-full pb-px pl-2 pr-1"
variant="secondary"
onClick={() =>
setFilters((prev) => ({
...prev,
clusterId: xor(prev.clusterId, [id]),
}))
}
onClick={() => setClusterIds(xor(clusterIds, [id]))}
>
<Text variant="caption-medium">{shortenAddress(id)}</Text>{" "}
<div className="flex size-4 items-center justify-center">
Expand All @@ -102,16 +112,13 @@ export function ClusterIdFilter() {
key={cluster.clusterId}
value={cluster.clusterId}
className="flex h-10 items-center space-x-2 px-2"
onSelect={() => {
setFilters((prev) => ({
...prev,
clusterId: xor(prev.clusterId, [cluster.clusterId]),
}))
}}
onSelect={() =>
setClusterIds(xor(clusterIds, [cluster.clusterId]))
}
>
<Checkbox
id={cluster.clusterId}
checked={filters.clusterId?.includes(cluster.clusterId)}
checked={clusterIds?.includes(cluster.clusterId)}
className="mr-2"
/>
<span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const ClusterTableFilters = () => {
)}
aria-hidden={!isFiltersOpen}
>
<ClusterIdFilter />
<ClusterIdFilter />
<OwnerAddressFilter />
<StatusFilter />
<IsLiquidatedFilter />
Expand Down
44 changes: 25 additions & 19 deletions src/app/_components/clusters/filters/operators-filter.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"use client"

import { useState } from "react"
import { FC, useState } from "react"
import { searchOperators } from "@/api/operator"
import { useQuery } from "@tanstack/react-query"
import { CommandLoading } from "cmdk"
import { xor } from "lodash-es"
import { Loader2, X } from "lucide-react"
import { useQueryState } from "nuqs"

import { useClustersSearchParams } from "@/hooks/search/use-clusters-search-params"
import { clustersSearchFilters } from "@/lib/search-parsers/clusters-search-parsers"
import { useNetworkQuery } from "@/hooks/search/use-network-query"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import {
Expand All @@ -21,10 +23,22 @@ import { Text } from "@/components/ui/text"
import { FilterButton } from "@/components/filter/filter-button"
import { OperatorInfo } from "@/components/operators/operator-info"

export function OperatorsFilter() {
type OperatorsFilterProps = {
searchQueryKey?: string
}

export const OperatorsFilter: FC<OperatorsFilterProps> = ({
searchQueryKey = "operators",
}) => {
const [open, setOpen] = useState(false)
const [search, setSearch] = useState<string>("")
const { network, filters, setFilters } = useClustersSearchParams()

const network = useNetworkQuery().query.value
const [operators, setOperators] = useQueryState(
searchQueryKey,
clustersSearchFilters.operators
)

const query = useQuery({
queryKey: ["operators", "search", search, network],
queryFn: async () => {
Expand All @@ -41,8 +55,8 @@ export function OperatorsFilter() {
return (
<FilterButton
name="Operators"
activeFiltersCount={filters.operators?.length ?? 0}
onClear={() => setFilters((prev) => ({ ...prev, operators: [] }))}
activeFiltersCount={operators?.length ?? 0}
onClear={() => setOperators([])}
popover={{
root: {
open,
Expand All @@ -61,20 +75,15 @@ export function OperatorsFilter() {
onValueChange={(value) => setSearch(value)}
/>
</div>
{Boolean(filters.operators?.length) && (
{Boolean(operators?.length) && (
<div className="flex flex-wrap gap-1 border-y border-gray-200 p-2">
{filters.operators?.map((id) => (
{operators?.map((id) => (
<Button
size="sm"
key={id}
className="h-6 gap-0.5 rounded-full pb-px pl-2 pr-1"
variant="secondary"
onClick={() =>
setFilters((prev) => ({
...prev,
operators: xor(prev.operators, [id]),
}))
}
onClick={() => setOperators(xor(operators, [id]))}
>
<Text variant="caption-medium">{id}</Text>{" "}
<div className="flex size-4 items-center justify-center">
Expand All @@ -98,15 +107,12 @@ export function OperatorsFilter() {
value={operator.id.toString()}
className="flex h-10 items-center space-x-2 px-2 py-1"
onSelect={() => {
setFilters((prev) => ({
...prev,
operators: xor(prev.operators, [operator.id]),
}))
setOperators(xor(operators, [operator.id]))
}}
>
<Checkbox
id={operator.id.toString()}
checked={filters.operators?.includes(operator.id)}
checked={operators?.includes(operator.id)}
className="mr-2"
/>
<OperatorInfo
Expand Down
Loading