mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
609 lines
14 KiB
Markdown
609 lines
14 KiB
Markdown
# YouTube Club Videos Integration - Activity Events
|
|
|
|
## 🎯 Enhancement Summary
|
|
|
|
The YouTube video selector in **Admin → Aktivity** now has TWO options:
|
|
|
|
1. ✅ **Select from club's YouTube channel** (new!)
|
|
2. ✅ **Manual URL input** (existing)
|
|
|
|
---
|
|
|
|
## ✨ New Features
|
|
|
|
### Dual Tab Interface
|
|
|
|
```
|
|
┌─ YouTube Video (volitelné) ──────────────────┐
|
|
│ │
|
|
│ [Z kanálu klubu (15)] [Vlastní odkaz] │ ← Tabs
|
|
│ ─────────────────────────────────────────── │
|
|
│ │
|
|
│ Tab 1: Club Videos (scrollable gallery) │
|
|
│ or │
|
|
│ Tab 2: Custom URL input │
|
|
│ │
|
|
└───────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Tab 1: From Club Channel
|
|
|
|
**Shows your club's YouTube videos:**
|
|
|
|
```
|
|
┌─────────────────────────────────────────────┐
|
|
│ [Thumbnail] Video Title │
|
|
│ 60x60 Published • Views [Vybráno] │
|
|
├─────────────────────────────────────────────┤
|
|
│ [Thumbnail] Another Video │
|
|
│ 60x60 3 days ago • 1.2K views │
|
|
├─────────────────────────────────────────────┤
|
|
│ [Thumbnail] Match Highlights │
|
|
│ 60x60 1 week ago • 5.4K views │
|
|
└─────────────────────────────────────────────┘
|
|
Scrollable list (max 300px height)
|
|
```
|
|
|
|
**Features:**
|
|
- ✅ Loads videos from club's YouTube channel automatically
|
|
- ✅ Shows 20 most recent videos
|
|
- ✅ Displays thumbnail, title, date, views
|
|
- ✅ Click any video to select it
|
|
- ✅ Visual feedback (red border when selected)
|
|
- ✅ Shows "Vybráno" badge on selected video
|
|
- ✅ Toast notification when selected
|
|
|
|
### Tab 2: Custom URL
|
|
|
|
**Manual URL input (for any YouTube video):**
|
|
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ https://www.youtube.com/watch?v=... │
|
|
└──────────────────────────────────────────┘
|
|
Vložte odkaz na jakékoliv YouTube video
|
|
```
|
|
|
|
**Use for:**
|
|
- Videos from other channels
|
|
- Unlisted videos
|
|
- Private videos
|
|
- Any YouTube content
|
|
|
|
---
|
|
|
|
## 🎬 How It Works
|
|
|
|
### Admin Workflow
|
|
|
|
#### Option A: Select from Club Videos (Recommended)
|
|
|
|
1. **Click "YouTube Video" section**
|
|
2. **Default tab: "Z kanálu klubu"** opens
|
|
3. **Scroll through your club's videos**
|
|
4. **Click video thumbnail/title to select**
|
|
5. ✅ **Toast notification: "Video vybráno"**
|
|
6. ✅ **Red border appears around selected video**
|
|
7. ✅ **Badge shows "Video nastaveno"**
|
|
8. **Save event**
|
|
|
|
**Example:**
|
|
```
|
|
Admin sees:
|
|
- "Match Highlights vs Sparta" (thumbnail)
|
|
- "Training Session March 2024" (thumbnail)
|
|
- "Youth Tournament 2024" (thumbnail)
|
|
|
|
Clicks "Match Highlights vs Sparta"
|
|
→ Video selected!
|
|
→ URL: https://www.youtube.com/watch?v=abc123
|
|
→ Saved to event
|
|
```
|
|
|
|
#### Option B: Custom URL
|
|
|
|
1. **Click "Vlastní odkaz" tab**
|
|
2. **Paste any YouTube URL**
|
|
3. **Save event**
|
|
|
|
**Supported formats:**
|
|
- `https://www.youtube.com/watch?v=VIDEO_ID`
|
|
- `https://youtu.be/VIDEO_ID`
|
|
- `https://www.youtube.com/embed/VIDEO_ID`
|
|
|
|
---
|
|
|
|
## 🎨 Visual Design
|
|
|
|
### Tab Buttons
|
|
|
|
**"Z kanálu klubu"** (active)
|
|
```
|
|
[🎬 Z kanálu klubu (15)] ← Red solid button
|
|
```
|
|
|
|
**"Vlastní odkaz"** (inactive)
|
|
```
|
|
[🔗 Vlastní odkaz] ← Gray outline button
|
|
```
|
|
|
|
### Video List Item
|
|
|
|
**Unselected:**
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ [Thumb] Video Title │
|
|
│ Published • Views │
|
|
└─────────────────────────────────────┘
|
|
Gray border, white background
|
|
Hover: Red border, red background
|
|
```
|
|
|
|
**Selected:**
|
|
```
|
|
┌═════════════════════════════════════┐ ← Red border
|
|
║ [Thumb] Video Title [Vybráno] ║ ← Red background
|
|
║ Published • Views ║
|
|
└═════════════════════════════════════┘
|
|
```
|
|
|
|
### Status Indicator
|
|
|
|
When video is set:
|
|
```
|
|
✅ Video nastaveno [Zrušit video]
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Data Flow
|
|
|
|
### Loading Club Videos
|
|
|
|
```
|
|
1. Admin opens event modal
|
|
↓
|
|
2. useEffect triggers on mount
|
|
↓
|
|
3. getCachedYouTube() called
|
|
↓
|
|
4. Fetches from: /youtube/videos
|
|
↓
|
|
5. Fallback: /cache/prefetch/youtube_channel.json
|
|
↓
|
|
6. Videos sorted by date (newest first)
|
|
↓
|
|
7. Limited to 20 videos
|
|
↓
|
|
8. Stored in clubVideos state
|
|
↓
|
|
9. Rendered in scrollable list
|
|
```
|
|
|
|
### Video Selection
|
|
|
|
```
|
|
User clicks video in list
|
|
↓
|
|
onClick handler:
|
|
- Set youtube_url to video URL
|
|
- Show toast notification
|
|
↓
|
|
UI updates:
|
|
- Red border on selected video
|
|
- "Vybráno" badge appears
|
|
- "Video nastaveno" status shown
|
|
↓
|
|
User saves event
|
|
↓
|
|
youtube_url included in payload
|
|
↓
|
|
Saved to database
|
|
```
|
|
|
|
### Frontend Display
|
|
|
|
```
|
|
Event detail page loads
|
|
↓
|
|
Checks if youtube_url exists
|
|
↓
|
|
If yes:
|
|
- Parse video ID from URL
|
|
- Generate embed URL
|
|
- Render responsive 16:9 iframe
|
|
↓
|
|
Video appears between map and content
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Video Source Comparison
|
|
|
|
| Feature | Club Videos Tab | Custom URL Tab |
|
|
|---------|----------------|----------------|
|
|
| **Source** | Club's YouTube channel | Any YouTube video |
|
|
| **Selection** | Visual gallery | Manual input |
|
|
| **Preview** | Thumbnail + metadata | URL only |
|
|
| **Videos** | Auto-loaded (20 recent) | User provides |
|
|
| **Use Case** | Club content | External/unlisted videos |
|
|
| **Ease** | Click to select ✨ | Copy/paste URL |
|
|
|
|
---
|
|
|
|
## 💡 Use Cases
|
|
|
|
### Use Case 1: Match Highlights
|
|
```
|
|
Event: Vítězství 3:1 vs Sparta
|
|
Video: "Match Highlights" from club channel
|
|
|
|
Admin:
|
|
1. Open event
|
|
2. Click "Z kanálu klubu"
|
|
3. Find "Match Highlights vs Sparta"
|
|
4. Click thumbnail
|
|
5. Save
|
|
|
|
Result: Highlights embedded in event page!
|
|
```
|
|
|
|
### Use Case 2: Training Announcement
|
|
```
|
|
Event: Trénink mládeže
|
|
Video: "Training drill tutorial" from external channel
|
|
|
|
Admin:
|
|
1. Open event
|
|
2. Click "Vlastní odkaz"
|
|
3. Paste: https://www.youtube.com/watch?v=xyz789
|
|
4. Save
|
|
|
|
Result: Tutorial video embedded!
|
|
```
|
|
|
|
### Use Case 3: Tournament Promo
|
|
```
|
|
Event: Annual Tournament 2024
|
|
Video: "Tournament Promo 2024" from club channel
|
|
|
|
Admin:
|
|
1. Click "Z kanálu klubu"
|
|
2. Scroll to promo video
|
|
3. Click to select
|
|
4. Save
|
|
|
|
Result: Promo video on event page!
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Benefits
|
|
|
|
### For Admins
|
|
|
|
1. **Easier Selection**
|
|
- See all videos visually
|
|
- No need to copy URLs
|
|
- One click to select
|
|
|
|
2. **Better Discovery**
|
|
- Browse club videos
|
|
- See thumbnails & titles
|
|
- View publish dates & views
|
|
|
|
3. **Flexibility**
|
|
- Use club videos OR
|
|
- Use any YouTube video
|
|
- Easy switching between options
|
|
|
|
### For Visitors
|
|
|
|
1. **Consistent Content**
|
|
- Videos from official club channel
|
|
- Professional content
|
|
- Trusted source
|
|
|
|
2. **Rich Experience**
|
|
- Watch videos inline
|
|
- No need to leave page
|
|
- Responsive on mobile
|
|
|
|
---
|
|
|
|
## 🔧 Technical Details
|
|
|
|
### State Management
|
|
|
|
```typescript
|
|
// YouTube videos from club
|
|
const [clubVideos, setClubVideos] = useState<YouTubeVideo[]>([]);
|
|
|
|
// Active tab
|
|
const [youtubeTab, setYoutubeTab] = useState<'club' | 'custom'>('club');
|
|
```
|
|
|
|
### Load Videos
|
|
|
|
```typescript
|
|
useEffect(() => {
|
|
(async () => {
|
|
const ytData = await getCachedYouTube();
|
|
if (ytData?.videos) {
|
|
setClubVideos(ytData.videos.slice(0, 20));
|
|
}
|
|
})();
|
|
}, []);
|
|
```
|
|
|
|
### Video Selection Handler
|
|
|
|
```typescript
|
|
onClick={() => {
|
|
const videoUrl = `https://www.youtube.com/watch?v=${video.video_id}`;
|
|
setEditing(prev => ({
|
|
...(prev || {}),
|
|
youtube_url: videoUrl
|
|
}));
|
|
toast({
|
|
title: 'Video vybráno',
|
|
description: video.title,
|
|
status: 'success',
|
|
});
|
|
}}
|
|
```
|
|
|
|
### Data Structure
|
|
|
|
```typescript
|
|
type YouTubeVideo = {
|
|
video_id: string; // "abc123"
|
|
title: string; // "Match Highlights"
|
|
thumbnail_url: string; // "https://i.ytimg.com/..."
|
|
views_text?: string; // "1.2K views"
|
|
views?: number; // 1234
|
|
published_text?: string; // "3 days ago"
|
|
published_date?: string; // "2024-03-15"
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 UI Components
|
|
|
|
### Tab Buttons
|
|
|
|
```typescript
|
|
<HStack mb={3} spacing={2}>
|
|
<Button
|
|
size="sm"
|
|
leftIcon={<FiYoutube />}
|
|
onClick={() => setYoutubeTab('club')}
|
|
variant={youtubeTab === 'club' ? 'solid' : 'outline'}
|
|
colorScheme={youtubeTab === 'club' ? 'red' : 'gray'}
|
|
>
|
|
Z kanálu klubu ({clubVideos.length})
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
leftIcon={<FiLink />}
|
|
onClick={() => setYoutubeTab('custom')}
|
|
variant={youtubeTab === 'custom' ? 'solid' : 'outline'}
|
|
colorScheme={youtubeTab === 'custom' ? 'blue' : 'gray'}
|
|
>
|
|
Vlastní odkaz
|
|
</Button>
|
|
</HStack>
|
|
```
|
|
|
|
### Video List Item
|
|
|
|
```typescript
|
|
<HStack
|
|
p={2}
|
|
borderWidth="1px"
|
|
borderColor={isSelected ? 'red.500' : borderColor}
|
|
bg={isSelected ? 'red.50' : cardBg}
|
|
cursor="pointer"
|
|
_hover={{ borderColor: 'red.300', bg: 'red.50' }}
|
|
onClick={() => selectVideo(video)}
|
|
>
|
|
<Image
|
|
src={video.thumbnail_url}
|
|
boxSize="60px"
|
|
objectFit="cover"
|
|
borderRadius="md"
|
|
/>
|
|
<VStack align="start" flex={1}>
|
|
<Text fontSize="sm" fontWeight="medium">
|
|
{video.title}
|
|
</Text>
|
|
<HStack fontSize="xs" color="gray.500">
|
|
<Text>{video.published_text}</Text>
|
|
<Text>• {video.views_text}</Text>
|
|
</HStack>
|
|
</VStack>
|
|
{isSelected && <Badge colorScheme="red">Vybráno</Badge>}
|
|
</HStack>
|
|
```
|
|
|
|
---
|
|
|
|
## 🆚 Before vs After
|
|
|
|
### Before
|
|
|
|
```
|
|
YouTube Video (volitelné)
|
|
┌──────────────────────────────────┐
|
|
│ [URL input field] │
|
|
└──────────────────────────────────┘
|
|
Paste URL manually
|
|
```
|
|
|
|
**Limitations:**
|
|
- ❌ No visual preview
|
|
- ❌ Must copy URL from YouTube
|
|
- ❌ Can't browse club videos
|
|
- ❌ No metadata shown
|
|
|
|
### After
|
|
|
|
```
|
|
YouTube Video (volitelné)
|
|
┌────────────────────────────────────┐
|
|
│ [Z kanálu klubu] [Vlastní odkaz] │ ← Tabs
|
|
├────────────────────────────────────┤
|
|
│ [Thumb] Match Highlights [Vybráno]│ ← Visual gallery
|
|
│ [Thumb] Training Session │
|
|
│ [Thumb] Tournament Promo │
|
|
│ ... (scrollable) │
|
|
└────────────────────────────────────┘
|
|
```
|
|
|
|
**Advantages:**
|
|
- ✅ Visual gallery
|
|
- ✅ One-click selection
|
|
- ✅ Browse club videos
|
|
- ✅ See metadata (views, date)
|
|
- ✅ Still supports custom URLs
|
|
|
|
---
|
|
|
|
## 📱 Responsive Design
|
|
|
|
### Desktop
|
|
|
|
```
|
|
Wide scrollable gallery
|
|
20 videos visible
|
|
Large thumbnails (60x60px)
|
|
```
|
|
|
|
### Mobile
|
|
|
|
```
|
|
Stacked layout
|
|
Scrollable list
|
|
Same thumbnail size
|
|
Touch-friendly selection
|
|
```
|
|
|
|
---
|
|
|
|
## 🐛 Edge Cases Handled
|
|
|
|
### No Club Videos
|
|
|
|
```
|
|
Žádná videa z kanálu klubu.
|
|
Nastavte YouTube kanál v Nastavení.
|
|
```
|
|
|
|
Shows helpful message if:
|
|
- No YouTube channel configured
|
|
- Channel has no videos
|
|
- Fetch failed
|
|
|
|
**Fallback:** Use "Vlastní odkaz" tab
|
|
|
|
### Video Already Selected
|
|
|
|
```
|
|
[Thumb] Selected Video [Vybráno]
|
|
═══════════════════════
|
|
Red border + badge
|
|
```
|
|
|
|
Visual indicator prevents double-selection
|
|
|
|
### Remove Selected Video
|
|
|
|
```
|
|
✅ Video nastaveno [Zrušit video]
|
|
```
|
|
|
|
Click "Zrušit video" to clear selection
|
|
|
|
---
|
|
|
|
## 🔍 Testing Checklist
|
|
|
|
- [ ] Tab switching works (club ↔ custom)
|
|
- [ ] Club videos load on modal open
|
|
- [ ] Videos display with thumbnails
|
|
- [ ] Click video selects it
|
|
- [ ] Selected video shows red border
|
|
- [ ] "Vybráno" badge appears
|
|
- [ ] Toast notification shows
|
|
- [ ] Status indicator appears
|
|
- [ ] Custom URL tab works
|
|
- [ ] Can paste any YouTube URL
|
|
- [ ] Remove video button works
|
|
- [ ] Event saves with youtube_url
|
|
- [ ] Frontend displays video correctly
|
|
- [ ] No club videos shows message
|
|
- [ ] Scrolling works (>20 videos)
|
|
|
|
---
|
|
|
|
## 📚 Related Components
|
|
|
|
### Used Services
|
|
- `getCachedYouTube()` - Fetches club videos
|
|
- `YouTubeVideo` type - Video data structure
|
|
|
|
### UI Components
|
|
- Tab buttons (club/custom)
|
|
- Video gallery (scrollable)
|
|
- Video list items (clickable)
|
|
- Status indicators
|
|
|
|
### Files Modified
|
|
- `AdminActivitiesPage.tsx` - Added dual YouTube selector
|
|
- Previous: `event.ts`, `ActivityDetailPage.tsx`
|
|
|
|
---
|
|
|
|
## 💡 Future Enhancements
|
|
|
|
Potential improvements:
|
|
|
|
1. **Search/Filter** - Search club videos by title
|
|
2. **Categories** - Filter by playlists
|
|
3. **Pagination** - Load more than 20 videos
|
|
4. **Preview** - Show video preview on hover
|
|
5. **Timestamps** - Start video at specific time
|
|
6. **Multiple Videos** - Attach multiple videos
|
|
7. **Auto-Suggest** - Suggest related videos
|
|
8. **Analytics** - Track which videos most used
|
|
|
|
---
|
|
|
|
## 🎯 Summary
|
|
|
|
Your Activity events now have **professional YouTube integration**:
|
|
|
|
✨ **Visual gallery** - Browse club videos
|
|
🖼️ **Thumbnails** - See before selecting
|
|
👆 **One-click** - Easy selection
|
|
📊 **Metadata** - Views, dates shown
|
|
🔗 **Flexible** - Club OR custom videos
|
|
📱 **Responsive** - Works on mobile
|
|
✅ **User-friendly** - Intuitive interface
|
|
|
|
**Perfect for:**
|
|
- Match highlights
|
|
- Training videos
|
|
- Tournament promos
|
|
- Club announcements
|
|
- Tutorial videos
|
|
- Promotional content
|
|
|
|
---
|
|
|
|
**Implementation Date:** 2025-10-10
|
|
**Status:** ✅ Production Ready
|
|
**User Experience:** Excellent ⭐⭐⭐⭐⭐
|
|
**Admin Efficiency:** Significantly Improved 🚀
|