Skip to content

Commit 13190eb

Browse files
committed
fix time diff and svgs
1 parent bb00e36 commit 13190eb

File tree

13 files changed

+362
-121
lines changed

13 files changed

+362
-121
lines changed

src/components/appointments/Appointments.js

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React, { Component } from 'react'
22
import {Link, Route} from 'react-router-dom'
3+
import ReactTooltip from 'react-tooltip'
34
import format from 'date-fns/format'
4-
import {slugify, colour_contrast, group_by} from '../../utils'
5+
import {colour_contrast, group_by} from '../../utils'
56
import {If} from '../shared/Tools'
7+
import {CalendarTimes, CalendarPlus} from '../shared/Svgs' // CalendarCheck
68
import AptModal from './AptModal'
79

810
const group_appointments = apts => {
@@ -14,6 +16,29 @@ const group_appointments = apts => {
1416
}))
1517
}
1618

19+
const Apt = ({apt, props}) => {
20+
const full = apt.attendees_max === apt.attendees_count
21+
const colour = full ? '#CCC' : apt.service_colour
22+
const tip = full ? 'Lesson fully subscribed' : null
23+
let Icon = full ? CalendarTimes : CalendarPlus
24+
return (
25+
<Link to={props.root.url(`appointment/${apt.link}`)} className="tcs-item">
26+
<div className={`tcs-apt ${colour_contrast(colour)}`} style={{background: colour}} data-tip={tip}>
27+
<div>
28+
<Icon/>
29+
<span>{format(apt.start, 'HH:mm')}</span>
30+
<span>{apt.topic} ({apt.service_name})</span>
31+
</div>
32+
<div>
33+
<span>{props.config.format_time_diff(apt.finish, apt.start)}</span>&bull;
34+
<span>{props.config.format_money(apt.price)}</span>
35+
</div>
36+
</div>
37+
<ReactTooltip effect="solid"/>
38+
</Link>
39+
)
40+
}
41+
1742
const AptDayGroup = ({appointments, props}) => {
1843
const first_apt = appointments[0]
1944
return (
@@ -25,20 +50,7 @@ const AptDayGroup = ({appointments, props}) => {
2550
</div>
2651
<div>
2752
{appointments.map((apt, i) => (
28-
<Link key={i} to={props.root.url(`appointment/${apt.link}`)}
29-
className="tcs-item">
30-
<div className={`tcs-apt ${colour_contrast(apt.service_colour)}`}
31-
style={{background: apt.service_colour}}>
32-
<div>
33-
<span>{format(apt.start, 'HH:mm')}</span>
34-
<span>{apt.topic} ({apt.service_name})</span>
35-
</div>
36-
<div>
37-
<span>{props.config.format_time_diff(apt.finish, apt.start)}</span>&bull;
38-
<span>{props.config.format_money(apt.price)}</span>
39-
</div>
40-
</div>
41-
</Link>
53+
<Apt key={i} apt={apt} props={props}/>
4254
))}
4355
</div>
4456
</div>
@@ -50,7 +62,6 @@ class Appointments extends Component {
5062
super(props)
5163
this.state = {
5264
appointments: null,
53-
selected_job: null,
5465
page: 1,
5566
more_pages: false,
5667
}
@@ -62,13 +73,9 @@ class Appointments extends Component {
6273
await this.update()
6374
}
6475

65-
page_url (page, selected_job) {
76+
page_url (page) {
6677
page = page || this.state.page
67-
selected_job = selected_job || this.state.selected_job
6878
let url = this.root_url
69-
if (this.state.selected_job) {
70-
url = this.props.root.url(`job/${selected_job.id}-${slugify(selected_job.name)}`)
71-
}
7279
if (page > 1) {
7380
url += `${url.substr(-1) === '/' ? '' : '/'}page/${page}`
7481
}

src/components/appointments/AptModal.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { Component } from 'react'
2-
import {IfElse, Markdown, DetailGrid, Detail, Tick} from '../shared/Tools'
2+
import {IfElse, Markdown, DetailGrid, Detail} from '../shared/Tools'
3+
import {Tick} from '../shared/Svgs'
34
import Modal from '../shared/Modal'
45

56
const LS_KEY = '_tcs_user_data_'
@@ -9,12 +10,8 @@ class AptModal extends Component {
910
super(props)
1011
this.apt_id = parseInt(this.props.id, 10)
1112
this.login = this.login.bind(this)
12-
this.process_message = this.process_message.bind(this)
13-
this.update_session = this.update_session.bind(this)
14-
this.check_client = this.check_client.bind(this)
1513
this.signout = this.signout.bind(this)
1614
this.book = this.book.bind(this)
17-
this.get_students = this.get_students.bind(this)
1815
this.state = {
1916
apt: props.appointments && props.appointments.find(a => a.id === this.apt_id),
2017
signature: null,
@@ -25,6 +22,7 @@ class AptModal extends Component {
2522
booking_allowed: false,
2623
extra_attendees: 0,
2724
}
25+
this.timeout_id = null
2826
}
2927

3028
componentDidMount () {
@@ -92,13 +90,12 @@ class AptModal extends Component {
9290
}
9391
const url = `book-appointment?signature=${this.state.signature}&sso_data=${encodeURIComponent(this.state.sso_data)}`
9492
await this.props.root.requests.post(url, data)
95-
this.props.update_apts()
9693
if (!student_id) {
9794
const display_data = Object.assign({}, this.state.display_data)
9895
student_id = 999999999
9996
display_data.srs[student_id] = data.student_name
10097
this.setState({new_student: null, display_data})
101-
window.sessionStorage.removeItem(LS_KEY) // force new login when opening appointment to update students
98+
window.sessionStorage.removeItem(LS_KEY) // force new login when opening another appointment to update students
10299
}
103100
const appointment_attendees = this.state.appointment_attendees.slice()
104101
appointment_attendees.push(student_id)
@@ -107,7 +104,9 @@ class AptModal extends Component {
107104
extra_attendees: this.state.extra_attendees + 1,
108105
appointment_attendees
109106
})
110-
setTimeout(() => this.props.update_apts(), 5000)
107+
// update_apts with enough time for the data to have been updated in tc
108+
this.timeout_id && clearTimeout(this.timeout_id)
109+
this.timeout_id = setTimeout(() => this.props.update_apts(), 5000)
111110
}
112111

113112
signout () {
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import React, { Component } from 'react'
2+
import {IfElse, Markdown, DetailGrid, Detail} from '../shared/Tools'
3+
import {Tick} from '../shared/Svgs'
4+
import Modal from '../shared/Modal'
5+
6+
const LS_KEY = '_tcs_user_data_'
7+
8+
class AptModal extends Component {
9+
constructor (props) {
10+
super(props)
11+
this.login = this.login.bind(this)
12+
this.process_message = this.process_message.bind(this)
13+
this.update_session = this.update_session.bind(this)
14+
this.check_client = this.check_client.bind(this)
15+
this.signout = this.signout.bind(this)
16+
this.state = {
17+
signature: null,
18+
sso_data: null,
19+
display_data: null,
20+
appointment_attendees: null,
21+
}
22+
this.timeout_id = null
23+
window.addEventListener('message', this.process_message, false)
24+
this.update_session(window.sessionStorage[LS_KEY])
25+
}
26+
27+
login () {
28+
const url = `${this.props.config.auth_url}?site=${encodeURIComponent(window.location.href)}`
29+
window.open(url, 'Auth', 'width=1000,height=700,left=100,top=100,scrollbars,toolbar=0,resizable')
30+
}
31+
32+
process_message (event) {
33+
const success = this.update_session(event.data)
34+
if (success) {
35+
event.source.close()
36+
window.sessionStorage[LS_KEY] = event.data
37+
}
38+
}
39+
40+
update_session (raw_data) {
41+
let data
42+
try {
43+
data = JSON.parse(raw_data)
44+
} catch (e) {
45+
return false
46+
}
47+
data.display_data = JSON.parse(data.sso_data)
48+
this.setState(data)
49+
this.check_client(data)
50+
return true
51+
}
52+
53+
async check_client (data) {
54+
data = data || this.state
55+
const args = {signature: data.signature, sso_data: data.sso_data}
56+
try {
57+
const r = await this.props.root.requests.get('check-client', args, {set_app_state: false})
58+
this.setState({appointment_attendees: r.appointment_attendees[this.apt_id] || [], booking_allowed: true})
59+
} catch (e) {
60+
if (e.xhr.status === 401) {
61+
this.signout()
62+
} else {
63+
this.props.root.setState({error: e.msg})
64+
}
65+
}
66+
}
67+
68+
signout () {
69+
this.setState({
70+
signature: null,
71+
sso_data: null,
72+
display_data: null,
73+
appointment_attendees: null,
74+
})
75+
window.sessionStorage.removeItem(LS_KEY)
76+
}
77+
78+
render () {
79+
if (!this.props.got_data) {
80+
return (
81+
<Modal history={this.props.history} title=''>
82+
<p>Loading...</p>
83+
</Modal>
84+
)
85+
}
86+
const apt = this.state.apt
87+
if (!apt) {
88+
return (
89+
<Modal history={this.props.history} title="Appointment not Found">
90+
<p>No Appointment found with id {this.props.id}.</p>
91+
</Modal>
92+
)
93+
}
94+
const title = (
95+
<span>
96+
<span className="tcs-circle" style={{background: apt.service_colour}}/>
97+
{apt.topic}
98+
</span>
99+
)
100+
const students = this.get_students()
101+
const spaces_available = apt.attendees_max - apt.attendees_count - this.state.extra_attendees
102+
const booking_allowed = this.state.booking_allowed && spaces_available > 0
103+
return (
104+
<Modal history={this.props.history} title={title} last_url={this.props.last_url} flex={false}>
105+
<div className="tcs-modal-flex">
106+
<div className="tcs-extra">
107+
<div className="tcs-price">
108+
{this.props.config.format_money(apt.price)}
109+
<div className="tcs-label">Price</div>
110+
</div>
111+
</div>
112+
<div className="tcs-content">
113+
<DetailGrid>
114+
<Detail label="Job">
115+
{apt.service_name}
116+
</Detail>
117+
{apt.attendees_max && <Detail label="Spaces Available">{spaces_available}</Detail>}
118+
<Detail label="Start" className="tcs-new-line">{this.props.config.format_datetime(apt.start)}</Detail>
119+
<Detail label="Finish">{this.props.config.format_datetime(apt.finish)}</Detail>
120+
{apt.location && <Detail label="Location">{apt.location}</Detail>}
121+
</DetailGrid>
122+
</div>
123+
</div>
124+
125+
<div>
126+
{apt.service_extra_attributes.map((attr, i) => (
127+
<div key={i} className="tcs-attr">
128+
<h3 className="tcs-attr-title">{attr.name}</h3>
129+
<IfElse v={attr.type === 'text_short' || attr.type === 'text_extended'}>
130+
<Markdown content={attr.value}/>
131+
{/*else*/}
132+
<p>{attr.value}</p>
133+
</IfElse>
134+
</div>
135+
))}
136+
<div className="tcs-book">
137+
<IfElse v={this.state.display_data}>
138+
<div>
139+
{students && (
140+
<div className="tcs-book-existing">
141+
<div>Add your existing Students to the lesson</div>
142+
{students.map(({id, name, already_on_apt}) => (
143+
<div key={id} className="tcs-book-item">
144+
<div className="tcs-existing-name">
145+
{name}
146+
</div>
147+
{already_on_apt ? (
148+
<div className="tcs-already-added">
149+
Added <Tick/>
150+
</div>
151+
) : (
152+
<button className="tcs-button tcs-add-button"
153+
onClick={() => this.book(id)}
154+
disabled={!booking_allowed || already_on_apt}>
155+
{this.props.root.get_text('add_to_lesson')}
156+
</button>
157+
)}
158+
</div>
159+
))}
160+
</div>
161+
)}
162+
<div className="tcs-book-new">
163+
<div>Add a new Student to the lesson</div>
164+
<div className="tcs-book-item">
165+
<input type="text"
166+
className="tcs-default-input"
167+
placeholder="Student Name"
168+
required={true}
169+
maxLength={255}
170+
value={this.state.new_student || ''}
171+
onChange={e => this.setState({new_student: e.target.value})}/>
172+
<button className="tcs-button tcs-add-button"
173+
onClick={() => this.book(null)}
174+
disabled={!booking_allowed || !this.state.new_student}>
175+
{this.props.root.get_text('add_to_lesson')}
176+
</button>
177+
</div>
178+
</div>
179+
</div>
180+
{/*else*/}
181+
<button className="tcs-button tcs-signin" onClick={this.login}>
182+
{this.props.root.get_text('book_appointment_button')}
183+
</button>
184+
</IfElse>
185+
</div>
186+
187+
{this.state.display_data &&
188+
<div className="tcs-session-name">
189+
{this.state.display_data.nm}
190+
<div className="tcs-signout" onClick={this.signout}>
191+
Not you? sign out
192+
</div>
193+
</div>
194+
}
195+
</div>
196+
</Modal>
197+
)
198+
}
199+
}
200+
201+
export default AptModal

src/components/contractors/ConModal.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Component } from 'react'
22
import Modal from '../shared/Modal'
3-
import {Location, IfElse, Photo} from '../shared/Tools'
3+
import {IfElse, Photo} from '../shared/Tools'
4+
import {Location} from '../shared/Svgs'
45
import ConDetails from './ConDetails'
56
import EnquiryForm from '../shared/EnquiryForm'
67
import Stars from './Stars'

src/components/contractors/List.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
2-
import {Location, Markdown, If, Photo, AnimateLink} from '../shared/Tools'
2+
import {Markdown, If, Photo, AnimateLink} from '../shared/Tools'
3+
import {Location} from '../shared/Svgs'
34
import Stars from './Stars'
45

56
export const Grid = ({contractors, root, config}) => (

src/components/shared/Modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { Component } from 'react'
2-
import { Cross, Footer } from './Tools'
2+
import { Cross, Footer } from './Svgs'
33

44
class Modal extends Component {
55
constructor (props) {

0 commit comments

Comments
 (0)