canvascraft / script.js
Onise's picture
Crée une application web SaaS nommée ShortForge AI : un générateur de vidéos courtes (YouTube Shorts, TikTok, Reels) à partir de texte/script/URL.
d03bb4e verified
// ShortForge AI – Landing page JS
document.getElementById('demoBtn').addEventListener('click', () => {
document.getElementById('demoSection').classList.toggle('hidden');
});
async loadTemplates() {
this.showLoading(true);
// Simulate API call with generated images
const categories = ['nature', 'technology', 'people', 'abstract', 'minimal'];
const sizes = ['200x200', '320x240', '640x360', '1024x576', '1200x630'];
const templates = [];
for (let i = 0; i < 20; i++) {
const category = categories[Math.floor(Math.random() * categories.length)];
const size = sizes[Math.floor(Math.random() * sizes.length)];
const seed = Math.floor(Math.random() * 999) + 1;
templates.push({
id: i + 1,
title: `${category.charAt(0).toUpperCase() + category.slice(1)} Template ${i + 1}`,
category: category,
size: size,
image: `http://static.photos/${category}/${size}/${seed}`,
downloads: Math.floor(Math.random() * 1000),
likes: Math.floor(Math.random() * 500),
tags: [category, 'template', 'ai-generated']
});
}
this.templates = templates;
this.renderTemplates();
this.showLoading(false);
}
renderTemplates() {
const container = document.getElementById('templatesContainer');
const filteredTemplates = this.filterTemplates();
container.innerHTML = filteredTemplates.map(template => `
<div class="template-card bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden cursor-pointer" data-id="${template.id}">
<div class="relative">
<img src="${template.image}" alt="${template.title}" class="w-full h-48 object-cover">
<div class="absolute top-2 right-2 flex gap-2">
<span class="bg-black bg-opacity-50 text-white px-2 py-1 rounded text-xs">
${template.size}
</span>
<button class="like-btn bg-white bg-opacity-80 hover:bg-opacity-100 rounded-full p-1 transition-all" data-id="${template.id}">
<i data-feather="heart" class="w-4 h-4 text-gray-600"></i>
</button>
</div>
</div>
<div class="p-4">
<h3 class="font-semibold text-gray-800 dark:text-white mb-2">${template.title}</h3>
<div class="flex items-center justify-between text-sm text-gray-600 dark:text-gray-400">
<span class="flex items-center">
<i data-feather="download" class="w-4 h-4 mr-1"></i>
${template.downloads}
</span>
<span class="flex items-center">
<i data-feather="heart" class="w-4 h-4 mr-1"></i>
${template.likes}
</span>
</div>
<div class="mt-3 flex flex-wrap gap-1">
${template.tags.map(tag => `
<span class="bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1 rounded text-xs">
${tag}
</span>
`).join('')}
</div>
</div>
</div>
`).join('');
this.attachCardEvents();
feather.replace();
}
filterTemplates() {
return this.templates.filter(template => {
const categoryMatch = !this.currentCategory || template.category === this.currentCategory;
const sizeMatch = !this.currentSize || template.size === this.currentSize;
return categoryMatch && sizeMatch;
});
}
attachCardEvents() {
document.querySelectorAll('.template-card').forEach(card => {
card.addEventListener('click', (e) => {
if (!e.target.closest('.like-btn')) {
const templateId = card.dataset.id;
this.openTemplateModal(templateId);
}
});
});
document.querySelectorAll('.like-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const templateId = btn.dataset.id;
this.toggleLike(templateId);
});
});
}
openTemplateModal(templateId) {
const template = this.templates.find(t => t.id == templateId);
if (!template) return;
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4';
modal.innerHTML = `
<div class="bg-white dark:bg-gray-800 rounded-lg max-w-4xl w-full max-h-full overflow-auto">
<div class="relative">
<button class="absolute top-4 right-4 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 z-10">
<i data-feather="x" class="w-6 h-6"></i>
</button>
<img src="${template.image}" alt="${template.title}" class="w-full h-96 object-cover rounded-t-lg">
</div>
<div class="p-6">
<h2 class="text-2xl font-bold text-gray-800 dark:text-white mb-4">${template.title}</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div class="text-center">
<p class="text-gray-500 dark:text-gray-400 text-sm">Category</p>
<p class="font-semibold text-gray-800 dark:text-white">${template.category}</p>
</div>
<div class="text-center">
<p class="text-gray-500 dark:text-gray-400 text-sm">Size</p>
<p class="font-semibold text-gray-800 dark:text-white">${template.size}</p>
</div>
<div class="text-center">
<p class="text-gray-500 dark:text-gray-400 text-sm">Downloads</p>
<p class="font-semibold text-gray-800 dark:text-white">${template.downloads}</p>
</div>
<div class="text-center">
<p class="text-gray-500 dark:text-gray-400 text-sm">Likes</p>
<p class="font-semibold text-gray-800 dark:text-white">${template.likes}</p>
</div>
</div>
<div class="flex gap-4">
<button class="flex-1 bg-blue-500 hover:bg-blue-600 text-white font-semibold py-3 rounded-lg transition-colors">
<i data-feather="download" class="inline w-5 h-5 mr-2"></i>Download
</button>
<button class="flex-1 bg-purple-500 hover:bg-purple-600 text-white font-semibold py-3 rounded-lg transition-colors">
<i data-feather="heart" class="inline w-5 h-5 mr-2"></i>Like
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
modal.querySelector('button').addEventListener('click', () => {
document.body.removeChild(modal);
});
modal.addEventListener('click', (e) => {
if (e.target === modal) {
document.body.removeChild(modal);
}
});
feather.replace();
}
toggleLike(templateId) {
const template = this.templates.find(t => t.id == templateId);
if (template) {
template.likes += 1;
this.renderTemplates();
}
}
setupEventListeners() {
document.getElementById('gridView').addEventListener('click', () => {
this.currentView = 'grid';
document.getElementById('gridView').className = 'px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors';
document.getElementById('listView').className = 'px-6 py-3 bg-gray-300 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition-colors';
});
document.getElementById('listView').addEventListener('click', () => {
this.currentView = 'list';
document.getElementById('listView').className = 'px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors';
document.getElementById('gridView').className = 'px-6 py-3 bg-gray-300 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition-colors';
});
document.getElementById('categoryFilter').addEventListener('change', (e) => {
this.currentCategory = e.target.value;
this.renderTemplates();
});
document.getElementById('sizeFilter').addEventListener('change', (e) => {
this.currentSize = e.target.value;
this.renderTemplates();
});
}
setupThemeToggle() {
const themeToggle = document.createElement('button');
themeToggle.id = 'themeToggle';
themeToggle.className = 'fixed bottom-4 right-4 bg-gray-800 dark:bg-gray-200 text-white dark:text-gray-800 p-3 rounded-full shadow-lg hover:shadow-xl transition-all';
themeToggle.innerHTML = '<i data-feather="sun" class="w-5 h-5"></i>';
document.body.appendChild(themeToggle);
themeToggle.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
const isDark = document.documentElement.classList.contains('dark');
themeToggle.innerHTML = isDark ?
'<i data-feather="sun" class="w-5 h-5"></i>' :
'<i data-feather="moon" class="w-5 h-5"></i>';
feather.replace();
});
}
showLoading(show) {
const spinner = document.getElementById('loadingSpinner');
spinner.classList.toggle('hidden', !show);
}
}
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
new TemplateManager();
});