Skip to content

Commit 6d3dd0d

Browse files
committed
location search
1 parent 6c76767 commit 6d3dd0d

File tree

10 files changed

+195
-110
lines changed

10 files changed

+195
-110
lines changed

public/simple/index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
<script>
3535
var public_key = '9c79f14df986a1ec693c'
3636
var api_root = null // 'https://socket-beta.tutorcruncher.com' 'http://localhost:8000'
37-
window.socket = socket(public_key, {api_root: api_root})
37+
window.socket = socket(public_key, {
38+
api_root: api_root,
39+
// subject_filter: false,
40+
// location_input: false
41+
})
3842
</script>
3943
</html>

src/components/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class App extends Component {
3030

3131
get_text (name, replacements) {
3232
let s = this.props.config.messages[name]
33+
if (!s) {
34+
console.warn(`not translation found for "${name}"`)
35+
return name
36+
}
3337
for (let [k, v] of Object.entries(replacements || {})) {
3438
s = s.replace(`{${k}}`, v)
3539
}

src/components/contractors/Contractors.js

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,28 @@ import {async_start, slugify} from '../../utils'
44
import {If} from '../shared/Tools'
55
import {Grid, List} from './List'
66
import ConModal from './ConModal'
7-
import SelectSubjects from './SelectSubjects'
7+
import {SubjectSelect, LocationInput} from './Filters'
88

99
class Contractors extends Component {
1010
constructor (props) {
1111
super(props)
1212
this.state = {
13-
contractors: [],
14-
got_contractors: false,
13+
contractor_response: null,
1514
page: 1,
1615
more_pages: false,
1716
subjects: [],
1817
selected_subject: null,
1918
last_url: null,
19+
location_str: null,
2020
}
2121
this.update_contractors = this.update_contractors.bind(this)
2222
this.get_contractor_details = this.get_contractor_details.bind(this)
2323
this.set_contractor_details = this.set_contractor_details.bind(this)
2424
this.subject_url = this.subject_url.bind(this)
2525
this.page_url = this.page_url.bind(this)
2626
this.subject_change = this.subject_change.bind(this)
27+
this.location_change = this.location_change.bind(this)
28+
this.submit_location = this.submit_location.bind(this)
2729
}
2830

2931
async componentDidMount () {
@@ -59,14 +61,25 @@ class Contractors extends Component {
5961
this.update_contractors(selected_subject)
6062
}
6163

62-
async update_contractors (selected_subject) {
64+
location_change (loc) {
65+
this.setState({location_str: loc})
66+
}
67+
68+
submit_location (location_str) {
69+
this.update_contractors(this.state.selected_subject, location_str)
70+
}
71+
72+
async update_contractors (selected_subject, location_str) {
6373
if (!selected_subject) {
6474
const m = this.props.history.location.pathname.match(/subject\/(\d+)/)
6575
const subject_id = m ? parseInt(m[1], 10) : null
6676
if (subject_id && this.state.subjects.length > 0) {
6777
selected_subject = this.state.subjects.find(s => s.id === subject_id)
6878
}
6979
}
80+
if (location_str === undefined) {
81+
location_str = this.state.location_str
82+
}
7083

7184
const m = this.props.history.location.pathname.match(/page\/(\d+)/)
7285
const page = m ? parseInt(m[1], 10) : 1
@@ -75,20 +88,15 @@ class Contractors extends Component {
7588
subject: selected_subject ? selected_subject.id : null,
7689
pagination: this.props.config.pagination,
7790
page: page,
91+
location: location_str,
7892
})
79-
const data = await this.props.root.requests.get('contractors', args)
80-
let contractors
81-
if (Array.isArray(data)) {
82-
contractors = data
83-
} else {
84-
contractors = data.results
85-
}
86-
this.props.config.event_callback('updated_contractors', contractors)
87-
this.setState({contractors: []})
93+
console.log(args)
94+
const contractor_response = await this.props.root.requests.get('contractors', args)
95+
this.props.config.event_callback('updated_contractors', contractor_response)
96+
this.setState({contractor_response: {results: []}})
8897
setTimeout(() => this.setState({
89-
contractors,
90-
got_contractors: true,
91-
more_pages: contractors.length === this.props.config.pagination,
98+
contractor_response,
99+
more_pages: contractor_response.count > contractor_response.results.length,
92100
}), 0)
93101
}
94102

@@ -109,18 +117,40 @@ class Contractors extends Component {
109117
}
110118

111119
render () {
120+
let description = ''
121+
const con_count = this.state.contractor_response && this.state.contractor_response.count
122+
if (con_count && this.state.selected_subject) {
123+
const msg_id_suffix = con_count === 1 ? 'single' : 'plural'
124+
description = this.props.root.get_text('subject_filter_summary_' + msg_id_suffix, {
125+
count: con_count,
126+
subject: this.state.selected_subject.name,
127+
})
128+
}
112129
const DisplayComponent = this.props.config.mode === 'grid' ? Grid : List
113130
return (
114131
<div className="tcs-app tcs-contractors">
115-
<If v={this.state.got_contractors && this.props.config.subject_filter}>
116-
<SelectSubjects get_text={this.props.root.get_text}
117-
contractors={this.state.contractors}
118-
subjects={this.state.subjects}
119-
selected_subject={this.state.selected_subject}
120-
subject_change={this.subject_change}/>
132+
<If v={this.state.contractor_response}>
133+
<div className="tcs-filters-container">
134+
<LocationInput get_text={this.props.root.get_text}
135+
show={this.props.config.location_input}
136+
loc_raw={this.state.location_str}
137+
loc_change={this.location_change}
138+
submit={this.submit_location}/>
139+
140+
<SubjectSelect get_text={this.props.root.get_text}
141+
show={this.props.config.subject_filter}
142+
subjects={this.state.subjects}
143+
selected_subject={this.state.selected_subject}
144+
subject_change={this.subject_change}/>
145+
</div>
146+
<div key="summary" className="tcs-summary">
147+
{description}
148+
</div>
121149
</If>
122-
<DisplayComponent contractors={this.state.contractors} root={this.props.root}/>
123-
<If v={this.state.got_contractors && this.state.contractors.length === 0}>
150+
<DisplayComponent
151+
contractors={this.state.contractor_response ? this.state.contractor_response.results : []}
152+
root={this.props.root}/>
153+
<If v={this.state.contractor_response && this.state.contractor_response.count === 0}>
124154
<div className="tcs-no-contractors">
125155
{this.props.root.get_text('no_tutors_found')}
126156
</div>
@@ -145,8 +175,8 @@ class Contractors extends Component {
145175
<Route path={this.props.root.url(':id(\\d+):_extra')} render={props => (
146176
<ConModal id={props.match.params.id}
147177
last_url={this.state.last_url}
148-
contractors={this.state.contractors}
149-
got_contractors={this.state.got_contractors}
178+
contractors={this.state.contractor_response.results}
179+
got_contractors={!!this.state.contractor_response}
150180
get_contractor_details={this.get_contractor_details}
151181
root={this.props.root}
152182
config={this.props.config}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react'
2+
import Select from 'react-select'
3+
import 'react-select/dist/react-select.css'
4+
import {If} from '../shared/Tools'
5+
6+
export const SubjectSelect = ({get_text, show, subjects, selected_subject, subject_change}) => {
7+
return (
8+
<div className="tcs-contractor-filter">
9+
<If v={show}>
10+
<Select
11+
value={selected_subject && selected_subject.id}
12+
onChange={subject_change}
13+
placeholder={get_text('subject_filter_placeholder')}
14+
labelKey='name'
15+
valueKey='id'
16+
options={subjects}/>
17+
</If>
18+
</div>
19+
)
20+
}
21+
22+
23+
export const LocationInput = ({get_text, show, loc_raw, loc_change, submit}) => {
24+
return (
25+
<div className="tcs-contractor-filter tcs-location-filter">
26+
<If v={show}>
27+
<input className="tcs-location-input"
28+
type="text"
29+
value={loc_raw || ''}
30+
onChange={v => loc_change(v.target.value || null)}
31+
onKeyPress={v => v.key === 'Enter' && submit()}
32+
placeholder={get_text('location_input_placeholder')}/>
33+
<span className="tcs-location-clear"
34+
style={{visibility: loc_raw === null ? 'hidden' : 'visible'}}
35+
onClick={() => loc_change(null) || submit(null)}>
36+
×
37+
</span>
38+
</If>
39+
</div>
40+
)
41+
}
42+

src/components/contractors/SelectSubjects.js

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ const STRINGS = {
3434
enquiry_title: 'Enquiry',
3535
grecaptcha_missing: 'This captcha is required',
3636
required: ' (Required)',
37-
subject_filter: 'Filter by subject',
37+
subject_filter_placeholder: 'Select a Subject...',
3838
subject_filter_summary_single: '{subject}: showing 1 result',
3939
subject_filter_summary_plural: '{subject}: showing {count} results',
40+
location_input_placeholder: 'Enter your Address or Postal code...',
4041
view_profile: 'View Profile',
4142
review_hours: '({hours} hours)',
4243
previous: 'Previous',
@@ -120,6 +121,10 @@ window.socket = async function (public_key, config) {
120121
config.subject_filter = true
121122
}
122123

124+
if (config.location_input === undefined) {
125+
config.location_input = true
126+
}
127+
123128
if (!config.event_callback) {
124129
config.event_callback = () => null
125130
}

src/main.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
@import './styles/stars';
2424
@import './styles/tools';
2525
@import './styles/contractors';
26+
@import './styles/contractor-filters';
2627
@import './styles/grid';
2728
@import './styles/list';
2829
@import './styles/input';
2930
@import './styles/modal';
30-
@import './styles/subject-select';

src/styles/contractor-filters.scss

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//@import './vue-multiselect/dist/vue-multiselect.min';
2+
@import '../conf';
3+
4+
.multiselect__select {
5+
cursor: default;
6+
right: 16px;
7+
}
8+
9+
.multiselect__option--highlight {
10+
background: $brand-colour;
11+
}
12+
13+
.multiselect__option--selected.multiselect__option--highlight {
14+
background: darken($brand-colour, 10%);
15+
}
16+
17+
//.cross {
18+
// position: absolute;
19+
// top: 1px;
20+
// right: 1px;
21+
// margin: 5px 4px;
22+
// padding: 4px 4px;
23+
// cursor: pointer;
24+
// z-index: 10;
25+
// svg.tcs-svg {
26+
// transition: all .4s ease;
27+
// opacity: 0.5;
28+
// width: 18px;
29+
// height: 18px;
30+
// }
31+
// &:hover {
32+
// svg.tcs-svg {
33+
// opacity: 1;
34+
// }
35+
// }
36+
//}
37+
38+
.tcs-filters-container {
39+
display: flex;
40+
}
41+
42+
.tcs-contractor-filter {
43+
width: 50%;
44+
padding: 0 2px;
45+
box-sizing: border-box;
46+
}
47+
48+
.tcs-location-filter {
49+
border-radius: 4px;
50+
border: 1px solid #ccc;
51+
height: 36px;
52+
}
53+
54+
.tcs-location-input {
55+
width: calc(100% - 30px);
56+
border: none;
57+
padding: 8px;
58+
font-size: 16px;
59+
outline: none !important;
60+
&::placeholder {
61+
color: #aaa;
62+
}
63+
}
64+
.tcs-location-clear {
65+
padding: 6px;
66+
width: 30px;
67+
background: none;
68+
border: none;
69+
font-size: 16px;
70+
color: #999;
71+
cursor: pointer;
72+
&:hover {
73+
color: #666;
74+
}
75+
}
76+
77+
@media(max-width: 600px) {
78+
.tcs-contractor-filter {
79+
width: 100%;
80+
}
81+
}

src/styles/contractors.scss

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,7 @@
44
padding: 10px;
55
}
66

7-
.subject-select {
8-
width: 50%;
9-
margin: 0 0 0 auto;
10-
padding: 0 2px;
11-
box-sizing: border-box;
12-
}
13-
14-
@media(max-width: 600px) {
15-
.subject-select {
16-
width: 100%;
17-
}
18-
}
19-
207
@media(max-width: 400px) {
21-
.subject-select {
22-
width: 100%;
23-
}
248
.tcs-contractors {
259
padding: 10px 2px;
2610
}

0 commit comments

Comments
 (0)