Skip to content

Admin bots list #315

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 14, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
Empty file added api/index.js
Empty file.
11 changes: 11 additions & 0 deletions api/partnerConnections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { axiosAdmin } from '@/utils/axios'

export const getPartnerConnections = async () => {
const response = await axiosAdmin.get('/partner/connections')
return response.data
}

export const getPartnerConnectionListeners = async (connectionId) => {
const response = await axiosAdmin.get(`/partner/connection/${connectionId}/listeners`)
return response.data
}
89 changes: 89 additions & 0 deletions components/Admin/ListenersList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react';
import dayjs from 'dayjs';

function formatTimestamp(ts) {
return dayjs.unix(ts).format('YYYY-MM-DD HH:mm');
}

export default function ListenersList({ listeners }) {
const [expandedId, setExpandedId] = useState(null);

if (!listeners || listeners.length === 0) {
return <div className="text-gray-500 text-center py-6 text-sm">No listeners found.</div>;
}

return (
<div className="flex flex-col px-2 sm:px-0">
{listeners.map((listener) => {
const panelId = `listener-panel-${listener.id}`;
return (
<div
key={listener.id}
className={`border-2 bg-white dark:bg-gray-800 dark:border-gray-700 hover:shadow-[2px_2px_0px_rgba(0,0,0,1)] transition-shadow duration-200 mb-3 ${
expandedId === listener.id ? 'ring-2 ring-blue-200' : ''
}`}
>
<button
className="border-none bg-white w-full flex justify-between items-center px-3 py-3 text-left focus:outline-none focus:ring-2 focus:ring-blue-400"
onClick={() => setExpandedId(expandedId === listener.id ? null : listener.id)}
aria-expanded={expandedId === listener.id}
aria-controls={panelId}
aria-label={`Expand details for listener ${listener.name}`}
>
<div className="flex-1 min-w-0">
<div className="text-base font-semibold text-gray-900 flex items-center gap-2 truncate">
<span className="truncate">{listener.name}</span>
{listener.enabled ? (
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-green-100 text-green-800 rounded">Enabled</span>
) : (
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-gray-200 text-gray-600 rounded">Disabled</span>
)}
</div>
<div className="text-xs text-gray-500 mt-1 truncate">
<span className="font-medium">Event:</span> {listener.event}
</div>
<div className="text-xs text-gray-400 mt-1 truncate">
<span className="font-medium">Connection:</span> {listener.connection?.name || '-'} ({listener.connection?.type || '-'})
</div>
</div>
<span className="ml-2 text-black text-2xl flex-shrink-0" aria-hidden>
{expandedId === listener.id ? '−' : '+'}
</span>
</button>
<div
id={panelId}
className={`overflow-hidden transition-all duration-300 ${expandedId === listener.id ? 'max-h-[1000px]' : 'max-h-0'}`}
aria-hidden={expandedId !== listener.id}
>
{expandedId === listener.id && (
<div className="px-3 pb-3 bg-blue-50 text-xs">
<div className="text-gray-700 mb-1">
<b>Created:</b> {formatTimestamp(listener.createdAt)}
</div>
<div className="text-gray-700 mb-1">
<b>Updated:</b> {formatTimestamp(listener.updatedAt)}
</div>
<div className="text-gray-700 mb-1 break-all">
<b>Connection Webhook:</b>{' '}
<a
href={listener.connection?.settings?.webhook}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 underline"
>
{listener.connection?.settings?.webhook}
</a>
</div>
<div className="text-gray-700 mt-2">
<b>Settings:</b>
<pre className="bg-gray-100 rounded p-2 mt-1 text-xs overflow-x-auto whitespace-pre-wrap break-all">{JSON.stringify(listener.settings, null, 2)}</pre>
</div>
</div>
)}
</div>
</div>
);
})}
</div>
);
}
6 changes: 3 additions & 3 deletions components/Tabs/AdminTabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function AdminTabs({ name, tab }) {
{ value: 'subscriptions', label: 'Subscriptions' },
{ value: 'pro', label: 'My addresses' },
{ value: 'api', label: 'API' }
//{ value: "bots", label: "Bots" },
//{ value: 'notifications', label: 'Notifications' } //disabled while is not ready yet
]

