bucketlist added
This commit is contained in:
1 parent
97841f3f24
commit
650211e8ab
7 files changed
+240
-15
No files matched your search
@@ -0,0 +1,87 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
```
|
||||
website/
|
||||
├── index.html # Single-page entry point — all 5 tabs live here
|
||||
├── gallery-container.html # Static gallery image tags (50 images)
|
||||
├── _redirects # Netlify SPA rewrite rule (/* → /index.html)
|
||||
├── js/
|
||||
│ ├── tabs.js # Client-side routing, tab switching, background video swap
|
||||
│ ├── comments.js # Steam-style comment wall (Firestore: comments + stats)
|
||||
│ ├── yuukabot.js # Live bot dashboard (Firestore: sysinfo/server)
|
||||
│ ├── calendar.js # Event calendar (Firestore: calendar_events)
|
||||
│ └── gallery.js # Lightbox + dynamic thumbnail loader
|
||||
├── style/
|
||||
│ ├── styles.css # Global layout, nav bar, base typography
|
||||
│ ├── comments.css # Steam-profile comment UI
|
||||
│ ├── yuukabot.css # Dashboard glassmorphism cards + grid
|
||||
│ ├── calendar.css # Calendar grid, popups, sidebar
|
||||
│ ├── gallery.css # Gallery grid + lightbox
|
||||
│ └── ahmet.css # Bucketlist tab button
|
||||
├── gallery/ # 50 full-quality images + thumbnails/ subdirectory
|
||||
├── textures/ # Background MP4 videos + clickable character PNG
|
||||
├── sounds/ # Click SFX (yuuka_click.mp3)
|
||||
├── generate_thumbnails.py # Pillow script: resizes gallery/ images → thumbnails/
|
||||
└── firebase.txt # Firestore security rules (reference only)
|
||||
```
|
||||
|
||||
- **No frameworks, no build step, no package manager.** Every file is served directly.
|
||||
- Assets are organised by type (images in `gallery/`, videos/PNGs in `textures/`).
|
||||
- Firebase config is duplicated per JS file, each guarding `firebase.initializeApp()` with `if (!firebase.apps.length)` to avoid collisions.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
This is a static site with **no build system**. Run locally with any HTTP server:
|
||||
|
||||
```sh
|
||||
# Python 3 (recommended)
|
||||
python -m http.server 8080
|
||||
|
||||
# Node.js alternative
|
||||
npx serve .
|
||||
```
|
||||
|
||||
- `_redirects` handles SPA routing on Netlify; for local dev, navigate to `/index.html` directly.
|
||||
- `generate_thumbnails.py` regenerates gallery thumbnails: `python generate_thumbnails.py`. Requires `Pillow`.
|
||||
|
||||
There are **no automated tests** in this project.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
- **HTML:** Semantic elements where practical. Tab contents use `<section id="…" class="tab-content">`. IDs are kebab-case.
|
||||
- **CSS:** Each tab has its own stylesheet under `style/`. Class names follow BEM-lite (`cal-day`, `dash-card`). CSS variables define layout knobs (`--dash-columns`, `--dash-gap`).
|
||||
- **JavaScript:** Vanilla ES6+, no transpilation. Functions are `camelCase`, DOM-referencing IDs match kebab-case HTML IDs. Each script is self-contained with a `DOMContentLoaded` guard. Firebase config is repeated per-file (same values) — keep this pattern.
|
||||
- **Whitespace:** 4-space indentation in JS/CSS, 2-space in HTML. No trailing semicolons are required but consistency within each file is expected.
|
||||
- No linters or formatters are configured.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Commit history follows an informal style:
|
||||
|
||||
```
|
||||
<verb> <short description>
|
||||
```
|
||||
|
||||
Examples from the repo: `calendar added`, `fix upcoming filter`, `css polish bs`, `tab specific routing added`.
|
||||
|
||||
- Use lowercase, imperative mood.
|
||||
- Keep messages short (under 72 characters).
|
||||
- Group related changes into a single commit where possible.
|
||||
- PRs should include a brief description of what changed and why; screenshots are appreciated for visual/UI changes.
|
||||
|
||||
## Security & Configuration
|
||||
|
||||
- **Firebase API keys** are public by design (they identify the project, not authenticate). Do not commit service-account credentials or `.env` files.
|
||||
- `firebase.txt` is in `.gitignore` — keep the live rules reference there, not hardcoded in source.
|
||||
- All Firestore writes are guarded by security rules (see `firebase.txt`): only `comments` and `stats/global_clicks` accept public writes; everything else is read-only.
|
||||
|
||||
## Adding a New Feature
|
||||
|
||||
1. Add the new `<section class="tab-content" id="…">` in `index.html`.
|
||||
2. Add a `<button id="…-btn">` to the nav bar (`#top-line`).
|
||||
3. Add the tab name to the `valid` array in `js/tabs.js`.
|
||||
4. Create a dedicated stylesheet in `style/` and JS module in `js/` if needed.
|
||||
5. Link both in `index.html` `<head>` (CSS) and before `</body>` (JS).
|
||||
6. If the feature reads/writes Firestore, add the matching security rules to `firebase.txt`.
|
||||
+9
-4
@@ -8,6 +8,7 @@
|
||||
<link rel="stylesheet" href="style/comments.css">
|
||||
<link rel="stylesheet" href="style/yuukabot.css">
|
||||
<link rel="stylesheet" href="style/calendar.css">
|
||||
<link rel="stylesheet" href="style/bucketlist.css">
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||||
<title>Esef</title>
|
||||
</head>
|
||||
@@ -19,14 +20,14 @@
|
||||
<video muted loop playsinline preload="none" id="calendar-background-video" style="display:none">
|
||||
<source src="textures/calendar_background.mp4" type="video/mp4">
|
||||
</video>
|
||||
<video muted loop playsinline preload="none" id="czscores-background-video" style="display:none">
|
||||
<video muted loop playsinline preload="none" id="bucketlist-background-video" style="display:none">
|
||||
<source src="textures/cz_background.mp4" type="video/mp4">
|
||||
</video>
|
||||
<div id="top-line">
|
||||
<a href="index.html" id="home-btn"></a>
|
||||
<button id="about-btn">About This Website</button>
|
||||
<button id="gallery-btn">Gallery</button>
|
||||
<button id="czscores-btn">Cz Scores</button>
|
||||
<button id="bucketlist-btn">Bucketlist</button>
|
||||
<button id="yuukabot-btn">Bot Status</button>
|
||||
<button id="calendar-btn">Calendar</button>
|
||||
</div>
|
||||
@@ -116,8 +117,11 @@
|
||||
<div id="gallery-container" class="gallery-grid"></div>
|
||||
</section>
|
||||
|
||||
<section id="czscores" class="tab-content">
|
||||
<div id="czscores-container">
|
||||
<section id="bucketlist" class="tab-content">
|
||||
<div class="bl-panel">
|
||||
<div class="bl-list" id="bl-list">
|
||||
<p class="bl-empty">Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -221,6 +225,7 @@
|
||||
<script src="js/comments.js"></script>
|
||||
<script src="js/yuukabot.js"></script>
|
||||
<script src="js/calendar.js"></script>
|
||||
<script src="js/bucketlist.js"></script>
|
||||
|
||||
<!-- Lightbox -->
|
||||
<div id="lightbox" class="lightbox">
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Bucketlist tab — real-time Firestore list
|
||||
const BL_FB_CONFIG = {
|
||||
apiKey: "AIzaSyDuSjEGEKx5FnWYnQq8f_owbpYRBRrl5x0",
|
||||
authDomain: "esef-514bf.firebaseapp.com",
|
||||
projectId: "esef-514bf",
|
||||
storageBucket: "esef-514bf.firebasestorage.app",
|
||||
messagingSenderId: "353969651290",
|
||||
appId: "1:353969651290:web:41e3aaf8e54c743702a263"
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const tab = document.getElementById("bucketlist");
|
||||
if (!tab) return;
|
||||
|
||||
if (!firebase.apps.length) firebase.initializeApp(BL_FB_CONFIG);
|
||||
const db = firebase.firestore();
|
||||
|
||||
const listEl = document.getElementById("bl-list");
|
||||
if (!listEl) return;
|
||||
|
||||
db.collection("bucketlist_items").onSnapshot(snap => {
|
||||
const items = [];
|
||||
snap.forEach(doc => {
|
||||
const d = doc.data();
|
||||
items.push({
|
||||
id: doc.id,
|
||||
title: d.title || "",
|
||||
description: d.description || "",
|
||||
date: d.date || "",
|
||||
utc: d.utc || "",
|
||||
done: d.done || false
|
||||
});
|
||||
});
|
||||
items.sort((a, b) => (a.utc || "").localeCompare(b.utc || ""));
|
||||
render(items);
|
||||
}, () => {
|
||||
if (listEl) listEl.innerHTML = '<p class="bl-empty">Could not load.</p>';
|
||||
});
|
||||
|
||||
function render(items) {
|
||||
if (items.length === 0) {
|
||||
listEl.innerHTML = '<p class="bl-empty">Nothing here yet.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
listEl.innerHTML = items.map(item =>
|
||||
'<div class="bl-item' + (item.done ? ' bl-done' : '') + '">' +
|
||||
'<div class="bl-title">' + esc(item.title) + '</div>' +
|
||||
(item.date ? '<div class="bl-date">' + esc(item.date) + '</div>' : '') +
|
||||
(item.description ? '<div class="bl-desc">' + esc(item.description) + '</div>' : '') +
|
||||
'</div>'
|
||||
).join("");
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
if (!s) return "";
|
||||
return String(s).replace(/&/g,"&").replace(/</g,"<")
|
||||
.replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");
|
||||
}
|
||||
});
|
||||
+6
-6
@@ -3,14 +3,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const tabs = document.querySelectorAll('.tab-content');
|
||||
const mainVideo = document.getElementById('background-video');
|
||||
const calVideo = document.getElementById('calendar-background-video');
|
||||
const czVideo = document.getElementById('czscores-background-video');
|
||||
const bucketlistVideo = document.getElementById('bucketlist-background-video');
|
||||
|
||||
// ── Map URL path → tab id ──────────────────────────────
|
||||
function pathToTab(path) {
|
||||
const slug = path.replace(/^\/+|\/+$/g, '') || 'about';
|
||||
const valid = ['about', 'gallery', 'czscores', 'yuukabot', 'calendar'];
|
||||
const valid = ['about', 'gallery', 'bucketlist', 'yuukabot', 'calendar'];
|
||||
// backward compat: old /ahmet links
|
||||
if (slug === 'ahmet') return 'czscores';
|
||||
if (slug === 'ahmet') return 'bucketlist';
|
||||
return valid.includes(slug) ? slug : 'about';
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// map tab id → its dedicated video element (null = use main)
|
||||
const tabVideos = {
|
||||
calendar: calVideo,
|
||||
czscores: czVideo
|
||||
bucketlist: bucketlistVideo
|
||||
};
|
||||
|
||||
function setBgForTab(tabId) {
|
||||
// Hide + pause ALL videos first
|
||||
[mainVideo, calVideo, czVideo].forEach(v => {
|
||||
[mainVideo, calVideo, bucketlistVideo].forEach(v => {
|
||||
if (v) { v.style.display = 'none'; v.pause(); }
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Top-bar theme toggle
|
||||
if (topLine) {
|
||||
topLine.classList.toggle('cal-theme', tabId === 'calendar');
|
||||
topLine.classList.toggle('czscores-theme', tabId === 'czscores');
|
||||
topLine.classList.toggle('bucketlist-theme', tabId === 'bucketlist');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* Cz Scores tab */
|
||||
/* Bucketlist tab */
|
||||
|
||||
#czscores-btn {
|
||||
#bucketlist-btn {
|
||||
background-color: aliceblue;
|
||||
color: #153dee;
|
||||
border: none;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Bucketlist tab — baked-in left panel, internal scroll only */
|
||||
|
||||
/* ── Lock section to viewport so body never scrolls ────────── */
|
||||
#bucketlist.tab-content {
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Panel (framed, no extra space) ────────────────────────── */
|
||||
.bl-panel {
|
||||
width: 420px;
|
||||
height: calc(100vh - 210px);
|
||||
margin: 125px 0 85px 48px;
|
||||
background: rgba(35, 35, 41, 0.6);
|
||||
border: 1px solid rgba(181, 177, 198, 0.18);
|
||||
border-radius: 10px;
|
||||
padding: 0;
|
||||
color: #F4F2F5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Scrollable list (fills the frame exactly) ────────────── */
|
||||
.bl-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
/* ── Item card ────────────────────────────────────────────── */
|
||||
.bl-item {
|
||||
padding: 14px 18px;
|
||||
background: rgba(35, 35, 41, 0.75);
|
||||
border: 1px solid rgba(181, 177, 198, 0.08);
|
||||
border-radius: 8px;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.bl-item:hover {
|
||||
border-color: rgba(184, 68, 87, 0.3);
|
||||
box-shadow: 0 3px 16px rgba(184, 68, 87, 0.08);
|
||||
}
|
||||
|
||||
.bl-item .bl-title {
|
||||
color: #F4F2F5;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.bl-item .bl-date {
|
||||
color: rgba(244, 242, 245, 0.45);
|
||||
font-size: 0.7rem;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.bl-item .bl-desc {
|
||||
color: rgba(244, 242, 245, 0.55);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ── Empty state ─────────────────────────────────────────── */
|
||||
.bl-empty {
|
||||
text-align: center;
|
||||
color: rgba(244, 242, 245, 0.3);
|
||||
padding: 60px 0;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
+3
-3
@@ -18,7 +18,7 @@ html, body {
|
||||
}
|
||||
#background-video,
|
||||
#calendar-background-video,
|
||||
#czscores-background-video {
|
||||
#bucketlist-background-video {
|
||||
position: fixed;
|
||||
right: 0%;
|
||||
left: 0%;
|
||||
@@ -76,8 +76,8 @@ html, body {
|
||||
transition: border-color 0.4s ease, color 0.4s ease;
|
||||
}
|
||||
|
||||
/* ── Cz Scores-themed top bar buttons ─────────────────── */
|
||||
#top-line.czscores-theme button {
|
||||
/* ── Bucketlist-themed top bar buttons ─────────────────── */
|
||||
#top-line.bucketlist-theme button {
|
||||
border-bottom-color: #B5B1C6;
|
||||
border-left-color: #B5B1C6;
|
||||
border-right-color: #B5B1C6;
|
||||
|
||||
Reference in new issue
Block a user