From a6273222209ed7f7a08481a3be314ed0f4ce30b5 Mon Sep 17 00:00:00 2001 From: keyan Date: Mon, 7 Mar 2022 15:50:13 -0600 Subject: [PATCH] add job company and location --- api/resolvers/item.js | 23 +++++----- api/typeDefs/item.js | 5 ++- components/form.js | 43 ++++++++++--------- components/item.js | 16 ++++++- components/job-form.js | 38 +++++++++++++--- fragments/items.js | 3 ++ .../20220307201437_job_col/migration.sql | 2 + prisma/schema.prisma | 1 + styles/post.module.css | 7 +++ worker/search.js | 16 ++++++- 10 files changed, 112 insertions(+), 42 deletions(-) create mode 100644 prisma/migrations/20220307201437_job_col/migration.sql diff --git a/api/resolvers/item.js b/api/resolvers/item.js index e7e958c0..ec2acd7b 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -492,34 +492,27 @@ export default { return await updateItem(parent, { id, data: { title, text } }, { me, models }) }, - upsertJob: async (parent, { id, sub, title, text, url, maxBid, status }, { me, models }) => { + upsertJob: async (parent, { id, sub, title, company, location, remote, text, url, maxBid, status }, { me, models }) => { if (!me) { throw new AuthenticationError('you must be logged in to create job') } - if (!sub) { - throw new UserInputError('jobs must have a sub', { argumentName: 'sub' }) - } - const fullSub = await models.sub.findUnique({ where: { name: sub } }) if (!fullSub) { throw new UserInputError('not a valid sub', { argumentName: 'sub' }) } - const params = { title, text, url } - for (const param in params) { - if (!params[param]) { - throw new UserInputError(`jobs must have ${param}`, { argumentName: param }) - } - } - if (fullSub.baseCost > maxBid) { throw new UserInputError(`bid must be at least ${fullSub.baseCost}`, { argumentName: 'maxBid' }) } + if (!location && !remote) { + throw new UserInputError('must specify location or remote', { argumentName: 'location' }) + } + const checkSats = async () => { // check if the user has the funds to run for the first minute - const minuteMsats = maxBid * 5 / 216 + const minuteMsats = maxBid * 1000 const user = await models.user.findUnique({ where: { id: me.id } }) if (user.msats < minuteMsats) { throw new UserInputError('insufficient funds') @@ -528,6 +521,9 @@ export default { const data = { title, + company, + location: location.toLowerCase() === 'remote' ? undefined : location, + remote, text, url, maxBid, @@ -878,6 +874,7 @@ function nestComments (flat, parentId) { export const SELECT = `SELECT "Item".id, "Item".created_at as "createdAt", "Item".updated_at as "updatedAt", "Item".title, "Item".text, "Item".url, "Item"."userId", "Item"."parentId", "Item"."pinId", "Item"."maxBid", + "Item".company, "Item".location, "Item".remote, "Item"."subName", "Item".status, ltree2text("Item"."path") AS "path"` const LEFT_JOIN_SATS_SELECT = 'SELECT i.id, SUM(CASE WHEN "ItemAct".act = \'VOTE\' THEN "ItemAct".sats ELSE 0 END) as sats, SUM(CASE WHEN "ItemAct".act = \'BOOST\' THEN "ItemAct".sats ELSE 0 END) as boost' diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index da5b15bc..edc5d40f 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -25,7 +25,7 @@ export default gql` updateDiscussion(id: ID!, title: String!, text: String): Item! createComment(text: String!, parentId: ID!): Item! updateComment(id: ID!, text: String!): Item! - upsertJob(id: ID, sub: ID!, title: String!, text: String!, url: String!, maxBid: Int!, status: String): Item! + upsertJob(id: ID, sub: ID!, title: String!, company: String!, location: String, remote: Boolean, text: String!, url: String!, maxBid: Int!, status: String): Item! act(id: ID!, sats: Int): ItemActResult! } @@ -66,6 +66,9 @@ export default gql` position: Int prior: Int maxBid: Int + company: String + location: String + remote: Boolean sub: Sub status: String } diff --git a/components/form.js b/components/form.js index fbc01af2..442cf63f 100644 --- a/components/form.js +++ b/components/form.js @@ -201,31 +201,34 @@ export function Input ({ label, groupClassName, ...props }) { ) } -export function Checkbox ({ children, label, extra, handleChange, inline, ...props }) { +export function Checkbox ({ children, label, groupClassName, hiddenLabel, extra, handleChange, inline, ...props }) { // React treats radios and checkbox inputs differently other input types, select, and textarea. // Formik does this too! When you specify `type` to useField(), it will // return the correct bag of props for you const [field] = useField({ ...props, type: 'checkbox' }) return ( - - { - field.onChange(e) - handleChange && handleChange(e.target.checked) - }} - /> - -
{label}
- {extra && -
- {extra} -
} -
-
+ + {hiddenLabel && {label}} + + { + field.onChange(e) + handleChange && handleChange(e.target.checked) + }} + /> + +
{label}
+ {extra && +
+ {extra} +
} +
+
+
) } diff --git a/components/item.js b/components/item.js index b39bd1c3..e14dccdd 100644 --- a/components/item.js +++ b/components/item.js @@ -34,7 +34,21 @@ export function ItemJob ({ item, rank, children }) {
- {item.searchTitle ? : item.title} + {item.searchTitle + ? + : ( + <>{item.title} + {item.company && + <> + \ + {item.company} + } + {(item.location || item.remote) && + <> + \ + {`${item.location || ''}${item.location && item.remote ? ' or ' : ''}${item.remote ? 'Remote' : ''}`} + } + )} {/* eslint-disable-next-line */} diff --git a/components/job-form.js b/components/job-form.js index 5c1cd59e..af7beb59 100644 --- a/components/job-form.js +++ b/components/job-form.js @@ -1,6 +1,6 @@ import { Checkbox, Form, Input, MarkdownInput, SubmitButton } from './form' import TextareaAutosize from 'react-textarea-autosize' -import { InputGroup, Modal } from 'react-bootstrap' +import { InputGroup, Modal, Form as BForm, Col } from 'react-bootstrap' import * as Yup from 'yup' import { useEffect, useState } from 'react' import Info from '../svgs/information-fill.svg' @@ -54,9 +54,10 @@ export default function JobForm ({ item, sub }) { }`, { fetchPolicy: 'network-only' }) const [upsertJob] = useMutation(gql` - mutation upsertJob($id: ID, $title: String!, $text: String!, - $url: String!, $maxBid: Int!, $status: String) { - upsertJob(sub: "${sub.name}", id: $id title: $title, text: $text, + mutation upsertJob($id: ID, $title: String!, $company: String!, $location: String, + $remote: Boolean, $text: String!, $url: String!, $maxBid: Int!, $status: String) { + upsertJob(sub: "${sub.name}", id: $id, title: $title, company: $company, + location: $location, remote: $remote, text: $text, url: $url, maxBid: $maxBid, status: $status) { id } @@ -65,12 +66,17 @@ export default function JobForm ({ item, sub }) { const JobSchema = Yup.object({ title: Yup.string().required('required').trim(), + company: Yup.string().required('required').trim(), text: Yup.string().required('required').trim(), url: Yup.string() .or([Yup.string().email(), Yup.string().url()], 'invalid url or email') .required('Required'), maxBid: Yup.number('must be number') - .integer('must be integer').min(sub.baseCost, `must be at least ${sub.baseCost}`) + .integer('must be integer').min(sub.baseCost, `must be at least ${sub.baseCost}`), + location: Yup.string().when('remote', { + is: (value) => !value, + then: Yup.string().required('required').trim() + }) }) const position = data?.auctionPosition @@ -101,6 +107,9 @@ export default function JobForm ({ item, sub }) { className='py-5' initial={{ title: item?.title || '', + company: item?.company || '', + location: item?.location || '', + remote: item?.remote || false, text: item?.text || '', url: item?.url || '', maxBid: item?.maxBid || sub.baseCost, @@ -134,11 +143,28 @@ export default function JobForm ({ item, sub }) { })} > + + + + + + remote
} name='remote' hiddenLabel + groupClassName={styles.inlineCheckGroup} + /> +