Merge pull request 'Updated navbar buttons, added about page, added rough hangman game' (#58) from TussockyJoker/Komodo-Hub:main into main

Reviewed-on: Ugric/Komodo-Hub#58
This commit is contained in:
Ugric committed 2026-03-19 03:04:46 +00:00
commit ed5147fafe
10 files changed
+201 -13

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from database.db_connection import get_db, get_db
from database.db_connection import get_db
import hashlib
import os
import bcrypt
+8
View File
@@ -0,0 +1,8 @@
from flask import render_template, Blueprint
page = Blueprint('about', __name__, url_prefix='/about')
@page.route('')
def home():
return render_template('about/about.jinja')
+11
View File
@@ -0,0 +1,11 @@
from flask import render_template, Blueprint
from database.db_commands import get_all_species
page = Blueprint('game', __name__)
@page.route('/game')
def game():
species = get_all_species()
species_list = [s['species_english'] for s in species]
return render_template('game/game.jinja', species_list=species_list)
+78
View File
@@ -0,0 +1,78 @@
const bodyParts = ['p-head','p-body','p-larm','p-rarm','p-lleg','p-rleg'];
let word, guessed, lives, over;
function initGame() {
word = SPECIES_LIST[Math.floor(Math.random() * SPECIES_LIST.length)].toUpperCase().replace(/\s+/g, ' ').trim();
guessed = [];
lives = 6;
over = false;
bodyParts.forEach(id => document.getElementById(id).setAttribute('display', 'none'));
document.getElementById('hm-msg').textContent = '';
document.getElementById('hm-msg').className = 'hm-msg';
document.getElementById('hm-restart-wrap').style.display = 'none';
renderWord();
renderKeyboard();
renderLives();
renderGuessed();
}
function renderWord() {
const el = document.getElementById('hm-word');
el.innerHTML = word.split('').map(l => {
const show = l === ' ' || guessed.includes(l) || over;
return `<div class="hm-letter"><span>${show ? l : ''}</span>${l !== ' ' ? '<hr>' : ''}</div>`;
}).join('');
}
function renderLives() {
document.getElementById('hm-lives').textContent = `Lives: ${lives}`;
}
function renderGuessed() {
const wrong = guessed.filter(l => !word.includes(l));
document.getElementById('hm-guessed').textContent = wrong.length ? `Wrong: ${wrong.join(', ')}` : '';
}
function renderKeyboard() {
const kb = document.getElementById('hm-keyboard');
kb.innerHTML = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(l => {
const used = guessed.includes(l);
const isRight = used && word.includes(l);
const isWrong = used && !word.includes(l);
return `<button class="hm-key${isRight ? ' right' : isWrong ? ' wrong' : ''}" ${used || over ? 'disabled' : ''} data-letter="${l}">${l}</button>`;
}).join('');
kb.querySelectorAll('.hm-key:not(:disabled)').forEach(btn => {
btn.addEventListener('click', () => guess(btn.dataset.letter));
});
}
function guess(letter) {
if (over || guessed.includes(letter)) return;
guessed.push(letter);
if (!word.includes(letter)) {
lives--;
document.getElementById(bodyParts[6 - lives - 1]).setAttribute('display', 'inline');
}
const won = word.split('').filter(l => l !== ' ').every(l => guessed.includes(l));
const lost = lives === 0;
if (won || lost) {
over = true;
const msg = document.getElementById('hm-msg');
msg.textContent = won ? `You won! The species was: ${word}` : `Game over! The species was: ${word}`;
msg.className = 'hm-msg ' + (won ? 'win' : 'lose');
document.getElementById('hm-restart-wrap').style.display = 'block';
}
renderWord();
renderKeyboard();
renderLives();
renderGuessed();
}
document.addEventListener('keydown', e => {
const l = e.key.toUpperCase();
if (/^[A-Z]$/.test(l) && !over) guess(l);
});
document.getElementById('hm-restart').addEventListener('click', initGame);
initGame();
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 41 KiB

+10 -10
View File
@@ -7,7 +7,7 @@
viewBox="0 0 200 200"
version="1.1"
id="svg1"
inkscape:export-filename="endangered.png"
inkscape:export-filename="leastconcern.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
@@ -27,12 +27,12 @@
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.59"
inkscape:cx="305.9322"
inkscape:cx="305.08475"
inkscape:cy="369.49153"
inkscape:window-width="1637"
inkscape:window-width="1512"
inkscape:window-height="685"
inkscape:window-x="1715"
inkscape:window-y="111"
inkscape:window-x="0"
inkscape:window-y="83"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
<defs
@@ -68,7 +68,7 @@
r="100"
inkscape:label="border" />
<circle
style="fill:#ff6600;fill-opacity:1;stroke-width:0.251354"
style="fill:#008000;fill-opacity:1;stroke-width:0.251354"
id="path1-6"
cy="100"
cx="100"
@@ -76,14 +76,14 @@
inkscape:label="bg" />
<text
xml:space="preserve"
transform="matrix(0.33940673,0,0,0.42414264,-29.953106,38.567598)"
transform="matrix(0.33940673,0,0,0.42414264,-29.612494,38.567598)"
id="text3"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:263.496px;font-family:'Arial Unicode MS';-inkscape-font-specification:'Arial Unicode MS, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect4);display:inline;fill:#1a1a1a"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:263.496px;font-family:'Arial Unicode MS';-inkscape-font-specification:'Arial Unicode MS, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:center;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect4);display:inline;fill:#ffffff"
x="375.42374"
y="0"
inkscape:label="text"><tspan
x="194.10007"
x="208.70299"
y="239.14691"
id="tspan2">EN</tspan></text>
id="tspan1">LC</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

