1185 lines
36 KiB
HTML
1185 lines
36 KiB
HTML
<!DOCTYPE html>
|
|
<!-- saved from url=(0069)file:///Users/home/Downloads/deepseek_html_20260120_cab869%20(2).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>Creating a Social Media Feed with Vue.js and APIs</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
}
|
|
|
|
body {
|
|
background: #f8fafc;
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
width: 100%;
|
|
height: calc(100vh - 80px);
|
|
}
|
|
|
|
.slide {
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
|
padding: 30px 40px;
|
|
margin: 10px 0;
|
|
display: none;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.slide.active {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
h1 {
|
|
color: #42b883;
|
|
font-size: 2.2rem;
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
h2 {
|
|
color: #2c3e50;
|
|
font-size: 1.8rem;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
h3 {
|
|
color: #35495e;
|
|
font-size: 1.4rem;
|
|
margin: 20px 0 10px 0;
|
|
}
|
|
|
|
p {
|
|
font-size: 1.1rem;
|
|
line-height: 1.6;
|
|
margin: 15px 0;
|
|
color: #4a5568;
|
|
}
|
|
|
|
.code-block {
|
|
background: #1a202c;
|
|
color: #e2e8f0;
|
|
padding: 20px;
|
|
border-radius: 6px;
|
|
margin: 15px 0;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
font-size: 0.95rem;
|
|
line-height: 1.5;
|
|
white-space: pre-wrap;
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
.instruction-block {
|
|
background: #f0f8ff;
|
|
border-left: 4px solid #42b883;
|
|
padding: 15px;
|
|
margin: 15px 0;
|
|
border-radius: 0 4px 4px 0;
|
|
}
|
|
|
|
.analogy-block {
|
|
background: #f0fff4;
|
|
border-left: 4px solid #38b2ac;
|
|
padding: 15px;
|
|
margin: 15px 0;
|
|
border-radius: 0 4px 4px 0;
|
|
}
|
|
|
|
.step-number {
|
|
display: inline-block;
|
|
background: #42b883;
|
|
color: white;
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
text-align: center;
|
|
line-height: 24px;
|
|
margin-right: 10px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.nav-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 25px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.nav-btn {
|
|
background: #42b883;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 25px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
min-width: 100px;
|
|
}
|
|
|
|
.nav-btn:hover {
|
|
background: #368e6c;
|
|
}
|
|
|
|
.nav-btn:disabled {
|
|
background: #cbd5e0;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.slide-counter {
|
|
text-align: center;
|
|
color: #718096;
|
|
font-size: 0.9rem;
|
|
margin-bottom: 10px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.terminal {
|
|
background: #2d3748;
|
|
color: #48bb78;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
font-family: 'Consolas', monospace;
|
|
margin: 10px 0;
|
|
font-size: 0.9rem;
|
|
line-height: 1.5;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.api-example {
|
|
background: #fffaf0;
|
|
border: 1px solid #ed8936;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
margin: 10px 0;
|
|
font-family: 'Consolas', monospace;
|
|
font-size: 0.9rem;
|
|
color: #c05621;
|
|
line-height: 1.5;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
ul {
|
|
margin: 15px 0 15px 20px;
|
|
color: #4a5568;
|
|
}
|
|
|
|
li {
|
|
margin: 8px 0;
|
|
}
|
|
|
|
code {
|
|
background: #edf2f7;
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-family: 'Consolas', monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.comparison-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 15px 0;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.comparison-table th {
|
|
background: #42b883;
|
|
color: white;
|
|
padding: 10px;
|
|
text-align: left;
|
|
border: 1px solid #368e6c;
|
|
}
|
|
|
|
.comparison-table td {
|
|
padding: 10px;
|
|
border: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.comparison-table tr:nth-child(even) {
|
|
background: #f7fafc;
|
|
}
|
|
|
|
.visual {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 20px 0;
|
|
padding: 20px;
|
|
background: #f8fafc;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.visual-item {
|
|
text-align: center;
|
|
margin: 0 20px;
|
|
}
|
|
|
|
.visual-icon {
|
|
font-size: 3rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.arrow {
|
|
font-size: 2rem;
|
|
color: #718096;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
body {
|
|
padding: 10px;
|
|
}
|
|
|
|
.slide {
|
|
padding: 20px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.visual {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.arrow {
|
|
transform: rotate(90deg);
|
|
margin: 10px 0;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="slide-counter" id="slide-counter">Slide 1 of 10</div>
|
|
|
|
<div class="container">
|
|
<!-- Slide 1: Title -->
|
|
<div class="slide active" id="slide1">
|
|
<div style="flex: 1; display: flex; flex-direction: column; justify-content: center;">
|
|
<h1>🌐 Building a Social Media Feed</h1>
|
|
<p style="font-size: 1.3rem; color: #4a5568;">Learn Vue.js, APIs, and Modern Web Development</p>
|
|
<p style="margin-top: 30px; font-size: 1.1rem; color: #718096;">
|
|
Create an interactive social media feed that connects to a real API
|
|
</p>
|
|
<div style="margin-top: 40px; background: #e6fffa; padding: 20px; border-radius: 8px;">
|
|
<h3 style="color: #0d9488;">What You'll Learn:</h3>
|
|
<ul>
|
|
<li>Why JavaScript frameworks make coding easier</li>
|
|
<li>What APIs are and how they work</li>
|
|
<li>How to install Node.js and create Vue apps</li>
|
|
<li>How to fetch data from an API with async/await</li>
|
|
<li>Build a working social media feed!</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn1" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn1">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 2: Why Frameworks? -->
|
|
<div class="slide" id="slide2">
|
|
<h2>🚀 Why JavaScript Frameworks?</h2>
|
|
|
|
<div class="analogy-block">
|
|
<h3>Think of Frameworks Like LEGO Kits!</h3>
|
|
<p><strong>Without a framework:</strong> You get a huge box of individual LEGO bricks. You have to figure everything out yourself!</p>
|
|
<p><strong>With a framework:</strong> You get a LEGO kit with pre-built sections and instructions. Just snap things together!</p>
|
|
</div>
|
|
|
|
<div class="visual">
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🧱</div>
|
|
<h4>Old Way (Vanilla JS)</h4>
|
|
<p>Everything by hand</p>
|
|
<p>Slow and messy</p>
|
|
</div>
|
|
<div class="arrow">→</div>
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🏗️</div>
|
|
<h4>New Way (Frameworks)</h4>
|
|
<p>Pre-built components</p>
|
|
<p>Fast and organized</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<h3>Framework Benefits:</h3>
|
|
<ul>
|
|
<li><strong>Save time:</strong> Don't reinvent the wheel</li>
|
|
<li><strong>Stay organized:</strong> Everything has its place</li>
|
|
<li><strong>Easy updates:</strong> Change one part, everything updates automatically</li>
|
|
<li><strong>Big community:</strong> Millions of developers to help you</li>
|
|
<li><strong>Best practices:</strong> Learn the right way from the start</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn2" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn3">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 3: Which Framework? -->
|
|
<div class="slide" id="slide3">
|
|
<h2>🟢 Why Vue.js is Our Choice</h2>
|
|
|
|
<table class="comparison-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Framework</th>
|
|
<th>Who Makes It</th>
|
|
<th>Learning Curve</th>
|
|
<th>For Russian Users</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>React.js</strong></td>
|
|
<td>Facebook/Meta (USA)</td>
|
|
<td>Medium</td>
|
|
<td>⚠️ Risk of sanctions/blocking</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Angular</strong></td>
|
|
<td>Google (USA)</td>
|
|
<td>Steep</td>
|
|
<td>⚠️ Risk of sanctions/blocking</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Vue.js</strong> 🎯</td>
|
|
<td>Open Source Community 🌍</td>
|
|
<td>Gentle</td>
|
|
<td>✅ Safe & Independent</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="instruction-block">
|
|
<h3>Vue.js Advantages:</h3>
|
|
<ul>
|
|
<li><strong>Open source:</strong> Belongs to everyone, not one company</li>
|
|
<li><strong>Gentle learning curve:</strong> Easy for beginners</li>
|
|
<li><strong>Great documentation:</strong> Like having a helpful teacher</li>
|
|
<li><strong>Single-file components:</strong> HTML + CSS + JavaScript together</li>
|
|
<li><strong>Reactive:</strong> Automatic updates (magic!)</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="analogy-block">
|
|
<h3>Choosing Vue is Like Choosing:</h3>
|
|
<p><strong>React/Angular:</strong> Renting a house - the owner (Meta/Google) can change rules or kick you out</p>
|
|
<p><strong>Vue.js:</strong> Owning a house - it's yours forever, no one can take it away!</p>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn3" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn4">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 4: What is an API? -->
|
|
<div class="slide" id="slide4">
|
|
<h2>🤝 What is an API?</h2>
|
|
|
|
<div class="analogy-block">
|
|
<h3>API = Restaurant Waiter</h3>
|
|
<p>Think of an API like a waiter in a restaurant:</p>
|
|
<ul>
|
|
<li><strong>You (Browser):</strong> "I'd like posts from user 1" (places order)</li>
|
|
<li><strong>API (Waiter):</strong> Takes your order to the kitchen</li>
|
|
<li><strong>Database (Kitchen):</strong> Prepares the data</li>
|
|
<li><strong>API (Waiter):</strong> Brings the data back to you</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="visual">
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🌐</div>
|
|
<h4>Your Vue App</h4>
|
|
<p>"Give me posts!"</p>
|
|
</div>
|
|
<div class="arrow">→</div>
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🤝</div>
|
|
<h4>API</h4>
|
|
<p>Messenger</p>
|
|
</div>
|
|
<div class="arrow">→</div>
|
|
<div class="visual-item">
|
|
<div class="visual-icon">💾</div>
|
|
<h4>Database</h4>
|
|
<p>Data storage</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<h3>Our API: api.techshare.cc</h3>
|
|
<p>We have a real API that serves data! Try these in your browser:</p>
|
|
<div class="api-example">
|
|
# See all posts:
|
|
https://api.techshare.cc/api/posts
|
|
|
|
# See all users:
|
|
https://api.techshare.cc/api/users
|
|
|
|
# See comments:
|
|
https://api.techshare.cc/api/comments
|
|
</div>
|
|
<p>Copy and paste these URLs in your browser to see the data!</p>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn4" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn5">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 5: Install Node.js -->
|
|
<div class="slide" id="slide5">
|
|
<h2>📦 Step 1: Install Node.js</h2>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">1</div>
|
|
<strong>What is Node.js?</strong>
|
|
<p>Think of Node.js as the <strong>engine</strong> that runs JavaScript on your computer!</p>
|
|
<p>Usually JavaScript only runs in browsers. Node.js lets it run on YOUR computer too!</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">2</div>
|
|
<strong>Download and Install</strong>
|
|
<ul>
|
|
<li>Go to <a href="https://nodejs.org/" target="_blank">nodejs.org</a></li>
|
|
<li>Download the <strong>LTS version</strong> (Long Term Support = stable)</li>
|
|
<li>Run the installer</li>
|
|
<li>Click "Next" through everything</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">3</div>
|
|
<strong>Check if it Works</strong>
|
|
<p>Open PowerShell (Windows) or Terminal (Mac):</p>
|
|
<div class="terminal">
|
|
# Check Node.js version
|
|
node --version
|
|
# Should show: v20.x.x or v22.x.x
|
|
|
|
# Check npm version
|
|
npm --version
|
|
# Should show: 10.x.x or higher
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">4</div>
|
|
<strong>Windows Users - Fix Execution Policy</strong>
|
|
<p>If PowerShell says "not allowed":</p>
|
|
<div class="terminal">
|
|
# Open PowerShell as Administrator
|
|
# Type this:
|
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
</div>
|
|
<p>Type <code>Y</code> and press Enter when asked!</p>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn5" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn6">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 6: Create Vue App -->
|
|
<div class="slide" id="slide6">
|
|
<h2>⚡ Step 2: Create Vue App with Vite</h2>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">1</div>
|
|
<strong>Open Terminal/PowerShell</strong>
|
|
<p>Navigate to where you want your project:</p>
|
|
<div class="terminal">
|
|
cd Documents
|
|
# or wherever you want your project
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">2</div>
|
|
<strong>Create Vue App</strong>
|
|
<p>Type this command (with double dash --):</p>
|
|
<div class="terminal">
|
|
# Create Vue app with Vite
|
|
npm create vite@latest my-vue-app -- --template vue
|
|
</div>
|
|
<p><strong>Note:</strong> <code>-- --template vue</code> means two dashes, space, two dashes</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">3</div>
|
|
<strong>Answer Prompts</strong>
|
|
<p>You'll see questions - just press Enter for all:</p>
|
|
<div class="terminal">
|
|
✔ Project name: … my-vue-app
|
|
✔ Package name: … my-vue-app
|
|
✔ Add TypeScript? … No
|
|
✔ Add JSX Support? … No
|
|
✔ Add Vue Router? … No (or Yes if you want pages)
|
|
✔ Add Pinia? … No (or Yes for state management)
|
|
✔ Add Vitest? … No
|
|
✔ Add ESLint? … No (or Yes for code quality)
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">4</div>
|
|
<strong>Navigate to your app</strong>
|
|
<div class="terminal">
|
|
cd my-vue-app
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn6" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn7">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 7: Install and Run -->
|
|
<div class="slide" id="slide7">
|
|
<h2>🚀 Step 3: Install and Run</h2>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">1</div>
|
|
<strong>Install Dependencies</strong>
|
|
<p>These are like "batteries" for your app:</p>
|
|
<div class="terminal">
|
|
npm install
|
|
</div>
|
|
<p>Wait 1-2 minutes while it downloads everything</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">2</div>
|
|
<strong>Start Development Server</strong>
|
|
<div class="terminal">
|
|
npm run dev
|
|
</div>
|
|
<p>You'll see:</p>
|
|
<div class="terminal">
|
|
VITE v5.x.x ready in xxx ms
|
|
|
|
➜ Local: http://localhost:5173/
|
|
➜ Network: use --host to expose
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">3</div>
|
|
<strong>Open in Browser</strong>
|
|
<p>Copy and paste this in your browser:</p>
|
|
<div class="code-block">
|
|
http://localhost:5173
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">4</div>
|
|
<strong>Explore the Default App</strong>
|
|
<ul>
|
|
<li>Click the count button - see it update!</li>
|
|
<li>Notice the Vue and Vite logos</li>
|
|
<li>This is your starting point</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn7" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn8">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 8: Project Structure -->
|
|
<div class="slide" id="slide8">
|
|
<h2>🗂️ Step 4: Understand Project Structure</h2>
|
|
|
|
<div class="instruction-block">
|
|
<p><strong>Your project folder looks like this:</strong></p>
|
|
<div class="code-block">
|
|
my-vue-app/ # Your project house
|
|
├── node_modules/ # Toolbox (don't touch!)
|
|
├── public/ # Front yard
|
|
│ └── vite.svg # Vite logo
|
|
├── src/ # Main house
|
|
│ ├── assets/ # Pictures & decorations
|
|
│ │ └── vue.svg # Vue logo
|
|
│ ├── components/ # Furniture
|
|
│ │ └── HelloWorld.vue # Example furniture
|
|
│ ├── App.vue # Living room (main area)
|
|
│ └── main.js # Front door (starts app)
|
|
├── index.html # Land plot
|
|
├── package.json # Shopping list
|
|
└── vite.config.js # House blueprints
|
|
</div>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<h3>Important Files:</h3>
|
|
<ul>
|
|
<li><strong>src/App.vue:</strong> Main component - we'll replace this</li>
|
|
<li><strong>src/main.js:</strong> Starts the Vue app</li>
|
|
<li><strong>index.html:</strong> Main HTML file</li>
|
|
<li><strong>package.json:</strong> Lists all tools we use</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<h3>Stop and Restart Server:</h3>
|
|
<p>In your terminal where <code>npm run dev</code> is running:</p>
|
|
<div class="terminal">
|
|
# Press Ctrl + C to stop
|
|
|
|
# Then restart:
|
|
npm run dev
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn8" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn9">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 9: Replace App.vue -->
|
|
<div class="slide" id="slide9">
|
|
<h2>🎨 Step 5: Create Social Media Feed</h2>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">1</div>
|
|
<strong>Open App.vue</strong>
|
|
<p>Navigate to: <code>my-vue-app/src/App.vue</code></p>
|
|
<p>Open it in your code editor (VS Code, Notepad++, etc.)</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">2</div>
|
|
<strong>Delete Everything</strong>
|
|
<p>Select all code in App.vue and delete it</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">3</div>
|
|
<strong>Paste Our Social Feed Code</strong>
|
|
<p>Copy and paste this complete code:</p>
|
|
</div>
|
|
|
|
<div class="code-block" style="max-height: 400px; overflow-y: auto; font-size: 0.8rem;">
|
|
<template>
|
|
<div class="social-feed">
|
|
<!-- Header -->
|
|
<header class="header">
|
|
<h1>📱 Social Media Feed</h1>
|
|
<p class="subtitle">Simple feed using Async/Await</p>
|
|
</header>
|
|
|
|
<!-- Main Feed -->
|
|
<main class="feed">
|
|
<!-- Stats -->
|
|
<div class="stats">
|
|
<p>Total posts: {{ posts.length }}</p>
|
|
<button @click="loadPosts" :disabled="loading" class="refresh-btn">
|
|
{{ loading ? '🔄 Loading...' : '🔄 Refresh' }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Loading -->
|
|
<div v-if="loading" class="loading">
|
|
<div class="spinner"></div>
|
|
<p>Loading posts...</p>
|
|
</div>
|
|
|
|
<!-- Error -->
|
|
<div v-else-if="error" class="error">
|
|
<p>❌ Error: {{ error }}</p>
|
|
<button @click="loadPosts" class="retry-btn">Try Again</button>
|
|
</div>
|
|
|
|
<!-- Posts -->
|
|
<div v-else class="posts">
|
|
<div v-if="posts.length === 0" class="empty">
|
|
<p>No posts available.</p>
|
|
</div>
|
|
|
|
<div v-else class="posts-list">
|
|
<div
|
|
v-for="post in posts"
|
|
:key="post._id || post.id"
|
|
class="post"
|
|
>
|
|
<!-- User info -->
|
|
<div class="user-info">
|
|
<div class="avatar">U{{ post.userId }}</div>
|
|
<div>
|
|
<h3>User {{ post.userId }}</h3>
|
|
<small>Post ID: {{ (post._id || post.id).substring(0, 10) }}...</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Post content -->
|
|
<div class="post-content">
|
|
<h4>{{ post.title }}</h4>
|
|
<p>{{ post.body || post.content }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Footer -->
|
|
<footer class="footer">
|
|
<p>API: https://api.techshare.cc/api/posts</p>
|
|
</footer>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
// State
|
|
const posts = ref([])
|
|
const loading = ref(false)
|
|
const error = ref(null)
|
|
|
|
// Load posts function
|
|
async function loadPosts() {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
try {
|
|
console.log('Fetching posts...')
|
|
const response = await fetch('https://api.techshare.cc/api/posts')
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch: ${response.status}`)
|
|
}
|
|
|
|
posts.value = await response.json()
|
|
console.log(`Loaded ${posts.value.length} posts`)
|
|
|
|
} catch (err) {
|
|
console.error('Error:', err)
|
|
error.value = err.message || 'Something went wrong'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// Load on page load
|
|
onMounted(() => {
|
|
loadPosts()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.social-feed {
|
|
min-height: 100vh;
|
|
background: #f5f7fa;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
}
|
|
|
|
/* Header */
|
|
.header {
|
|
background: white;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
border-bottom: 1px solid #eaeaea;
|
|
}
|
|
|
|
.header h1 {
|
|
color: #333;
|
|
font-size: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #666;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* Feed */
|
|
.feed {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
/* Stats */
|
|
.stats {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 2rem;
|
|
padding: 1rem;
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.stats p {
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.refresh-btn {
|
|
padding: 0.5rem 1rem;
|
|
background: #4361ee;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.refresh-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Loading */
|
|
.loading {
|
|
text-align: center;
|
|
padding: 3rem;
|
|
}
|
|
|
|
.spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid #ddd;
|
|
border-top: 3px solid #4361ee;
|
|
border-radius: 50%;
|
|
margin: 0 auto 1rem;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Error */
|
|
.error {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
background: #ffebee;
|
|
color: #c62828;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.retry-btn {
|
|
margin-top: 1rem;
|
|
padding: 0.5rem 1.5rem;
|
|
background: #c62828;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* Empty */
|
|
.empty {
|
|
text-align: center;
|
|
padding: 3rem;
|
|
color: #666;
|
|
}
|
|
|
|
/* Posts list */
|
|
.posts-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
/* Single post */
|
|
.post {
|
|
background: white;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
}
|
|
|
|
/* User info */
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.avatar {
|
|
width: 50px;
|
|
height: 50px;
|
|
background: #4361ee;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-weight: bold;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.user-info h3 {
|
|
color: #333;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.user-info small {
|
|
color: #888;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* Post content */
|
|
.post-content h4 {
|
|
color: #333;
|
|
margin-bottom: 0.75rem;
|
|
font-size: 1.1rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.post-content p {
|
|
color: #555;
|
|
line-height: 1.6;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
/* Footer */
|
|
.footer {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: #666;
|
|
font-size: 0.9rem;
|
|
border-top: 1px solid #eaeaea;
|
|
margin-top: 3rem;
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.header {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.feed {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.stats {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.post {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.user-info {
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.avatar {
|
|
width: 40px;
|
|
height: 40px;
|
|
font-size: 1rem;
|
|
}
|
|
}
|
|
</style>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn9" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn10">Next</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Slide 10: See the Magic! -->
|
|
<div class="slide" id="slide10">
|
|
<h2>🎉 Step 6: See the Magic Happen!</h2>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">1</div>
|
|
<strong>Save App.vue</strong>
|
|
<p>Save the file (Ctrl + S or Cmd + S)</p>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<div class="step-number">2</div>
|
|
<strong>Check Your Browser</strong>
|
|
<p>Go to <code>http://localhost:5173</code></p>
|
|
<p>You should see:</p>
|
|
<ul>
|
|
<li>A beautiful social media feed header</li>
|
|
<li>Posts loading from the API</li>
|
|
<li>Real data from api.techshare.cc</li>
|
|
<li>A refresh button to get new data</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="instruction-block">
|
|
<h3>What Just Happened? Magic!</h3>
|
|
<div class="visual">
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🌐</div>
|
|
<h4>Your Vue App</h4>
|
|
<p>Said "give me posts"</p>
|
|
</div>
|
|
<div class="arrow">→</div>
|
|
<div class="visual-item">
|
|
<div class="visual-icon">🤝</div>
|
|
<h4>API</h4>
|
|
<p>Messenger delivered</p>
|
|
</div>
|
|
<div class="arrow">→</div>
|
|
<div class="visual-item">
|
|
<div class="visual-icon">💾</div>
|
|
<h4>Database</h4>
|
|
<p>Gave real data</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="analogy-block">
|
|
<h3>Async/Await = Super Fast Messenger</h3>
|
|
<p><strong>async function loadPosts():</strong> "I'll do this job, but I might need to wait"</p>
|
|
<p><strong>await fetch():</strong> "Wait here until you get the data"</p>
|
|
<p><strong>.then() vs async/await:</strong> Phone call vs text message!</p>
|
|
</div>
|
|
|
|
<div class="instruction-block" style="background: #e6fffa; border-left: 4px solid #0d9488;">
|
|
<h3>🎓 What You've Accomplished:</h3>
|
|
<ul>
|
|
<li>✅ Learned why frameworks save time</li>
|
|
<li>✅ Chose Vue.js (safe open source option)</li>
|
|
<li>✅ Installed Node.js and npm</li>
|
|
<li>✅ Created a Vue app with Vite</li>
|
|
<li>✅ Connected to a real API</li>
|
|
<li>✅ Built a working social media feed!</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="nav-container">
|
|
<button class="nav-btn" id="prevBtn10" disabled="">Previous</button>
|
|
<button class="nav-btn" id="nextBtn11">Finish</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const totalSlides = 10;
|
|
let currentSlide = 1;
|
|
|
|
function updateSlideCounter() {
|
|
document.getElementById('slide-counter').textContent = `Slide ${currentSlide} of ${totalSlides}`;
|
|
}
|
|
|
|
function showSlide(slideNumber) {
|
|
for (let i = 1; i <= totalSlides; i++) {
|
|
const slide = document.getElementById(`slide${i}`);
|
|
if (slide) slide.classList.remove('active');
|
|
}
|
|
|
|
const slideElement = document.getElementById(`slide${slideNumber}`);
|
|
if (slideElement) {
|
|
slideElement.classList.add('active');
|
|
currentSlide = slideNumber;
|
|
updateSlideCounter();
|
|
updateButtons();
|
|
}
|
|
}
|
|
|
|
function updateButtons() {
|
|
for (let i = 1; i <= totalSlides; i++) {
|
|
const prevBtn = document.getElementById(`prevBtn${i}`);
|
|
const nextBtn = document.getElementById(`nextBtn${i}`);
|
|
if (prevBtn) prevBtn.disabled = currentSlide === 1;
|
|
if (nextBtn) {
|
|
nextBtn.textContent = currentSlide === totalSlides ? 'Finish' : 'Next';
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let i = 1; i <= totalSlides; i++) {
|
|
const nextBtn = document.getElementById(`nextBtn${i}`);
|
|
const prevBtn = document.getElementById(`prevBtn${i}`);
|
|
|
|
if (nextBtn) {
|
|
nextBtn.addEventListener('click', () => {
|
|
if (currentSlide < totalSlides) {
|
|
showSlide(currentSlide + 1);
|
|
} else {
|
|
alert('🎉 Congratulations! You now understand Vue.js, APIs, and async/await! You\'ve built a real social media feed that connects to live data. Keep exploring and building!');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (prevBtn) {
|
|
prevBtn.addEventListener('click', () => {
|
|
if (currentSlide > 1) {
|
|
showSlide(currentSlide - 1);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
if (currentSlide < totalSlides) showSlide(currentSlide + 1);
|
|
} else if (e.key === 'ArrowLeft') {
|
|
if (currentSlide > 1) showSlide(currentSlide - 1);
|
|
}
|
|
});
|
|
|
|
updateSlideCounter();
|
|
updateButtons();
|
|
</script>
|
|
|
|
</body></html> |