eversync/notes.html

305 lines
No EOL
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Eversync</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
{% load static %}
<link rel="icon" href="{% static 'favicon.ico' %}">
<link rel="stylesheet" href="{% static 'index-style.css' %}" ">
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js"></script>
</head>
</head>
<body>
<div class="header">
<div class="header-content">
<a href="/"><img src="{% static 'eversync2.png' %}" alt="Eversync Logo" style="height: 80px; margin-right: 10px; display: flex; align-items: center; gap: 5px;"></a>
<a href="/" class="logo" >eversync</a>
<div class="nav-links" style="position: relative;">
<div class="dropdown">
<button class="dropdown-toggle" style="background: none; border: none; color: white; font-size: 16px; cursor: pointer;">
Welcome, {{ user.username }} <i class="fas fa-caret-down"></i>
</button>
<div class="dropdown-menu" style="display: none; position: absolute; right: 0; background-color: #333; border: 1px solid #444; border-radius: 4px; padding: 10px; width: 184px;">
<form action="{% url 'manage' %}" method="post" style="margin: 0;">
{% csrf_token %}
<button type="submit" class="logout-button" style="background-color: transparent; color: white; border: none; cursor: pointer;">Manage Account</button>
</form>
<form action="{% url 'logoutz' %}" method="post" style="margin: 0;">
{% csrf_token %}
<button type="submit" class="logout-button" style="background-color: transparent; color: white; border: none; cursor: pointer;">Log Out</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
async function fetchNotes() {
const response = await fetch("/note_list");
const notes = await response.json();
const container = document.getElementById("notes-container");
container.innerHTML = "";
notes.forEach(note => {
const noteDiv = document.createElement("div");
noteDiv.innerHTML = `
<h3>${note.title}</h3>
<p>${note.content}</p>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px;">
<small>Created at: ${note.time}</small>
<button class='delete-note-btn' data-id='${note.id}' style='background-color: #e74c3c; color: white; border: none; border-radius: 4px; padding: 6px 12px; cursor: pointer;'>Delete</button>
</div>
`;
container.appendChild(noteDiv);
});
document.querySelectorAll('.delete-note-btn').forEach(btn => {
btn.addEventListener('click', async function() {
if (confirm('Are you sure you want to delete this note?')) {
const noteId = this.getAttribute('data-id');
const response = await fetch(`/note_delete/${noteId}/`, {
method: 'POST',
headers: {
'X-CSRFToken': getCSRFToken(),
'Content-Type': 'application/x-www-form-urlencoded'
}
});
if (response.ok) {
fetchNotes();
} else {
alert('Failed to delete note');
}
}
});
});
}
async function addNote(event) {
event.preventDefault();
const title = document.getElementById("title").value;
const content = document.getElementById("content").value;
const response = await fetch("/note_add/", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"X-CSRFToken": getCSRFToken()
},
body: `title=${encodeURIComponent(title)}&content=${encodeURIComponent(content)}`
});
if (response.status === 201) {
document.getElementById("note-form").reset();
fetchNotes();
} else {
alert("Failed to create note");
}
}
function getCSRFToken() {
const name = "csrftoken=";
const decodedCookie = decodeURIComponent(document.cookie);
const cookies = decodedCookie.split(";");
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.indexOf(name) === 0) {
return cookie.substring(name.length, cookie.length);
}
}
return "";
}
let recognition;
let userManuallyStopped = false;
function startVoiceRecognition() {
userManuallyStopped = false;
if (!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) {
alert("Your browser does not support speech recognition.");
return;
}
document.getElementById('voiceModal').style.display = 'block';
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SpeechRecognition();
recognition.lang = 'en-US';
recognition.continuous = true;
recognition.interimResults = true;
recognition.maxAlternatives = 1;
let finalTranscript = '';
recognition.onresult = function(event) {
let finalTranscript = '';
for (let i = event.resultIndex; i < event.results.length; ++i) {
if (event.results[i].isFinal) {
finalTranscript += event.results[i][0].transcript + ' ';
}
}
if (finalTranscript) {
document.getElementById('content').value += finalTranscript;
}
};
recognition.onerror = function(event) {
console.warn("Speech recognition error:", event.error);
if (!userManuallyStopped && event.error === "no-speech") {
setTimeout(() => recognition.start(), 500); // retry after brief pause
} else {
closeVoiceModal();
}
};
recognition.onend = function() {
if (!userManuallyStopped) {
console.log("Recognition ended. Restarting...");
recognition.start(); // Keep listening forever
}
};
recognition.start();
}
function stopVoiceRecognition() {
if (recognition) {
userManuallyStopped = true;
recognition.stop();
closeVoiceModal();
}
}
async function performOCR() {
const fileInput = document.getElementById('ocrImageInput');
if (!fileInput.files.length) {
alert("Please select an image first.");
return;
}
const image = fileInput.files[0];
const reader = new FileReader();
reader.onload = async function () {
const result = await Tesseract.recognize(reader.result, 'eng');
document.getElementById('content').value += result.data.text;
closeOCRModal();
};
reader.readAsDataURL(image);
}
function closeVoiceModal() {
document.getElementById('voiceModal').style.display = 'none';
}
function showOCRModal() {
document.getElementById('ocrModal').style.display = 'block';
}
function closeOCRModal() {
document.getElementById('ocrModal').style.display = 'none';
}
document.addEventListener("DOMContentLoaded", () => {
fetchNotes();
document.getElementById("note-form").addEventListener("submit", addNote);
});
</script>
<style>
h1 {
text-align: center;
color: #333;
}
form {
margin-bottom: 30px;
text-align: center;
}
input, textarea {
width: 80%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1em;
}
button {
padding: 10px 20px;
font-size: 1em;
background-color: #ffec99;
border: 1px solid #e0c97f;
border-radius: 5px;
cursor: pointer;
}
#notes-container > div {
background-color: #fff6cc;
border: 2px solid #ffe066;
border-radius: 10px;
padding: 15px;
box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
max-width: 500px;
margin: 0 auto 10px auto;
}
#notes-container h3 {
margin-top: 0;
}
</style>
</head>
<body>
<h1>Your Notes</h1>
<form id="note-form">
<input type="text" id="title" name="title" placeholder="Note Title" required><br>
<textarea id="content" name="content" placeholder="Note Content" required></textarea><br>
<button type="button" onclick="startVoiceRecognition()">🎤 Speak</button>
<button type="button" onclick="showOCRModal()">🧠 Extract text from image</button>
<button type="submit">Add Note</button>
</form>
<div id="ocrModal" style="display:none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.4);">
<div style="background-color: #fff; margin: 15% auto; padding: 20px; border: 1px solid #888; width: 300px; text-align: center; border-radius: 10px;">
<h3>Select an Image</h3>
<input type="file" id="ocrImageInput" accept="image/*"><br><br>
<button onclick="performOCR()">Extract</button>
<button onclick="closeOCRModal()">Cancel</button>
</div>
</div>
<div id="notes-container"> </div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const toggle = document.querySelector('.dropdown-toggle');
const menu = document.querySelector('.dropdown-menu');
toggle.addEventListener('click', function () {
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
});
});
</script>
</body>
<div id="voiceModal" style="display:none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.4);">
<div style="background-color: #fff; margin: 15% auto; padding: 20px; border: 1px solid #888; width: 300px; text-align: center; border-radius: 10px;">
<h3>Speak now...</h3>
<button onclick="stopVoiceRecognition()">❌ Cancel</button>
</div>
</div>
</html>