diff --git a/src/backend/data/schema.js b/src/backend/data/schema.js index 7003c46f..6efc7ff1 100644 --- a/src/backend/data/schema.js +++ b/src/backend/data/schema.js @@ -456,6 +456,14 @@ const GraphQLListContainer = new GraphQLObjectType({ interfaces: [nodeInterface] }) +const GeoPoint = new GraphQLInputObjectType({ + name: 'GeoPoint', + fields: { + lat: {type: GraphQLFloat}, + lon: {type: GraphQLFloat}, + } +}) + const GraphQLUser = new GraphQLObjectType({ name: 'User', description: 'User of ground control', @@ -554,10 +562,13 @@ const GraphQLUser = new GraphQLObjectType({ intervieweeForCallAssignment: { type: GraphQLPerson, args: { - callAssignmentId: {type: GraphQLString} + callAssignmentId: {type: GraphQLString}, + center: {type: GeoPoint}, + radiusMeters: {type: GraphQLFloat} }, - resolve: async(user, {callAssignmentId}, {rootValue}) => { - + resolve: async( + user, {callAssignmentId, center, radiusMeters}, {rootValue} + ) => { let localCallAssignmentId = fromGlobalId(callAssignmentId) if (localCallAssignmentId.type !== 'CallAssignment') localCallAssignmentId = callAssignmentId @@ -647,6 +658,21 @@ const GraphQLUser = new GraphQLObjectType({ if (userAddress) query = query.whereNot('bsd_people.cons_id', userAddress.cons_id) + // Filter by distance from a geographical point. + // Spatial ref 4326 is WGS 84, in degrees + // Spatial ref 900913 is Google Web Mercator, in meters + if (center && radiusMeters > 0) { + query = query.whereRaw(` + ST_DWithin(bsd_addresses.geom, + ST_Transform( + ST_SetSRID(ST_MakePoint(${center.lon}, ${center.lat}), 4326), + 900913 + ), + ${radiusMeters} + ) + `) + } + log.info(`Running query: ${query}`) let person = await query diff --git a/src/frontend/app.js b/src/frontend/app.js index 563d4735..c1e42c95 100644 --- a/src/frontend/app.js +++ b/src/frontend/app.js @@ -204,6 +204,7 @@ ReactDOM.render( ...CallAssignmentQueries, ...CurrentUserQueries }} + queryParams={['ll', 'miles']} renderLoading={() => } /> diff --git a/src/frontend/components/CallAssignment.js b/src/frontend/components/CallAssignment.js index 3ebc24a9..3369a6dc 100644 --- a/src/frontend/components/CallAssignment.js +++ b/src/frontend/components/CallAssignment.js @@ -12,6 +12,7 @@ import SubmitCallSurvey from '../mutations/SubmitCallSurvey' import CallStatsBar from './CallStatsBar' import MutationHandler from './MutationHandler' import {PhoneNumberFormat, PhoneNumberUtil} from 'google-libphonenumber' +import getDefaultQuery from '../helpers/getDefaultQuery' const phoneUtil = PhoneNumberUtil.getInstance() const SurveyRenderers = { @@ -408,8 +409,29 @@ ${userFirstName}` } } +// Convert the query parameters 'll' and 'miles' into the API arguments +// for the center and radius of the filter region. +var center = null; +var radiusMeters = null; + +var queryParams = getDefaultQuery({ + ll: null, // URL query parameter: point in lat,lon format + miles: null // URL query parameter: radius in miles +}) +var llParts = (queryParams.ll || '').split(',') +if (llParts.length === 2) { + center = {lat: Number(llParts[0]), lon: Number(llParts[1])} +} +if (queryParams.miles) { + radiusMeters = Number(queryParams.miles) * 1609.34 +} + export default Relay.createContainer(CallAssignment, { - initialVariables: { id: '' }, + initialVariables: { + id: '', + center: center, + radiusMeters: radiusMeters + }, fragments: { callAssignment: () => Relay.QL` fragment on CallAssignment { @@ -428,9 +450,13 @@ export default Relay.createContainer(CallAssignment, { fragment on User { id firstName - allCallsMade:callsMade(forAssignmentId:$id) - completedCallsMade:callsMade(forAssignmentId:$id,completed:true) - intervieweeForCallAssignment(callAssignmentId:$id) { + allCallsMade: callsMade(forAssignmentId: $id) + completedCallsMade: callsMade(forAssignmentId: $id, completed: true) + intervieweeForCallAssignment( + callAssignmentId: $id, + center: $center, + radiusMeters: $radiusMeters + ) { id prefix firstName diff --git a/src/frontend/helpers/convertType.js b/src/frontend/helpers/convertType.js new file mode 100644 index 00000000..2b389e92 --- /dev/null +++ b/src/frontend/helpers/convertType.js @@ -0,0 +1,26 @@ +const convertType = (value) => { + if (typeof value === 'object'){ + let updatedValue = {} + Object.keys(value).forEach((key) => { + const currentValue = convertType(value[key]) + if (currentValue != undefined) + value[key] = currentValue + }) + return value + } + else if (value === 'none') + return null + else if (value === 'true') + return true + else if (value === 'false') + return false + else if (value != '' && !isNaN(value) && String(Number(value)) === value) + return Number(value) + else if (value) + return String(value) + else { + return undefined + } +} + +export default convertType diff --git a/src/frontend/helpers/getDefaultQuery.js b/src/frontend/helpers/getDefaultQuery.js new file mode 100644 index 00000000..aa69548f --- /dev/null +++ b/src/frontend/helpers/getDefaultQuery.js @@ -0,0 +1,18 @@ +import qs from 'qs' +import convertType from './convertType' + +export default function(defaultParams) { + const hashParams = convertType(qs.parse(location.hash.substr(1), { strictNullHandling: true })) + if (hashParams.query){ + try { + let newQueryParams = {...defaultParams, ...hashParams.query} + newQueryParams.filters = {...defaultParams.filters, ...hashParams.query.filters} + return newQueryParams + } + catch(ex) { + console.error('Invalid query parameters', ex) + } + } + + return {...defaultParams, ...hashParams} +}