Skip to content

Commit 4a280e5

Browse files
committed
Fields, response saving, keyboard navigation
1 parent 62da496 commit 4a280e5

6 files changed

Lines changed: 242 additions & 14 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ commtrackr.init({ // Initialize CommTracker with configurations
7272
},
7373
commissions: 'commissions', // req.session object variable for user commissions array
7474
},
75+
fields: [
76+
{
77+
id: 'name', // Unique identifier for the field
78+
type: 'text', // Field type (e.g., text, number, date, textarea, checkbox)
79+
label: 'Website Name', // Field label
80+
description: 'The name of the website or project.', // Field description
81+
placeholder: 'e.g. My Website', // Placeholder text for the field
82+
required: true // Whether the field is required
83+
},
84+
],
7585
});
7686

7787
app.listen(3000, () => {

frontend/pages/foot.ejs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
</div>
22
<p class="fixed"<%= new Date().getFullYear() %> CommTrackr</p>
3+
<p class="fixed2">Changes saved</p>
34
</main>
45
<script src="<%= tenant.domain %><%= tenant.path %>/scripts.js"></script>
56
</body>

frontend/pages/user.ejs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,24 @@
22
<img src="<%= tenant.logo %>" alt="<%= tenant.name %>">
33
<h1>New Commission</h1>
44
<p>Estimated time to complete: 2 minutes</p>
5+
<% var step = 1; fields.forEach(field => { %>
6+
<div class="inputField" id="<%= field.id %>" step="<%= step %>">
7+
<h3><%= field.label %><%= field.required ? '*' : '' %></h3>
8+
<p><%= field.required ? 'Required' : 'Optional' %><%= field.description ? ': ' + field.description : '' %></p>
9+
<% if (field.type === 'textarea') { %>
10+
<textarea placeholder="<%= field.placeholder || '' %>"></textarea>
11+
<% } else if (field.type === 'checkbox') { %>
12+
<input type="checkbox" class="hidden">
13+
<div class="checkbox" change="cms">|||</div>
14+
<% } else { %>
15+
<input type="<%= field.type %>" placeholder="<%= field.placeholder || '' %>">
16+
<% } %>
17+
</div>
18+
<% step++; }) %>
519
<div class="buttons">
6-
<div class="button hidden">Back</div>
7-
<div class="button active hidden">Next</div>
8-
<div class="button active" onclick="start();">Start</div>
20+
<div id="back" class="button hidden" onclick="back();">Back</div>
21+
<div id="next" class="button active hidden" onclick="next();">Next</div>
22+
<div id="start" class="button active" onclick="start();">Start</div>
23+
<div id="create" class="button active hidden" onclick="create();">Create</div>
924
</div>
1025
<%- include('foot.ejs') %>

frontend/public/scripts.js

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
document.querySelectorAll('.button[href]').forEach(button => button.addEventListener('click', function(event) {
1+
document.querySelectorAll('.button[href]').forEach(button => button.addEventListener('click', function (event) {
22
event.preventDefault();
33
anim_out();
44
setTimeout(() => {
@@ -16,9 +16,129 @@ function anim_out() {
1616
document.querySelector('main').classList.add('out');
1717
};
1818

19-
document.onload = anim_in();
19+
window.onload = function () {
20+
anim_in();
21+
document.querySelectorAll('.inputField').forEach(field => {
22+
field.querySelectorAll('input, textarea').forEach(input => {
23+
if (localStorage.getItem(field.id)) {
24+
input.value = localStorage.getItem(field.id);
25+
document.querySelector('.fixed2').classList.add('visible');
26+
setTimeout(() => {
27+
document.querySelector('.fixed2').classList.remove('visible');
28+
}, 2000);
29+
};
30+
});
31+
field.querySelectorAll('.checkbox').forEach(checkbox => {
32+
if (localStorage.getItem(field.id) === 'true') {
33+
checkbox.classList.add('checked');
34+
const input = field.querySelector('input[type="checkbox"]');
35+
input.checked = true;
36+
document.querySelector('.fixed2').classList.add('visible');
37+
setTimeout(() => {
38+
document.querySelector('.fixed2').classList.remove('visible');
39+
}, 2000);
40+
};
41+
});
42+
});
43+
};
2044

2145
// anim_out();
2246
// setTimeout(() => {
2347
// anim_in();
24-
// }, 750)
48+
// }, 750)
49+
50+
var step = 0;
51+
52+
function start() {
53+
next();
54+
document.getElementById('start').classList.add('hidden');
55+
document.getElementById('next').classList.remove('hidden');
56+
};
57+
58+
function next() {
59+
if (step < document.querySelectorAll('.inputField').length) {
60+
document.querySelector(`.inputField[step="${step}"]`)?.classList.remove('active');
61+
step++;
62+
document.querySelector(`.inputField[step="${step}"]`)?.classList.add('active');
63+
} else {
64+
document.getElementById('next')?.classList.add('hidden');
65+
document.getElementById('create')?.classList.remove('hidden');
66+
};
67+
if (step >= document.querySelectorAll('.inputField').length) {
68+
document.getElementById('next')?.classList.add('hidden');
69+
document.getElementById('create')?.classList.remove('hidden');
70+
};
71+
if (step === 1) {
72+
document.getElementById('back')?.classList.add('hidden');
73+
} else {
74+
document.getElementById('back')?.classList.remove('hidden');
75+
};
76+
};
77+
78+
function back() {
79+
if (step > 1) {
80+
document.querySelector(`.inputField[step="${step}"]`)?.classList.remove('active');
81+
step--;
82+
document.querySelector(`.inputField[step="${step}"]`)?.classList.add('active');
83+
} else {
84+
document.getElementById('back')?.classList.add('hidden');
85+
};
86+
if (step < document.querySelectorAll('.inputField').length) document.getElementById('next')?.classList.remove('hidden');
87+
if (step === 1) {
88+
document.getElementById('back')?.classList.add('hidden');
89+
} else {
90+
document.getElementById('back')?.classList.remove('hidden');
91+
};
92+
document.getElementById('create')?.classList.add('hidden');
93+
};
94+
95+
document.querySelectorAll('.inputField').forEach(field => {
96+
field.querySelectorAll('input, textarea').forEach(input => {
97+
input.addEventListener('keydown', function (event) {
98+
if (event.key === 'Enter') {
99+
event.preventDefault();
100+
next();
101+
};
102+
localStorage.setItem(field.id, input.value);
103+
document.querySelector('.fixed2').classList.add('visible');
104+
setTimeout(() => {
105+
document.querySelector('.fixed2').classList.remove('visible');
106+
}, 2000);
107+
});
108+
});
109+
field.querySelectorAll('.checkbox').forEach(checkbox => {
110+
checkbox.addEventListener('click', function () {
111+
checkbox.classList.toggle('checked');
112+
const input = field.querySelector('input[type="checkbox"]');
113+
input.checked = !input.checked;
114+
localStorage.setItem(field.id, input.checked);
115+
document.querySelector('.fixed2').classList.add('visible');
116+
setTimeout(() => {
117+
document.querySelector('.fixed2').classList.remove('visible');
118+
}, 2000);
119+
});
120+
});
121+
});
122+
123+
document.addEventListener('keydown', function (event) {
124+
if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
125+
event.preventDefault();
126+
console.log('Step:', step);
127+
if (step === 0) {
128+
start();
129+
} else {
130+
next();
131+
};
132+
} else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
133+
event.preventDefault();
134+
back();
135+
};
136+
});
137+
138+
function create() {
139+
localStorage.clear();
140+
};
141+
142+
// progress bar
143+
// radio input
144+
// select input

frontend/public/styles.css

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ h1 {
3232
line-height: min(50px, 12.5vw);
3333
}
3434

35+
h3 {
36+
font-weight: 400;
37+
}
38+
3539
p {
3640
opacity: 0.75;
3741
font-size: 15px;
@@ -69,6 +73,7 @@ code {
6973
letter-spacing: -0.09px;
7074
text-decoration: none;
7175
cursor: pointer;
76+
user-select: none;
7277
transition: 0.25s;
7378
}
7479

@@ -89,6 +94,49 @@ code {
8994
opacity: 0.5;
9095
}
9196

97+
input,
98+
textarea {
99+
border: none;
100+
width: -webkit-fill-available;
101+
max-width: 500px;
102+
min-height: 45px;
103+
background: transparent;
104+
border-bottom: 1px solid #ffffff;
105+
outline: none;
106+
}
107+
108+
input.hidden,
109+
textarea.hidden {
110+
display: none;
111+
}
112+
113+
input {
114+
font-size: 20px;
115+
}
116+
117+
textarea {
118+
font-size: 15px;
119+
}
120+
121+
.checkbox {
122+
display: inline-flex;
123+
justify-content: center;
124+
align-items: center;
125+
width: fit-content;
126+
height: fit-content;
127+
padding: 5px 10px;
128+
border: 1px solid #ffffff;
129+
border-radius: 3px;
130+
cursor: pointer;
131+
user-select: none;
132+
transition: 0.25s;
133+
}
134+
135+
.checkbox.checked {
136+
background: #ffffff;
137+
color: #161616;
138+
}
139+
92140
main {
93141
display: flex;
94142
padding: min(100px, 10vh) 0;
@@ -110,15 +158,28 @@ main {
110158
left: 30px;
111159
}
112160

161+
> .fixed2 {
162+
position: absolute;
163+
bottom: 25px;
164+
right: 30px;
165+
opacity: 0;
166+
transition: 0.25s;
167+
}
168+
169+
> .fixed2.visible {
170+
opacity: 1;
171+
transition: 0.25s;
172+
}
173+
113174
.inner {
114175
display: flex;
115176
flex-direction: column;
116177
align-items: flex-start;
117178
justify-content: center;
118-
align-self: center;
179+
align-self: center;
119180
gap: 10px;
120-
width: 80%;
121-
max-width: 1000px;
181+
width: 80%;
182+
max-width: 1000px;
122183
height: 100%;
123184
position: relative;
124185
transition-timing-function: ease-in-out;
@@ -128,6 +189,24 @@ main {
128189
width: 50px;
129190
}
130191
}
192+
193+
.inputField {
194+
display: flex;
195+
flex-direction: column;
196+
gap: 7.5px;
197+
width: -webkit-fill-available;
198+
margin-top: 5px;
199+
max-height: 200px;
200+
transition: 0.25s;
201+
}
202+
203+
.inputField:not(.active) {
204+
margin-top: -10px;
205+
max-height: 0px;
206+
overflow: hidden;
207+
opacity: 0;
208+
transition: 0.25s;
209+
}
131210
}
132211

133212
@keyframes out {

index.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ app.use(rateLimit({
2424
let on = false;
2525
let tenant = {};
2626
let vars = {};
27+
let fields = [];
2728
let returnHandler = null;
2829

2930
function init({
@@ -50,6 +51,7 @@ function init({
5051
access: {},
5152
commissions: 'commissions'
5253
},
54+
fields: newFields = [],
5355
handler: newHandler = null
5456
}) {
5557
tenant = {
@@ -76,8 +78,9 @@ function init({
7678
access: {},
7779
commissions: 'commissions',
7880
...newVars
79-
},
80-
returnHandler = newHandler;
81+
};
82+
fields = newFields;
83+
returnHandler = newHandler;
8184
};
8285

8386
function activate(isOn = true) {
@@ -120,13 +123,13 @@ app.get('/', async (req, res) => {
120123
console.log('Role:', getUserRole(req.session));
121124
switch (getUserRole(req.session)) {
122125
case 'admin':
123-
res.render('admin', { tenant, title: 'Admin - ' });
126+
res.render('admin', { tenant, title: 'Admin View - ' });
124127
break;
125128
case 'dev':
126-
res.render('dev', { tenant, title: 'Developer - ' });
129+
res.render('dev', { tenant, title: 'Developer View - ' });
127130
break;
128131
default:
129-
res.render('user', { tenant, title: 'User - ' });
132+
res.render('user', { tenant, title: 'New Commission - ', fields });
130133
break;
131134
};
132135
});

0 commit comments

Comments
 (0)