shortner / templates /index.html
triflix's picture
Create templates/index.html
823219d verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Shortener</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: #000000;
--text-primary: #ffffff;
--text-secondary: #888888;
--accent: #FF3B30;
}
body {
font-family: 'Roboto', sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
overflow-x: hidden;
}
.input-container {
transition: transform 0.3s ease-out;
}
.input-container.focused {
transform: translateY(-60vh);
}
.history-item {
transition: all 0.3s ease;
}
.history-item:hover {
background: rgba(255, 255, 255, 0.05);
}
@keyframes glowAnimation {
0% { box-shadow: 0 0 0 rgba(255, 59, 48, 0); }
50% { box-shadow: 0 0 20px rgba(255, 59, 48, 0.3); }
100% { box-shadow: 0 0 0 rgba(255, 59, 48, 0); }
}
.success-animation {
animation: glowAnimation 1s ease-out;
}
#developerInfo {
transition: all 0.3s ease-out;
}
#developerInfo > div {
transition: all 0.3s ease-out;
}
.social-link {
transition: all 0.2s ease;
}
.social-link:hover {
transform: translateY(-2px);
}
</style>
</head>
<body class="p-0 m-0">
<!-- Time and Location -->
<div class="fixed top-0 left-0 right-0 p-6 z-10">
<div class="text-2xl font-light" id="time"></div>
<div class="text-sm text-gray-400" id="date"></div>
</div>
<!-- History Section -->
<div class="pt-24 px-6 pb-32">
<div class="text-xs uppercase tracking-wider text-gray-500 mb-4">Recent Links</div>
<div id="history" class="space-y-4">
<!-- History items will be added here -->
</div>
</div>
<!-- Bottom Input Section -->
<div id="inputContainer" class="input-container fixed bottom-0 left-0 right-0 p-6 bg-black">
<form id="urlForm" class="space-y-4">
<div class="relative">
<input type="text" id="url"
class="w-full bg-white/10 text-white px-4 py-3 rounded-2xl border-0
focus:outline-none focus:ring-2 focus:ring-red-500/30
placeholder-gray-500 text-base"
placeholder="Paste your URL here"
autocomplete="off">
</div>
<button type="submit"
class="w-full bg-white/10 text-white py-4 rounded-2xl font-medium
active:scale-[0.98] transition-all duration-300
hover:bg-red-500/20 focus:outline-none">
Shorten URL
</button>
</form>
<!-- Result Section -->
<div id="result" class="mt-4 hidden">
<div class="bg-white/5 rounded-2xl p-4">
<div class="flex items-center justify-between">
<input type="text" id="shortUrl" readonly
class="flex-1 bg-transparent border-none focus:outline-none
text-white font-light">
<button onclick="copyToClipboard()"
class="ml-2 p-2 rounded-xl bg-white/5 text-white/80 hover:bg-white/10
transition-colors duration-200">
Copy
</button>
</div>
</div>
</div>
</div>
<!-- Developer Info Popup -->
<div id="developerInfo" class="fixed inset-0 z-50 flex items-center justify-center px-4 bg-black/80 backdrop-blur-sm">
<div class="bg-white/10 rounded-3xl p-6 max-w-md w-full backdrop-blur-md border border-white/10">
<div class="flex justify-between items-start mb-6">
<div class="text-xl font-light">About Developer</div>
<button onclick="closeDeveloperInfo()"
class="text-white/60 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<div class="space-y-4">
<p class="text-gray-300 leading-relaxed">
Aditya Devarshi is an AI Engineer passionate about creating innovative projects
and continuous learning. This URL shortener is one of his many projects
showcasing modern design principles and functionality.
</p>
<div class="space-y-3 mt-6">
<div class="text-sm text-gray-400 uppercase tracking-wider">Connect & Follow</div>
<div class="grid grid-cols-2 gap-3">
<a href="https://www.adityadevarshi.online/" target="_blank" rel="noopener"
class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10
transition-colors text-sm text-white/80 hover:text-white">
<span>🌐</span>
<span>Portfolio</span>
</a>
<a href="https://www.linkedin.com/in/aditya-devarshi/" target="_blank" rel="noopener"
class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10
transition-colors text-sm text-white/80 hover:text-white">
<span>πŸ’Ό</span>
<span>LinkedIn</span>
</a>
<a href="https://github.com/devarshiadi/" target="_blank" rel="noopener"
class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10
transition-colors text-sm text-white/80 hover:text-white">
<span>πŸ“¦</span>
<span>GitHub</span>
</a>
<a href="https://medium.com/@devarshia5" target="_blank" rel="noopener"
class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10
transition-colors text-sm text-white/80 hover:text-white">
<span>πŸ“</span>
<span>Medium</span>
</a>
</div>
</div>
<div class="mt-6 text-center">
<button onclick="closeDeveloperInfo()"
class="px-6 py-2 rounded-xl bg-white/10 text-white/80 hover:bg-white/20
transition-all duration-300 text-sm">
Close
</button>
</div>
</div>
</div>
</div>
<script>
// Initialize localStorage history
let urlHistory = JSON.parse(localStorage.getItem('urlHistory') || '[]');
updateHistoryDisplay(); // Show saved history on load
// Time and Date Update
function updateDateTime() {
const now = new Date();
const timeString = now.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit'
});
const dateString = now.toLocaleDateString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric'
});
document.getElementById('time').textContent = timeString;
document.getElementById('date').textContent = dateString;
}
updateDateTime();
setInterval(updateDateTime, 1000);
// Input Focus Behavior with improved UX
const inputContainer = document.getElementById('inputContainer');
const urlInput = document.getElementById('url');
const historySection = document.querySelector('.pt-24');
urlInput.addEventListener('focus', () => {
inputContainer.classList.add('focused');
historySection.style.opacity = '0.3';
navigator.vibrate(1);
});
document.addEventListener('click', (e) => {
if (!inputContainer.contains(e.target)) {
inputContainer.classList.remove('focused');
historySection.style.opacity = '1';
}
});
// Clear input on successful submission
function resetForm() {
urlInput.value = '';
document.getElementById('result').classList.add('hidden');
}
// Form Submission with improved error handling
document.getElementById('urlForm').addEventListener('submit', async (e) => {
e.preventDefault();
const url = urlInput.value.trim();
if (!url) {
showNotification('Please enter a URL', 'error');
return;
}
if (!url.startsWith('http://') && !url.startsWith('https://')) {
showNotification('Please enter a valid URL starting with http:// or https://', 'error');
return;
}
const submitButton = e.target.querySelector('button');
submitButton.disabled = true;
submitButton.innerHTML = '<span class="inline-block animate-pulse">Shortening...</span>';
try {
const response = await fetch('/shorten/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ original_url: url })
});
const data = await response.json();
if (response.ok) {
document.getElementById('result').classList.remove('hidden');
document.getElementById('shortUrl').value = data.shortened_url;
// Add to history and save to localStorage
addToHistory(url, data.shortened_url);
// Success animation and feedback
inputContainer.classList.add('success-animation');
setTimeout(() => inputContainer.classList.remove('success-animation'), 1000);
navigator.vibrate([50, 50, 50]);
// Clear input after short delay
setTimeout(resetForm, 3000);
} else {
showNotification(data.detail || 'Error shortening URL', 'error');
}
} catch (error) {
showNotification('Network error occurred', 'error');
} finally {
submitButton.disabled = false;
submitButton.textContent = 'Shorten URL';
}
});
function addToHistory(originalUrl, shortUrl) {
const historyItem = {
original: originalUrl,
shortened: shortUrl,
timestamp: new Date().toISOString()
};
urlHistory.unshift(historyItem);
if (urlHistory.length > 10) urlHistory.pop(); // Keep last 10 items
// Save to localStorage
localStorage.setItem('urlHistory', JSON.stringify(urlHistory));
updateHistoryDisplay();
}
function updateHistoryDisplay() {
const historyContainer = document.getElementById('history');
if (urlHistory.length === 0) {
historyContainer.innerHTML = `
<div class="text-center text-gray-500 py-8">
No shortened URLs yet
</div>
`;
return;
}
historyContainer.innerHTML = urlHistory.map(item => `
<div class="history-item rounded-xl p-4 bg-white/5 backdrop-blur-sm">
<div class="flex justify-between items-start">
<div class="text-sm font-light text-gray-400 truncate flex-1">
${item.original}
</div>
<div class="text-xs text-gray-500 ml-2">
${new Date(item.timestamp).toLocaleDateString()}
</div>
</div>
<div class="flex justify-between items-center mt-2">
<div class="text-white truncate flex-1">${item.shortened}</div>
<div class="flex gap-2">
<button onclick="copyUrl('${item.shortened}')"
class="text-xs text-white/60 hover:text-white px-3 py-1 rounded-lg bg-white/10 transition-colors">
Copy
</button>
<button onclick="deleteHistoryItem('${item.timestamp}')"
class="text-xs text-red-400/60 hover:text-red-400 px-3 py-1 rounded-lg bg-red-500/10 transition-colors">
Delete
</button>
</div>
</div>
</div>
`).join('');
}
function deleteHistoryItem(timestamp) {
urlHistory = urlHistory.filter(item => item.timestamp !== timestamp);
localStorage.setItem('urlHistory', JSON.stringify(urlHistory));
updateHistoryDisplay();
navigator.vibrate(50);
}
function copyUrl(url) {
navigator.clipboard.writeText(url).then(() => {
showNotification('Copied to clipboard', 'success');
navigator.vibrate(50);
});
}
function copyToClipboard() {
const shortUrl = document.getElementById('shortUrl');
navigator.clipboard.writeText(shortUrl.value).then(() => {
showNotification('Copied to clipboard', 'success');
navigator.vibrate(50);
});
}
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `fixed bottom-32 left-1/2 transform -translate-x-1/2
px-6 py-3 rounded-2xl text-white text-center text-sm
${type === 'error' ? 'bg-red-500/90' : 'bg-white/10'}
opacity-0 transition-opacity duration-300 z-50
backdrop-blur-sm shadow-lg`;
notification.textContent = message;
document.body.appendChild(notification);
requestAnimationFrame(() => notification.classList.add('opacity-100'));
setTimeout(() => {
notification.classList.remove('opacity-100');
setTimeout(() => notification.remove(), 300);
}, 2000);
}
function closeDeveloperInfo() {
const popup = document.getElementById('developerInfo');
popup.style.opacity = '0';
popup.style.transform = 'scale(0.95)';
setTimeout(() => {
popup.style.display = 'none';
}, 300);
// Save to localStorage so it doesn't show again in this session
localStorage.setItem('developerInfoShown', 'true');
}
// Check if we should show the popup
window.addEventListener('DOMContentLoaded', () => {
const popup = document.getElementById('developerInfo');
if (!localStorage.getItem('developerInfoShown')) {
popup.style.display = 'flex';
popup.style.opacity = '0';
popup.style.transform = 'scale(0.95)';
// Trigger animation
setTimeout(() => {
popup.style.opacity = '1';
popup.style.transform = 'scale(1)';
}, 100);
} else {
popup.style.display = 'none';
}
// Console Developer Info
console.log(`%c
╔══════════════════════════════════════════════════════════════╗
β•‘ Aditya Devarshi β•‘
β•‘ AI Engineer & Full Stack Developer β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ β•‘
β•‘ 🌐 Portfolio: https://www.adityadevarshi.online β•‘
β•‘ πŸ’Ό LinkedIn: https://www.linkedin.com/in/aditya-devarshi β•‘
β•‘ πŸ“¦ GitHub: https://github.com/devarshiadi β•‘
β•‘ πŸ“ Medium: https://medium.com/@devarshia5 β•‘
β•‘ β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ β•‘
β•‘ πŸš€ Skills: AI/ML, Full Stack Development, Cloud Computing β•‘
β•‘ πŸ“§ Contact: devarshia5@gmail.com β•‘
β•‘ β•‘
β•‘ πŸ’‘ "Building the future with AI and innovation" β•‘
β•‘ β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
`, 'font-family: monospace; color: #00ff00; font-size: 12px;');
console.log('%cWelcome to URL Shortener!', 'color: #FF3B30; font-size: 20px; font-weight: bold;');
console.log('%cDeveloped with ❀️ by Aditya Devarshi', 'color: #888; font-size: 14px;');
// Tech stack info
console.log('%cTech Stack:', 'color: #00ff00; font-size: 16px; font-weight: bold;');
console.table({
'Frontend': ['HTML5', 'TailwindCSS', 'JavaScript'],
'Backend': ['Python', 'FastAPI', 'SQLite'],
'Deployment': ['Docker', 'Cloud Ready'],
'Design': ['Nothing OS Inspired', 'Modern Minimal UI']
});
// Contact info as an object
console.log('%cContact Information:', 'color: #00ff00; font-size: 16px; font-weight: bold;');
console.table({
'Email': 'devarshia5@gmail.com',
'LinkedIn': 'https://www.linkedin.com/in/aditya-devarshi',
'Portfolio': 'https://www.adityadevarshi.online',
'GitHub': 'https://github.com/devarshiadi',
'Medium': 'https://medium.com/@devarshia5'
});
// Project info
console.log('%cProject Information:', 'color: #00ff00; font-size: 16px; font-weight: bold;');
console.table({
'Name': 'Modern URL Shortener',
'Version': '1.0.0',
'License': 'MIT',
'Repository': 'https://github.com/devarshiadi/url-shortener',
'Design Inspiration': 'Nothing OS',
'Features': [
'Modern UI/UX',
'URL Shortening',
'History Management',
'Responsive Design',
'Dark Mode',
'Local Storage'
]
});
// Easter egg message
console.log('%cπŸ‘‹ Hey there, curious developer!', 'color: #FF3B30; font-size: 16px; font-weight: bold;');
console.log('%cLooking for opportunities or collaboration? Feel free to reach out!', 'color: #888; font-size: 14px;');
});
</script>
</body>
</html>