11import React , { Component } from 'react'
22import { Link , Route } from 'react-router-dom'
33import ReactTooltip from 'react-tooltip'
4- import format from 'date-fns/format'
54import { colour_contrast , group_by } from '../../utils'
65import { If } from '../shared/Tools'
7- import { CalendarTimes , CalendarPlus } from '../shared/Svgs' // CalendarCheck
6+ import { CalendarCheck , CalendarPlus , CalendarTimes } from '../shared/Svgs'
87import AptModal from './AptModal'
98
9+ const LS_KEY = '_tcs_user_data_'
10+
1011const group_appointments = apts => {
1112 // group appointments by month then day
1213 return group_by ( apts , a => a . start . match ( / \d { 4 } - \d { 2 } / ) [ 0 ] )
@@ -16,41 +17,47 @@ const group_appointments = apts => {
1617 } ) )
1718}
1819
19- const Apt = ( { apt, props} ) => {
20+ const Apt = ( { apt, props, appointment_attendees } ) => {
2021 const full = apt . attendees_max === apt . attendees_count
2122 const colour = full ? '#CCC' : apt . service_colour
22- const tip = full ? 'Lesson fully subscribed' : null
23- let Icon = full ? CalendarTimes : CalendarPlus
23+ let Icon , tip
24+ const spaces_ctx = { spaces : apt . attendees_max - apt . attendees_count }
25+ if ( appointment_attendees && appointment_attendees [ apt . id ] !== undefined ) {
26+ Icon = CalendarCheck
27+ tip = props . config . get_text ( full ? 'no_spaces_attending' : 'spaces_attending' , spaces_ctx )
28+ } else {
29+ Icon = full ? CalendarTimes : CalendarPlus
30+ tip = props . config . get_text ( full ? 'no_spaces' : 'spaces' , spaces_ctx )
31+ }
2432 return (
2533 < Link to = { props . root . url ( `appointment/${ apt . link } ` ) } className = "tcs-item" >
2634 < div className = { `tcs-apt ${ colour_contrast ( colour ) } ` } style = { { background : colour } } data-tip = { tip } >
2735 < div >
2836 < Icon />
29- < span > { format ( apt . start , 'HH:mm' ) } </ span >
37+ < span > { props . config . date_fns . format ( apt . start , 'HH:mm' ) } </ span >
3038 < span > { apt . topic } ({ apt . service_name } )</ span >
3139 </ div >
3240 < div >
3341 < span > { props . config . format_time_diff ( apt . finish , apt . start ) } </ span > •
3442 < span > { props . config . format_money ( apt . price ) } </ span >
3543 </ div >
3644 </ div >
37- < ReactTooltip effect = "solid" />
3845 </ Link >
3946 )
4047}
4148
42- const AptDayGroup = ( { appointments, props} ) => {
49+ const AptDayGroup = ( { appointments, props, appointment_attendees } ) => {
4350 const first_apt = appointments [ 0 ]
4451 return (
4552 < div className = "tcs-apt-group-day" >
4653 < div className = "tcs-day" >
47- { format ( first_apt . start , 'Do' ) }
54+ { props . config . date_fns . format ( first_apt . start , 'Do' ) }
4855 < br />
49- { format ( first_apt . start , 'ddd' ) }
56+ { props . config . date_fns . format ( first_apt . start , 'ddd' ) }
5057 </ div >
5158 < div >
52- { appointments . map ( ( apt , i ) => (
53- < Apt key = { i } apt = { apt } props = { props } />
59+ { appointments . map ( apt => (
60+ < Apt key = { apt . id } apt = { apt } props = { props } appointment_attendees = { appointment_attendees } />
5461 ) ) }
5562 </ div >
5663 </ div >
@@ -64,13 +71,19 @@ class Appointments extends Component {
6471 appointments : null ,
6572 page : 1 ,
6673 more_pages : false ,
74+ display_data : null ,
75+ appointment_attendees : null ,
6776 }
77+ this . sso_args = null
6878 this . update = this . update . bind ( this )
79+ this . signin = this . signin . bind ( this )
80+ this . signout = this . signout . bind ( this )
6981 this . root_url = this . props . root . url ( '' )
7082 }
7183
72- async componentDidMount ( ) {
73- await this . update ( )
84+ componentDidMount ( ) {
85+ this . update_display_data ( )
86+ this . update ( )
7487 }
7588
7689 page_url ( page ) {
@@ -83,23 +96,69 @@ class Appointments extends Component {
8396 }
8497
8598 async update ( ) {
86- const mj = this . props . history . location . pathname . match ( / j o b \/ ( \d + ) / )
87- const job_id = mj ? parseInt ( mj [ 1 ] , 10 ) : null
88-
8999 const mp = this . props . history . location . pathname . match ( / p a g e \/ ( \d + ) / )
90100 const page = mp ? parseInt ( mp [ 1 ] , 10 ) : 1
91- this . setState ( { job_id, page} )
92101 const appointments = await this . props . root . requests . get ( 'appointments' , {
93- service : job_id ,
94- page : page ,
95- pagination : this . props . config . pagination ,
102+ page, pagination : this . props . config . pagination ,
96103 } )
97104 this . props . config . event_callback ( 'updated_appointments' , appointments )
98105 const on_previous_pages = ( page - 1 ) * this . props . config . pagination
106+ this . sso_args && await this . update_attendees ( )
99107 this . setState ( {
100108 appointments,
101109 more_pages : appointments . count > appointments . results . length + on_previous_pages ,
102110 } )
111+ ReactTooltip . rebuild ( )
112+ }
113+
114+ signin ( ) {
115+ const process_message = event => {
116+ try {
117+ JSON . parse ( event . data )
118+ } catch ( e ) {
119+ return
120+ }
121+ event . source . close ( )
122+ window . sessionStorage [ LS_KEY ] = event . data
123+ this . update_display_data ( )
124+ this . update_attendees ( )
125+ }
126+ window . addEventListener ( 'message' , process_message , false )
127+ window . open (
128+ `${ this . props . config . auth_url } ?site=${ encodeURIComponent ( window . location . href ) } ` ,
129+ 'Auth' ,
130+ 'width=1000,height=700,left=100,top=100,scrollbars,toolbar=0,resizable'
131+ )
132+ }
133+
134+ signout ( ) {
135+ this . setState ( {
136+ display_data : null ,
137+ appointment_attendees : null ,
138+ } )
139+ this . sso_args = null
140+ window . sessionStorage . removeItem ( LS_KEY )
141+ }
142+
143+ update_display_data ( ) {
144+ const raw_data = window . sessionStorage [ LS_KEY ]
145+ if ( raw_data ) {
146+ this . sso_args = JSON . parse ( raw_data )
147+ this . setState ( { display_data : JSON . parse ( this . sso_args . sso_data ) } )
148+ }
149+ }
150+
151+ async update_attendees ( ) {
152+ try {
153+ const r = await this . props . root . requests . get ( 'check-client' , this . sso_args , { set_app_state : false } )
154+ this . setState ( { appointment_attendees : r . appointment_attendees } )
155+ } catch ( e ) {
156+ if ( e . xhr . status === 401 ) {
157+ this . signout ( )
158+ } else {
159+ this . props . root . setState ( { error : e . msg } )
160+ }
161+ }
103162 }
104163
105164 render ( ) {
@@ -109,13 +168,15 @@ class Appointments extends Component {
109168 < div className = "tcs-app tcs-appointments" >
110169 { months . map ( ( { date, appointments} , i ) => (
111170 < div className = "tcs-apt-group-month" key = { i } >
112- < div className = "tcs-title" > { format ( date , 'MMMM' ) } </ div >
113- { appointments . map ( ( appointments , i ) => (
114- < AptDayGroup appointments = { appointments } key = { i } props = { this . props } />
171+ < div className = "tcs-title" > { this . props . config . date_fns . format ( date , 'MMMM' ) } </ div >
172+ { appointments . map ( ( appointments , j ) => (
173+ < AptDayGroup key = { j } appointments = { appointments } props = { this . props }
174+ appointment_attendees = { this . state . appointment_attendees } />
115175 ) ) }
116176 </ div >
117177 ) ) }
118178
179+ < ReactTooltip effect = "solid" />
119180 < If v = { this . state . page > 1 || this . state . more_pages } >
120181 < div className = "tcs-pagination" >
121182 < Link
@@ -137,9 +198,14 @@ class Appointments extends Component {
137198 last_url = { this . root_url }
138199 appointments = { this . state . appointments && this . state . appointments . results }
139200 got_data = { Boolean ( this . state . appointments ) }
201+ display_data = { this . state . display_data }
202+ appointment_attendees = { this . state . appointment_attendees }
203+ sso_args = { this . sso_args }
140204 root = { this . props . root }
141205 config = { this . props . config }
142- update_apts = { this . update }
206+ update = { this . update }
207+ signin = { this . signin }
208+ signout = { this . signout }
143209 history = { props . history } />
144210 ) } />
145211 </ div >
0 commit comments