diff --git a/package.json b/package.json index 74a8c8c..0022b46 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dev-https": "cross-env APPMODE=development webpack-dev-server --https --port 8008", "build": "webpack --mode=${APPMODE:-development} --env.config=${APPENV:-dev}", "analyze": "webpack --mode=production --env.analyze=true", - "lint": "eslint src --ext js,jsx", + "lint": "eslint src --ext js,jsx --fix", "format": "prettier --write \"./**\"", "test": "cross-env BABEL_ENV=test jest", "watch-tests": "cross-env BABEL_ENV=test jest --watch", diff --git a/src/App.jsx b/src/App.jsx index b53e71c..c50708b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,6 +5,7 @@ import React, { useState, useLayoutEffect, useEffect, useRef } from "react"; import { Router, useLocation, Redirect } from "@reach/router"; import Challenges from "./containers/Challenges"; import Filter from "./containers/Filter"; +import MyGigsFilter from "./containers/MyGigsFilter"; import MyGigs from "./containers/MyGigs"; import Menu from "./components/Menu"; import { disableSidebarForRoute } from "@topcoder/micro-frontends-navbar-app"; @@ -12,7 +13,7 @@ import * as constants from "./constants"; import actions from "./actions"; import * as utils from "./utils"; import store from "./store"; -import { initialChallengeFilter } from "./reducers/filter"; +import { initialChallengeFilter, initialGigFilter } from "./reducers/filter"; import _ from "lodash"; import { usePreviousLocation } from "./utils/hooks"; import { useSelector } from "react-redux"; @@ -38,6 +39,9 @@ const App = () => { selected={selectedMenuItem} onSelect={(item) => { setSelectedMenuItem(item); + if (item == "Gigs") { + window.location.href = `${process.env.URL.BASE}/gigs`; + } }} isLoggedIn={isLoggedIn} /> @@ -46,7 +50,7 @@ const App = () => { const location = useLocation(); const previousLocation = usePreviousLocation(); - const getChallengesDebounced = useRef(_.debounce((f) => f(), 500)); + const getDataDebounced = useRef(_.debounce((f) => f(), 500)); useEffect(() => { store.dispatch(actions.lookup.checkIsLoggedIn()); @@ -74,12 +78,46 @@ const App = () => { if (diff) { store.dispatch(actions.filter.updateFilter(updatedFilter)); } - getChallengesDebounced.current(() => + getDataDebounced.current(() => store.dispatch(actions.challenges.getChallenges(updatedFilter)) ); } }, [location]); + useEffect(() => { + if (location.pathname === "/earn/my-gigs" && isLoggedIn) { + if (!location.search) { + store.dispatch(actions.filter.updateGigFilter(initialGigFilter)); + + store.dispatch( + actions.myGigs.getMyGigs( + constants.GIGS_FILTER_STATUSES_PARAM[initialGigFilter.status] + ) + ); + return; + } + const params = utils.url.parseUrlQuery(location.search); + if (_.keys(params).length == 1 && params.externalId) { + return; + } + const updatedGigFilter = { + status: params.status || "Open Applications", + }; + const currentGig = store.getState().filter.gig; + const diff = !_.isEqual(updatedGigFilter, currentGig); + if (diff) { + store.dispatch(actions.filter.updateGigFilter(updatedGigFilter)); + } + getDataDebounced.current(() => + store.dispatch( + actions.myGigs.getMyGigs( + constants.GIGS_FILTER_STATUSES_PARAM[updatedGigFilter.status] + ) + ) + ); + } + }, [location, isLoggedIn]); + const varsRef = useRef(); varsRef.current = { previousLocation }; @@ -108,7 +146,8 @@ const App = () => {
{menu}
- + {location.pathname === "/earn/find/challenges" && } + {location.pathname === "/earn/my-gigs" && }
item.status == constants.MY_GIGS_JOB_STATUS.PLACED + ); + if (placedJobs.length == 0) { + return; + } + const token = await getM2MToken(); + const url = `${config.API.V5}/resourceBookings`; + const criteria = { + userId: userId, + page: 1, + perPage: placedJobs.length, + }; + const body = { + jobIds: _.map(placedJobs, "jobId"), + }; + const res = await request + .get(url) + .query(criteria) + .send(body) + .set("Authorization", `Bearer ${token}`) + .set("Accept", "application/json"); + localLogger.debug({ + context: "handlePlacedJobCandidates", + message: `response body: ${JSON.stringify(res.body)}`, + }); + if (res.body && res.body.length == 0) { + return; + } + // Handle placed job status with RB result + const rbRes = res.body; + _.each(rbRes, (rb) => { + const jc = jobCandidates.find( + (item) => item.userId == rb.userId && item.jobId == rb.jobId + ); + if (jc) { + if (rb.endDate) { + if ( + new Date(rb.endDate) < new Date() && + new Date(rb.endDate).toDateString() != new Date().toDateString() + ) { + jc.status = "completed"; + } + } + } + }); + return; +} + /** * Return jobs by given criteria * @param {string} criteria the search criteria @@ -467,6 +536,7 @@ module.exports = { getM2MToken, getCurrentUserDetails, getJobCandidates, + handlePlacedJobCandidates, getJobs, getMember, getMemberTraits, diff --git a/src/api/docs/swagger.yaml b/src/api/docs/swagger.yaml index f438d06..24ab579 100644 --- a/src/api/docs/swagger.yaml +++ b/src/api/docs/swagger.yaml @@ -49,6 +49,12 @@ paths: type: string default: desc enum: ["desc", "asc"] + - in: query + name: status + required: false + schema: + type: string + enum: ["active_jobs", "open_jobs", "completed_jobs", "archived_jobs"] responses: "200": description: OK @@ -260,6 +266,7 @@ components: - status - remark - interview + - jobExternalId properties: title: type: string @@ -305,6 +312,10 @@ components: description: "The remark of candidate" interview: $ref: "#/components/schemas/Interview" + jobExternalId: + type: string + example: "51313517" + description: "The corresponding gig ID on community app" Payment: required: - min diff --git a/src/api/services/JobApplicationService.js b/src/api/services/JobApplicationService.js index cee5854..c44ef24 100644 --- a/src/api/services/JobApplicationService.js +++ b/src/api/services/JobApplicationService.js @@ -18,6 +18,7 @@ async function getMyJobApplications(currentUser, criteria) { const perPage = criteria.perPage; const sortBy = criteria.sortBy; const sortOrder = criteria.sortOrder; + const status = criteria.status || ""; const emptyResult = { total: 0, page, @@ -44,16 +45,34 @@ async function getMyJobApplications(currentUser, criteria) { perPage, sortBy, sortOrder, + status, }); // if no candidates found then return empty result if (jobCandidates.result.length === 0) { return emptyResult; } - const jobIds = _.map(jobCandidates.result, "jobId"); + let jcResult = jobCandidates.result; + // handle placed status for completed_jobs, archived_jobs query + if (status && (status == "active_jobs" || status == "completed_jobs")) { + await helper.handlePlacedJobCandidates(jobCandidates.result, userId); + if (status == "completed_jobs") { + jcResult = jobCandidates.result.filter( + (item) => item.status == "completed" + ); + } + if (status == "active_jobs") { + jcResult = jobCandidates.result.filter( + (item) => item.status != "completed" + ); + } + } + + const jobIds = _.map(jcResult, "jobId"); // get jobs of current user by calling taas-api const { result: jobs } = await helper.getJobs({ jobIds, page: 1, perPage }); + // apply desired structure - const jobApplications = _.map(jobCandidates.result, (jobCandidate) => { + const jobApplications = _.map(jcResult, (jobCandidate) => { const job = _.find(jobs, ["id", jobCandidate.jobId]); return { title: job.title, @@ -73,6 +92,7 @@ async function getMyJobApplications(currentUser, criteria) { : null, remark: jobCandidate.remark, duration: job.duration, + jobExternalId: job.externalId, }; }); return { @@ -92,6 +112,12 @@ getMyJobApplications.schema = Joi.object() perPage: Joi.perPage(), sortBy: Joi.string().valid("id", "status").default("id"), sortOrder: Joi.string().valid("desc", "asc").default("desc"), + status: Joi.string().valid( + "active_jobs", + "open_jobs", + "completed_jobs", + "archived_jobs" + ), }) .required(), }) diff --git a/src/components/Empty/index.jsx b/src/components/Empty/index.jsx index 7ef4d8f..98140bc 100644 --- a/src/components/Empty/index.jsx +++ b/src/components/Empty/index.jsx @@ -1,26 +1,42 @@ import React from "react"; -import { EMPTY_GIGS_TEXT } from "../../constants"; +import PT from "prop-types"; +import { + MY_GIGS_STATUS_EMPTY_TEXT, + GIGS_FILTER_STATUSES, +} from "../../constants"; import Button from "../Button"; import "./styles.scss"; -const Empty = () => { +const Empty = ({ gigStatus }) => { return (
-
{EMPTY_GIGS_TEXT}
- Interested in getting a gig? - +
{MY_GIGS_STATUS_EMPTY_TEXT[gigStatus]}
+ {gigStatus == GIGS_FILTER_STATUSES.OPEN_JOBS && ( + Interested in getting a gig? + )} + {gigStatus == GIGS_FILTER_STATUSES.OPEN_JOBS && ( + + )}
); }; +Empty.defaultProps = { + gigStatus: GIGS_FILTER_STATUSES.OPEN_JOBS, +}; + +Empty.propTypes = { + gigStatus: PT.string, +}; + export default Empty; diff --git a/src/constants/index.js b/src/constants/index.js index a250274..152179d 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -29,6 +29,10 @@ export const NAV_MENU = { name: "Challenges", path: "/earn/find/challenges", }, + { + name: "Gigs", + path: "", + }, ], }, ], @@ -99,6 +103,20 @@ export const CURRENCY_SYMBOL = { USD: "$", }; +export const GIGS_FILTER_STATUSES = { + ACTIVE_JOBS: "Active Gigs", + OPEN_JOBS: "Open Applications", + COMPLETED_JOBS: "Completed Gigs", + ARCHIVED_JOBS: "Archived Applications", +}; + +export const GIGS_FILTER_STATUSES_PARAM = { + [GIGS_FILTER_STATUSES.ACTIVE_JOBS]: "active_jobs", + [GIGS_FILTER_STATUSES.OPEN_JOBS]: "open_jobs", + [GIGS_FILTER_STATUSES.COMPLETED_JOBS]: "completed_jobs", + [GIGS_FILTER_STATUSES.ARCHIVED_JOBS]: "archived_jobs", +}; + export const MY_GIG_PHASE = { APPLIED: "Applied", SKILLS_TEST: "Skills Test", @@ -110,6 +128,8 @@ export const MY_GIG_PHASE = { PLACED: "Placed", NOT_SELECTED: "Not Selected", JOB_CLOSED: "Job Closed", + WITHDRAWN: "Withdrawn", + COMPLETED: "Completed", }; export const MY_GIG_PHASE_LABEL = { @@ -123,6 +143,8 @@ export const MY_GIG_PHASE_LABEL = { PLACED: "PLACED", NOT_SELECTED: "NOT SELECTED", JOB_CLOSED: "JOB CLOSED", + WITHDRAWN: "WITHDRAWN", + COMPLETED: "COMPLETED", }; export const MY_GIG_PHASE_STATUS = { @@ -149,6 +171,9 @@ export const MY_GIGS_JOB_STATUS = { CLIENT_REJECTED_INTERVIEW: "client rejected - interview", CLIENT_REJECTED_SCREENING: "client rejected - screening", JOB_CLOSED: "job-closed", + WITHDRAWN: "withdrawn", + WITHDRAWN_PRESCREEN: "withdrawn-prescreen", + COMPLETED: "completed", }; /** * Maps the status from API to gig status @@ -167,6 +192,9 @@ export const JOB_STATUS_MAPPER = { [MY_GIGS_JOB_STATUS.CLIENT_REJECTED_INTERVIEW]: MY_GIG_PHASE.NOT_SELECTED, [MY_GIGS_JOB_STATUS.CLIENT_REJECTED_SCREENING]: MY_GIG_PHASE.NOT_SELECTED, [MY_GIGS_JOB_STATUS.JOB_CLOSED]: MY_GIG_PHASE.JOB_CLOSED, + [MY_GIGS_JOB_STATUS.WITHDRAWN]: MY_GIG_PHASE.WITHDRAWN, + [MY_GIGS_JOB_STATUS.WITHDRAWN_PRESCREEN]: MY_GIG_PHASE.WITHDRAWN, + [MY_GIGS_JOB_STATUS.COMPLETED]: MY_GIG_PHASE.COMPLETED, }; /** @@ -191,6 +219,9 @@ export const JOB_STATUS_MESSAGE_MAPPER = { [MY_GIG_PHASE.NOT_SELECTED]: "You were not selected for this position.", [MY_GIG_PHASE.JOB_CLOSED]: "This position is no longer active. Please apply to other open gigs.", + [MY_GIG_PHASE.WITHDRAWN]: + "You withdrew your application for this gig or you have been placed in another gig.", + [MY_GIG_PHASE.COMPLETED]: "Congrats on completing the gig!", }; export const ACTIONS_AVAILABLE_FOR_MY_GIG_PHASE = { @@ -304,6 +335,9 @@ export const PHASES_FOR_JOB_STATUS = { MY_GIG_PHASE.NOT_SELECTED, ], [MY_GIGS_JOB_STATUS.JOB_CLOSED]: [MY_GIG_PHASE.JOB_CLOSED], + [MY_GIGS_JOB_STATUS.WITHDRAWN]: [MY_GIG_PHASE.WITHDRAWN], + [MY_GIGS_JOB_STATUS.WITHDRAWN_PRESCREEN]: [MY_GIG_PHASE.WITHDRAWN], + [MY_GIGS_JOB_STATUS.COMPLETED]: [MY_GIG_PHASE.COMPLETED], }; /** @@ -321,6 +355,8 @@ export const SORT_STATUS_ORDER = [ MY_GIG_PHASE.APPLIED, MY_GIG_PHASE.JOB_CLOSED, MY_GIG_PHASE.NOT_SELECTED, + MY_GIG_PHASE.COMPLETED, + MY_GIG_PHASE.WITHDRAWN, ]; export const PER_PAGE = 10; @@ -350,8 +386,14 @@ export const GIG_STATUS_TOOLTIP = { UNAVAILABLE: "You’re not open to take on new jobs.", }; -export const EMPTY_GIGS_TEXT = - "LOOKS LIKE YOU HAVEN'T APPLIED TO ANY GIG OPPORTUNITIES YET."; +export const MY_GIGS_STATUS_EMPTY_TEXT = { + [GIGS_FILTER_STATUSES.ACTIVE_JOBS]: "YOU DON'T HAVE ANY ACTIVE GIGS YET.", + [GIGS_FILTER_STATUSES.OPEN_JOBS]: + "LOOKS LIKE YOU HAVEN'T APPLIED TO ANY GIG OPPORTUNITIES YET.", + [GIGS_FILTER_STATUSES.COMPLETED_JOBS]: + "YOU DON'T HAVE ANY COMPLETED GIGS YET.", + [GIGS_FILTER_STATUSES.ARCHIVED_JOBS]: "YOU DON'T HAVE ANY ARCHIVED GIGS YET.", +}; export const CHECKING_GIG_TIMES = 3; diff --git a/src/containers/MyGigs/JobListing/JobCard/index.jsx b/src/containers/MyGigs/JobListing/JobCard/index.jsx index 906f278..2828366 100644 --- a/src/containers/MyGigs/JobListing/JobCard/index.jsx +++ b/src/containers/MyGigs/JobListing/JobCard/index.jsx @@ -55,7 +55,13 @@ const JobCard = ({ job }) => {
-

{job.title}

+

+ + {job.title} + +

  • @@ -129,6 +135,9 @@ const JobCard = ({ job }) => { {![ MY_GIGS_JOB_STATUS.JOB_CLOSED, MY_GIGS_JOB_STATUS.REJECTED_OTHER, + MY_GIGS_JOB_STATUS.COMPLETED, + MY_GIGS_JOB_STATUS.WITHDRAWN, + MY_GIGS_JOB_STATUS.WITHDRAWN_PRESCREEN, ].includes(job.status) && (
    @@ -46,6 +51,7 @@ JobListing.propTypes = { loadMore: PT.func, total: PT.number, numLoaded: PT.number, + gigStatus: PT.string, }; export default JobListing; diff --git a/src/containers/MyGigs/index.jsx b/src/containers/MyGigs/index.jsx index 4f7e2ae..e30da7b 100644 --- a/src/containers/MyGigs/index.jsx +++ b/src/containers/MyGigs/index.jsx @@ -28,6 +28,7 @@ const MyGigs = ({ getAllCountries, checkingGigs, startCheckingGigs, + gigStatus, }) => { const location = useLocation(); const params = utils.url.parseUrlQuery(location.search); @@ -46,7 +47,7 @@ const MyGigs = ({ if (propsRef.current.params.externalId) { propsRef.current.startCheckingGigs(propsRef.current.params.externalId); } else { - propsRef.current.getMyGigs(); + // propsRef.current.getMyGigs(); } }, []); @@ -98,9 +99,12 @@ const MyGigs = ({
- {!checkingGigs && myGigs && myGigs.length == 0 && } + {!checkingGigs && myGigs && myGigs.length == 0 && ( + + )} {!checkingGigs && myGigs && myGigs.length > 0 && ( ({ + gigStatus: state.filter.gig.status, checkingGigs: state.myGigs.checkingGigs, myGigs: state.myGigs.myGigs, total: state.myGigs.total, diff --git a/src/containers/MyGigs/modals/UpdateGigProfile/index.jsx b/src/containers/MyGigs/modals/UpdateGigProfile/index.jsx index 9f407c1..851c744 100644 --- a/src/containers/MyGigs/modals/UpdateGigProfile/index.jsx +++ b/src/containers/MyGigs/modals/UpdateGigProfile/index.jsx @@ -272,7 +272,7 @@ const UpdateGigProfile = ({
Update your skills from Topcoder Profile page diff --git a/src/containers/MyGigsFilter/GigsFilter/index.jsx b/src/containers/MyGigsFilter/GigsFilter/index.jsx new file mode 100644 index 0000000..c62f014 --- /dev/null +++ b/src/containers/MyGigsFilter/GigsFilter/index.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import PT from "prop-types"; +import _ from "lodash"; +import RadioButton from "../../../components/RadioButton"; +import * as utils from "../../../utils"; + +import "./styles.scss"; + +const GigsFilter = ({ gigStatus, gigsStatuses, updateGigFilter }) => { + const bucketOptions = utils.createRadioOptions(gigsStatuses, gigStatus); + + return ( +
+
+ { + const filterChange = { + status: utils.getSelectedRadioOption(newBucketOptions).label, + }; + updateGigFilter(filterChange); + }} + /> + +
+
+ ); +}; + +GigsFilter.propTypes = { + gigStatus: PT.string, + gigsStatuses: PT.arrayOf(PT.string), + updateGigFilter: PT.func, +}; + +export default GigsFilter; diff --git a/src/containers/MyGigsFilter/GigsFilter/styles.scss b/src/containers/MyGigsFilter/GigsFilter/styles.scss new file mode 100644 index 0000000..7e538c4 --- /dev/null +++ b/src/containers/MyGigsFilter/GigsFilter/styles.scss @@ -0,0 +1,28 @@ +@import "styles/variables"; +@import "styles/mixins"; + +$filter-padding-x: 4 * $base-unit; +$filter-padding-y: 3 * $base-unit; + +.filter { + padding: $filter-padding-y $filter-padding-x; + font-size: $font-size-sm; +} + +.buckets { + margin-bottom: 18px; + + > h3 { + margin-bottom: 15px; + font-size: inherit; + line-height: 19px; + } +} + +.buckets { + &.vertical-list { + > div > div { + margin: $base-unit 0; + } + } +} diff --git a/src/containers/MyGigsFilter/index.jsx b/src/containers/MyGigsFilter/index.jsx new file mode 100644 index 0000000..6e3dfcf --- /dev/null +++ b/src/containers/MyGigsFilter/index.jsx @@ -0,0 +1,65 @@ +import React, { useRef } from "react"; +import PT from "prop-types"; +import { useLocation } from "@reach/router"; +import { connect } from "react-redux"; +import GigsFilter from "./GigsFilter"; +import actions from "../../actions"; +import { updateQuery } from "../../utils/url"; + +const MyGigsFilter = ({ gigStatus, gigsStatuses, updateGigFilter }) => { + const location = useLocation(); + const propsRef = useRef(null); + propsRef.current = { location }; + + if (location.pathname === "/earn/my-gigs") { + return ( + { + updateGigFilter(gigFilterChanged); + updateQuery(gigFilterChanged); + }} + /> + ); + } + + return null; +}; + +MyGigsFilter.propTypes = { + gigStatus: PT.string, + gigsStatuses: PT.arrayOf(PT.string), + updateGigFilter: PT.func, +}; + +const mapStateToProps = (state) => ({ + state: state, + gigStatus: state.filter.gig.status, + gigsStatuses: state.lookup.gigsStatuses, +}); + +const mapDispatchToProps = { + updateGigFilter: actions.filter.updateGigFilter, + updateGigQuery: actions.filter.updateGigQuery, +}; + +const mergeProps = (stateProps, dispatchProps, ownProps) => ({ + ...ownProps, + ...stateProps, + ...dispatchProps, + updateQuery: (change) => + dispatchProps.updateGigQuery( + { + ...stateProps.state.filter.gig, + ...change, + }, + change + ), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps, + mergeProps +)(MyGigsFilter); diff --git a/src/reducers/filter.js b/src/reducers/filter.js index fe27c0d..fe7a191 100644 --- a/src/reducers/filter.js +++ b/src/reducers/filter.js @@ -21,6 +21,9 @@ const defaultState = { bucket: constants.FILTER_BUCKETS[1], }, + gig: { + status: constants.GIGS_FILTER_STATUSES.OPEN_JOBS, + }, }; function onInitApp(state, { payload }) { @@ -41,13 +44,25 @@ function onClearChallengeFilter(state, { payload }) { return { ...state, challenge: { ...state.challenge, ...payload } }; } +function onUpdateGigFilter(state, { payload }) { + return { + ...state, + gig: { + ...state.gig, + ...payload, + }, + }; +} + export default handleActions( { INIT_APP: onInitApp, UPDATE_FILTER: onUpdateFilter, CLEAR_CHALLENGE_FILTER: onClearChallengeFilter, + UPDATE_GIG_FILTER: onUpdateGigFilter, }, defaultState ); export const initialChallengeFilter = _.cloneDeep(defaultState.challenge); +export const initialGigFilter = _.cloneDeep(defaultState.gig); diff --git a/src/reducers/lookup.js b/src/reducers/lookup.js index abfe8be..df5e02f 100644 --- a/src/reducers/lookup.js +++ b/src/reducers/lookup.js @@ -1,5 +1,6 @@ import { handleActions } from "redux-actions"; import * as constants from "../constants"; +import _ from "lodash"; const defaultState = { buckets: constants.FILTER_BUCKETS, @@ -9,6 +10,7 @@ const defaultState = { subCommunities: [], isLoggedIn: null, countries: [], + gigsStatuses: _.values(constants.GIGS_FILTER_STATUSES), }; function onGetTagsDone(state, { payload }) { diff --git a/src/services/myGigs.js b/src/services/myGigs.js index 03bbcd8..56e8cb6 100644 --- a/src/services/myGigs.js +++ b/src/services/myGigs.js @@ -40,6 +40,7 @@ const mapMyGigsData = (serverResponse) => { return { label: (gigPhase || "").toUpperCase(), title: myGig.title, + jobExternalId: myGig.jobExternalId, paymentRangeFrom: myGig.payment.min, paymentRangeTo: myGig.payment.max, paymentRangeRateType: myGig.payment.frequency, @@ -82,9 +83,9 @@ const mapMyGigsData = (serverResponse) => { * @param {*} perPage item per page to request * @returns */ -async function getMyGigs(page, perPage) { +async function getMyGigs(status, page, perPage) { const response = await api.get( - `/earn-app/api/my-gigs/myJobApplications?page=${page}&perPage=${perPage}`, + `/earn-app/api/my-gigs/myJobApplications?status=${status}&page=${page}&perPage=${perPage}`, process.env.URL.PLATFORM_WEBSITE_URL ); diff --git a/src/utils/myGig.js b/src/utils/myGig.js index 2fc240b..efaa6d1 100644 --- a/src/utils/myGig.js +++ b/src/utils/myGig.js @@ -1,5 +1,7 @@ import codes from "country-calling-code"; import countries from "i18n-iso-countries"; +import _ from "lodash"; +import Joi from "joi"; import enLocale from "i18n-iso-countries/langs/en.json"; import * as constants from "../constants"; @@ -38,6 +40,17 @@ export function createTextImage(text) { return canvas.toDataURL(); } +const queryScheme = { + bucket: Joi.string(), +}; + +export function createGigParams(filter) { + let params = _.pick(filter, Object.keys(queryScheme)); + return { + status: constants.GIGS_FILTER_STATUSES_PARAM[params.status || "open_jobs"], + }; +} + function validateTextRequired(value) { value = (value || "").trim();