Skip to content

Commit e257133

Browse files
committed
monet ux improvements
1 parent 081379c commit e257133

File tree

12 files changed

+271
-60
lines changed

12 files changed

+271
-60
lines changed

server.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def valid_recipient(recipient):
5959
"""
6060
Checks if the recipient is valid.
6161
"""
62-
valid_recipients = ['legal', 'devcon', 'esp', 'security', 'oleh']
62+
valid_recipients = ['legal', 'devcon', 'security']
6363
return recipient in valid_recipients
6464

6565
def get_identifier(recipient, now=None, randint=None):
@@ -247,9 +247,6 @@ def submit():
247247
reference = data.get('reference', '')
248248
files = data['files']
249249

250-
if not message:
251-
raise ValueError('Error: empty message!')
252-
253250
if not valid_recipient(recipient):
254251
raise ValueError('Error: Invalid recipient!')
255252

@@ -269,7 +266,7 @@ def submit():
269266

270267
send_email(message)
271268

272-
notice = f'Thank you! The relevant team was notified of your submission. You could use the following identifier to refer to it in correspondence: <b>{identifier}</b>'
269+
notice = f'Thank you! The relevant team was notified of your submission. Please record the identifier and refer to it in correspondence: {identifier}'
273270

274271
return jsonify({'status': 'success', 'message': notice})
275272

static/android-chrome-192x192.png

11 KB
Loading

static/android-chrome-512x512.png

27.2 KB
Loading

static/apple-touch-icon.png

9.75 KB
Loading

static/css/style.css

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,74 @@ textarea#text { width: 760px; height: 200px; }
99
.result-message {line-height: 200%;}
1010
#add-file-button { margin-bottom: 15px; background-color: green; color: white; }
1111
.dropzone .dz-progress { display: none; }
12-
.dropzone .dz-preview .dz-remove { margin-top: 15px; color: #b70d0d; }
12+
.dropzone .dz-preview .dz-remove { margin-top: 15px; color: #b70d0d; }
13+
14+
/* Success message styling */
15+
.pure-form-message.success-message {
16+
color: #1b1b1b; /* Black text instead of gray */
17+
}
18+
19+
/* Legal identifier styling */
20+
.legal-identifier {
21+
display: inline-block;
22+
background-color: #f0f0f0;
23+
border: 1px solid #d0d0d0;
24+
border-radius: 4px;
25+
padding: 4px 8px;
26+
font-family: 'Courier New', Courier, monospace;
27+
font-size: 18px;
28+
margin-left: 4px;
29+
color: #000;
30+
font-weight: 500;
31+
letter-spacing: 0.5px;
32+
user-select: all; /* Makes it easy to select the entire identifier */
33+
}
34+
35+
.legal-identifier:hover {
36+
background-color: #e8e8e8;
37+
border-color: #b0b0b0;
38+
}
39+
40+
/* Error message styling */
41+
.error-message {
42+
background-color: #fee;
43+
border: 1px solid #fcc;
44+
border-radius: 4px;
45+
color: #c00;
46+
padding: 12px 16px;
47+
margin: 10px 0;
48+
font-size: 16px;
49+
line-height: 1.4;
50+
}
51+
52+
.error-message:before {
53+
content: "⚠ ";
54+
font-size: 20px;
55+
vertical-align: middle;
56+
}
57+
58+
/* Required field indicator */
59+
.required {
60+
color: #c00;
61+
font-weight: bold;
62+
}
63+
64+
/* Reference help text styling */
65+
.reference-help {
66+
display: block;
67+
margin-top: 5px;
68+
font-size: 14px;
69+
color: #666;
70+
line-height: 1.4;
71+
}
72+
73+
/* Reference container styling */
74+
#referenceContainer {
75+
margin-bottom: 10px;
76+
}
77+
78+
/* Reference input field styling */
79+
#reference {
80+
width: 100%;
81+
min-width: 300px;
82+
}

static/favicon-16x16.png

595 Bytes
Loading

static/favicon-32x32.png

1.29 KB
Loading

static/favicon.ico

15 KB
Binary file not shown.

static/js/app.js

Lines changed: 179 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
1+
// Function to display error messages
2+
function showError(message) {
3+
const errorDiv = document.getElementById('error-message');
4+
if (errorDiv) {
5+
errorDiv.innerHTML = message;
6+
errorDiv.style.display = 'block';
7+
// Auto-hide after 10 seconds
8+
setTimeout(() => {
9+
errorDiv.style.display = 'none';
10+
}, 10000);
11+
} else {
12+
// Fallback to alert if error div doesn't exist
13+
alert(message);
14+
}
15+
}
16+
17+
// Function to hide error messages
18+
function hideError() {
19+
const errorDiv = document.getElementById('error-message');
20+
if (errorDiv) {
21+
errorDiv.style.display = 'none';
22+
}
23+
}
24+
125
Dropzone.options.dropzoneArea = {
2-
maxFilesize: 5, // Max file size per file
26+
maxFilesize: 15, // Max file size per file in MB
327
maxFiles: 10, // Max number of files
428
url: '/fake',
529
paramName: 'attachment',
@@ -8,28 +32,52 @@ Dropzone.options.dropzoneArea = {
832
addRemoveLinks: true,
933
uploadMultiple: true,
1034
dictDefaultMessage: 'You may drop files here to upload',
35+
dictFileTooBig: 'File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.',
36+
dictMaxFilesExceeded: 'You can only upload a maximum of {{maxFiles}} files.',
1137
init: function() {
38+
var dropzone = this;
39+
1240
this.on("addedfile", function(file) {
13-
// Calculate the total added file size
14-
var totalSize = this.files.reduce(function(total, f) {
15-
return total + f.size;
16-
}, 0);
17-
18-
// If the total added file size is greater than 15 MB, remove the file
19-
if (totalSize > 15 * 1024 * 1024) {
41+
hideError(); // Clear any existing errors
42+
43+
// Check individual file size
44+
if (file.size > 15 * 1024 * 1024) {
45+
this.removeFile(file);
46+
showError(`Error: File "${file.name}" is too large (${(file.size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 15MB.`);
47+
return;
48+
}
49+
50+
// Calculate the total added file size
51+
var totalSize = this.files.reduce(function(total, f) {
52+
return total + f.size;
53+
}, 0);
54+
55+
// If the total added file size is greater than 15 MB, remove the file
56+
if (totalSize > 15 * 1024 * 1024) {
57+
this.removeFile(file);
58+
showError(`Error: Total file size would exceed the 15MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`);
59+
}
60+
});
61+
62+
this.on("maxfilesexceeded", function(file) {
2063
this.removeFile(file);
21-
alert("Total file size exceeded the limit of 15 MB, file cannot be added.");
22-
}
64+
showError(`Error: You can only upload a maximum of ${this.options.maxFiles} files.`);
2365
});
2466

2567
this.on("removedfile", function(file) {
26-
// Calculate the total added file size
27-
var totalSize = this.files.reduce(function(total, f) {
28-
return total + f.size;
29-
}, 0);
30-
31-
// Log the total added file size
32-
console.log("Total file size: " + totalSize);
68+
hideError(); // Clear errors when file is removed
69+
// Calculate the total added file size
70+
var totalSize = this.files.reduce(function(total, f) {
71+
return total + f.size;
72+
}, 0);
73+
74+
// Log the total added file size
75+
console.log("Total file size: " + (totalSize / 1024 / 1024).toFixed(2) + "MB");
76+
});
77+
78+
this.on("error", function(file, errorMessage) {
79+
this.removeFile(file);
80+
showError(`Error: ${errorMessage}`);
3381
});
3482
}
3583
};
@@ -43,33 +91,57 @@ function initDropzone() {
4391
addRemoveLinks: true,
4492
uploadMultiple: true,
4593
dictDefaultMessage: 'You may drop files here to upload',
46-
maxFilesize: 15, // Max file size per file
94+
dictFileTooBig: 'File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.',
95+
dictMaxFilesExceeded: 'You can only upload a maximum of {{maxFiles}} files.',
96+
maxFilesize: 15, // Max file size per file in MB
4797
maxFiles: 10, // Max number of files
4898
init: function() {
49-
this.on("addedfile", function(file) {
50-
// Calculate the total added file size
51-
var totalSize = this.files.reduce(function(total, f) {
52-
return total + f.size;
53-
}, 0);
99+
var dropzone = this;
54100

55-
// If the total added file size is greater than 15 MB, remove the file
56-
if (totalSize > 15 * 1024 * 1024) {
57-
this.removeFile(file);
58-
alert("Total file size exceeded the limit of 15 MB.");
59-
}
60-
});
61-
62-
this.on("removedfile", function(file) {
63-
// Calculate the total added file size
64-
var totalSize = this.files.reduce(function(total, f) {
65-
return total + f.size;
66-
}, 0);
101+
this.on("addedfile", function(file) {
102+
hideError(); // Clear any existing errors
103+
104+
// Check individual file size
105+
if (file.size > 15 * 1024 * 1024) {
106+
this.removeFile(file);
107+
showError(`Error: File "${file.name}" is too large (${(file.size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 15MB.`);
108+
return;
109+
}
110+
111+
// Calculate the total added file size
112+
var totalSize = this.files.reduce(function(total, f) {
113+
return total + f.size;
114+
}, 0);
115+
116+
// If the total added file size is greater than 15 MB, remove the file
117+
if (totalSize > 15 * 1024 * 1024) {
118+
this.removeFile(file);
119+
showError(`Error: Total file size would exceed the 15MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`);
120+
}
121+
});
67122

68-
// Log the total added file size
69-
console.log("Total file size: " + totalSize);
70-
});
123+
this.on("maxfilesexceeded", function(file) {
124+
this.removeFile(file);
125+
showError(`Error: You can only upload a maximum of ${this.options.maxFiles} files.`);
126+
});
127+
128+
this.on("removedfile", function(file) {
129+
hideError(); // Clear errors when file is removed
130+
// Calculate the total added file size
131+
var totalSize = this.files.reduce(function(total, f) {
132+
return total + f.size;
133+
}, 0);
134+
135+
// Log the total added file size
136+
console.log("Total file size: " + (totalSize / 1024 / 1024).toFixed(2) + "MB");
137+
});
138+
139+
this.on("error", function(file, errorMessage) {
140+
this.removeFile(file);
141+
showError(`Error: ${errorMessage}`);
142+
});
71143
}
72-
});
144+
});
73145
}
74146

75147

@@ -122,10 +194,25 @@ document.addEventListener('DOMContentLoaded', function() {
122194
recipientLabel.style.visibility = 'hidden';
123195
};
124196

125-
// Custom text for ESP recipient only
197+
// Handle reference field visibility based on recipient
126198
recipient.addEventListener("change", function() {
127-
messageLabel.innerHTML = (recipient.value == "esp") ? "Please include Grant ID in the message. Example: \"FY22-0123\":" : "Message:";
199+
const referenceContainer = document.getElementById("referenceContainer");
200+
const referenceInput = document.getElementById("reference");
201+
202+
if (recipient.value === "security") {
203+
// Hide reference field for Security
204+
referenceContainer.style.display = "none";
205+
referenceInput.removeAttribute("required");
206+
referenceInput.value = ""; // Clear the value
207+
} else {
208+
// Show reference field for Legal and Devcon
209+
referenceContainer.style.display = "block";
210+
referenceInput.setAttribute("required", "required");
211+
}
128212
});
213+
214+
// Trigger change event on page load to set initial state
215+
recipient.dispatchEvent(new Event('change'));
129216

130217
// Redirect clicks on a greed button
131218
const addFileButton = document.getElementById('add-file-button');
@@ -140,9 +227,46 @@ document.addEventListener('DOMContentLoaded', function() {
140227
// Multi file upload meets encryption
141228
document.forms[0].addEventListener("submit", function(evt) {
142229
evt.preventDefault();
143-
captchaExpired(); // disable the submit button this way to prevent double submission
230+
hideError(); // Clear any existing errors
144231

232+
// Validate form before submission
145233
const selectedFiles = Dropzone.instances[0].files || [];
234+
235+
// Check if reference is required and empty
236+
const referenceInput = document.getElementById("reference");
237+
const recipient = document.getElementById("recipientSelect");
238+
if (recipient.value !== "security" && !referenceInput.value.trim()) {
239+
showError("Error: Please enter a Reference ID before submitting.");
240+
referenceInput.focus();
241+
return false;
242+
}
243+
244+
// Check number of files
245+
if (selectedFiles.length > 10) {
246+
showError(`Error: Too many files selected. You can only upload a maximum of 10 files. Currently selected: ${selectedFiles.length}`);
247+
return false;
248+
}
249+
250+
// Check total file size
251+
const totalSize = selectedFiles.reduce(function(total, file) {
252+
return total + file.size;
253+
}, 0);
254+
255+
if (totalSize > 15 * 1024 * 1024) {
256+
showError(`Error: Total file size exceeds the 15MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`);
257+
return false;
258+
}
259+
260+
// Check individual file sizes
261+
for (let i = 0; i < selectedFiles.length; i++) {
262+
if (selectedFiles[i].size > 15 * 1024 * 1024) {
263+
showError(`Error: File "${selectedFiles[i].name}" is too large (${(selectedFiles[i].size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 15MB.`);
264+
return false;
265+
}
266+
}
267+
268+
captchaExpired(); // disable the submit button this way to prevent double submission
269+
146270
dataArray = { message: '', files: [], requiredChunks: selectedFiles.length+1, receivedChunks: 0 };
147271

148272
encrypt(text.value).then(acceptEncryptedData);
@@ -217,5 +341,17 @@ async function postData(url = '/', data = {}) {
217341
function displayResult(status, message) {
218342
const formElement = document.getElementById("submission-form");
219343
const statusText = (status == "success") ? "Success!" : "Error";
220-
formElement.innerHTML = `<fieldset><legend>${statusText}</legend><span class='pure-form-message'>${message}</span><br><br><span class='pure-form-message'><a href="#" onclick="location.reload()">Send one more submission</a></span></fieldset>`
344+
345+
// If success message, format the identifier specially
346+
if (status === "success" && message.includes("Please record the identifier")) {
347+
// Extract the identifier (format: recipient:YYYY:MM:DD:HH:MM:SS:XXXX)
348+
const identifierMatch = message.match(/([a-zA-Z]+:\d{4}:\d{2}:\d{2}:\d{2}:\d{2}:\d{2}:\d{4})$/);
349+
if (identifierMatch) {
350+
const identifier = identifierMatch[1];
351+
const messageWithoutId = message.substring(0, message.lastIndexOf(identifier)).trim();
352+
message = `${messageWithoutId} <span class="legal-identifier">${identifier}</span>`;
353+
}
354+
}
355+
356+
formElement.innerHTML = `<fieldset><legend>${statusText}</legend><span class='pure-form-message ${status === "success" ? "success-message" : ""}'>${message}</span><br><br><span class='pure-form-message'><a href="#" onclick="location.reload()">Send one more submission</a></span></fieldset>`
221357
}

static/site.webmanifest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

0 commit comments

Comments
 (0)