@saltcorn/reservable 0.1.4 → 0.2.1
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 +114 -54
- package/package.json +1 -1
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,24 @@ 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
|
+
state,
|
|
400
|
+
table,
|
|
401
|
+
})
|
|
402
|
+
)
|
|
384
403
|
)
|
|
385
404
|
)
|
|
386
405
|
)
|
|
@@ -388,6 +407,17 @@ const run = async (
|
|
|
388
407
|
);
|
|
389
408
|
};
|
|
390
409
|
|
|
410
|
+
//https://stackoverflow.com/a/74206554
|
|
411
|
+
function groupArray(array, num) {
|
|
412
|
+
const group = [];
|
|
413
|
+
|
|
414
|
+
for (let i = 0; i < array.length; i += num) {
|
|
415
|
+
group.push(array.slice(i, i + num));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return group;
|
|
419
|
+
}
|
|
420
|
+
|
|
391
421
|
const reserve_btn = ({
|
|
392
422
|
viewname,
|
|
393
423
|
date,
|
|
@@ -396,26 +426,48 @@ const reserve_btn = ({
|
|
|
396
426
|
req,
|
|
397
427
|
reservable_entity_key,
|
|
398
428
|
entity_wanted,
|
|
429
|
+
available,
|
|
430
|
+
state,
|
|
431
|
+
table,
|
|
399
432
|
}) =>
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
433
|
+
available
|
|
434
|
+
? form(
|
|
435
|
+
{ action: `/view/${viewname}`, method: "post", class: "d-inline" },
|
|
436
|
+
input({ type: "hidden", name: "_csrf", value: req.csrfToken() }),
|
|
437
|
+
input({ type: "hidden", name: "date", value: date.toISOString() }),
|
|
438
|
+
input({ type: "hidden", name: "serviceIx", value: serviceIx }),
|
|
439
|
+
entity_wanted &&
|
|
440
|
+
input({
|
|
441
|
+
type: "hidden",
|
|
442
|
+
name: reservable_entity_key,
|
|
443
|
+
value: entity_wanted,
|
|
444
|
+
}),
|
|
445
|
+
table.fields
|
|
446
|
+
.filter((f) => f.name !== reservable_entity_key && state[f.name])
|
|
447
|
+
.map((f) =>
|
|
448
|
+
input({
|
|
449
|
+
type: "hidden",
|
|
450
|
+
name: f.name,
|
|
451
|
+
value: state[f.name],
|
|
452
|
+
})
|
|
453
|
+
),
|
|
454
|
+
input({ type: "hidden", name: "step", value: "getReservationForm" }),
|
|
412
455
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
456
|
+
button(
|
|
457
|
+
{ type: "submit", class: "btn btn-primary mt-2 me-2" },
|
|
458
|
+
localeTime(date)
|
|
459
|
+
// `${hour}:${String(minute).padStart(2, "0")}`
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
: button(
|
|
463
|
+
{
|
|
464
|
+
type: "button",
|
|
465
|
+
disabled: true,
|
|
466
|
+
class: "btn btn-secondary mt-2 me-2",
|
|
467
|
+
},
|
|
468
|
+
localeTime(date)
|
|
469
|
+
// `${hour}:${String(minute).padStart(2, "0")}`
|
|
470
|
+
);
|
|
419
471
|
const getReservationForm = async ({ table, viewname, config, body, req }) => {
|
|
420
472
|
const {
|
|
421
473
|
view_to_create,
|
|
@@ -431,7 +483,11 @@ const getReservationForm = async ({ table, viewname, config, body, req }) => {
|
|
|
431
483
|
const form = await getForm(table, viewname, columns, layout, null, req);
|
|
432
484
|
form.hidden(start_field, duration_field, "step");
|
|
433
485
|
if (reservable_entity_key) form.hidden(reservable_entity_key);
|
|
434
|
-
|
|
486
|
+
table.fields
|
|
487
|
+
.filter((f) => f.name !== reservable_entity_key && body[f.name])
|
|
488
|
+
.forEach((f) => {
|
|
489
|
+
form.hidden(f.name);
|
|
490
|
+
});
|
|
435
491
|
return form;
|
|
436
492
|
};
|
|
437
493
|
const makeReservation = async ({ table, viewname, config, body, req, res }) => {
|
|
@@ -501,7 +557,11 @@ const runPost = async (
|
|
|
501
557
|
[config.reservable_entity_key]: +body[config.reservable_entity_key],
|
|
502
558
|
step: "makeReservation",
|
|
503
559
|
};
|
|
504
|
-
|
|
560
|
+
table.fields
|
|
561
|
+
.filter((f) => f.name !== config.reservable_entity_key && body[f.name])
|
|
562
|
+
.forEach((f) => {
|
|
563
|
+
form.values[f.name] = body[f.name];
|
|
564
|
+
});
|
|
505
565
|
res.sendWrap(viewname, renderForm(form, req.csrfToken()));
|
|
506
566
|
} else if (body.step === "makeReservation") {
|
|
507
567
|
return await makeReservation({
|