Skip to content

Commit 8e48d5f

Browse files
committed
Edit commission metadata, possibleStatuses variable, disableFieldEditing variable, update schema, clear changes, bug fixes
1 parent f4b00fa commit 8e48d5f

6 files changed

Lines changed: 211 additions & 69 deletions

File tree

README.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ commtrackr.init({ // Initialize CommTracker with configurations
7272
admin: [2] // Access levels for admins
7373
},
7474
commissions: 'commissions', // req.session object variable for user commissions array
75+
possibleStatuses: [ // Possible commission status strings
76+
{
77+
label: 'Completed', // Status label
78+
value: 'Completed' // Status value
79+
}, {
80+
label: 'In Progress',
81+
value: 'In Progress'
82+
}, {
83+
label: 'On Hold',
84+
value: 'On Hold'
85+
}, {
86+
label: 'Cancelled',
87+
value: 'Cancelled'
88+
}
89+
],
90+
disableFieldEditing: ['amount', 'currency'] // Array of field IDs that admins cannot edit
7591
},
7692
fields: [
7793
{
@@ -102,7 +118,9 @@ commtrackr.init({ // Initialize CommTracker with configurations
102118
// This function is called when a commission is updated
103119
// You can implement your own logic here, such as saving to a database
104120
// data contains the updated commission object
105-
// Action metadata can be accessed via data.updatedAt and data.updatedBy
121+
// The constant data.id contains the unique commission ID
122+
// Action metadata can be accessed via data.updatedAt, data.updatedBy, and data.sendEmail
123+
// Updated metadata can be accessed via data.user, data.amount, data.currency, data.date, data.status, and data.locked
106124
// Updated fields can be accessed via data.fields
107125
},
108126
sync: (req) => {
@@ -220,3 +238,27 @@ Session Example:
220238
}
221239
]
222240
```
241+
242+
### possibleStatuses
243+
244+
`possibleStatuses` should be an array of possible commission status objects. Each status object should have a label and value property.
245+
246+
Type: `Array`
247+
248+
Default:
249+
250+
```javascript
251+
[]
252+
```
253+
254+
### disableFieldEditing
255+
256+
`disableFieldEditing` should be an array of field IDs that admins cannot edit. This is useful for restricting access to certain fields even for admin users.
257+
258+
Type: `Array`
259+
260+
Default:
261+
262+
```javascript
263+
[]
264+
```

frontend/pages/edit.ejs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,62 @@
11
<%- include('head.ejs') %>
22
<img src="<%= tenant.logo %>" alt="<%= tenant.name %>">
33
<h1><%= title %></h1>
4+
<% if (!vars.disableFieldEditing.includes('status')) { %>
5+
<div class="inputField active" id="status">
6+
<h3>Status*</h3>
7+
<p>Required: Commission status.</p>
8+
<select id="statusSelect">
9+
<% vars.possibleStatuses.forEach(option => { %>
10+
<option value="<%= option.value %>" <%= (option.value === commission.status) ? 'selected' : '' %>><%= option.label %></option>
11+
<% }) %>
12+
</select>
13+
</div>
14+
<% } %>
15+
<% if (!vars.disableFieldEditing.includes('owner')) { %>
16+
<div class="inputField active" id="owner">
17+
<h3>Owner*</h3>
18+
<p>Required: Commission owner.</p>
19+
<input id="ownerInput" type="text" placeholder="<%= commission.user %>" value="<%= commission.user %>">
20+
</div>
21+
<% } %>
22+
<% if (!vars.disableFieldEditing.includes('amount')) { %>
23+
<div class="inputField active" id="amount">
24+
<h3>Amount</h3>
25+
<p>Optional: Commission amount.</p>
26+
<input id="amountInput" type="number" step="0.01" placeholder="<%= (commission.amount !== null) ? commission.amount : '' %>" value="<%= (commission.amount !== null) ? commission.amount : '' %>">
27+
</div>
28+
<% } %>
29+
<% if (!vars.disableFieldEditing.includes('currency')) { %>
30+
<div class="inputField active" id="currency">
31+
<h3>Currency</h3>
32+
<p>Optional: Commission currency.</p>
33+
<input id="currencyInput" type="text" placeholder="<%= (commission.currency !== null) ? commission.currency : '' %>" value="<%= (commission.currency !== null) ? commission.currency : '' %>">
34+
</div>
35+
<% } %>
36+
<% if (!vars.disableFieldEditing.includes('date')) { %>
37+
<div class="inputField active" id="date">
38+
<h3>Date</h3>
39+
<p>Optional: Commission creation date.</p>
40+
<input id="dateInput" type="datetime-local" placeholder="<%= (commission.date !== null) ? new Date(commission.date).toISOString().slice(0,16) : '' %>" value="<%= (commission.date !== null) ? new Date(commission.date).toISOString().slice(0,16) : '' %>">
41+
</div>
42+
<% } %>
43+
<% if (!vars.disableFieldEditing.includes('locked')) { %>
44+
<div class="inputField active" id="locked">
45+
<h3>Locked</h3>
46+
<p>Optional: Whether or not the commission is locked from user editing.</p>
47+
<input id="lockedInput" type="checkbox" class="hidden" <%= commission.locked ? 'checked' : '' %>>
48+
<div class="checkbox" change="lockedInput">|||</div>
49+
</div>
50+
<% } %>
451
<% fields.forEach(field => { %>
552
<div class="inputField active <%= field.required ? 'required' : '' %>" id="<%= field.id %>">
653
<h3><%= field.label %><%= field.required ? '*' : '' %></h3>
754
<p><%= field.required ? 'Required' : 'Optional' %><%= field.description ? ': ' + field.description : '' %></p>
855
<% if (field.type === 'textarea') { %>
9-
<textarea id="<%= field.id %>Textarea" placeholder="<%= field.placeholder || '' %>"></textarea>
56+
<textarea id="<%= field.id %>Textarea" placeholder="<%= field.placeholder || '' %>" <%= field.required ? 'required' : '' %>></textarea>
1057
<% } else if (field.type === 'checkbox') { %>
1158
<input id="<%= field.id %>Input" type="checkbox" class="hidden">
12-
<div class="checkbox" change="cms">|||</div>
59+
<div class="checkbox" change="<%= field.id %>Input">|||</div>
1360
<% } else if (field.type === 'radio') { %>
1461
<div class="radioOptions">
1562
<% if (!field.required) { %>
@@ -35,16 +82,22 @@
3582
<% }) %>
3683
</select>
3784
<% } else { %>
38-
<input id="<%= field.id %>Input" type="<%= field.type %>" placeholder="<%= field.placeholder || '' %>">
85+
<input id="<%= field.id %>Input" type="<%= field.type %>" placeholder="<%= field.placeholder || '' %>" <%= field.required ? 'required' : '' %>>
3986
<% } %>
4087
</div>
4188
<% }) %>
89+
<div class="inputField active" id="sendEmail">
90+
<h3>Send Email</h3>
91+
<p>Optional: Send an email to the user regarding this update.</p>
92+
<input id="sendEmailInput" type="checkbox" class="hidden">
93+
<div class="checkbox" change="sendEmailInput">|||</div>
94+
</div>
4295
<div class="buttons">
4396
<% if (!commission.locked || (role !== 'user')) { %><div id="save" class="button">Save</div><% } %>
4497
<a id="error" class="button active hidden" href="<%= tenant.domain %><%= tenant.path %>/<%= commission.id %>/edit">Restart</a>
4598
</div>
4699
<script>
47-
const commissionId = '<%= commission.id %>';
48-
const restore = <%- JSON.stringify(commission.fields) %>;
100+
const commissionId = '<%= commission.id %>';
101+
const restore = <%- JSON.stringify(commission.fields) %>;
49102
</script>
50103
<%- include('foot.ejs') %>

frontend/pages/foot.ejs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
<% if (typeof tenant !== 'undefined') { %>
33
<p class="fixed">© <%= new Date().getFullYear() %> CommTrackr</p>
44
<p class="fixed2">Changes saved</p>
5-
<p class="fixed3">← Back</p>
5+
<p class="fixed3">Changes restored</p>
6+
<p class="fixed4">← Back</p>
7+
<p class="fixed5">Clear changes</p>
68
<% } else { %>
79
<p class="fixed">Internal error</p>
810
<% } %>

frontend/public/scripts.js

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ document.getElementById('start')?.addEventListener('click', start);
1414
document.getElementById('create')?.addEventListener('click', create);
1515
document.getElementById('sync')?.addEventListener('click', sync);
1616
document.getElementById('save')?.addEventListener('click', save);
17+
document.querySelector('.fixed5')?.addEventListener('click', clearChanges);
1718

1819
function anim_in() {
1920
document.querySelector('main').classList.remove('out');
@@ -28,54 +29,46 @@ function anim_out() {
2829
window.onload = function () {
2930
anim_in();
3031
if (document.referrer && (document.referrer !== window.location.href)) {
31-
document.querySelector('.fixed3').classList.add('visible');
32-
document.querySelector('.fixed3').addEventListener('click', function () {
32+
document.querySelector('.fixed4').classList.add('visible');
33+
document.querySelector('.fixed4').addEventListener('click', function () {
3334
window.history.back();
3435
});
3536
};
36-
document.querySelectorAll('.inputField').forEach(field => {
37-
field.querySelectorAll('input:not([type="radio"]), textarea').forEach(input => {
38-
if (localStorage.getItem(field.id)) {
39-
input.value = localStorage.getItem(field.id);
40-
document.querySelector('.fixed2').classList.add('visible');
41-
setTimeout(() => {
42-
document.querySelector('.fixed2').classList.remove('visible');
43-
}, 2000);
44-
};
45-
});
46-
field.querySelectorAll('.checkbox').forEach(checkbox => {
47-
if (localStorage.getItem(field.id) === 'true') {
48-
checkbox.classList.add('checked');
49-
const input = field.querySelector('input[type="checkbox"]');
50-
input.checked = true;
51-
document.querySelector('.fixed2').classList.add('visible');
52-
setTimeout(() => {
53-
document.querySelector('.fixed2').classList.remove('visible');
54-
}, 2000);
55-
};
56-
});
57-
field.querySelectorAll('.radioOption').forEach(radio => {
58-
const input = radio.querySelector('input[type="radio"]');
59-
if (localStorage.getItem(field.id) === input.value) {
60-
field.querySelectorAll('.radioOption input[type="radio"]').forEach(r => r.checked = false);
61-
input.checked = true;
62-
document.querySelector('.fixed2').classList.add('visible');
63-
setTimeout(() => {
64-
document.querySelector('.fixed2').classList.remove('visible');
65-
}, 2000);
66-
};
67-
});
68-
field.querySelectorAll('select').forEach(select => {
69-
if (localStorage.getItem(field.id)) {
70-
select.value = localStorage.getItem(field.id);
71-
document.querySelector('.fixed2').classList.add('visible');
72-
setTimeout(() => {
73-
document.querySelector('.fixed2').classList.remove('visible');
74-
}, 2000);
75-
};
37+
const pathStorage = JSON.parse(localStorage.getItem(window.location.href.replace(appPath, '')) || '{}');
38+
if (pathStorage && (Object.keys(pathStorage).length > 0)) {
39+
document.querySelectorAll('.inputField').forEach(field => {
40+
field.querySelectorAll('input:not([type="radio"]), textarea').forEach(input => {
41+
if (pathStorage[field.id]) {
42+
input.value = pathStorage[field.id];
43+
};
44+
});
45+
field.querySelectorAll('.checkbox').forEach(checkbox => {
46+
if ((pathStorage[field.id] === true) || (pathStorage[field.id] === 'true')) {
47+
checkbox.classList.add('checked');
48+
const input = field.querySelector('input[type="checkbox"]');
49+
input.checked = true;
50+
};
51+
});
52+
field.querySelectorAll('.radioOption').forEach(radio => {
53+
const input = radio.querySelector('input[type="radio"]');
54+
if (pathStorage[field.id] === input.value) {
55+
field.querySelectorAll('.radioOption input[type="radio"]').forEach(r => r.checked = false);
56+
input.checked = true;
57+
};
58+
});
59+
field.querySelectorAll('select').forEach(select => {
60+
if (pathStorage[field.id]) {
61+
select.value = pathStorage[field.id];
62+
};
63+
});
7664
});
77-
});
78-
if (restore) {
65+
document.querySelector('.fixed3').classList.add('visible');
66+
setTimeout(() => {
67+
document.querySelector('.fixed3').classList.remove('visible');
68+
document.querySelector('.fixed5').classList.add('visible');
69+
}, 2000);
70+
};
71+
if (typeof restore === 'object' && (Object.keys(restore).length > 0)) {
7972
Object.keys(restore).forEach(key => {
8073
const field = document.getElementById(key);
8174
if (field) {
@@ -207,9 +200,12 @@ function back() {
207200
};
208201
};
209202

210-
function saveChange(key, value) {
211-
localStorage.setItem(key, value);
212-
console.log(key, value)
203+
function saveChange(path, key, value) {
204+
var pathChanges = JSON.parse(localStorage.getItem(path) || '{}');
205+
pathChanges[key] = value;
206+
localStorage.setItem(path, JSON.stringify(pathChanges));
207+
console.log(key, value, path, pathChanges);
208+
document.querySelector('.fixed5').classList.remove('visible');
213209
document.querySelector('.fixed2').classList.add('visible');
214210
setTimeout(() => {
215211
document.querySelector('.fixed2').classList.remove('visible');
@@ -223,28 +219,28 @@ document.querySelectorAll('.inputField').forEach(field => {
223219
event.preventDefault();
224220
next();
225221
};
226-
setTimeout(() => saveChange(field.id, input.value), 100);
222+
setTimeout(() => saveChange(window.location.href.replace(appPath, ''), field.id, input.value), 100);
227223
});
228224
});
229225
field.querySelectorAll('.checkbox').forEach(checkbox => {
230226
checkbox.addEventListener('click', function () {
231227
checkbox.classList.toggle('checked');
232228
const input = field.querySelector('input[type="checkbox"]');
233229
input.checked = !input.checked;
234-
setTimeout(() => saveChange(field.id, input.checked), 100);
230+
setTimeout(() => saveChange(window.location.href.replace(appPath, ''), field.id, input.checked), 100);
235231
});
236232
});
237233
field.querySelectorAll('.radioOption').forEach(radio => {
238234
radio.addEventListener('change', function () {
239235
field.querySelectorAll('.radioOption input[type="radio"]').forEach(r => r.checked = false);
240236
const input = radio.querySelector('input[type="radio"]');
241237
input.checked = true;
242-
setTimeout(() => saveChange(field.id, input.value), 100);
238+
setTimeout(() => saveChange(window.location.href.replace(appPath, ''), field.id, input.value), 100);
243239
});
244240
});
245241
field.querySelectorAll('select').forEach(select => {
246242
select.addEventListener('change', function () {
247-
setTimeout(() => saveChange(field.id, select.selectedOptions[0].value), 100);
243+
setTimeout(() => saveChange(window.location.href.replace(appPath, ''), field.id, select.selectedOptions[0].value), 100);
248244
});
249245
});
250246
});
@@ -272,7 +268,7 @@ async function create() {
272268
if (input) {
273269
if (input.type === 'checkbox') {
274270
data[field.id] = input.checked;
275-
} if (input.type === 'radio') {
271+
} else if (input.type === 'radio') {
276272
const radios = field.querySelectorAll('input[type="radio"]');
277273
radios.forEach(radio => {
278274
if (radio.checked) data[field.id] = radio.value;
@@ -298,7 +294,7 @@ async function create() {
298294
field.classList.remove('active');
299295
});
300296
document.getElementById('success').classList.remove('hidden');
301-
localStorage.clear();
297+
localStorage.removeItem(window.location.href.replace(appPath, ''));
302298
backDisabled = true;
303299
document.querySelector('.inner h1').innerText = 'Creation successful';
304300
document.querySelector('.inner p').innerText = result.message || 'Your commission was created successfully.';
@@ -351,7 +347,8 @@ async function save() {
351347
if (input) {
352348
if (input.type === 'checkbox') {
353349
data[field.id] = input.checked;
354-
} if (input.type === 'radio') {
350+
console.log(field.id, input.checked)
351+
} else if (input.type === 'radio') {
355352
const radios = field.querySelectorAll('input[type="radio"]');
356353
radios.forEach(radio => {
357354
if (radio.checked) data[field.id] = radio.value;
@@ -373,6 +370,7 @@ async function save() {
373370
backDisabled = true;
374371
document.getElementById('save').classList.add('hidden');
375372
if (result.status === 'success') {
373+
localStorage.removeItem(window.location.href.replace(appPath, ''));
376374
window.location.href = `${appPath}/${commissionId}`;
377375
} else {
378376
document.querySelectorAll('.inputField').forEach(field => {
@@ -398,4 +396,10 @@ function shake(element) {
398396
setTimeout(() => {
399397
element.classList.remove('shake');
400398
}, 500);
399+
};
400+
401+
function clearChanges() {
402+
localStorage.removeItem(window.location.href.replace(appPath, ''));
403+
document.querySelector('.fixed5').classList.remove('visible');
404+
window.location.reload();
401405
};

0 commit comments

Comments
 (0)