+1 -1
View File
@@ -822,7 +822,7 @@ input:focus {
flex:1;
}
/* flash message css please review */
/* flash message css */
.flash-container {
margin: 20px;
}
+26
View File
@@ -0,0 +1,26 @@
{% extends "base.jinja" %}
{% block title %}About{% endblock %}
{% block head %}
{% endblock %}
{% block content %}
{% from 'button.jinja' import button %}
<section style="padding: 100px 20px;">
<h1>About Komodo Hub</h1>
<p>
Komodo Hub is a website deisgned to assist and educate people worldwide about conservation, with a focus on Indonesia. We aim to inform people on how to better
protect wildlife and their habitats.
</p>
<p>
Badan Pusat Statistik, the Indonesian Bureau of Statistics, has recently published data on the country’s endangered endemic species. Despite spending millions
of dollars on conservation efforts, the government might have not seen any significant breakthroughs in the population of some species. The success of Komodo
has been largely exclusive, and the world is now left with merely less than 100 Sumatran Tigers, Javan Rhinoceros, Bali Mynas, Javan Eagles, Tarsius, and
Celebes Crested Macaques, respectively. Habitat loss remains the primary cause of the problems. Other causes include habitat changes, commercial
over-exploitation, the introduction of harmful non-native species, pollution, and the spread of diseases.
</p>
<p>
In 2022 the Komodo Foundation, a not-for-profit organization, was given a substantial amount of funds to initiate the development of a revolutionary digital
platform for animal conservation in Indonesia. Komodo Hub.
</p>
</div>
</section>
{% endblock %}
+64
View File
@@ -0,0 +1,64 @@
{% extends "base.jinja" %}
{% block title %}Hangman{% endblock %}
{% block head %}
<style>
#hangman-app { padding: 1.5rem 0; max-width: 520px; margin: 0 auto; }
.hm-title { font-size: 22px; font-weight: 500; margin: 0 0 0.25rem; }
.hm-subtitle { font-size: 14px; color: #6b7280; margin: 0 0 1.5rem; }
.hm-canvas-wrap { background: #f9fafb; border-radius: 12px; padding: 1rem; margin-bottom: 1.25rem; display: flex; justify-content: center; }
.hm-word { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 1.25rem; }
.hm-letter { width: 32px; text-align: center; }
.hm-letter span { display: block; font-size: 22px; font-weight: 500; min-height: 28px; }
.hm-letter hr { border: none; border-top: 2px solid #111827; margin: 4px 0 0; }
.hm-guessed { font-size: 13px; color: #6b7280; text-align: center; margin-bottom: 1.25rem; min-height: 18px; }
.hm-keyboard { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 1.25rem; }
.hm-key { width: 36px; height: 36px; border-radius: 8px; border: 1px solid #d1d5db; background: #fff; font-size: 13px; font-weight: 500; cursor: pointer; transition: background 0.1s; }
.hm-key:hover:not(:disabled) { background: #f3f4f6; }
.hm-key:disabled { opacity: 0.3; cursor: not-allowed; }
.hm-key.wrong { background: #fee2e2; color: #991b1b; border-color: #fca5a5; }
.hm-key.right { background: #dcfce7; color: #166534; border-color: #86efac; }
.hm-msg { text-align: center; font-size: 15px; font-weight: 500; margin-bottom: 1rem; min-height: 22px; }
.hm-msg.win { color: #166534; }
.hm-msg.lose { color: #991b1b; }
.hm-lives { text-align: center; font-size: 13px; color: #6b7280; margin-bottom: 0.75rem; }
</style>
{% endblock %}
{% block content %}
{% from 'button.jinja' import button %}
<div id="hangman-app">
<p class="hm-title">Hangman</p>
<p class="hm-subtitle">Guess the species name, one letter at a time.</p>
<div class="hm-canvas-wrap">
<svg id="gallows" width="160" height="160" viewBox="0 0 160 160">
<line x1="20" y1="155" x2="140" y2="155" stroke="#9ca3af" stroke-width="2"/>
<line x1="50" y1="155" x2="50" y2="10" stroke="#9ca3af" stroke-width="2"/>
<line x1="50" y1="10" x2="110" y2="10" stroke="#9ca3af" stroke-width="2"/>
<line x1="110" y1="10" x2="110" y2="30" stroke="#9ca3af" stroke-width="2"/>
<circle id="p-head" cx="110" cy="42" r="12" stroke="#111827" stroke-width="2" fill="none" display="none"/>
<line id="p-body" x1="110" y1="54" x2="110" y2="100" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-larm" x1="110" y1="65" x2="88" y2="85" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-rarm" x1="110" y1="65" x2="132" y2="85" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-lleg" x1="110" y1="100" x2="88" y2="125" stroke="#111827" stroke-width="2" display="none"/>
<line id="p-rleg" x1="110" y1="100" x2="132" y2="125" stroke="#111827" stroke-width="2" display="none"/>
</svg>
</div>
<div id="hm-word" class="hm-word"></div>
<div id="hm-lives" class="hm-lives">Lives: 6</div>
<div id="hm-guessed" class="hm-guessed"></div>
<div id="hm-msg" class="hm-msg"></div>
<div id="hm-keyboard" class="hm-keyboard"></div>
<div id="hm-restart-wrap" style="display:none; text-align:center;">
{{ button(url='/game', button_text='Play Again!', extra_classes='homepage-btn', id='hm-restart') }}
</div>
</div>
<script>
const SPECIES_LIST = {{ species_list | tojson }};
</script>
<script src="{{ url_for('static', filename='game.js') }}"></script>
{% endblock %}
+2 -1
View File
@@ -29,7 +29,8 @@
<a href="/settings">Settings</a>
<a href="/logout">Logout</a>
{% else %}
{{ button(url='/login', button_text='Login/Register', extra_classes='') }}
{{ button(url='/login', button_text='Login', extra_classes='') }}
{{ button(url='/register', button_text='Register', extra_classes='') }}
{% endif %}
</div>
</div>