import { useState, useEffect } from 'react'; import Head from 'next/head'; import Link from 'next/link'; import { useRouter } from 'next/router'; import dynamic from 'next/dynamic'; import moment from 'moment'; import { useUser } from '../../../lib/auth/hooks'; import { Layout, Breadcrumb, Button, Typography, Table, Progress, message, Spin, Modal, } from 'antd'; import { GithubFilled } from '@ant-design/icons'; import Sidebar from '../../../components/sidebar'; const ReactJson = dynamic(import('react-json-view'), { ssr: false }); const { Title } = Typography; const { Header, Content, Footer } = Layout; const columns = [ { title: 'Jobs', dataIndex: 'name', key: 'name', }, { title: 'Timestamp', dataIndex: 'timestamp', key: 'timestamp', render: function ProgressCol(timestamp) { return <div>{moment(timestamp).calendar()}</div>; }, }, { title: 'Attempts', dataIndex: 'attemptsMade', key: 'attemptsMade', }, { title: 'Progress', dataIndex: 'progress', key: 'progress', render: function ProgressCol(progress) { return <Progress percent={progress} />; }, }, { title: 'Action', key: 'action', render: function ActionCol(_, record) { const ActionColWrapper = () => { const [showLogs, setShowLogs] = useState(false); const [jobLogs, setJobLogs] = useState(null); const loadLogs = async () => { try { setJobLogs(null); const res = await fetch(`/api/queue/${record.queueId}/logs/${record.id}`); const { data, error } = await res.json(); switch (res.status) { case 200: setJobLogs(data.logs); break; default: throw new Error(error); } } catch (error) { message.error(error.message); setJobLogs(null); } }; return ( <div> <Button type="link" onClick={() => { setShowLogs(true); loadLogs(); }} > Logs </Button> <Modal title={`Job logs for id: ${record.id}`} visible={showLogs} onCancel={() => setShowLogs(false)} footer={null} width="80%" > <div className="dashboard-logs"> {(Array.isArray(jobLogs) && ( <ReactJson style={{ width: '100%' }} src={jobLogs} /> )) || <Spin />} </div> </Modal> </div> ); }; return <ActionColWrapper />; }, }, ]; export default function JobStatus() { const user = useUser({ redirectTo: '/' }); const router = useRouter(); const { id, status } = router.query; const [jobStatus, setJobStatus] = useState({ jobs: [], count: 0 }); const [jobStatusLoading, setJobStatusLoading] = useState(false); const fetchJobStatus = async (page, size) => { if (!id || !status) return; try { setJobStatusLoading(true); const res = await fetch(`/api/queue/${id}/${status}?page=${page}&size=${size}`); const { data, error } = await res.json(); switch (res.status) { case 200: setJobStatus(data); break; default: throw new Error(error); } } catch (error) { message.error(error.message); } finally { setJobStatusLoading(false); } }; useEffect(() => { fetchJobStatus(1, 10); }, [user]); const onPageChange = (page, pageSize) => { fetchJobStatus(page, pageSize); }; const onPageSizeChange = (page, pageSize) => { fetchJobStatus(page, pageSize); }; const expandedRowRender = (record) => { return <ReactJson src={record} />; }; return ( <div> <Head> <title>Zero Queue</title> <link rel="icon" href="/favicon.ico" /> </Head> {user && ( <Layout className="dashboard-layout"> <Sidebar /> <Layout className="dashboard-layout"> <Header className="dashboard-header"> <div className="dashboard-header__space" /> <Button type="link" href="/api/auth/logout"> Logout </Button> </Header> <Content className="dashboard-content"> <Breadcrumb className="dashboard-content__breadcrumb"> <Breadcrumb.Item>ZeroQueue</Breadcrumb.Item> <Breadcrumb.Item> <Link href={`/dashboard`}> <a>Queue</a> </Link> </Breadcrumb.Item> <Breadcrumb.Item> <Link href={`/queue/${id}`}> <a>{id}</a> </Link> </Breadcrumb.Item> <Breadcrumb.Item>{status}</Breadcrumb.Item> </Breadcrumb> <div className="dashboard-content__background"> <div className="dashboard-overview-header"> <Title className="dashboard-overview-header__title" level={3}> {status} </Title> </div> <Table rowKey="id" loading={jobStatusLoading} columns={columns} expandable={{ expandedRowRender, }} dataSource={jobStatus.jobs.map((job) => ({ queueId: id, ...job }))} pagination={{ total: jobStatus.count, onChange: onPageChange, onShowSizeChange: onPageSizeChange, }} /> </div> </Content> <Footer className="dashboard-footer"> <Button type="link" href="https://github.com/thezeroqueue/zeroqueue" target="blank" icon={<GithubFilled />} /> </Footer> </Layout> </Layout> )} </div> ); }