Skip to content

Commit

Permalink
Update Queue.tsx to use top level types
Browse files Browse the repository at this point in the history
  • Loading branch information
Yurickh committed Jan 25, 2020
1 parent cf3b47b commit 64ec27c
Showing 1 changed file with 90 additions and 61 deletions.
151 changes: 90 additions & 61 deletions src/ui/components/Queue.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// REVIEW: I feel like this file has become quite chockfull of responsabilities.
// WDYT about splitting it?

import React, { useState } from 'react'
import {
getYear,
Expand All @@ -7,10 +10,9 @@ import {
formatDistanceStrict,
} from 'date-fns'
import Highlight from 'react-highlight'
import { Job } from 'bull'
import { Job as JobMq } from 'bullmq'

import { FIELDS, STATUSES } from './constants'
import { FIELDS, STATUSES, Status, Field } from './constants'
import { AppQueue, AppJob } from '../../@types/app'

const today = new Date()

Expand All @@ -26,10 +28,16 @@ const formatDate = (ts: TimeStamp) => {
: format(ts, 'MM/dd/yyyy HH:mm:ss')
}

const Timestamp = ({ ts, prev }: { ts?: TimeStamp; prev?: TimeStamp }) => {
if (ts === undefined) {
// REVIEW: what should be the timestamp for undefined?
// Currently we're passing `job.finishedOn` to this component, which might be undefined
const Timestamp = ({
ts,
prev,
}: {
ts: TimeStamp | null
prev?: TimeStamp | null
}) => {
if (ts === null) {
// REVIEW: what should be the timestamp for null?
// Currently we're passing `job.finishedOn` to this component, which might be null
return null
}

Expand All @@ -47,8 +55,14 @@ const Timestamp = ({ ts, prev }: { ts?: TimeStamp; prev?: TimeStamp }) => {
)
}

// FIXME: typings
const MenuItem = ({ status, count, onClick, selected }: any) => (
type MenuItemProps = {
status: Status
count: number
onClick: () => void
selected: boolean
}

const MenuItem = ({ status, count, onClick, selected }: MenuItemProps) => (
<div
className={`menu-item ${status} ${selected ? 'selected' : ''} ${
count === 0 ? 'off' : 'on'
Expand Down Expand Up @@ -85,10 +99,15 @@ const CheckIcon = () => (
</svg>
)

const fieldComponents = {
id: ({ job }: { job: Job | JobMq }) => <b>#{job.id}</b>,
type FieldProps = {
job: AppJob
retryJob: () => Promise<void>
}

timestamps: ({ job }: { job: Job | JobMq }) => (
const fieldComponents: Record<Field, React.FC<FieldProps>> = {
id: ({ job }) => <b>#{job.id}</b>,

timestamps: ({ job }) => (
<div className="timestamps">
<div>
<PlusIcon /> <Timestamp ts={job.timestamp} />
Expand All @@ -106,14 +125,11 @@ const fieldComponents = {
</div>
),

name: ({ job }: { job: Job | JobMq }) =>
job.name === '__default__' ? '--' : job.name,
name: ({ job }) => <>{job.name === '__default__' ? '--' : job.name}</>,

finish: ({ job }: { job: Job | JobMq }) => (
<Timestamp ts={job.finishedOn} prev={job.processedOn} />
),
finish: ({ job }) => <Timestamp ts={job.finishedOn} prev={job.processedOn} />,

progress: ({ job }: { job: Job | JobMq }) => {
progress: ({ job }) => {
switch (typeof job.progress) {
case 'object':
return (
Expand All @@ -140,19 +156,19 @@ const fieldComponents = {
</div>
)
default:
return '--'
return <>--</>
}
},

// FIXME: typings
attempts: ({ job }: { job: any }) => job.attempts,
attempts: ({ job }) => <>{job.attempts}</>,

// FIXME: typings
delay: ({ job }: { job: any }) =>
formatDistanceStrict(job.timestamp + job.delay, Date.now()),
delay: ({ job }) => (
// REVIEW: What should we do if timestamp comes as null?
// Also, job.delay is a boolean, why are summing 1 or 0 to it? D:
<>{formatDistanceStrict((job.timestamp || 0) + +job.delay, Date.now())}</>
),

// FIXME: typings
failedReason: ({ job }: any) => {
failedReason: ({ job }) => {
return (
<>
{job.failedReason || 'NA'}
Expand All @@ -161,7 +177,7 @@ const fieldComponents = {
)
},

data: ({ job }: { job: Job | JobMq }) => {
data: ({ job }) => {
const [showData, toggleData] = useState(false)

return (
Expand All @@ -174,28 +190,21 @@ const fieldComponents = {
)
},

opts: ({ job }: { job: Job | JobMq }) => (
opts: ({ job }) => (
<Highlight className="json">{JSON.stringify(job.opts, null, 2)}</Highlight>
),

// FIXME: typings
retry: ({ retryJob }: { retryJob: () => void }) => (
<button onClick={retryJob}>Retry</button>
),
retry: ({ retryJob }) => <button onClick={retryJob}>Retry</button>,
}

const Jobs = ({
retryJob,
queue: { jobs, name },
status,
}: {
// FIXME: typings
retryJob: any
queue: {
jobs: any
name: any
}
status: string
retryJob: (job: AppJob) => () => Promise<void>
queue: AppQueue
status: Status
}) => {
if (!jobs.length) {
return <>No jobs with status {status}</>
Expand All @@ -211,12 +220,9 @@ const Jobs = ({
</tr>
</thead>
<tbody>
{jobs.map((job: Job | JobMq) => (
{jobs.map(job => (
<tr key={job.id}>
{FIELDS[status].map(field => {
// FIXME: typings
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const Field = fieldComponents[field]

return (
Expand All @@ -232,7 +238,17 @@ const Jobs = ({
)
}

const actions = {
type Actionable = 'failed' | 'delayed'

interface QueueActionProps {
queue: QueueProps['queue']
retryAll: QueueProps['retryAll']
cleanAllFailed: QueueProps['cleanAllFailed']
cleanAllDelayed: QueueProps['cleanAllDelayed']
status: Status
}

const actions: Record<Actionable, React.FC<QueueActionProps>> = {
failed: ({ retryAll, cleanAllFailed }: any) => (
<div>
<button onClick={retryAll}>Retry all</button>
Expand All @@ -244,16 +260,17 @@ const actions = {
),
}

// FIXME: typings
const QueueActions = (props: {
[key: string]: any
status: 'failed' | 'delayed'
}) => {
const Actions =
actions[props.status] ||
(() => {
return null
})
const isStatusActionable = (status: Status): status is Actionable =>
status in actions

const QueueActions = (props: QueueActionProps) => {
const { status } = props

if (!isStatusActionable(status)) {
return <div />
}

const Actions = actions[status]

return (
<div>
Expand All @@ -262,7 +279,22 @@ const QueueActions = (props: {
)
}

// FIXME: typings
interface QueueProps {
queue: AppQueue
selectedStatus: Status
selectStatus: (statuses: Record<string, Status>) => void
cleanAllDelayed: () => Promise<void>
cleanAllFailed: () => Promise<void>
retryAll: () => Promise<void>
retryJob: (job: AppJob) => () => Promise<void>
}

// REVIEW: this is in place so we can manipulate the return type
// of Object.keys which is string[] by default
// We need to extend so babel doesn't think it's JSX
const keysOf = <Target extends {}>(target: Target) =>
Object.keys(target) as (keyof Target)[]

export const Queue = ({
cleanAllDelayed,
cleanAllFailed,
Expand All @@ -271,14 +303,11 @@ export const Queue = ({
retryJob,
selectedStatus,
selectStatus,
}: any) => (
}: QueueProps) => (
<section>
<h3>
{queue.name}
{queue.version === 4 && <small> bullmq</small>}
</h3>
<h3>{queue.name}</h3>
<div className="menu-list">
{Object.keys(STATUSES).map(status => (
{keysOf(STATUSES).map(status => (
<MenuItem
key={`${queue.name}-${status}`}
status={status}
Expand Down

0 comments on commit 64ec27c

Please sign in to comment.