|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Tools Collection</title> |
|
<style> |
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
min-height: 100vh; |
|
padding: 20px; |
|
} |
|
|
|
.container { |
|
max-width: 1200px; |
|
margin: 0 auto; |
|
background: white; |
|
border-radius: 20px; |
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1); |
|
overflow: hidden; |
|
} |
|
|
|
.header { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
color: white; |
|
padding: 40px 30px; |
|
text-align: center; |
|
} |
|
|
|
.header h1 { |
|
font-size: 3rem; |
|
margin-bottom: 15px; |
|
font-weight: 300; |
|
} |
|
|
|
.header p { |
|
font-size: 1.2rem; |
|
opacity: 0.9; |
|
margin-bottom: 30px; |
|
} |
|
|
|
.search-container { |
|
max-width: 500px; |
|
margin: 0 auto; |
|
position: relative; |
|
margin-bottom: 20px; |
|
} |
|
|
|
.search-box { |
|
width: 100%; |
|
padding: 15px 50px 15px 20px; |
|
border: none; |
|
border-radius: 50px; |
|
font-size: 1.1rem; |
|
background: rgba(255, 255, 255, 0.9); |
|
backdrop-filter: blur(10px); |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.search-box:focus { |
|
outline: none; |
|
background: white; |
|
box-shadow: 0 5px 20px rgba(0,0,0,0.1); |
|
} |
|
|
|
.search-icon { |
|
position: absolute; |
|
right: 20px; |
|
top: 50%; |
|
transform: translateY(-50%); |
|
font-size: 1.2rem; |
|
color: #667eea; |
|
} |
|
|
|
.filter-container { |
|
max-width: 300px; |
|
margin: 0 auto; |
|
} |
|
|
|
.category-dropdown { |
|
width: 100%; |
|
padding: 12px 20px; |
|
border: none; |
|
border-radius: 50px; |
|
font-size: 1rem; |
|
background: rgba(255, 255, 255, 0.9); |
|
backdrop-filter: blur(10px); |
|
color: #495057; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.category-dropdown:focus { |
|
outline: none; |
|
background: white; |
|
box-shadow: 0 5px 20px rgba(0,0,0,0.1); |
|
} |
|
|
|
.content { |
|
padding: 40px; |
|
} |
|
|
|
.features-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); |
|
gap: 25px; |
|
margin-top: 30px; |
|
} |
|
|
|
.feature-card { |
|
background: white; |
|
border-radius: 15px; |
|
padding: 30px; |
|
border: 1px solid #e9ecef; |
|
transition: all 0.3s ease; |
|
cursor: pointer; |
|
text-decoration: none; |
|
color: inherit; |
|
display: block; |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.feature-card::before { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
right: 0; |
|
height: 4px; |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
transform: scaleX(0); |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.feature-card:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 15px 40px rgba(0,0,0,0.1); |
|
} |
|
|
|
.feature-card:hover::before { |
|
transform: scaleX(1); |
|
} |
|
|
|
.feature-card.coming-soon { |
|
opacity: 0.6; |
|
cursor: not-allowed; |
|
} |
|
|
|
.feature-card.coming-soon:hover { |
|
transform: none; |
|
box-shadow: none; |
|
} |
|
|
|
.feature-header { |
|
display: flex; |
|
align-items: center; |
|
gap: 15px; |
|
margin-bottom: 15px; |
|
} |
|
|
|
.feature-icon { |
|
font-size: 2.5rem; |
|
width: 60px; |
|
height: 60px; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
background: linear-gradient(135deg, #667eea15, #764ba215); |
|
border-radius: 12px; |
|
} |
|
|
|
.feature-card h3 { |
|
color: #495057; |
|
font-size: 1.4rem; |
|
font-weight: 600; |
|
} |
|
|
|
.feature-card p { |
|
color: #6c757d; |
|
margin-bottom: 20px; |
|
line-height: 1.6; |
|
} |
|
|
|
.feature-status { |
|
display: flex; |
|
align-items: center; |
|
gap: 10px; |
|
margin-bottom: 15px; |
|
} |
|
|
|
.status-indicator { |
|
width: 12px; |
|
height: 12px; |
|
border-radius: 50%; |
|
background: #28a745; |
|
} |
|
|
|
.status-indicator.busy { |
|
background: #ffc107; |
|
animation: pulse 2s infinite; |
|
} |
|
|
|
.status-indicator.coming-soon { |
|
background: #6c757d; |
|
} |
|
|
|
@keyframes pulse { |
|
0% { transform: scale(1); opacity: 1; } |
|
50% { transform: scale(1.2); opacity: 0.7; } |
|
100% { transform: scale(1); opacity: 1; } |
|
} |
|
|
|
.feature-tags { |
|
display: flex; |
|
flex-wrap: wrap; |
|
gap: 8px; |
|
margin-top: 15px; |
|
} |
|
|
|
.tag { |
|
background: #f8f9fa; |
|
color: #495057; |
|
padding: 4px 12px; |
|
border-radius: 20px; |
|
font-size: 0.8rem; |
|
border: 1px solid #e9ecef; |
|
} |
|
|
|
.coming-soon-badge { |
|
position: absolute; |
|
top: 15px; |
|
right: 15px; |
|
background: #6c757d; |
|
color: white; |
|
padding: 4px 12px; |
|
border-radius: 20px; |
|
font-size: 0.8rem; |
|
font-weight: 500; |
|
} |
|
|
|
.stat-item h4 { |
|
color: #667eea; |
|
font-size: 1.5rem; |
|
margin-bottom: 5px; |
|
} |
|
|
|
.stat-item p { |
|
color: #6c757d; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.no-results { |
|
text-align: center; |
|
padding: 60px 20px; |
|
color: #6c757d; |
|
} |
|
|
|
.no-results .icon { |
|
font-size: 4rem; |
|
margin-bottom: 20px; |
|
opacity: 0.5; |
|
} |
|
|
|
.feature-path { |
|
color: #667eea; |
|
font-size: 0.9rem; |
|
font-weight: 500; |
|
margin-bottom: 5px; |
|
} |
|
|
|
@media (max-width: 768px) { |
|
.header h1 { |
|
font-size: 2rem; |
|
} |
|
|
|
.header p { |
|
font-size: 1rem; |
|
} |
|
|
|
.content { |
|
padding: 20px; |
|
} |
|
|
|
.features-grid { |
|
grid-template-columns: 1fr; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<div class="header"> |
|
<h1>🛠️ Tools Collection</h1> |
|
<p>Simple, fast, and reliable utility tools for your daily needs</p> |
|
|
|
<div class="search-container"> |
|
<input type="text" id="search-box" class="search-box" placeholder="Search tools... (e.g., image, pdf, convert)"> |
|
<span class="search-icon">🔍</span> |
|
</div> |
|
|
|
<div class="filter-container"> |
|
<select id="category-dropdown" class="category-dropdown"> |
|
<option value="">All Categories</option> |
|
</select> |
|
</div> |
|
</div> |
|
|
|
<div class="content"> |
|
<div class="features-grid" id="features-grid"> |
|
|
|
</div> |
|
|
|
<div class="no-results" id="no-results" style="display: none;"> |
|
<div class="icon">🔍</div> |
|
<h3>No tools found</h3> |
|
<p>Try searching with different keywords like "image", "pdf", or "convert"</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
let allFeatures = {}; |
|
let expandedFeatures = []; |
|
let currentCategory = ''; |
|
let currentSearch = ''; |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => { |
|
await loadFeatures(); |
|
setupSearch(); |
|
setupCategoryFilter(); |
|
}); |
|
|
|
async function loadFeatures() { |
|
try { |
|
const response = await fetch('/api/features'); |
|
if (response.ok) { |
|
const data = await response.json(); |
|
allFeatures = data.features; |
|
expandFeatures(); |
|
populateCategories(); |
|
displayFeatures(expandedFeatures); |
|
} |
|
} catch (error) { |
|
console.error('Error loading features:', error); |
|
|
|
loadMockData(); |
|
} |
|
} |
|
|
|
function loadMockData() { |
|
allFeatures = { |
|
"image": { |
|
"name": "Image Tools", |
|
"description": "HEIC to PNG/JPG conversion and metadata removal", |
|
"icon": "🖼️", |
|
"features": ["convert", "remove_metadata"], |
|
"folder": "image", |
|
"tags": ["image", "heic", "png", "jpg", "convert", "metadata"] |
|
}, |
|
"pdf": { |
|
"name": "PDF Tools", |
|
"description": "Convert images to PDF, merge PDFs, and more", |
|
"icon": "📄", |
|
"features": ["images_to_pdf"], |
|
"folder": "pdf", |
|
"tags": ["pdf", "merge", "convert", "images", "document"], |
|
"coming_soon": true |
|
}, |
|
"audio": { |
|
"name": "Audio Tools", |
|
"description": "Convert audio formats and compress audio files", |
|
"icon": "🎵", |
|
"features": ["convert_audio", "compress_audio"], |
|
"folder": "audio", |
|
"tags": ["audio", "music", "convert", "compress", "mp3", "wav"], |
|
"coming_soon": true |
|
}, |
|
"video": { |
|
"name": "Video Tools", |
|
"description": "Basic video editing and format conversion", |
|
"icon": "🎬", |
|
"features": ["convert_video", "compress_video"], |
|
"folder": "video", |
|
"tags": ["video", "convert", "compress", "mp4", "avi", "editing"], |
|
"coming_soon": true |
|
} |
|
}; |
|
expandFeatures(); |
|
populateCategories(); |
|
displayFeatures(expandedFeatures); |
|
} |
|
|
|
function expandFeatures() { |
|
expandedFeatures = []; |
|
|
|
Object.entries(allFeatures).forEach(([categoryKey, categoryData]) => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (categoryData.features && categoryData.features.length > 0) { |
|
categoryData.features.forEach(feature => { |
|
expandedFeatures.push({ |
|
id: `${categoryKey}-${feature}`, |
|
path: `${categoryKey}/${feature}`, |
|
name: `${categoryData.name} - ${formatFeatureName(feature)}`, |
|
description: getFeatureDescription(categoryKey, feature), |
|
icon: categoryData.icon, |
|
tags: [...(categoryData.tags || []), feature], |
|
coming_soon: categoryData.coming_soon || false, |
|
category: categoryKey, |
|
type: 'feature' |
|
}); |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
function formatFeatureName(feature) { |
|
return feature.split('_') |
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1)) |
|
.join(' '); |
|
} |
|
|
|
function getFeatureDescription(category, feature) { |
|
const descriptions = { |
|
'image': { |
|
'convert': 'Convert HEIC images to PNG/JPG format', |
|
'remove_metadata': 'Remove metadata from image files' |
|
}, |
|
'pdf': { |
|
'images_to_pdf': 'Convert multiple images into a single PDF' |
|
}, |
|
'audio': { |
|
'convert_audio': 'Convert between different audio formats', |
|
'compress_audio': 'Reduce audio file size while maintaining quality' |
|
}, |
|
'video': { |
|
'convert_video': 'Convert between different video formats', |
|
'compress_video': 'Reduce video file size while maintaining quality' |
|
} |
|
}; |
|
|
|
return descriptions[category]?.[feature] || `${formatFeatureName(feature)} functionality`; |
|
} |
|
|
|
function populateCategories() { |
|
const dropdown = document.getElementById('category-dropdown'); |
|
const categories = Object.keys(allFeatures); |
|
|
|
categories.forEach(category => { |
|
const option = document.createElement('option'); |
|
option.value = category; |
|
option.textContent = allFeatures[category].name; |
|
dropdown.appendChild(option); |
|
}); |
|
} |
|
|
|
function displayFeatures(features) { |
|
const grid = document.getElementById('features-grid'); |
|
const noResults = document.getElementById('no-results'); |
|
|
|
if (features.length === 0) { |
|
grid.style.display = 'none'; |
|
noResults.style.display = 'block'; |
|
return; |
|
} |
|
|
|
grid.style.display = 'grid'; |
|
noResults.style.display = 'none'; |
|
|
|
grid.innerHTML = features.map((feature) => { |
|
const isComingSoon = feature.coming_soon || false; |
|
const href = isComingSoon ? '#' : `/${feature.path}`; |
|
|
|
return ` |
|
<a href="${href}" class="feature-card ${isComingSoon ? 'coming-soon' : ''}" |
|
${isComingSoon ? 'onclick="return false;"' : ''}> |
|
${isComingSoon ? '<div class="coming-soon-badge">Coming Soon</div>' : ''} |
|
|
|
<div class="feature-path">${feature.path}</div> |
|
|
|
<div class="feature-header"> |
|
<div class="feature-icon">${feature.icon}</div> |
|
<h3>${feature.name}</h3> |
|
</div> |
|
|
|
<p>${feature.description}</p> |
|
|
|
<div class="feature-status"> |
|
<div class="status-indicator ${isComingSoon ? 'coming-soon' : ''}" |
|
id="${feature.id}-status"></div> |
|
<span id="${feature.id}-status-text">${isComingSoon ? 'Coming Soon' : 'Ready'}</span> |
|
</div> |
|
|
|
<div class="feature-tags"> |
|
${feature.tags ? feature.tags.map(tag => `<span class="tag">${tag}</span>`).join('') : ''} |
|
</div> |
|
</a> |
|
`; |
|
}).join(''); |
|
} |
|
|
|
function setupSearch() { |
|
const searchBox = document.getElementById('search-box'); |
|
let debounceTimer; |
|
|
|
searchBox.addEventListener('input', (e) => { |
|
clearTimeout(debounceTimer); |
|
debounceTimer = setTimeout(() => { |
|
currentSearch = e.target.value.toLowerCase(); |
|
filterFeatures(); |
|
}, 300); |
|
}); |
|
} |
|
|
|
function setupCategoryFilter() { |
|
const dropdown = document.getElementById('category-dropdown'); |
|
|
|
dropdown.addEventListener('change', (e) => { |
|
currentCategory = e.target.value; |
|
filterFeatures(); |
|
}); |
|
} |
|
|
|
function filterFeatures() { |
|
let filteredFeatures = expandedFeatures; |
|
|
|
|
|
if (currentCategory) { |
|
filteredFeatures = filteredFeatures.filter(feature => |
|
feature.category === currentCategory |
|
); |
|
} |
|
|
|
|
|
if (currentSearch) { |
|
filteredFeatures = filteredFeatures.filter(feature => |
|
feature.name.toLowerCase().includes(currentSearch) || |
|
feature.description.toLowerCase().includes(currentSearch) || |
|
feature.path.toLowerCase().includes(currentSearch) || |
|
feature.tags.some(tag => tag.toLowerCase().includes(currentSearch)) |
|
); |
|
} |
|
|
|
displayFeatures(filteredFeatures); |
|
} |
|
</script> |
|
</body> |
|
</html> |