Files
ai7-m3/Building a Professional Scheduler Bot MVP.html
2026-01-15 00:13:51 +03:00

1111 lines
38 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<!-- saved from url=(0063)file:///Users/home/Downloads/deepseek_html_20260114_010afa.html -->
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Building a Professional Scheduler Bot MVP</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: #f8fafc;
color: #1a202c;
overflow-x: hidden;
line-height: 1.6;
}
.presentation-container {
display: flex;
flex-direction: column;
min-height: 100vh;
padding: 20px;
}
.slide {
display: none;
background: white;
border-radius: 8px;
padding: 40px;
margin: 20px auto;
max-width: 1000px;
width: 90%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
}
.slide.active {
display: block;
}
h1, h2, h3, h4 {
color: #2d3748;
margin-bottom: 20px;
font-weight: 600;
}
h1 {
font-size: 2.5rem;
text-align: center;
margin-bottom: 15px;
color: #2d3748;
}
h2 {
font-size: 2rem;
border-bottom: 2px solid #e2e8f0;
padding-bottom: 10px;
margin-bottom: 25px;
}
h3 {
font-size: 1.5rem;
color: #4f46e5;
margin-top: 20px;
margin-bottom: 15px;
}
p {
margin-bottom: 15px;
font-size: 1.1rem;
color: #4a5568;
line-height: 1.6;
}
ul, ol {
margin-left: 25px;
margin-bottom: 20px;
}
li {
margin-bottom: 8px;
color: #4a5568;
}
.code-block {
background: #1a202c;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #4f46e5;
font-family: 'Courier New', monospace;
overflow-x: auto;
}
.code-block code {
color: #e2e8f0;
font-size: 0.95rem;
line-height: 1.5;
}
.code-to-type {
background-color: #f7fafc;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
color: #2d3748;
border: 1px solid #e2e8f0;
}
.instruction-box {
background: #f7fafc;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #4f46e5;
}
.instruction-box h4 {
color: #2d3748;
margin-bottom: 10px;
font-size: 1.2rem;
}
.test-box {
background: #f0fff4;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #48bb78;
}
.test-box h4 {
color: #2d3748;
margin-bottom: 10px;
font-size: 1.2rem;
display: flex;
align-items: center;
gap: 10px;
}
.test-box h4::before {
content: "💻";
font-size: 1.5rem;
}
.warning-box {
background: #fef3c7;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #f59e0b;
}
.problem-box {
background: #fff5f5;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #fc8181;
}
.solution-box {
background: #e6fffa;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
border-left: 4px solid #38b2ac;
}
.nav-buttons {
display: flex;
justify-content: space-between;
margin-top: 30px;
}
.nav-button {
background: white;
color: #4f46e5;
border: 1px solid #4f46e5;
padding: 12px 25px;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.2s;
}
.nav-button:hover {
background: #4f46e5;
color: white;
}
.nav-button:disabled {
background: #e2e8f0;
border-color: #cbd5e0;
color: #a0aec0;
cursor: not-allowed;
}
.slide-indicator {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 20px;
}
.indicator-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #cbd5e0;
cursor: pointer;
transition: background 0.3s;
}
.indicator-dot.active {
background: #4f46e5;
}
.slide-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
}
.slide-number {
color: #718096;
font-size: 0.9rem;
font-weight: 500;
}
.project-preview {
background: #f7fafc;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
text-align: center;
border: 1px solid #e2e8f0;
}
.big-emoji {
font-size: 4rem;
text-align: center;
margin: 20px 0;
}
.terminal {
background: #1a202c;
color: #48bb78;
padding: 15px;
border-radius: 6px;
font-family: 'Courier New', monospace;
margin: 15px 0;
overflow-x: auto;
}
.terminal-command {
color: #e2e8f0;
}
.terminal-comment {
color: #718096;
}
.feature-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: 20px 0;
}
.feature-card {
background: #f7fafc;
border-radius: 6px;
padding: 20px;
border: 1px solid #e2e8f0;
}
.feature-card h4 {
color: #4f46e5;
margin-bottom: 10px;
}
.before-after {
display: flex;
gap: 20px;
margin: 20px 0;
}
.before, .after {
flex: 1;
padding: 15px;
border-radius: 6px;
}
.before {
background: #fff5f5;
border: 1px solid #fed7d7;
}
.after {
background: #f0fff4;
border: 1px solid #c6f6d5;
}
@media (max-width: 768px) {
.slide {
padding: 25px;
width: 95%;
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.6rem;
}
.big-emoji {
font-size: 3rem;
}
.before-after {
flex-direction: column;
}
.feature-list {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="presentation-container">
<!-- Slide 0: Title Slide -->
<div class="slide" id="slide0">
<div class="slide-header">
<h1>Upgrading to Scheduler Bot MVP</h1>
<div class="slide-number">Slide 1 of 12</div>
</div>
<div class="project-preview">
<div class="big-emoji">🤖📅</div>
<h2>From Basic Bot to Professional MVP</h2>
<p>Transform your simple scheduler into a robust, user-friendly application</p>
</div>
<div class="instruction-box">
<h4>What You'll Build</h4>
<p>A complete scheduler bot MVP with:</p>
<ul>
<li>Database for user-specific schedules</li>
<li>Interactive setup wizard</li>
<li>Multiple schedule management</li>
<li>Better error handling and UX</li>
</ul>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 1: Current Code Analysis -->
<div class="slide active" id="slide1">
<div class="slide-header">
<h2>Analyzing Current Code Issues</h2>
<div class="slide-number">Slide 2 of 12</div>
</div>
<div class="big-emoji">🔍</div>
<div class="problem-box">
<h4>Major Problems in Current Code:</h4>
<ul>
<li><strong>No Database:</strong> Single CSV file for all users</li>
<li><strong>Fixed Schedule:</strong> Can't customize per user</li>
<li><strong>Wrong Period Time:</strong> Period_7 has incorrect time (10:00-10:40)</li>
<li><strong>Missing Periods:</strong> Only 7 periods instead of 13</li>
<li><strong>No Error Handling:</strong> Crashes easily</li>
<li><strong>Basic Features:</strong> Limited commands</li>
</ul>
</div>
<div class="warning-box">
<h4>Critical Bug Found!</h4>
<p>In your current code, Period_7 has wrong times:</p>
<div class="code-to-type">'Period_7': ('10:00', '10:40'), # WRONG! Should be 14:20-15:00</div>
<p>Also missing Periods 8-13 from your CSV template!</p>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 2: MVP Architecture -->
<div class="slide" id="slide2">
<div class="slide-header">
<h2>MVP Architecture Design</h2>
<div class="slide-number">Slide 3 of 12</div>
</div>
<div class="big-emoji">🏗️</div>
<div class="instruction-box">
<h4>Choose: SQLite vs NoSQL</h4>
<p>For our scheduler, <strong>SQLite is better</strong> because:</p>
<ul>
<li>Structured schedule data (days, periods, classes)</li>
<li>Easy queries (find classes by day/time)</li>
<li>Built into Python - no extra setup</li>
<li>Reliable and ACID compliant</li>
</ul>
</div>
<div class="feature-list">
<div class="feature-card">
<h4>Database Schema</h4>
<ul>
<li>users table (user info)</li>
<li>classes table (schedule entries)</li>
<li>Simple, clean structure</li>
</ul>
</div>
<div class="feature-card">
<h4>Key Features</h4>
<ul>
<li>User-specific schedules</li>
<li>Interactive setup wizard</li>
<li>Multiple schedule support</li>
<li>Better error handling</li>
</ul>
</div>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 3: Step 1 - Fix Period Times -->
<div class="slide" id="slide3">
<div class="slide-header">
<h2>Step 1: Fix Period Times</h2>
<div class="slide-number">Slide 4 of 12</div>
</div>
<div class="big-emoji"></div>
<div class="before-after">
<div class="before">
<h4>Current (WRONG):</h4>
<div class="code-block">
<code>PERIOD_TIMES = {
'Period_1': ('09:00', '09:40'),
'Period_2': ('10:00', '10:40'),
'Period_3': ('11:00', '11:40'),
'Period_4': ('11:50', '12:30'),
'Period_5': ('12:40', '13:20'),
'Period_6': ('13:30', '14:10'),
'Period_7': ('10:00', '10:40'), # WRONG!
}</code>
</div>
</div>
<div class="after">
<h4>Fixed Version:</h4>
<div class="code-block">
<code>PERIODS = [
(1, "09:00-09:40"),
(2, "10:00-10:40"),
(3, "11:00-11:40"),
(4, "11:50-12:30"),
(5, "12:40-13:20"),
(6, "13:30-14:10"),
(7, "14:20-15:00"),
(8, "15:20-16:00"),
(9, "16:15-16:55"),
(10, "17:05-17:45"),
(11, "17:55-18:35"),
(12, "18:45-19:20"),
(13, "19:20-20:00")
]</code>
</div>
</div>
</div>
<div class="test-box">
<h4>Why This Matters:</h4>
<p>Your current Period_7 (10:00-10:40) overlaps with Period_2! This causes wrong class detection.</p>
<p>The correct times match your CSV template with all 13 periods.</p>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 4: Step 2 - Database Setup -->
<div class="slide" id="slide4">
<div class="slide-header">
<h2>Step 2: Database Setup</h2>
<div class="slide-number">Slide 5 of 12</div>
</div>
<div class="big-emoji">🗄️</div>
<div class="instruction-box">
<h4>Create SQLite Database</h4>
<p>Add this database class to your code:</p>
</div>
<div class="code-block">
<code>import sqlite3
import datetime
class SimpleDatabase:
def __init__(self, db_file="schedule_bot.db"):
self.db_file = db_file
self.init_database()
def init_database(self):
"""Initialize database with tables"""
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
# Users table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
last_name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# Classes table - one row per class entry
cursor.execute('''
CREATE TABLE IF NOT EXISTS classes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
day TEXT,
period INTEGER,
period_time TEXT,
subject TEXT,
class_group TEXT,
room TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (user_id)
)
''')
conn.commit()
conn.close()</code>
</div>
<div class="test-box">
<h4>Test Database Setup:</h4>
<div class="terminal">
<span class="terminal-comment"># Initialize database</span><br>
<span class="terminal-command">db = SimpleDatabase()</span><br>
<span class="terminal-comment"># Check if database file was created</span><br>
<span class="terminal-command">print("Database initialized successfully")</span>
</div>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 5: Step 3 - Interactive Setup -->
<div class="slide" id="slide5">
<div class="slide-header">
<h2>Step 3: Interactive Setup Wizard</h2>
<div class="slide-number">Slide 6 of 12</div>
</div>
<div class="big-emoji">🧙‍♂️</div>
<div class="instruction-box">
<h4>Add Conversation Handler</h4>
<p>Replace your simple commands with an interactive setup:</p>
</div>
<div class="code-block">
<code>from telegram.ext import ConversationHandler
from telegram import ReplyKeyboardMarkup, KeyboardButton
# Conversation states
SELECT_DAY, SELECT_PERIOD, ENTER_SUBJECT, ENTER_GROUP, ENTER_ROOM = range(5)
async def setup_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Start the setup conversation"""
await update.message.reply_text(
"📅 Let's add a class to your schedule.\n"
"First, select the day:",
reply_markup=ReplyKeyboardMarkup(
[["Monday"], ["Tuesday"], ["Wednesday"],
["Thursday"], ["Friday"], ["Cancel"]],
resize_keyboard=True, one_time_keyboard=True
)
)
return SELECT_DAY</code>
</div>
<div class="solution-box">
<h4>Benefits:</h4>
<ul>
<li>Users can create their own schedules</li>
<li>No need for CSV files</li>
<li>Interactive and user-friendly</li>
<li>Multiple schedules per user</li>
</ul>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 6: Step 4 - Enhanced Commands -->
<div class="slide" id="slide6">
<div class="slide-header">
<h2>Step 4: Enhanced Commands</h2>
<div class="slide-number">Slide 7 of 12</div>
</div>
<div class="big-emoji">🎯</div>
<div class="before-after">
<div class="before">
<h4>Current Commands:</h4>
<ul>
<li>/start - Basic welcome</li>
<li>/whereami - Current class</li>
<li>/schedule - Today's schedule</li>
<li>/tomorrow - Tomorrow's schedule</li>
<li>/help - Basic help</li>
</ul>
</div>
<div class="after">
<h4>Enhanced MVP Commands:</h4>
<ul>
<li>/setup - Interactive schedule setup</li>
<li>/whereami - Enhanced with next class info</li>
<li>/today - Today's detailed schedule</li>
<li>/tomorrow - Tomorrow's schedule</li>
<li>/week - Full weekly overview</li>
<li>/clear - Remove all classes</li>
<li>/help - Comprehensive help</li>
</ul>
</div>
</div>
<div class="code-block">
<code>async def where_am_i(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Enhanced: Show current OR next class"""
user_id = update.effective_user.id
now = datetime.datetime.now()
current_day = now.strftime("%A")
current_time = now.strftime("%H:%M")
# Get current class from database
current_class = db.get_current_class(user_id, current_day, current_time)
if current_class:
response = f"🎯 You should be in class now!\n📚 {current_class['subject']}"
else:
# Find next class
next_class = db.get_next_class(user_id, current_day, current_time)
if next_class:
response = f"✅ Free period!\n➡ Next: {next_class['subject']} at {next_class['time']}"
else:
response = f"🎉 No more classes today!"</code>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 7: Step 5 - Better Error Handling -->
<div class="slide" id="slide7">
<div class="slide-header">
<h2>Step 5: Better Error Handling</h2>
<div class="slide-number">Slide 8 of 12</div>
</div>
<div class="big-emoji">🛡️</div>
<div class="problem-box">
<h4>Current Issues:</h4>
<ul>
<li>Crashes if CSV file missing</li>
<li>No user input validation</li>
<li>Poor error messages</li>
<li>No logging for debugging</li>
</ul>
</div>
<div class="solution-box">
<h4>Add Professional Error Handling:</h4>
<div class="code-block">
<code>import logging
# Setup logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logger = logging.getLogger(__name__)
def save_class(user_id, day, period, subject, group, room):
"""Save class with error handling"""
try:
if not all([day, period, subject]):
raise ValueError("Missing required fields")
db.save_class(user_id, day, period, subject, group, room)
logger.info(f"Class saved for user {user_id}")
return True
except Exception as e:
logger.error(f"Error saving class: {e}")
return False</code>
</div>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 8: Step 6 - Requirements Update -->
<div class="slide" id="slide8">
<div class="slide-header">
<h2>Step 6: Update Requirements</h2>
<div class="slide-number">Slide 9 of 12</div>
</div>
<div class="big-emoji">📦</div>
<div class="before-after">
<div class="before">
<h4>Current requirements.txt:</h4>
<div class="code-block">
<code># Missing file or minimal content</code>
</div>
</div>
<div class="after">
<h4>Updated requirements.txt:</h4>
<div class="code-block">
<code>python-telegram-bot==20.3
python-dotenv==1.0.0
pandas==2.1.0 # Optional for CSV import</code>
</div>
</div>
</div>
<div class="instruction-box">
<h4>Environment Variables (.env file):</h4>
<div class="terminal">
<span class="terminal-comment"># Create .env file in project root</span><br>
<span class="terminal-command">BOT_TOKEN=your_actual_bot_token_here</span><br>
<span class="terminal-comment"># Remove token from code for security!</span>
</div>
</div>
<div class="test-box">
<h4>Install Dependencies:</h4>
<div class="terminal">
<span class="terminal-command">pip install -r requirements.txt</span><br>
<span class="terminal-comment"># Or install individually:</span><br>
<span class="terminal-command">pip install python-telegram-bot python-dotenv</span>
</div>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 9: Complete MVP Code -->
<div class="slide" id="slide9">
<div class="slide-header">
<h2>Complete MVP Structure</h2>
<div class="slide-number">Slide 10 of 12</div>
</div>
<div class="big-emoji">📁</div>
<div class="instruction-box">
<h4>Project Structure:</h4>
</div>
<div class="code-block">
<code>scheduler_bot_mvp/
├── scheduler_bot.py # Main bot file
├── requirements.txt # Dependencies
├── .env # Environment variables
├── schedule_bot.db # SQLite database (auto-created)
├── simple_database.py # Database class (optional)
└── README.md # Documentation</code>
</div>
<div class="feature-list">
<div class="feature-card">
<h4>Database Functions:</h4>
<ul>
<li>save_user() - Store user info</li>
<li>save_class() - Add class entry</li>
<li>get_user_classes() - Get schedule</li>
<li>get_current_class() - Find current</li>
<li>clear_schedule() - Remove all</li>
</ul>
</div>
<div class="feature-card">
<h4>Bot Commands:</h4>
<ul>
<li>/setup - Interactive wizard</li>
<li>/whereami - Smart detection</li>
<li>/today - Daily schedule</li>
<li>/week - Weekly overview</li>
<li>/clear - Clean slate</li>
</ul>
</div>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 10: Testing Your MVP -->
<div class="slide" id="slide10">
<div class="slide-header">
<h2>Testing Your MVP</h2>
<div class="slide-number">Slide 11 of 12</div>
</div>
<div class="big-emoji">🧪</div>
<div class="instruction-box">
<h4>Step-by-Step Testing:</h4>
<ol>
<li>Run the bot: <span class="code-to-type">python scheduler_bot.py</span></li>
<li>Open Telegram and find your bot</li>
<li>Send <span class="code-to-type">/start</span> command</li>
<li>Use <span class="code-to-type">/setup</span> to add classes</li>
<li>Test <span class="code-to-type">/whereami</span> throughout day</li>
<li>Try <span class="code-to-type">/today</span> and <span class="code-to-type">/week</span></li>
<li>Test error cases (wrong inputs)</li>
</ol>
</div>
<div class="test-box">
<h4>Expected Behavior:</h4>
<ul>
<li>✅ Database file created automatically</li>
<li>✅ Interactive setup works with keyboards</li>
<li>✅ /whereami shows current or next class</li>
<li>✅ /week displays full schedule</li>
<li>✅ /clear removes all classes</li>
<li>✅ Error messages are user-friendly</li>
</ul>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button" onclick="changeSlide(1)">
Next →
</button>
</div>
</div>
<!-- Slide 11: Next Steps & Challenges -->
<div class="slide" id="slide11">
<div class="slide-header">
<h2>Next Steps &amp; Challenges</h2>
<div class="slide-number">Slide 12 of 12</div>
</div>
<div class="big-emoji">🚀</div>
<div class="instruction-box">
<h4>Enhancement Challenges:</h4>
</div>
<div class="feature-list">
<div class="feature-card">
<h4>Easy Enhancements:</h4>
<ul>
<li>Add /edit command to modify classes</li>
<li>Implement /export to generate CSV</li>
<li>Add /import from CSV feature</li>
<li>Create /reminders for upcoming classes</li>
</ul>
</div>
<div class="feature-card">
<h4>Advanced Features:</h4>
<ul>
<li>Multiple schedule versions</li>
<li>Class notifications (10 min before)</li>
<li>Teacher/room-specific searches</li>
<li>Web interface for management</li>
</ul>
</div>
</div>
<div class="solution-box">
<h4>Key Improvements Made:</h4>
<ol>
<li>✅ Fixed period times (all 13 periods)</li>
<li>✅ Added SQLite database</li>
<li>✅ Created interactive setup wizard</li>
<li>✅ Enhanced commands with better UX</li>
<li>✅ Added proper error handling</li>
<li>✅ Implemented user-specific schedules</li>
</ol>
</div>
<div class="nav-buttons">
<button class="nav-button" onclick="changeSlide(-1)">
← Previous
</button>
<button class="nav-button">
Complete! 🎉
</button>
</div>
</div>
<!-- Slide Indicator -->
<div class="slide-indicator">
<div class="indicator-dot" onclick="goToSlide(0)"></div>
<div class="indicator-dot active" onclick="goToSlide(1)"></div>
<div class="indicator-dot" onclick="goToSlide(2)"></div>
<div class="indicator-dot" onclick="goToSlide(3)"></div>
<div class="indicator-dot" onclick="goToSlide(4)"></div>
<div class="indicator-dot" onclick="goToSlide(5)"></div>
<div class="indicator-dot" onclick="goToSlide(6)"></div>
<div class="indicator-dot" onclick="goToSlide(7)"></div>
<div class="indicator-dot" onclick="goToSlide(8)"></div>
<div class="indicator-dot" onclick="goToSlide(9)"></div>
<div class="indicator-dot" onclick="goToSlide(10)"></div>
<div class="indicator-dot" onclick="goToSlide(11)"></div>
</div>
</div>
<script>
// Presentation state
let currentSlide = 0;
const totalSlides = 12;
// Initialize presentation
document.addEventListener('DOMContentLoaded', function() {
updateSlideNavigation();
});
// Change slide
function changeSlide(direction) {
const newSlide = currentSlide + direction;
if (newSlide >= 0 && newSlide < totalSlides) {
// Hide current slide
document.getElementById(`slide${currentSlide}`).classList.remove('active');
// Update indicator
document.querySelectorAll('.indicator-dot')[currentSlide].classList.remove('active');
// Show new slide
currentSlide = newSlide;
document.getElementById(`slide${currentSlide}`).classList.add('active');
// Update indicator
document.querySelectorAll('.indicator-dot')[currentSlide].classList.add('active');
// Update navigation buttons
updateSlideNavigation();
// Update slide numbers
updateSlideNumbers();
}
}
// Go to specific slide
function goToSlide(slideIndex) {
if (slideIndex >= 0 && slideIndex < totalSlides) {
// Hide current slide
document.getElementById(`slide${currentSlide}`).classList.remove('active');
// Update indicator
document.querySelectorAll('.indicator-dot')[currentSlide].classList.remove('active');
// Show new slide
currentSlide = slideIndex;
document.getElementById(`slide${currentSlide}`).classList.add('active');
// Update indicator
document.querySelectorAll('.indicator-dot')[currentSlide].classList.add('active');
// Update navigation buttons
updateSlideNavigation();
// Update slide numbers
updateSlideNumbers();
}
}
// Update navigation buttons
function updateSlideNavigation() {
const slides = document.querySelectorAll('.slide');
slides.forEach((slide, index) => {
const buttons = slide.querySelectorAll('.nav-button');
buttons.forEach(button => {
if (button.textContent.includes('Previous')) {
button.disabled = currentSlide === 0;
} else if (button.textContent.includes('Next')) {
button.disabled = currentSlide === totalSlides - 1;
}
});
});
}
// Update slide numbers
function updateSlideNumbers() {
const slideNumbers = document.querySelectorAll('.slide-number');
slideNumbers.forEach((number, index) => {
if (index === currentSlide) {
number.textContent = `Slide ${currentSlide + 1} of ${totalSlides}`;
}
});
}
// Keyboard navigation
document.addEventListener('keydown', function(event) {
if (event.key === 'ArrowRight' || event.key === ' ') {
changeSlide(1);
} else if (event.key === 'ArrowLeft') {
changeSlide(-1);
} else if (event.key >= '1' && event.key <= '9') {
const slideNum = parseInt(event.key) - 1;
if (slideNum < totalSlides) {
goToSlide(slideNum);
}
}
});
</script>
</body></html>