@saltcorn/reservable 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -2,36 +2,34 @@
2
2
 
3
3
  To use this Plugin, you should create a table for reservations. This table should have the following fields:
4
4
 
5
- * A Date field for the time of the start of the reservation
6
- * An integer field for the duration After reservation in minutes
7
- * Any other fields for the reservation, for instance the email of the person reserving the slot
5
+ - A Date field for the time of the start of the reservation
6
+ - An integer field for the duration of the reservation in minutes
7
+ - Any other fields for the reservation, for instance the email of the person reserving the slot
8
8
 
9
9
  This table also needs to have two views
10
10
 
11
- * A view to create, typically an Edit view obtain the information required for the booking.
11
+ - A view to create, typically an Edit view obtain the information required for the booking.
12
12
  This Edit view does not need to have fields for the date or the duration.
13
- * A confirmation view, typically a Show view, which is displayed to the user on a successful reservation.
13
+ - A confirmation view, typically a Show view, which is displayed to the user on a successful reservation.
14
14
  This may or may not contain Date and duration
15
15
 
16
16
  There may be another table indicating reservable resources. This is optional. If you are building a reservation system
17
- for a single resource, you can leave this out. On the other hand, if you are building a system where multiple resources
18
- need to be tracked for whether they are available, use this to denote the resources that can be taken/available.
17
+ for a single resource, you can leave this out. On the other hand, if you are building a system where multiple resources
18
+ need to be tracked for whether they are available, use this to denote the resources that can be taken/available.
19
19
  To do this, create a foreign key field on your reservations Table to the reservable resource table.
20
20
 
21
21
  You should then create a Reserve view on the reservations table. In the configuration, you specify the fields
22
22
  As explained above. You will also need to configure:
23
23
 
24
- * What services are available. Each service has a name and a duration.
25
- * What the availability is, your opening hours. You specify these in blocks of time and the day of the week.
24
+ - What services are available. Each service has a name and a duration.
25
+ - What the availability is, your opening hours. You specify these in blocks of time and the day of the week.
26
26
 
27
27
  ### Example
28
28
 
29
- Goldilocks and Rapunzel together run the successful GoldiRap Hair Salon. In fact they are so successful that
30
- they must now implement an online booking system. Their opening hours are Monday to Friday 9-12 and 13-17
31
- (they close for lunch) and Saturday 10-13. They offer two services: A haircut taking 30 minutes
29
+ Goldilocks and Rapunzel together run the successful GoldiRap Hair Salon. In fact they are so successful that
30
+ they must now implement an online booking system. Their opening hours are Monday to Friday 9-12 and 13-17
31
+ (they close for lunch) and Saturday 10-13. They offer two services: A haircut taking 30 minutes
32
32
  and a full restyle taking 45 minutes. You can book either Goldilocks or Rapunzel for each of the services.
33
33
 
34
- Here, the reservable resource is the hairdresser with two rows: Goldilocks and Rapunzel. They have two services,
34
+ Here, the reservable resource is the hairdresser with two rows: Goldilocks and Rapunzel. They have two services,
35
35
  haircut and full restyle. The availability can be specified as: Mon-Fri 9-12, Mon-Fri 13-17 and Saturday 10-13.
