Fixes and service.sh.

This commit is contained in:
Oleksandr Kozachuk 2025-06-08 13:23:54 +02:00
parent c66772fd67
commit fdf214c7d4
2 changed files with 947 additions and 819 deletions

View File

@ -409,6 +409,89 @@
margin-bottom: 4px; margin-bottom: 4px;
display: block; display: block;
} }
#voteModal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
}
#voteModal.hidden {
display: none;
}
.modal-backdrop {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(10, 10, 15, 0.8);
backdrop-filter: blur(2px);
z-index: 1;
}
.modal-content {
position: relative;
z-index: 2;
width: 90%;
max-width: 400px;
padding: 30px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid var(--border-color);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
text-align: center;
animation: modalFadeIn 0.3s ease-out;
}
.modal-title {
font-size: 1.5rem;
margin-bottom: 10px;
color: var(--accent-primary);
}
.modal-subtitle {
color: var(--text-secondary);
font-size: 0.9rem;
margin-bottom: 20px;
}
#voterNameInput {
width: 100%;
padding: 12px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
color: var(--text-primary);
font-size: 1rem;
margin-bottom: 20px;
transition: all 0.3s ease;
}
#voterNameInput:focus {
outline: none;
border-color: var(--accent-secondary);
box-shadow: 0 0 0 3px rgba(123, 47, 247, 0.1);
background: rgba(255, 255, 255, 0.08);
}
.modal-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.modal-buttons .btn {
font-size: 0.9rem;
padding: 8px 16px;
}
@keyframes modalFadeIn {
from { opacity: 0; transform: translateY(-10px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes modalFadeOut {
from { opacity: 1; transform: translateY(0) scale(1); }
to { opacity: 0; transform: translateY(-10px) scale(0.97); }
}
</style> </style>
</head> </head>
<body> <body>
@ -491,6 +574,25 @@
<div id="loader" class="loader hidden"></div> <div id="loader" class="loader hidden"></div>
</div> </div>
<div id="voteModal" class="hidden">
<div class="modal-backdrop"></div>
<div class="modal-content glass-card">
<h3 class="modal-title">Cast Your Vote</h3>
<p class="modal-subtitle">Enter your name to vote for this idea</p>
<input
type="text"
id="voterNameInput"
placeholder="Your name…"
maxlength="50"
autofocus
/>
<div class="modal-buttons">
<button id="voteCancelBtn" class="btn btn-secondary btn-small">Cancel</button>
<button id="voteSubmitBtn" class="btn btn-small">Submit</button>
</div>
</div>
</div>
<script> <script>
// Mock API URL - Replace with your actual API endpoint // Mock API URL - Replace with your actual API endpoint
const API_URL = 'https://kaizenkodo.no/tour/v1'; const API_URL = 'https://kaizenkodo.no/tour/v1';
@ -648,43 +750,6 @@
} }
} }
// Vote for idea
async function voteForIdea(ideaId) {
const voterName = prompt('Enter your name to vote:');
if (!voterName) return;
showLoader();
try {
const response = await fetch(
`${API_URL}/tours/${currentTour.id}/ideas/${ideaId}/vote`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ voterName })
}
);
if (response.status === 404) {
throw new Error('Tour or Idea not found');
}
if (!response.ok) {
throw new Error(`Server responded with ${response.status}`);
}
// FastAPI returns the Idea object (with updated voters list)
const updatedIdea = await response.json();
// Replace the old idea in currentTour.ideas with the updated one
currentTour.ideas = currentTour.ideas.map(i =>
i.id === updatedIdea.id ? updatedIdea : i
);
renderIdeas();
} catch (error) {
console.error('Error voting:', error);
alert('Failed to vote. Please try again.');
} finally {
hideLoader();
}
}
// Show tour view // Show tour view
function showTourView() { function showTourView() {
document.getElementById('createTourSection').classList.add('hidden'); document.getElementById('createTourSection').classList.add('hidden');
@ -889,6 +954,65 @@
function hideLoader() { function hideLoader() {
document.getElementById('loader').classList.add('hidden'); document.getElementById('loader').classList.add('hidden');
} }
const voteModal = document.getElementById('voteModal');
const voterNameInput = document.getElementById('voterNameInput');
const voteCancelBtn = document.getElementById('voteCancelBtn');
const voteSubmitBtn = document.getElementById('voteSubmitBtn');
let _pendingVoteIdeaId = null;
function voteForIdea(ideaId) {
_pendingVoteIdeaId = ideaId;
voterNameInput.value = '';
voteSubmitBtn.disabled = true;
voteModal.classList.remove('hidden');
setTimeout(() => voterNameInput.focus(), 100);
}
voterNameInput.addEventListener('input', () => {
voteSubmitBtn.disabled = voterNameInput.value.trim().length === 0;
});
function closeVoteModal() {
const content = voteModal.querySelector('.modal-content');
content.style.animation = 'modalFadeOut 0.2s ease-out forwards';
setTimeout(() => {
voteModal.classList.add('hidden');
content.style.animation = 'modalFadeIn 0.3s ease-out'; // reset for next open
_pendingVoteIdeaId = null;
}, 200);
}
voteCancelBtn.addEventListener('click', closeVoteModal);
voteSubmitBtn.addEventListener('click', async () => {
const voterName = voterNameInput.value.trim();
if (!voterName) return;
closeVoteModal();
showLoader();
try {
const resp = await fetch(
`${API_URL}/tours/${currentTour.id}/ideas/${_pendingVoteIdeaId}/vote`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ voterName }),
}
);
if (resp.status === 404) throw new Error('Tour or Idea not found');
if (!resp.ok) throw new Error(`Server responded with ${resp.status}`);
const updatedIdea = await resp.json();
currentTour.ideas = currentTour.ideas.map((i) =>
i.id === updatedIdea.id ? updatedIdea : i
);
renderIdeas();
} catch (err) {
console.error('Error voting:', err);
alert('Failed to vote. Please try again.');
} finally {
hideLoader();
_pendingVoteIdeaId = null;
}
});
</script> </script>
</body> </body>
</html> </html>

4
service.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
cd /home/kaizen/repos/tourplanner || exit 1
exec python3.11 server.py --port=3002 --data-dir=/home/kaizen/repos/tourplanner/data