Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
third-party
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cooperatic-foodcoops
third-party
Commits
056fe5d4
Commit
056fe5d4
authored
Sep 28, 2021
by
Damien Moulard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
shifts exchange & choose makeups
parent
683fab8a
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
380 additions
and
30 deletions
+380
-30
members-space-shifts-exchange.css
members_space/static/css/members-space-shifts-exchange.css
+63
-8
members-space-header.js
members_space/static/js/members-space-header.js
+4
-0
members-space-my-shifts.js
members_space/static/js/members-space-my-shifts.js
+1
-1
members-space-shifts-exchange.js
members_space/static/js/members-space-shifts-exchange.js
+234
-10
members-space.js
members_space/static/js/members-space.js
+4
-1
common.css
outils/static/css/common.css
+5
-0
models.py
shifts/models.py
+15
-0
views.py
shifts/views.py
+36
-9
index.html
templates/members_space/index.html
+10
-0
shifts_exchange.html
templates/members_space/shifts_exchange.html
+8
-1
No files found.
members_space/static/css/members-space-shifts-exchange.css
View file @
056fe5d4
#shifts_exchange_content
{
width
:
95%
;
margin
:
3rem
auto
;
display
:
flex
;
flex-direction
:
column
;
}
#shifts_list
{
flex-direction
:
column
;
display
:
none
;
width
:
min-content
;
/* ??? */
width
:
min-content
;
max-width
:
100%
;
}
@media
screen
and
(
max-width
:
768px
)
{
#partner_shifts_list
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
}
}
.selectable_shift_line
{
display
:
flex
;
align-items
:
center
;
margin-left
:
15px
;
margin
:
1
rem
0
;
margin
:
0.75
rem
0
;
border-radius
:
5px
;
}
...
...
@@ -21,14 +32,33 @@
margin-right
:
10px
;
}
/* .selectable_shift_line_text {
border: 2px solid #424242;
border-radius: 5px;
padding: 0.75rem;
} */
.selectable_shift_line.btn
{
cursor
:
not-allowed
;
}
#need_to_select_makeups_message
{
display
:
none
;
align-self
:
center
;
background-color
:
#d9534f
;
color
:
white
;
margin
:
0
1rem
1rem
1rem
;
padding
:
1rem
1.25rem
;
text-align
:
center
;
}
.makeups_warning
{
margin-right
:
3px
;
}
@media
screen
and
(
max-width
:
768px
)
{
.select_makeups_message_block
{
display
:
block
;
}
}
#calendar
{
margin
:
3rem
auto
;
margin
:
3rem
1rem
;
display
:
none
;
}
.fc
.fc-event
{
...
...
@@ -36,6 +66,30 @@
margin
:
1px
10px
!important
;
}
.fc-event.shift_booked
{
background-color
:
#585858
;
cursor
:
auto
;
border-color
:
#585858
;
}
.fc-list-event.shift_booked
{
color
:
white
;
}
#calendar
.fc-list-table
{
table-layout
:
auto
;
}
.resp-header-toolbar
{
display
:
flex
;
flex-direction
:
column
;
}
.resp-header-toolbar
.fc-toolbar-chunk
{
text-align
:
center
;
margin
:
0.25rem
;
}
.date_old_shift
,
.time_old_shift
,
.date_new_shift
,
.time_new_shift
{
font-weight
:
bold
;
}
\ No newline at end of file
members_space/static/js/members-space-header.js
View file @
056fe5d4
...
...
@@ -18,23 +18,27 @@ $(document).ready(function() {
if
(
current_location
!==
"home"
)
{
goto
(
'home'
);
}
toggleHeader
();
});
$
(
'#nav_my_info'
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
if
(
current_location
!==
"my_info"
)
{
goto
(
'mes-infos'
);
}
toggleHeader
();
});
$
(
'#nav_my_shifts'
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
if
(
current_location
!==
"my_shifts"
)
{
goto
(
'mes-services'
);
}
toggleHeader
();
});
$
(
'#nav_shifts_exchange'
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
if
(
current_location
!==
"shifts_exchange"
)
{
goto
(
'echange-de-services'
);
}
toggleHeader
();
});
});
members_space/static/js/members-space-my-shifts.js
View file @
056fe5d4
...
...
@@ -55,7 +55,7 @@ function prepare_server_data(data) {
f_date_shift_start
=
f_date_shift_start
.
charAt
(
0
).
toUpperCase
()
+
f_date_shift_start
.
slice
(
1
);
history_item
.
movement_date
=
f_date_shift_start
+
" - "
+
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
);
history_item
.
movement_date
=
f_date_shift_start
+
" - "
+
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
,
time_options
);
// Text replacements
history_item
.
name
=
(
history_item
.
name
===
"Clôturer le service"
)
?
"Décompte 28j"
:
history_item
.
name
;
//Clôlturer le service
...
...
members_space/static/js/members-space-shifts-exchange.js
View file @
056fe5d4
var
calendar
=
null
,
selected_shift
=
null
;
/**
* A partner can exchange shifts if:
* - s.he doesn't have to choose a makeup shift
* - s.he's not an associated partner
* @returns boolean
*/
function
can_exchange_shifts
()
{
return
partner_data
.
makeups_to_do
==
0
&&
partner_data
.
is_associated_people
===
"False"
;
}
/**
* A partner can add a shift if:
* - s.he has makeups to do
* - s.he's not an associated partner
* @returns boolean
*/
function
can_select_makeup
()
{
return
partner_data
.
makeups_to_do
>
0
&&
partner_data
.
is_associated_people
===
"False"
;
}
/**
* Proceed to shift exchange or registration
* @param {int} new_shift_id
*/
function
add_or_change_shift
(
new_shift_id
)
{
if
(
is_time_to
(
'change_shift'
))
{
setTimeout
(
openModal
,
100
);
// loading on
tData
=
'idNewShift='
+
new_shift_id
+
'&idPartner='
+
partner_data
.
partner_id
+
'&in_ftop_team='
+
partner_data
.
in_ftop_team
+
'&verif_token='
+
partner_data
.
verif_token
;
if
(
selected_shift
===
null
)
{
tUrl
=
'/shifts/add_shift'
;
}
else
{
tUrl
=
'/shifts/change_shift'
;
tData
=
tData
+
'&idOldShift='
+
selected_shift
.
shift_id
[
0
]
+
'&idRegister='
+
selected_shift
.
id
;
}
$
.
ajax
({
type
:
'POST'
,
url
:
tUrl
,
dataType
:
"json"
,
data
:
tData
,
timeout
:
3000
,
success
:
function
(
data
)
{
if
(
data
.
result
)
{
// Decrement makeups to do if needed
if
(
partner_data
.
makeups_to_do
>
0
)
{
partner_data
.
makeups_to_do
=
parseInt
(
partner_data
.
makeups_to_do
,
10
)
-
1
;
if
(
partner_data
.
makeups_to_do
===
0
)
{
$
(
"#need_to_select_makeups_message"
).
hide
();
}
else
{
$
(
".makeups_nb"
).
text
(
partner_data
.
makeups_to_do
);
}
}
let
msg
=
"Parfait! "
;
msg
+=
(
selected_shift
===
null
)
?
"Le service choisi a été ajouté."
:
"Le service a été échangé."
;
selected_shift
=
null
;
// Refetch partner shifts list & update DOM
load_partner_shifts
(
partner_data
.
concerned_partner_id
)
.
then
(()
=>
{
init_shifts_list
();
closeModal
();
setTimeout
(()
=>
{
alert
(
msg
);
},
100
);
});
// Redraw calendar
calendar
.
refetchEvents
();
}
},
error
:
function
(
error
)
{
closeModal
();
selected_shift
=
null
if
(
error
.
status
===
400
)
{
alert
(
`Désolé ! Le service que vous souhaitez échanger démarre dans moins de 24h. `
+
`Il n'est plus possible de l'échanger.`
);
}
else
{
alert
(
`Une erreur est survenue.`
+
`Il est néanmoins possible que la requête ait abouti,`
+
`veuillez patienter quelques secondes puis vérifier vos services enregistrés.`
);
}
// Refectch shifts anyway, if registration/exchange was still succesful
setTimeout
(()
=>
{
load_partner_shifts
(
partner_data
.
concerned_partner_id
)
.
then
(
init_shifts_list
);
},
300
);
}
});
}
}
function
init_shifts_list
()
{
$
(
".loading-incoming-shifts"
).
hide
();
$
(
"#shifts_list"
).
show
();
...
...
@@ -16,15 +125,52 @@ function init_shifts_list() {
f_date_shift_start
=
f_date_shift_start
.
charAt
(
0
).
toUpperCase
()
+
f_date_shift_start
.
slice
(
1
);
shift_line_template
.
find
(
".shift_line_date"
).
text
(
f_date_shift_start
);
shift_line_template
.
find
(
".shift_line_time"
).
text
(
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
));
shift_line_template
.
find
(
".shift_line_time"
).
text
(
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
,
time_options
));
if
(
!
can_exchange_shifts
())
{
shift_line_template
.
find
(
".selectable_shift_line"
).
removeClass
(
"btn--primary"
);
shift_line_template
.
find
(
".selectable_shift_line"
).
addClass
(
"btn"
);
shift_line_template
.
find
(
".checkbox"
).
prop
(
"disabled"
,
"disabled"
);
}
else
{
shift_line_template
.
find
(
".selectable_shift_line"
).
removeClass
(
"btn"
);
shift_line_template
.
find
(
".selectable_shift_line"
).
addClass
(
"btn--primary"
);
shift_line_template
.
find
(
".checkbox"
).
removeProp
(
"disabled"
);
shift_line_template
.
find
(
".checkbox"
).
prop
(
"value"
,
shift
.
id
);
}
$
(
"#shifts_list"
).
append
(
shift_line_template
.
html
());
}
$
(
".selectable_shift_line"
).
on
(
"click"
,
function
(
e
)
{
if
(
can_exchange_shifts
())
{
let
cb
=
$
(
this
).
find
(
".checkbox"
);
// Select checkbox on click on button
if
(
!
$
(
e
.
target
).
hasClass
(
"checkbox"
))
{
cb
.
prop
(
"checked"
,
!
cb
.
prop
(
"checked"
));
}
if
(
cb
.
prop
(
"checked"
))
{
selected_shift
=
incoming_shifts
.
find
(
s
=>
s
.
id
==
cb
.
prop
(
"value"
));
}
else
{
selected_shift
=
null
;
}
// Unselect other checkboxes
if
(
$
(
this
).
find
(
".checkbox"
).
prop
(
"checked"
))
{
for
(
let
cb_item
of
$
(
"#shifts_list"
).
find
(
".checkbox"
))
{
if
(
cb
.
prop
(
"value"
)
!==
$
(
cb_item
).
prop
(
"value"
))
{
$
(
cb_item
).
prop
(
"checked"
,
false
);
}
}
}
}
});
}
}
function
init_shifts_exchange
()
{
// TODO :
block everything if is_associated_people (or just block actions ?)
// TODO :
loading
if
(
incoming_shifts
!==
null
)
{
init_shifts_list
();
...
...
@@ -33,19 +179,40 @@ function init_shifts_exchange() {
.
then
(
init_shifts_list
);
}
if
(
can_select_makeup
())
{
$
(
".makeups_nb"
).
text
(
partner_data
.
makeups_to_do
);
$
(
"#need_to_select_makeups_message"
).
show
();
}
const
vw
=
window
.
innerWidth
;
const
default_initial_view
=
(
vw
<=
768
)
?
'listWeek'
:
'dayGridMonth'
;
let
default_initial_view
=
""
;
let
header_toolbar
=
{};
if
(
vw
<=
768
)
{
default_initial_view
=
'listWeek'
;
header_toolbar
=
{
left
:
'title'
,
center
:
'dayGridMonth,listWeek,timeGridDay'
,
right
:
'prev,next today'
}
}
else
{
default_initial_view
=
'dayGridMonth'
;
header_toolbar
=
{
left
:
'prev,next today'
,
center
:
'title'
,
right
:
'dayGridMonth,listWeek,timeGridDay'
}
}
const
hidden_days
=
$
.
map
(
days_to_hide
.
split
(
", "
),
Number
);
const
calendarEl
=
document
.
getElementById
(
'calendar'
);
let
calendar
=
new
FullCalendar
.
Calendar
(
calendarEl
,
{
calendar
=
new
FullCalendar
.
Calendar
(
calendarEl
,
{
locale
:
'fr'
,
initialView
:
default_initial_view
,
headerToolbar
:
{
left
:
'prev,next today'
,
center
:
'title'
,
right
:
'dayGridMonth,listWeek,timeGridDay'
headerToolbar
:
header_toolbar
,
buttonText
:
{
list
:
"Semaine"
},
eventTimeFormat
:
{
hour
:
'2-digit'
,
...
...
@@ -55,9 +222,66 @@ function init_shifts_exchange() {
contentHeight
:
"auto"
,
eventDisplay
:
"block"
,
hiddenDays
:
hidden_days
,
events
:
'/shifts/get_list_shift_calendar/'
+
partner_data
.
partner_id
,
events
:
'/shifts/get_list_shift_calendar/'
+
partner_data
.
concerned_
partner_id
,
eventClick
:
function
(
info
)
{
console
.
log
(
info
);
if
(
!
$
(
info
.
el
).
hasClass
(
"shift_booked"
))
{
const
new_shift_id
=
info
.
event
.
id
;
// Set new shift
const
datetime_new_shift
=
info
.
event
.
start
;
let
new_shift_date
=
datetime_new_shift
.
toLocaleDateString
(
"fr-fr"
,
date_options
);
let
new_shift_time
=
datetime_new_shift
.
toLocaleTimeString
(
"fr-fr"
,
time_options
)
if
(
selected_shift
!==
null
&&
can_exchange_shifts
())
{
/* shift exchange */
// Set old shift
let
datetime_old_shift
=
new
Date
(
selected_shift
.
date_begin
);
let
old_shift_date
=
datetime_old_shift
.
toLocaleDateString
(
"fr-fr"
,
date_options
);
let
old_shift_time
=
datetime_old_shift
.
toLocaleTimeString
(
"fr-fr"
,
time_options
)
// Display modal
let
modal_template
=
$
(
"#modal_shift_exchange_template"
);
modal_template
.
find
(
".date_old_shift"
).
text
(
old_shift_date
);
modal_template
.
find
(
".time_old_shift"
).
text
(
old_shift_time
);
modal_template
.
find
(
".date_new_shift"
).
text
(
new_shift_date
);
modal_template
.
find
(
".time_new_shift"
).
text
(
new_shift_time
);
openModal
(
modal_template
.
html
(),
()
=>
{
add_or_change_shift
(
new_shift_id
);
},
"Valider"
);
}
else
if
(
selected_shift
===
null
&&
can_exchange_shifts
())
{
/* could exchange shift but no old shift selected */
openModal
(
"Je dois sélectionner un service à échanger."
,
closeModal
,
"J'ai compris"
);
}
else
if
(
can_select_makeup
())
{
/* choose a makeup service */
let
modal_template
=
$
(
"#modal_add_shift_template"
);
modal_template
.
find
(
".date_new_shift"
).
text
(
new_shift_date
);
modal_template
.
find
(
".time_new_shift"
).
text
(
new_shift_time
);
openModal
(
modal_template
.
html
(),
()
=>
{
add_or_change_shift
(
new_shift_id
);
},
"Valider"
);
}
}
},
eventDidMount
:
function
()
{
if
(
vw
<=
768
)
{
$
(
".fc .fc-header-toolbar"
).
addClass
(
"resp-header-toolbar"
);
}
$
(
"#calendar"
).
show
();
}
});
...
...
members_space/static/js/members-space.js
View file @
056fe5d4
...
...
@@ -8,6 +8,7 @@ var base_location = null,
partner_history
=
null
;
var
date_options
=
{
weekday
:
"long"
,
year
:
"numeric"
,
month
:
"long"
,
day
:
"numeric"
};
var
time_options
=
{
hour
:
'2-digit'
,
minute
:
'2-digit'
};
/* - Data */
...
...
@@ -124,7 +125,7 @@ function prepare_shift_line_template(date_begin) {
f_date_shift_start
=
f_date_shift_start
.
charAt
(
0
).
toUpperCase
()
+
f_date_shift_start
.
slice
(
1
);
shift_line_template
.
find
(
".shift_line_date"
).
text
(
f_date_shift_start
);
shift_line_template
.
find
(
".shift_line_time"
).
text
(
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
));
shift_line_template
.
find
(
".shift_line_time"
).
text
(
datetime_shift_start
.
toLocaleTimeString
(
"fr-fr"
,
time_options
));
return
shift_line_template
;
}
...
...
@@ -190,6 +191,8 @@ function init_my_info_data() {
}
$
(
document
).
ready
(
function
()
{
// TODO essayer de ne charger les js que au besoin
$
.
ajaxSetup
({
headers
:
{
"X-CSRFToken"
:
getCookie
(
'csrftoken'
)
}
});
// If partner is associated (attached), display the pair's main partner shift data
...
...
outils/static/css/common.css
View file @
056fe5d4
...
...
@@ -60,6 +60,11 @@ footer { position: fixed;
margin
:
auto
;
padding
:
15px
;
}
@media
screen
and
(
max-width
:
768px
)
{
.overlay-content
.mconfirm
{
width
:
100%
;
}
}
.overlay-content
.mconfirm
button
{
margin
:
5px
;}
.overlay-content
>
em
{
color
:
#fff
;
...
...
shifts/models.py
View file @
056fe5d4
...
...
@@ -300,3 +300,17 @@ class CagetteShift(models.Model):
def
get_test
(
self
,
odooModel
,
cond
,
fieldsDatas
):
return
self
.
o_api
.
search_read
(
odooModel
,
cond
,
fieldsDatas
,
limit
=
1000
)
def
decrement_makeups_to_do
(
self
,
partner_id
):
""" Decrements partners makeups to do if > 0 """
cond
=
[[
'id'
,
'='
,
partner_id
]]
fields
=
[
'makeups_to_do'
]
makeups_to_do
=
self
.
o_api
.
search_read
(
'res.partner'
,
cond
,
fields
)[
0
][
"makeups_to_do"
]
if
makeups_to_do
>
0
:
makeups_to_do
-=
1
f
=
{
"makeups_to_do"
:
makeups_to_do
}
return
self
.
o_api
.
update
(
'res.partner'
,
partner_id
,
f
)
else
:
return
"makeups already at 0"
\ No newline at end of file
shifts/views.py
View file @
056fe5d4
...
...
@@ -119,9 +119,6 @@ def get_list_shift_calendar(request, partner_id):
smax
=
int
(
value
[
'seats_available'
])
+
int
(
value
[
'seats_reserved'
])
title_prefix
=
''
# if use_new_members_space is True:
# title_prefix = value["name"] + " ~ "
# else:
if
len
(
value
[
'address_id'
])
==
2
and
','
in
value
[
'address_id'
][
1
]:
title_prefix
=
str
(
value
[
'address_id'
][
1
])
.
split
(
","
)[
1
]
+
" -- "
...
...
@@ -131,19 +128,31 @@ def get_list_shift_calendar(request, partner_id):
event
[
"end"
]
=
dateIsoUTC
(
value
[
'date_begin_tz'
])
if
len
(
l
)
>
0
:
event
[
"className"
]
=
"shift_booked"
if
use_new_members_space
is
True
:
event
[
"classNames"
]
=
[
"shift_booked"
]
else
:
event
[
"className"
]
=
"shift_booked"
event
[
"changed"
]
=
False
# elif int(value['seats_reserved']) == int(value['seats_max']):
# event["className"] = "shift_full"
# event["changed"] = False
elif
int
(
value
[
'seats_reserved'
])
==
0
:
event
[
"className"
]
=
"shift_empty"
if
use_new_members_space
is
True
:
event
[
"classNames"
]
=
[
"shift_empty"
]
else
:
event
[
"className"
]
=
"shift_empty"
event
[
"changed"
]
=
True
elif
_is_middled_filled_considered
(
value
[
'seats_reserved'
],
smax
)
is
True
:
event
[
"className"
]
=
"shift_less_alf"
if
use_new_members_space
is
True
:
event
[
"classNames"
]
=
[
"shift_less_alf"
]
else
:
event
[
"className"
]
=
"shift_less_alf"
event
[
"changed"
]
=
True
else
:
event
[
"className"
]
=
"shift_other"
if
use_new_members_space
is
True
:
event
[
"classNames"
]
=
[
"shift_other"
]
else
:
event
[
"className"
]
=
"shift_other"
event
[
"changed"
]
=
True
event
[
"registration_ids"
]
=
value
[
'registration_ids'
]
...
...
@@ -215,18 +224,36 @@ def add_shift(request):
cs
=
CagetteShift
()
if
'idNewShift'
in
request
.
POST
and
'idPartner'
in
request
.
POST
:
data
=
{
"idPartner"
:
int
(
request
.
POST
[
'idPartner'
]),
"idShift"
:
int
(
request
.
POST
[
'idNewShift'
]),
"in_ftop_team"
:
request
.
POST
[
'in_ftop_team'
]}
data
=
{
"idPartner"
:
int
(
request
.
POST
[
'idPartner'
]),
"idShift"
:
int
(
request
.
POST
[
'idNewShift'
]),
"in_ftop_team"
:
request
.
POST
[
'in_ftop_team'
]
}
#Insertion du nouveau shift
st_r_id
=
False
try
:
st_r_id
=
cs
.
set_shift
(
data
)
except
Exception
as
e
:
coop_logger
.
error
(
"Add shift :
%
s,
%
s"
,
str
(
e
),
str
(
data
))
if
st_r_id
:
response
=
{
'result'
:
True
}
else
:
response
=
{
'result'
:
False
}
# decrement makeups_to_do
res_decrement
=
False
try
:
res_decrement
=
cs
.
decrement_makeups_to_do
(
int
(
request
.
POST
[
'idPartner'
]))
except
Exception
as
e
:
coop_logger
.
error
(
"Decrement makeups to do :
%
s,
%
s"
,
str
(
e
),
str
(
data
))
if
res_decrement
:
response
[
"decrement_makeups"
]
=
res_decrement
else
:
response
[
"decrement_makeups"
]
=
False
else
:
response
=
{
'result'
:
False
}
return
JsonResponse
(
response
)
...
...
templates/members_space/index.html
View file @
056fe5d4
...
...
@@ -42,6 +42,16 @@
</div>
</div>
</div>
<div
id=
"modal_shift_exchange_template"
>
<div>
Je suis sur le point d'échanger le service du :
</div>
<div><span
class=
"date_old_shift"
></span>
à
<span
class=
"time_old_shift"
></span></div>
<div>
par celui du :
</div>
<div><span
class=
"date_new_shift"
></span>
à
<span
class=
"time_new_shift"
></span></div>
</div>
<div
id=
"modal_add_shift_template"
>
<div>
Je suis sur le point de m'inscrire au service du :
<span
class=
"date_new_shift"
></span>
à
<span
class=
"time_new_shift"
></span></div>
</div>
</div>
</div>
...
...
templates/members_space/shifts_exchange.html
View file @
056fe5d4
<div
id=
"shifts_exchange"
>
<div
id=
"shifts_exchange_content"
>
<div
id=
"need_to_select_makeups_message"
>
<span
class=
"select_makeups_message_block"
>
<i
class=
"fas fa-exclamation-triangle makeups_warning"
></i>
J'ai
<span
class=
"makeups_nb"
></span>
rattrapage(s) à faire.
</span>
<span
class=
"select_makeups_message_block"
>
Je dois les sélectionner dans le calendrier.
</span>
<span
class=
"select_makeups_message_block"
>
Je ne peux pas échanger de service tant que je n'ai pas choisi mes rattrapages.
</span>
</div>
<div
id=
"partner_shifts_list"
>
Liste de mes services :
<h4>
Liste de mes services :
</h4>
<div
class=
"loading-incoming-shifts"
>
<i
class=
"fas fa-spinner fa-spin fa-lg"
></i>
</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment