refactor: split scan.html js into new static file

Refs: OPS-84
This commit is contained in:
Christoph J. Scherr 2025-03-23 16:30:24 +01:00
parent bf3f70ed2f
commit 959c493a5f
No known key found for this signature in database
GPG key ID: 9EB784BB202BB7BB
2 changed files with 269 additions and 206 deletions

120
senju/static/js/scan.js Normal file
View file

@ -0,0 +1,120 @@
// Get all needed elements
const dropzoneFile = document.getElementById("dropzone-file");
const uploadArea = document.getElementById("upload-area");
const imagePreview = document.getElementById("image-preview");
const previewImg = document.getElementById("preview-img");
const removeImageBtn = document.getElementById("remove-image");
const responseBox = document.getElementById("response-box");
const submitButton = document.getElementById("submit-button");
const errorMessage = document.getElementById("error-message");
const yesButton = document.getElementById("yes-button");
const noButton = document.getElementById("no-button");
const generatingHaikuBox = document.getElementById("generating-haiku-box");
const generatedHaikuBox = document.getElementById("generated-haiku-box");
let imageUploaded = false;
function handleFileSelect(event) {
const file = event.target.files[0];
if (file && file.type.startsWith("image/")) {
// Create a URL for the selected image
const imageUrl = URL.createObjectURL(file);
// Set the image source
previewImg.src = imageUrl;
// Hide upload area and show image preview
uploadArea.classList.add("hidden");
imagePreview.classList.remove("hidden");
errorMessage.classList.add("hidden");
// Set flag that image is uploaded
imageUploaded = true;
}
}
function removeImage() {
dropzoneFile.value = "";
// Hide image
imagePreview.classList.add("hidden");
uploadArea.classList.remove("hidden");
URL.revokeObjectURL(previewImg.src);
previewImg.src = "";
imageUploaded = false;
responseBox.classList.add("opacity-0");
generatingHaikuBox.classList.add("hidden");
setTimeout(() => {
document.getElementById("ai-response").textContent = "Waiting for input...";
}, 500);
}
function handleSubmit() {
if (imageUploaded) {
// Hide error
errorMessage.classList.add("hidden");
// Show response box
responseBox.classList.remove("opacity-0");
// Example response
document.getElementById("ai-response").textContent =
"Dominic Monaghan interviewing Elijah Wood if he will wear wigs";
} else {
errorMessage.classList.remove("hidden");
uploadArea.classList.add("shake");
setTimeout(() => {
uploadArea.classList.remove("shake");
}, 600);
}
}
function handleYesClick() {
// Hide response box
responseBox.classList.add("opacity-0");
// Show generating haiku box first
setTimeout(() => {
responseBox.classList.add("hidden");
generatingHaikuBox.classList.remove("hidden");
// After a delay, hide generating box and show result
setTimeout(() => {
generatingHaikuBox.classList.add("hidden");
generatedHaikuBox.classList.remove("hidden");
}, 3000); // Show loading animation for 3 seconds before revealing the haiku
}, 500); // Wait for response box fade out
}
function handleNoClick() {
// Reset everything
removeImage();
}
dropzoneFile.addEventListener("change", handleFileSelect);
removeImageBtn.addEventListener("click", removeImage);
submitButton.addEventListener("click", handleSubmit);
yesButton.addEventListener("click", handleYesClick);
noButton.addEventListener("click", handleNoClick);
// Add some CSS animation
document.head.insertAdjacentHTML(
"beforeend",
`
<style>
@keyframes shake {
0% { transform: translateX(0); }
25% { transform: translateX(-5px); }
50% { transform: translateX(5px); }
75% { transform: translateX(-5px); }
100% { transform: translateX(0); }
}
.shake {
animation: shake 0.5s ease-in-out;
border-color: #ef4444 !important;
}
</style>
`,
);

View file

@ -1,213 +1,156 @@
{% extends "base.html" %} {% extends "base.html" %} {% block content %}
<div
{% block content %} class="flex flex-col items-center justify-center min-h-screen bg-violet-900 text-white p-6"
<!DOCTYPE html> >
<html lang="en"> <div
<head> class="bg-white text-gray-900 p-8 rounded-xl shadow-lg max-w-lg w-full text-center transform transition duration-300 hover:scale-105 mb-8"
<meta charset="UTF-8"> >
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <h1 class="text-3xl font-bold text-violet-700 mb-4">Upload your image</h1>
<title>Image Upload with Preview</title> <!-- File Upload container-->
<script src="https://cdn.tailwindcss.com"></script> <div id="upload-area" class="flex items-center justify-center w-full">
</head> <label
<body> for="dropzone-file"
<div class="flex flex-col items-center justify-center min-h-screen bg-violet-900 text-white p-6"> class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"
<div class="bg-white text-gray-900 p-8 rounded-xl shadow-lg max-w-lg w-full text-center transform transition duration-300 hover:scale-105 mb-8"> >
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<h1 class="text-3xl font-bold text-violet-700 mb-4">Upload your image</h1> <svg
<!-- File Upload container--> class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"
<div id="upload-area" class="flex items-center justify-center w-full"> aria-hidden="true"
<label for="dropzone-file" class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500 dark:hover:bg-gray-600"> xmlns="http://www.w3.org/2000/svg"
<div class="flex flex-col items-center justify-center pt-5 pb-6"> fill="none"
<svg class="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16"> viewBox="0 0 20 16"
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/> >
</svg> <path
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Click to upload</span> or drag and drop</p> stroke="currentColor"
<p class="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p> stroke-linecap="round"
</div> stroke-linejoin="round"
<input id="dropzone-file" type="file" accept="image/*" class="hidden" /> stroke-width="2"
</label> d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
</div> />
</svg>
<!-- Image Preview container--> <p class="mb-2 text-sm text-gray-500 dark:text-gray-400">
<div id="image-preview" class="w-full hidden"> <span class="font-semibold">Click to upload</span> or drag and drop
<div class="relative"> </p>
<img id="preview-img" src="" alt="Preview" class="w-full h-auto rounded-lg"> <p class="text-xs text-gray-500 dark:text-gray-400">
<button id="remove-image" class="absolute top-2 right-2 bg-red-500 text-white rounded-full p-1 hover:bg-red-600" title="Remove image"> SVG, PNG, JPG or GIF (MAX. 800x400px)
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </p>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<!-- Error message -->
<div id="error-message" class="mt-4 text-red-500 hidden">
Please upload an image first.
</div>
<button id="submit-button" type="submit" class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300">
Submit
</button>
</div>
<div id="response-box" class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center opacity-0 transition-opacity duration-500 ease-in-out">
<h2 class="text-2xl font-semibold text-violet-700">AI recognized the following:</h2>
<p id="ai-response" class="text-lg text-gray-700 mt-2 italic">Waiting for input...</p>
<div class="flex justify-center space-x-4">
<button id="yes-button" type="button" class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300">
Generate Haiku
</button>
<button id="no-button" type="button" class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300">
Input new image
</button>
</div>
</div>
<!-- New generating haiku div that appears after "Yes" is clicked -->
<div id="generating-haiku-box" class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center hidden transition-opacity duration-500 ease-in-out">
<h2 class="text-2xl font-semibold text-violet-700">Generating Haiku</h2>
<div class="flex justify-center mt-4">
<div class="loader animate-pulse flex space-x-4">
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
</div>
</div>
<p class="text-lg text-gray-700 mt-4 italic">Creating a beautiful haiku based on your image...</p>
</div>
<div id="generated-haiku-box" class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center hidden transition-opacity duration-500 ease-in-out">
<h2 class="text-2xl font-semibold text-violet-700">Red suit, vents unseen,<br>
Sus behavior, crew unsure,<br>
Vote him, task complete.</h2>
<div class="flex justify-center mt-4">
<div class="loader animate-pulse flex space-x-4">
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
<div class="w-3 h-3 bg-violet-600 rounded-full"></div>
</div>
</div>
<p class="text-lg text-gray-700 mt-4 italic">Creating a beautiful haiku based on your image...</p>
</div> </div>
<input id="dropzone-file" type="file" accept="image/*" class="hidden" />
</label>
</div> </div>
<script> <!-- Image Preview container-->
// Get all needed elements <div id="image-preview" class="w-full hidden">
const dropzoneFile = document.getElementById('dropzone-file'); <div class="relative">
const uploadArea = document.getElementById('upload-area'); <img
const imagePreview = document.getElementById('image-preview'); id="preview-img"
const previewImg = document.getElementById('preview-img'); src=""
const removeImageBtn = document.getElementById('remove-image'); alt="Preview"
const responseBox = document.getElementById('response-box'); class="w-full h-auto rounded-lg"
const submitButton = document.getElementById('submit-button'); />
const errorMessage = document.getElementById('error-message'); <button
const yesButton = document.getElementById('yes-button'); id="remove-image"
const noButton = document.getElementById('no-button'); class="absolute top-2 right-2 bg-red-500 text-white rounded-full p-1 hover:bg-red-600"
const generatingHaikuBox = document.getElementById('generating-haiku-box'); title="Remove image"
const generatedHaikuBox = document.getElementById('generated-haiku-box'); >
let imageUploaded = false; <svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
function handleFileSelect(event) { <!-- Error message -->
const file = event.target.files[0]; <div id="error-message" class="mt-4 text-red-500 hidden">
Please upload an image first.
if (file && file.type.startsWith('image/')) { </div>
// Create a URL for the selected image
const imageUrl = URL.createObjectURL(file);
// Set the image source
previewImg.src = imageUrl;
// Hide upload area and show image preview
uploadArea.classList.add('hidden');
imagePreview.classList.remove('hidden');
errorMessage.classList.add('hidden');
// Set flag that image is uploaded
imageUploaded = true;
}
}
function removeImage() { <button
dropzoneFile.value = ''; id="submit-button"
type="submit"
// Hide image class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300"
imagePreview.classList.add('hidden'); >
uploadArea.classList.remove('hidden'); Submit
</button>
URL.revokeObjectURL(previewImg.src); </div>
previewImg.src = '';
imageUploaded = false;
responseBox.classList.add('opacity-0');
generatingHaikuBox.classList.add('hidden');
setTimeout(() => {
document.getElementById('ai-response').textContent = 'Waiting for input...';
}, 500);
}
function handleSubmit() { <div
if (imageUploaded) { id="response-box"
// Hide error class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center opacity-0 transition-opacity duration-500 ease-in-out"
errorMessage.classList.add('hidden'); >
<h2 class="text-2xl font-semibold text-violet-700">
// Show response box AI recognized the following:
responseBox.classList.remove('opacity-0'); </h2>
<p id="ai-response" class="text-lg text-gray-700 mt-2 italic">
// Example response Waiting for input...
document.getElementById('ai-response').textContent = 'Dominic Monaghan interviewing Elijah Wood if he will wear wigs'; </p>
} else { <div class="flex justify-center space-x-4">
<button
errorMessage.classList.remove('hidden'); id="yes-button"
type="button"
uploadArea.classList.add('shake'); class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300"
setTimeout(() => { >
uploadArea.classList.remove('shake'); Generate Haiku
}, 600); </button>
} <button
} id="no-button"
type="button"
function handleYesClick() { class="mt-6 bg-violet-600 hover:bg-violet-700 text-white font-bold py-2 px-4 rounded transition duration-300"
// Hide response box >
responseBox.classList.add('opacity-0'); Input new image
</button>
// Show generating haiku box first </div>
setTimeout(() => { </div>
responseBox.classList.add('hidden');
generatingHaikuBox.classList.remove('hidden'); <!-- New generating haiku div that appears after "Yes" is clicked -->
<div
// After a delay, hide generating box and show result id="generating-haiku-box"
setTimeout(() => { class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center hidden transition-opacity duration-500 ease-in-out"
generatingHaikuBox.classList.add('hidden'); >
generatedHaikuBox.classList.remove('hidden'); <h2 class="text-2xl font-semibold text-violet-700">Generating Haiku</h2>
}, 3000); // Show loading animation for 3 seconds before revealing the haiku <div class="flex justify-center mt-4">
}, 500); // Wait for response box fade out <div class="loader animate-pulse flex space-x-4">
} function handleNoClick() { <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
// Reset everything <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
removeImage(); <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
} </div>
</div>
dropzoneFile.addEventListener('change', handleFileSelect); <p class="text-lg text-gray-700 mt-4 italic">
removeImageBtn.addEventListener('click', removeImage); Creating a beautiful haiku based on your image...
submitButton.addEventListener('click', handleSubmit); </p>
yesButton.addEventListener('click', handleYesClick); </div>
noButton.addEventListener('click', handleNoClick); <div
id="generated-haiku-box"
// Add some CSS animation class="mt-8 bg-white text-gray-900 p-6 rounded-lg shadow-lg max-w-lg w-full text-center hidden transition-opacity duration-500 ease-in-out"
document.head.insertAdjacentHTML('beforeend', ` >
<style> <h2 class="text-2xl font-semibold text-violet-700">
@keyframes shake { Red suit, vents unseen,<br />
0% { transform: translateX(0); } Sus behavior, crew unsure,<br />
25% { transform: translateX(-5px); } Vote him, task complete.
50% { transform: translateX(5px); } </h2>
75% { transform: translateX(-5px); } <div class="flex justify-center mt-4">
100% { transform: translateX(0); } <div class="loader animate-pulse flex space-x-4">
} <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
.shake { <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
animation: shake 0.5s ease-in-out; <div class="w-3 h-3 bg-violet-600 rounded-full"></div>
border-color: #ef4444 !important; </div>
} </div>
</style> <p class="text-lg text-gray-700 mt-4 italic">
`); Creating a beautiful haiku based on your image...
</script> </p>
</body> </div>
</html> </div>
<script src="/static/js/scan.js"></script>
{% endblock %} {% endblock %}