const apiTabs = [
Expand All @@ -23,8 +23,6 @@ export default function AdminTabs({ name, tab }) {
const changePage = (tab) => {
if (tab === 'api') {
router.push('/admin/api')
} else if (tab === 'bots') {
router.push('/admin/bots')
} else if (tab === 'subscriptions') {
router.push('/admin/subscriptions')
} else if (tab === 'profile') {
Expand All @@ -41,6 +39,8 @@ export default function AdminTabs({ name, tab }) {
router.push('/admin/pro')
} else if (tab === 'watchlist') {
router.push('/admin/watchlist')
} else if (tab === 'notifications') {
router.push('/admin/notifications')
}
}

Expand Down
54 changes: 54 additions & 0 deletions hooks/usePartherConnectionsApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useEffect, useState } from 'react'

import { getPartnerConnections, getPartnerConnectionListeners } from '@/api/partnerConnections'

export const useGetPartherListeners = () => {
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
const [listeners, setListeners] = useState([])

useEffect(() => {
const fetchAllListeners = async () => {
setIsLoading(true)
try {
const data = await getPartnerConnections()
const connections = data.connections
if (!connections || !Array.isArray(connections)) {
setListeners([])
return
}
// Fetch listeners for all connections concurrently
const allListeners = await Promise.all(
connections.map(async (connection) => {
try {
const response = await getPartnerConnectionListeners(connection.id)
const listenersArr = response.listeners || []
// Attach connection info to each listener
return listenersArr.map(listener => ({
...listener,
connection
}))
} catch (err) {
// If error, return empty array for this connection
return []
}
})
)
// Flatten the array
setListeners(allListeners.flat())
} catch (error) {
setError(error)
setListeners([])
} finally {
setIsLoading(false)
}
}
fetchAllListeners()
}, [])

return {
data: listeners,
isLoading,
error
}
}
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ module.exports = withBundleAnalyzer({
test: /\.svg$/,
use: ['@svgr/webpack']
})
//alias for @
config.resolve = config.resolve || {};
config.resolve.alias = config.resolve.alias || {};
config.resolve.alias['@'] = __dirname;
return config
},
images: {
Expand Down
42 changes: 0 additions & 42 deletions pages/admin/bots/index.js

This file was deleted.

38 changes: 38 additions & 0 deletions pages/admin/notifications/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'

import ListenersList from '@/components/Admin/ListenersList'

import AdminTabs from '@/components/Tabs/AdminTabs'
import { getIsSsrMobile } from '@/utils/mobile'
import { useGetPartherListeners } from '@/hooks/usePartherConnectionsApi'

export const getServerSideProps = async (context) => {
const { locale } = context
return {
props: {
isSsrMobile: getIsSsrMobile(context),
...(await serverSideTranslations(locale, ['common', 'admin']))
}
}
}


export default function Integrations() {
const { data: listeners, isLoading, error } = useGetPartherListeners()

if (isLoading) {
return <div>Loading...</div>
}

if (error) {
return <div>Error: {error.message}</div>
}

return (
<main className="page-admin content-center">
<h1 className="center">Notifications</h1>
<AdminTabs name="mainTabs" tab="notifications" />
<ListenersList listeners={listeners} />
</main>
)
}
2 changes: 1 addition & 1 deletion pages/terms-and-conditions.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ export default function TermsAndConditions() {
date of the latest version of this API Terms is indicated at the top of the document.
</p>

<h2> Bots Terms of Use</h2>
<h2>Bots Terms of Use</h2>

<p>
<strong>1. General Provisions</strong>
Expand Down