jackson/internal-ui/src/dsync/DirectoryWebhookLogs.tsx

188 lines
4.9 KiB
TypeScript

import useSWR from 'swr';
import { useState } from 'react';
import { useTranslation } from 'next-i18next';
import EyeIcon from '@heroicons/react/24/outline/EyeIcon';
import type { WebhookEventLog } from '../types';
import { fetcher, addQueryParamsToPath } from '../utils';
import { DirectoryTab } from '../dsync';
import { usePaginate, useDirectory } from '../hooks';
import { TableBodyType } from '../shared/Table';
import {
Loading,
Table,
EmptyState,
Error,
Pagination,
PageHeader,
pageLimit,
ConfirmationModal,
} from '../shared';
import { ButtonDanger } from '../shared/ButtonDanger';
import { useRouter } from '../hooks';
export const DirectoryWebhookLogs = ({
urls,
onView,
onDelete,
}: {
urls: { getEvents: string; getDirectory: string; tabBase: string; deleteEvents?: string };
onView?: (event: WebhookEventLog) => void;
onDelete?: () => void;
}) => {
const { router } = useRouter();
const { t } = useTranslation('common');
const [delModalVisible, setDelModalVisible] = useState(false);
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
const params = {
pageOffset: paginate.offset,
pageLimit,
};
// For DynamoDB
if (paginate.offset > 0 && pageTokenMap[paginate.offset - pageLimit]) {
params['pageToken'] = pageTokenMap[paginate.offset - pageLimit];
}
const getUrl = addQueryParamsToPath(urls.getEvents, params);
const { directory, isLoadingDirectory, directoryError } = useDirectory(urls.getDirectory);
const { data, isLoading, error } = useSWR<{ data: WebhookEventLog[] }>(getUrl, fetcher);
if (isLoading || isLoadingDirectory) {
return <Loading />;
}
if (error || directoryError) {
return <Error message={error.message || directoryError.message} />;
}
if (!data || !directory) {
return null;
}
const events = data?.data || [];
const noEvents = events.length === 0 && paginate.offset === 0;
const noMoreResults = events.length === 0 && paginate.offset > 0;
const columns = [
{
key: 'webhook_endpoint',
label: t('bui-dsync-webhook-endpoint'),
wrap: true,
dataIndex: 'webhook_endpoint',
},
{
key: 'status_code',
label: t('bui-dsync-status-code'),
wrap: true,
dataIndex: 'status_code',
},
{
key: 'created_at',
label: t('bui-dsync-sent-at'),
wrap: true,
dataIndex: 'created_at',
},
{
key: 'actions',
label: t('bui-dsync-actions'),
wrap: true,
dataIndex: null,
},
];
const cols = columns.map(({ label }) => label);
const body: TableBodyType[] = events.map((event) => {
return {
id: event.id,
cells: columns.map((column) => {
const dataIndex = column.dataIndex as keyof typeof event;
if (dataIndex === null) {
return {
actions: [
{
text: t('bui-dsync-view'),
onClick: () => onView?.(event),
icon: <EyeIcon className='w-5' />,
},
],
};
}
if (dataIndex === 'status_code') {
return {
badge: {
text: event[dataIndex] as any,
color: event[dataIndex] === 200 ? 'success' : 'error',
},
};
}
return {
wrap: column.wrap,
text: event[dataIndex] as string,
};
}),
};
});
const removeEvents = async () => {
if (!urls.deleteEvents) {
return;
}
await fetch(urls.deleteEvents, {
method: 'DELETE',
});
onDelete?.();
};
return (
<>
<PageHeader title={directory.name} />
<DirectoryTab activeTab='events' baseUrl={urls.tabBase} />
{noEvents ? (
<EmptyState title={t('bui-dsync-no-events')} />
) : (
<>
{urls.deleteEvents && (
<>
<div className='py-2 flex justify-end'>
<ButtonDanger onClick={() => setDelModalVisible(true)}>
{t('bui-dsync-remove-events')}
</ButtonDanger>
</div>
<ConfirmationModal
title={t('bui-dsync-delete-events-title')}
description={t('bui-dsync-delete-events-desc')}
visible={delModalVisible}
onConfirm={() => removeEvents()}
onCancel={() => setDelModalVisible(false)}
/>
</>
)}
<Table noMoreResults={noMoreResults} cols={cols} body={body} />
<Pagination
itemsCount={events.length}
offset={paginate.offset}
onPrevClick={() => {
setPaginate({
offset: paginate.offset - pageLimit,
});
}}
onNextClick={() => {
setPaginate({
offset: paginate.offset + pageLimit,
});
}}
/>
</>
)}
</>
);
};