Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion public/simple/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
<script>
var public_key = '9c79f14df986a1ec693c'
var api_root = null // 'https://socket-beta.tutorcruncher.com' 'http://localhost:8000'
window.socket = socket(public_key, {api_root: api_root})
window.socket = socket(public_key, {
api_root: api_root,
// subject_filter: false,
// location_input: false
})
</script>
</html>
4 changes: 4 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class App extends Component {

get_text (name, replacements) {
let s = this.props.config.messages[name]
if (!s) {
console.warn(`not translation found for "${name}"`)
return name
}
for (let [k, v] of Object.entries(replacements || {})) {
s = s.replace(`{${k}}`, v)
}
Expand Down
84 changes: 57 additions & 27 deletions src/components/contractors/Contractors.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@ import {async_start, slugify} from '../../utils'
import {If} from '../shared/Tools'
import {Grid, List} from './List'
import ConModal from './ConModal'
import SelectSubjects from './SelectSubjects'
import {SubjectSelect, LocationInput} from './Filters'

class Contractors extends Component {
constructor (props) {
super(props)
this.state = {
contractors: [],
got_contractors: false,
contractor_response: null,
page: 1,
more_pages: false,
subjects: [],
selected_subject: null,
last_url: null,
location_str: null,
}
this.update_contractors = this.update_contractors.bind(this)
this.get_contractor_details = this.get_contractor_details.bind(this)
this.set_contractor_details = this.set_contractor_details.bind(this)
this.subject_url = this.subject_url.bind(this)
this.page_url = this.page_url.bind(this)
this.subject_change = this.subject_change.bind(this)
this.location_change = this.location_change.bind(this)
this.submit_location = this.submit_location.bind(this)
}

async componentDidMount () {
Expand Down Expand Up @@ -59,36 +61,42 @@ class Contractors extends Component {
this.update_contractors(selected_subject)
}

async update_contractors (selected_subject) {
location_change (loc) {
this.setState({location_str: loc})
}

submit_location (location_str) {
this.update_contractors(this.state.selected_subject, location_str)
}

async update_contractors (selected_subject, location_str) {
if (!selected_subject) {
const m = this.props.history.location.pathname.match(/subject\/(\d+)/)
const subject_id = m ? parseInt(m[1], 10) : null
if (subject_id && this.state.subjects.length > 0) {
selected_subject = this.state.subjects.find(s => s.id === subject_id)
}
}
if (location_str === undefined) {
location_str = this.state.location_str
}

const m = this.props.history.location.pathname.match(/page\/(\d+)/)
const page = m ? parseInt(m[1], 10) : 1
this.setState({selected_subject, page})
const args = Object.assign({}, this.props.config.contractor_filter, {
subject: selected_subject ? selected_subject.id : null,
pagination: this.props.config.pagination,
sort: this.props.config.sort_on,
page: page,
location: location_str,
})
const data = await this.props.root.requests.get('contractors', args)
let contractors
if (Array.isArray(data)) {
contractors = data
} else {
contractors = data.results
}
this.props.config.event_callback('updated_contractors', contractors)
this.setState({contractors: []})
const contractor_response = await this.props.root.requests.get('contractors', args)
this.props.config.event_callback('updated_contractors', contractor_response)
this.setState({contractor_response: {results: []}})
setTimeout(() => this.setState({
contractors,
got_contractors: true,
more_pages: contractors.length === this.props.config.pagination,
contractor_response,
more_pages: contractor_response.count > contractor_response.results.length,
}), 0)
}

Expand All @@ -109,20 +117,42 @@ class Contractors extends Component {
}

render () {
let description = ''
const con_count = this.state.contractor_response && this.state.contractor_response.count
if (con_count && this.state.selected_subject) {
const msg_id_suffix = con_count === 1 ? 'single' : 'plural'
description = this.props.root.get_text('subject_filter_summary_' + msg_id_suffix, {
count: con_count,
subject: this.state.selected_subject.name,
})
}
const DisplayComponent = this.props.config.mode === 'grid' ? Grid : List
return (
<div className="tcs-app tcs-contractors">
<If v={this.state.got_contractors && this.props.config.subject_filter}>
<SelectSubjects get_text={this.props.root.get_text}
contractors={this.state.contractors}
subjects={this.state.subjects}
selected_subject={this.state.selected_subject}
subject_change={this.subject_change}/>
<If v={this.state.contractor_response}>
<div className="tcs-filters-container">
<LocationInput get_text={this.props.root.get_text}
show={this.props.config.show_location_search}
loc_raw={this.state.location_str}
loc_change={this.location_change}
submit={this.submit_location}/>

<SubjectSelect get_text={this.props.root.get_text}
show={this.props.config.show_subject_filter}
subjects={this.state.subjects}
selected_subject={this.state.selected_subject}
subject_change={this.subject_change}/>
</div>
<div key="summary" className="tcs-summary">
{description}
</div>
</If>
<DisplayComponent contractors={this.state.contractors} root={this.props.root}/>
<If v={this.state.got_contractors && this.state.contractors.length === 0}>
<DisplayComponent
contractors={this.state.contractor_response ? this.state.contractor_response.results : []}
root={this.props.root}/>
<If v={this.state.contractor_response && this.state.contractor_response.count === 0}>
<div className="tcs-no-contractors">
{this.props.root.get_text('no_tutors_found')}
{this.props.root.get_text(this.state.location_str === null ? 'no_tutors_found' : 'no_tutors_found_loc')}
</div>
</If>

Expand All @@ -145,8 +175,8 @@ class Contractors extends Component {
<Route path={this.props.root.url(':id(\\d+):_extra')} render={props => (
<ConModal id={props.match.params.id}
last_url={this.state.last_url}
contractors={this.state.contractors}
got_contractors={this.state.got_contractors}
contractors={this.state.contractor_response.results}
got_contractors={!!this.state.contractor_response}
get_contractor_details={this.get_contractor_details}
root={this.props.root}
config={this.props.config}
Expand Down
44 changes: 44 additions & 0 deletions src/components/contractors/Filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import Select from 'react-select'
import 'react-select/dist/react-select.css'
import {If} from '../shared/Tools'

export const SubjectSelect = ({get_text, show, subjects, selected_subject, subject_change}) => {
return (
<div className="tcs-contractor-filter">
<If v={show}>
<Select
value={selected_subject && selected_subject.id}
onChange={subject_change}
placeholder={get_text('subject_filter_placeholder')}
labelKey='name'
valueKey='id'
options={subjects}/>
</If>
</div>
)
}


export const LocationInput = ({get_text, show, loc_raw, loc_change, submit}) => {
return (
<div className="tcs-contractor-filter">
<If v={show}>
<div className="tcs-location-filter">
<input className="tcs-location-input"
type="text"
value={loc_raw || ''}
onChange={v => loc_change(v.target.value || null)}
onKeyPress={v => v.key === 'Enter' && submit()}
placeholder={get_text('location_input_placeholder')}/>
<span className="tcs-location-clear"
style={{visibility: loc_raw === null ? 'hidden' : 'visible'}}
onClick={() => loc_change(null) || submit(null)}>
×
</span>
</div>
</If>
</div>
)
}

7 changes: 6 additions & 1 deletion src/components/contractors/List.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { Component } from 'react'
import {Link} from 'react-router-dom'
import {Location, Markdown} from '../shared/Tools'
import {Location, Markdown, If} from '../shared/Tools'
import Stars from './Stars'

class AnimateLink extends Component {
Expand Down Expand Up @@ -64,6 +64,11 @@ export const List = ({contractors, root}) => (
<div className="tcs-location">
<Location/>
<span>{contractor.town}</span>
<div className="tcs-distance">
<If v={contractor.distance !== null}>
{root.get_text('distance_away', {distance: Math.round(contractor.distance / 100) / 10})}
</If>
</div>
</div>
</div>
</AnimateLink>
Expand Down
29 changes: 0 additions & 29 deletions src/components/contractors/SelectSubjects.js

This file was deleted.

1 change: 1 addition & 0 deletions src/conf.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ $size-sm: 768px;
$button-colour: $highlight;
// size used for images in grid and list view
$dft-image-size: 150px;
$grey-text: #888;
54 changes: 29 additions & 25 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ const STRINGS = {
enquiry_title: 'Enquiry',
grecaptcha_missing: 'This captcha is required',
required: ' (Required)',
subject_filter: 'Filter by subject',
subject_filter_placeholder: 'Select a subject...',
subject_filter_summary_single: '{subject}: showing 1 result',
subject_filter_summary_plural: '{subject}: showing {count} results',
location_input_placeholder: 'Enter your address or zip/postal code...',
view_profile: 'View Profile',
review_hours: '({hours} hours)',
previous: 'Previous',
next: 'Next',
no_tutors_found: 'No more tutors found',
no_tutors_found_loc: 'No more tutors found near this location',
distance_away: '{distance}km away',
}

const MODES = ['grid', 'list', 'enquiry', 'enquiry-modal']
Expand All @@ -66,11 +69,8 @@ window.socket = async function (public_key, config) {
}
}

let options_required = false
let error = null
if (!config.mode) {
options_required = true
} else if (MODES.indexOf(config.mode) === -1) {
if (config.mode && MODES.indexOf(config.mode) === -1) {
error = `invalid mode "${config.mode}", options are: ${MODES.join(', ')}`
config.mode = 'grid'
}
Expand All @@ -94,8 +94,6 @@ window.socket = async function (public_key, config) {
// use history mode with enquiry so it doesn't add the hash
if (config.mode === 'enquiry') {
config.router_mode = 'history'
} else {
options_required = true
}
} else if (ROUTER_MODES.indexOf(config.router_mode) === -1) {
error = `invalid router mode "${config.router_mode}", options are: ${ROUTER_MODES.join(', ')}`
Expand All @@ -116,10 +114,6 @@ window.socket = async function (public_key, config) {
delete config.labels_exclude
}

if (config.subject_filter === undefined) {
config.subject_filter = true
}

if (!config.event_callback) {
config.event_callback = () => null
}
Expand All @@ -130,7 +124,6 @@ window.socket = async function (public_key, config) {
return
}

config.pagination = config.pagination || 100
config.messages = config.messages || {}
for (let k of Object.keys(STRINGS)) {
if (!config.messages[k]) {
Expand All @@ -140,20 +133,31 @@ window.socket = async function (public_key, config) {
config.random_id = Math.random().toString(36).substring(2, 10)
config.grecaptcha_key = process.env.REACT_APP_GRECAPTCHA_KEY

if (options_required) {
let company_options
try {
company_options = await get_company_options(public_key, config)
} catch(e) {
error = e.toString()
company_options = {
display_mode: 'grid',
router_mode: 'hash',
}
let company_options
try {
company_options = await get_company_options(public_key, config)
} catch(e) {
error = e.toString()
// these are the default values
company_options = {
display_mode: 'grid',
pagination: 100,
router_mode: 'hash',
show_hours_reviewed: true,
show_labels: true,
show_location_search: true,
show_stars: true,
show_subject_filter: true,
sort_on: 'name',
}
}

company_options.mode = company_options.display_mode
console.debug('company options:', company_options)
for (let [k, v] of Object.entries(company_options)) {
if (config[k] === undefined) {
config[k] = v
}
console.debug('company options:', company_options)
config.mode = config.mode || company_options.display_mode
config.router_mode = config.router_mode || company_options.router_mode
}

console.debug('using config:', config)
Expand Down
2 changes: 1 addition & 1 deletion src/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
@import './styles/stars';
@import './styles/tools';
@import './styles/contractors';
@import './styles/contractor-filters';
@import './styles/grid';
@import './styles/list';
@import './styles/input';
@import './styles/modal';
@import './styles/subject-select';
Loading