157 lines
5.3 KiB
JavaScript
157 lines
5.3 KiB
JavaScript
import { Checkbox, Form, Input, MarkdownInput, SubmitButton } from './form'
|
|
import Row from 'react-bootstrap/Row'
|
|
import Col from 'react-bootstrap/Col'
|
|
import Image from 'react-bootstrap/Image'
|
|
import { useEffect, useState } from 'react'
|
|
import Info from './info'
|
|
import styles from '@/styles/post.module.css'
|
|
import { useLazyQuery, gql } from '@apollo/client'
|
|
import Avatar from './avatar'
|
|
import { jobSchema } from '@/lib/validate'
|
|
import { BOOST_MIN, BOOST_MULT, MAX_TITLE_LENGTH, MEDIA_URL } from '@/lib/constants'
|
|
import { UPSERT_JOB } from '@/fragments/paidAction'
|
|
import useItemSubmit from './use-item-submit'
|
|
import { BoostInput } from './adv-post-form'
|
|
import { numWithUnits, giveOrdinalSuffix } from '@/lib/format'
|
|
import useDebounceCallback from './use-debounce-callback'
|
|
import FeeButton from './fee-button'
|
|
import CancelButton from './cancel-button'
|
|
|
|
// need to recent list items
|
|
export default function JobForm ({ item, sub }) {
|
|
const storageKeyPrefix = item ? undefined : `${sub.name}-job`
|
|
const [logoId, setLogoId] = useState(item?.uploadId)
|
|
|
|
const [getAuctionPosition, { data }] = useLazyQuery(gql`
|
|
query AuctionPosition($id: ID, $boost: Int) {
|
|
auctionPosition(sub: "${item?.subName || sub?.name}", id: $id, boost: $boost)
|
|
}`,
|
|
{ fetchPolicy: 'cache-and-network' })
|
|
|
|
const getPositionDebounce = useDebounceCallback((...args) => getAuctionPosition(...args), 1000, [getAuctionPosition])
|
|
|
|
useEffect(() => {
|
|
if (item?.boost) {
|
|
getPositionDebounce({ variables: { boost: item.boost, id: item.id } })
|
|
}
|
|
}, [item?.boost])
|
|
|
|
const extraValues = logoId ? { logo: Number(logoId) } : {}
|
|
const onSubmit = useItemSubmit(UPSERT_JOB, { item, sub, extraValues })
|
|
|
|
return (
|
|
<>
|
|
<Form
|
|
className='pb-5 pt-3'
|
|
initial={{
|
|
title: item?.title || '',
|
|
company: item?.company || '',
|
|
location: item?.location || '',
|
|
remote: item?.remote || false,
|
|
boost: item?.boost || '',
|
|
text: item?.text || '',
|
|
url: item?.url || '',
|
|
stop: false,
|
|
start: false
|
|
}}
|
|
schema={jobSchema({ existingBoost: item?.boost })}
|
|
storageKeyPrefix={storageKeyPrefix}
|
|
requireSession
|
|
onSubmit={onSubmit}
|
|
>
|
|
<div className='form-group'>
|
|
<label className='form-label'>logo</label>
|
|
<div className='position-relative' style={{ width: 'fit-content' }}>
|
|
<Image
|
|
src={logoId ? `${MEDIA_URL}/${logoId}` : '/jobs-default.png'} width='135' height='135' roundedCircle
|
|
/>
|
|
<Avatar onSuccess={setLogoId} />
|
|
</div>
|
|
</div>
|
|
<Input
|
|
label='job title'
|
|
name='title'
|
|
required
|
|
autoFocus
|
|
clear
|
|
maxLength={MAX_TITLE_LENGTH}
|
|
/>
|
|
<Input
|
|
label='company'
|
|
name='company'
|
|
required
|
|
clear
|
|
/>
|
|
<Row className='me-0'>
|
|
<Col>
|
|
<Input
|
|
label='location'
|
|
name='location'
|
|
clear
|
|
/>
|
|
</Col>
|
|
<Col className='d-flex ps-0' xs='auto'>
|
|
<Checkbox
|
|
label={<div className='fw-bold'>remote</div>} name='remote' hiddenLabel
|
|
groupClassName={styles.inlineCheckGroup}
|
|
/>
|
|
</Col>
|
|
</Row>
|
|
<MarkdownInput
|
|
topLevel
|
|
label='description'
|
|
name='text'
|
|
minRows={6}
|
|
required
|
|
/>
|
|
<Input
|
|
label={<>how to apply <small className='text-muted ms-2'>url or email address</small></>}
|
|
name='url'
|
|
required
|
|
clear
|
|
/>
|
|
<BoostInput
|
|
label={
|
|
<div className='d-flex align-items-center'>boost
|
|
<Info>
|
|
<ol className='line-height-md'>
|
|
<li>Boost ranks jobs higher based on the amount</li>
|
|
<li>The minimum boost is {numWithUnits(BOOST_MIN, { abbreviate: false })}</li>
|
|
<li>Boost must be divisible by {numWithUnits(BOOST_MULT, { abbreviate: false })}</li>
|
|
<li>100% of boost goes to the territory founder and top stackers as rewards</li>
|
|
</ol>
|
|
</Info>
|
|
</div>
|
|
}
|
|
hint={<span className='text-muted'>{data?.auctionPosition ? `your job will rank ${giveOrdinalSuffix(data.auctionPosition)}` : 'higher boost ranks your job higher'}</span>}
|
|
onChange={(_, e) => getPositionDebounce({ variables: { boost: Number(e.target.value), id: item?.id } })}
|
|
/>
|
|
<JobButtonBar itemId={item?.id} />
|
|
</Form>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export function JobButtonBar ({
|
|
itemId, disable, className, children, handleStop, onCancel, hasCancel = true,
|
|
createText = 'post', editText = 'save', stopText = 'remove'
|
|
}) {
|
|
return (
|
|
<div className={`mt-3 ${className}`}>
|
|
<div className='d-flex justify-content-between'>
|
|
{itemId &&
|
|
<SubmitButton valueName='status' value='STOPPED' variant='grey-medium'>{stopText}</SubmitButton>}
|
|
{children}
|
|
<div className='d-flex align-items-center ms-auto'>
|
|
{hasCancel && <CancelButton onClick={onCancel} />}
|
|
<FeeButton
|
|
text={itemId ? editText : createText}
|
|
variant='secondary'
|
|
disabled={disable}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|