36
-
37
-
@@ -0,0 +1,186 @@
1
+ const {
2
+ input,
3
+ div,
4
+ text,
5
+ script,
6
+ domReady,
7
+ style,
8
+ button,
9
+ h3,
10
+ ul,
11
+ li,
12
+ form,
13
+ a,
14
+ b,
15
+ } = require("@saltcorn/markup/tags");
16
+
17
+ const View = require("@saltcorn/data/models/view");
18
+ const Workflow = require("@saltcorn/data/models/workflow");
19
+ const Table = require("@saltcorn/data/models/table");
20
+ const Form = require("@saltcorn/data/models/form");
21
+ const Field = require("@saltcorn/data/models/field");
22
+ const {
23
+ InvalidConfiguration,
24
+ isNode,
25
+ isWeb,
26
+ mergeConnectedObjects,
27
+ hashState,
28
+ } = require("@saltcorn/data/utils");
29
+ const {
30
+ link_view,
31
+ stateToQueryString,
32
+ stateFieldsToWhere,
33
+ stateFieldsToQuery,
34
+ readState,
35
+ } = require("@saltcorn/data/plugin-helper");
36
+ const get_state_fields = async (table_id, viewname, { show_view }) => {
37
+ const table = Table.findOne(table_id);
38
+ const table_fields = table.fields;
39
+ return table_fields
40
+ .filter((f) => !f.primary_key)
41
+ .map((f) => {
42
+ const sf = new Field(f);
43
+ sf.required = false;
44
+ return sf;
45
+ });
46
+ };
47
+
48
+ const configuration_workflow = (req) =>
49
+ new Workflow({
50
+ steps: [
51
+ {
52
+ name: req.__("Views"),
53
+ form: async (context) => {
54
+ const table = Table.findOne(context.table_id);
55
+ const fields = table.fields;
56
+
57
+ const reservable_entity_fields = fields.filter((f) => f.is_fkey);
58
+ const show_view_opts = {};
59
+ for (const rfield of reservable_entity_fields) {
60
+ const retable = Table.findOne(rfield.reftable_name);
61
+ const show_views = await View.find_table_views_where(
62
+ retable.id,
63
+ ({ state_fields, viewtemplate, viewrow }) =>
64
+ viewtemplate.runMany &&
65
+ viewrow.name !== context.viewname &&
66
+ state_fields.some((sf) => sf.name === "id")
67
+ );
68
+ show_view_opts[rfield.name] = show_views.map((v) => v.name);
69
+ }
70
+ return new Form({
71
+ fields: [
72
+ {
73
+ name: "reservable_entity_key",
74
+ label: "Key to reservable entity",
75
+ type: "String",
76
+ required: true,
77
+ attributes: {
78
+ options: reservable_entity_fields.map((f) => f.name),
79
+ },
80
+ },
81
+ {
82
+ name: "start_field",
83
+ label: "Start date field",
84
+ type: "String",
85
+ required: true,
86
+ attributes: {
87
+ options: fields
88
+ .filter((f) => f.type.name === "Date")
89
+ .map((f) => f.name),
90
+ },
91
+ },
92
+ {
93
+ name: "end_field",
94
+ label: "End date field",
95
+ type: "String",
96
+ attributes: {
97
+ options: fields
98
+ .filter((f) => f.type.name === "Date")
99
+ .map((f) => f.name),
100
+ },
101
+ },
102
+ {
103
+ name: "duration_field",
104
+ label: "Duration field",
105
+ sublabel: "Integer field holding booked duration in minutes",
106
+ type: "String",
107
+ attributes: {
108
+ options: fields
109
+ .filter((f) => f.type.name === "Integer")
110
+ .map((f) => f.name),
111
+ },
112
+ },
113
+ {
114
+ name: "show_view",
115
+ label: req.__("Single item view"),
116
+ type: "String",
117
+ sublabel:
118
+ req.__("The underlying individual view of each table row") +
119
+ ". " +
120
+ a(
121
+ {
122
+ "data-dyn-href": `\`/viewedit/config/\${show_view}\``,
123
+ target: "_blank",
124
+ },
125
+ req.__("Configure")
126
+ ),
127
+ required: true,
128
+ attributes: {
129
+ calcOptions: ["reservable_entity_key", show_view_opts],
130
+ },
131
+ },
132
+ ],
133
+ });
134
+ },
135
+ },
136
+ ],
137
+ });
138
+
139
+ const run = async (
140
+ table_id,
141
+ viewname,
142
+ { reservable_entity_key, start_field, end_field, duration_field, show_view },
143
+ state,
144
+ extraArgs
145
+ ) => {
146
+ const restable = Table.findOne({ id: table_id });
147
+ const resfields = restable.getFields();
148
+
149
+ const refield = restable.getField(reservable_entity_key);
150
+ const retable = Table.findOne(refield.reftable_name);
151
+
152
+ const state_res = { ...state };
153
+
154
+ readState(state_res, restable.fields);
155
+
156
+ //get reservations
157
+ const reswhere = await stateFieldsToWhere({
158
+ fields: resfields,
159
+ state: state_res,
160
+ table: restable,
161
+ });
162
+
163
+ const ress = await restable.getRows(reswhere);
164
+ const resEnts = new Set(ress.map((r) => r[reservable_entity_key]));
165
+
166
+ const sview = await View.findOne({ name: show_view });
167
+ if (!sview)
168
+ throw new InvalidConfiguration(
169
+ `View ${viewname} incorrectly configured: cannot find view ${show_view}`
170
+ );
171
+ const srespAll = await sview.runMany(state, extraArgs);
172
+ const srespsAvailable = [];
173
+ for (const sresp of srespAll) {
174
+ if (!resEnts.has(sresp.row[retable.pk_name])) srespsAvailable.push(sresp);
175
+ }
176
+ const showRow = (r) => r.html;
177
+ return div(srespsAvailable.map(showRow));
178
+ };
179
+
180
+ module.exports = {
181
+ name: "Available Resources Feed",
182
+ display_state_form: false,
183
+ get_state_fields,
184
+ configuration_workflow,
185
+ run,
186
+ };
package/index.js CHANGED
@@ -167,7 +167,8 @@ const configuration_workflow = () =>
167
167
  });
168
168
 
169
169
  const get_state_fields = async (table_id, viewname, { show_view }) => {
170
- const table_fields = await Field.find({ table_id });
170
+ const table = Table.findOne(table_id);
171
+ const table_fields = table.fields;
171
172
  return table_fields.map((f) => {
172
173
  const sf = new Field(f);
173
174
  sf.required = false;
@@ -525,6 +526,7 @@ module.exports = {
525
526
  run,
526
527
  runPost,
527
528
  },
529
+ require("./available-feed"),
528
530
  ],
529
531
  };
530
532
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saltcorn/reservable",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Reservable resources",
5
5
  "main": "index.js",
6
6
  "dependencies": {
@@ -18,5 +18,21 @@
18
18
  },
19
19
  "scripts": {
20
20
  "test": "jest"
21
+ },
22
+ "eslintConfig": {
23
+ "extends": "eslint:recommended",
24
+ "parserOptions": {
25
+ "ecmaVersion": 2020
26
+ },
27
+ "env": {
28
+ "node": true,
29
+ "es6": true
30
+ },
31
+ "rules": {
32
+ "no-unused-vars": "off",
33
+ "no-case-declarations": "off",
34
+ "no-empty": "warn",
35
+ "no-fallthrough": "warn"
36
+ }
21
37
  }
22
38
  }