@saltcorn/reservable 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +91 -52
- package/package.json +1 -1
- package/validate.js +4 -2
package/index.js
CHANGED
|
@@ -105,6 +105,12 @@ const configuration_workflow = () =>
|
|
|
105
105
|
options: show_view_opts,
|
|
106
106
|
},
|
|
107
107
|
},
|
|
108
|
+
{
|
|
109
|
+
name: "btn_columns",
|
|
110
|
+
label: "Button columns",
|
|
111
|
+
type: "Integer",
|
|
112
|
+
default: 1,
|
|
113
|
+
},
|
|
108
114
|
new FieldRepeat({
|
|
109
115
|
name: "availability",
|
|
110
116
|
fields: [
|
|
@@ -254,7 +260,7 @@ const get_available_slots = async ({
|
|
|
254
260
|
available_slots[i] = false;
|
|
255
261
|
}
|
|
256
262
|
});
|
|
257
|
-
return { available_slots, from, durGCD };
|
|
263
|
+
return { available_slots, from, durGCD, taken_slots };
|
|
258
264
|
};
|
|
259
265
|
|
|
260
266
|
const run = async (
|
|
@@ -266,6 +272,7 @@ const run = async (
|
|
|
266
272
|
duration_field,
|
|
267
273
|
availability,
|
|
268
274
|
services,
|
|
275
|
+
btn_columns,
|
|
269
276
|
},
|
|
270
277
|
state,
|
|
271
278
|
{ req, res }
|
|
@@ -280,37 +287,44 @@ const run = async (
|
|
|
280
287
|
}
|
|
281
288
|
|
|
282
289
|
const date = state.day ? new Date(state.day) : new Date(); //todo from state
|
|
283
|
-
const { available_slots, from, durGCD } =
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
const { available_slots, from, durGCD, taken_slots } =
|
|
291
|
+
await get_available_slots({
|
|
292
|
+
table,
|
|
293
|
+
availability,
|
|
294
|
+
date,
|
|
295
|
+
entity_wanted,
|
|
296
|
+
reservable_entity_key,
|
|
297
|
+
services,
|
|
298
|
+
start_field,
|
|
299
|
+
duration_field,
|
|
300
|
+
});
|
|
293
301
|
const minSlot = Math.min(...Object.keys(available_slots));
|
|
294
302
|
const maxSlot = Math.max(...Object.keys(available_slots));
|
|
295
303
|
const service_availabilities = services.map((service, serviceIx) => {
|
|
296
304
|
const nslots = service.duration / durGCD;
|
|
297
305
|
const availabilities = [];
|
|
298
306
|
for (let i = minSlot; i <= maxSlot; i++) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const hour = Math.floor(mins_since_midnight / 60);
|
|
307
|
+
const mins_since_midnight = i * durGCD;
|
|
308
|
+
const hour = Math.floor(mins_since_midnight / 60);
|
|
302
309
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
const minute = mins_since_midnight - hour * 60;
|
|
311
|
+
const date1 = new Date(date);
|
|
312
|
+
date1.setHours(hour);
|
|
313
|
+
date1.setMinutes(minute);
|
|
314
|
+
date1.setSeconds(0);
|
|
315
|
+
date1.setMilliseconds(0);
|
|
316
|
+
if (date1 > new Date())
|
|
317
|
+
if (range(nslots, i).every((j) => available_slots[j])) {
|
|
310
318
|
availabilities.push({
|
|
311
319
|
date: date1,
|
|
320
|
+
available: true,
|
|
312
321
|
});
|
|
313
|
-
|
|
322
|
+
} else {
|
|
323
|
+
availabilities.push({
|
|
324
|
+
date: date1,
|
|
325
|
+
available: false,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
314
328
|
}
|
|
315
329
|
//console.log({ availabilities, service });
|
|
316
330
|
return { availabilities, service, serviceIx };
|
|
@@ -368,19 +382,22 @@ const run = async (
|
|
|
368
382
|
)
|
|
369
383
|
),
|
|
370
384
|
service_availabilities.map(({ availabilities, service, serviceIx }) =>
|
|
371
|
-
|
|
385
|
+
ul(
|
|
372
386
|
h3(service.title || `${service.duration} minutes`),
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
387
|
+
groupArray(availabilities, btn_columns || 1).map((gavail) =>
|
|
388
|
+
div(
|
|
389
|
+
gavail.map(({ date, available }) =>
|
|
390
|
+
reserve_btn({
|
|
391
|
+
viewname,
|
|
392
|
+
service,
|
|
393
|
+
date,
|
|
394
|
+
serviceIx,
|
|
395
|
+
req,
|
|
396
|
+
entity_wanted,
|
|
397
|
+
reservable_entity_key,
|
|
398
|
+
available,
|
|
399
|
+
})
|
|
400
|
+
)
|
|
384
401
|
)
|
|
385
402
|
)
|
|
386
403
|
)
|
|
@@ -388,6 +405,17 @@ const run = async (
|
|
|
388
405
|
);
|
|
389
406
|
};
|
|
390
407
|
|
|
408
|
+
//https://stackoverflow.com/a/74206554
|
|
409
|
+
function groupArray(array, num) {
|
|
410
|
+
const group = [];
|
|
411
|
+
|
|
412
|
+
for (let i = 0; i < array.length; i += num) {
|
|
413
|
+
group.push(array.slice(i, i + num));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return group;
|
|
417
|
+
}
|
|
418
|
+
|
|
391
419
|
const reserve_btn = ({
|
|
392
420
|
viewname,
|
|
393
421
|
date,
|
|
@@ -396,26 +424,37 @@ const reserve_btn = ({
|
|
|
396
424
|
req,
|
|
397
425
|
reservable_entity_key,
|
|
398
426
|
entity_wanted,
|
|
427
|
+
available,
|
|
399
428
|
}) =>
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
429
|
+
available
|
|
430
|
+
? form(
|
|
431
|
+
{ action: `/view/${viewname}`, method: "post", class: "d-inline" },
|
|
432
|
+
input({ type: "hidden", name: "_csrf", value: req.csrfToken() }),
|
|
433
|
+
input({ type: "hidden", name: "date", value: date.toISOString() }),
|
|
434
|
+
input({ type: "hidden", name: "serviceIx", value: serviceIx }),
|
|
435
|
+
entity_wanted &&
|
|
436
|
+
input({
|
|
437
|
+
type: "hidden",
|
|
438
|
+
name: reservable_entity_key,
|
|
439
|
+
value: entity_wanted,
|
|
440
|
+
}),
|
|
441
|
+
input({ type: "hidden", name: "step", value: "getReservationForm" }),
|
|
412
442
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
443
|
+
button(
|
|
444
|
+
{ type: "submit", class: "btn btn-primary mt-2 me-2" },
|
|
445
|
+
localeTime(date)
|
|
446
|
+
// `${hour}:${String(minute).padStart(2, "0")}`
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
: button(
|
|
450
|
+
{
|
|
451
|
+
type: "button",
|
|
452
|
+
disabled: true,
|
|
453
|
+
class: "btn btn-secondary mt-2 me-2",
|
|
454
|
+
},
|
|
455
|
+
localeTime(date)
|
|
456
|
+
// `${hour}:${String(minute).padStart(2, "0")}`
|
|
457
|
+
);
|
|
419
458
|
const getReservationForm = async ({ table, viewname, config, body, req }) => {
|
|
420
459
|
const {
|
|
421
460
|
view_to_create,
|
package/package.json
CHANGED
package/validate.js
CHANGED
|
@@ -35,7 +35,8 @@ module.exports = {
|
|
|
35
35
|
//get all relevant reservations
|
|
36
36
|
|
|
37
37
|
const ress = await table.getRows({
|
|
38
|
-
[reservable_entity_key]:
|
|
38
|
+
[reservable_entity_key]:
|
|
39
|
+
row[reservable_entity_key]?.id || row[reservable_entity_key],
|
|
39
40
|
[start_field]: { lt: row[end_field], equal: true, day_only: true },
|
|
40
41
|
[end_field]: { gt: row[start_field], equal: true, day_only: true },
|
|
41
42
|
...(valid_field ? { [valid_field]: true } : {}),
|
|
@@ -45,7 +46,8 @@ module.exports = {
|
|
|
45
46
|
const refield = table.getField(reservable_entity_key);
|
|
46
47
|
const retable = Table.findOne(refield.reftable_name);
|
|
47
48
|
const entity = await retable.getRow({
|
|
48
|
-
[retable.pk_name]:
|
|
49
|
+
[retable.pk_name]:
|
|
50
|
+
row[reservable_entity_key]?.id || row[reservable_entity_key],
|
|
49
51
|
});
|
|
50
52
|
//check that for every day, there is availablity
|
|
51
53
|
const from = new Date(row[start_field]);
|