url_shortener_form.html 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <!-- URL Shortener Form Component -->
  2. <div class="max-w-2xl mx-auto">
  3. <div class="bg-white rounded-2xl shadow-sm border border-gray-100 p-8 relative">
  4. <form id="shortenForm" class="space-y-6">
  5. <div class="relative">
  6. <input
  7. type="url"
  8. id="url"
  9. name="url"
  10. placeholder="Pega tu enlace aquí..."
  11. class="w-full px-6 py-4 text-lg border border-gray-200 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent transition-all duration-200 bg-gray-50 focus:bg-white"
  12. required
  13. >
  14. </div>
  15. <div class="relative">
  16. <label for="duration" class="block text-sm font-medium text-gray-700 mb-2">
  17. <i class="fas fa-clock mr-1"></i>Duración del enlace
  18. </label>
  19. <select
  20. id="duration"
  21. name="duration"
  22. class="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-primary focus:border-transparent transition-all duration-200 bg-gray-50 focus:bg-white"
  23. >
  24. <option value="never">Nunca expira</option>
  25. <option value="1hour">1 hora</option>
  26. <option value="1day">1 día</option>
  27. <option value="1week">1 semana</option>
  28. <option value="1month">1 mes</option>
  29. <option value="1year">1 año</option>
  30. </select>
  31. </div>
  32. <button
  33. type="submit"
  34. class="w-full bg-primary hover:bg-secondary text-white font-medium py-4 px-6 rounded-xl transition-all duration-200 text-lg"
  35. >
  36. Acortar enlace
  37. </button>
  38. </form>
  39. <!-- Success Animation (overlays the form) -->
  40. <div id="successAnimation" class="hidden absolute inset-0 bg-white bg-opacity-95 flex items-center justify-center rounded-2xl z-10">
  41. <div class="text-center">
  42. <div id="checkmarkContainer" class="w-24 h-24 mx-auto mb-4 relative">
  43. <div class="w-24 h-24 bg-green-500 rounded-full flex items-center justify-center transform scale-0 transition-transform duration-600 ease-out" id="checkmarkCircle">
  44. <i class="fas fa-check text-white text-4xl opacity-0 transition-opacity duration-400" id="checkmarkIcon"></i>
  45. </div>
  46. </div>
  47. <h3 class="text-2xl font-bold text-green-600 opacity-0 transition-opacity duration-500" id="successText">¡Enlace acortado!</h3>
  48. </div>
  49. </div>
  50. <!-- Result Section -->
  51. <div id="result" class="hidden p-6 bg-green-50 border border-green-200 rounded-xl transform translate-y-4 opacity-0 transition-all duration-500 ease-out">
  52. <div class="text-center mb-4">
  53. <i class="fas fa-check-circle text-green-600 text-2xl mb-2"></i>
  54. <h3 class="text-lg font-medium text-green-800">¡Listo!</h3>
  55. <p class="text-green-700">Tu enlace ha sido acortado exitosamente</p>
  56. </div>
  57. <div class="space-y-4">
  58. <div>
  59. <label class="block text-sm font-medium text-green-700 mb-2">Tu enlace acortado:</label>
  60. <div class="flex items-center space-x-3">
  61. <input
  62. type="text"
  63. id="shortUrl"
  64. readonly
  65. class="flex-1 font-mono bg-white p-3 rounded-lg border focus:outline-none text-center"
  66. >
  67. <button
  68. onclick="copyToClipboard(document.getElementById('shortUrl').value)"
  69. class="bg-primary hover:bg-secondary text-white px-4 py-3 rounded-lg transition-colors duration-200 flex-shrink-0"
  70. title="Copiar enlace"
  71. >
  72. <i class="fas fa-copy"></i>
  73. </button>
  74. </div>
  75. </div>
  76. <div class="text-center pt-4">
  77. <button
  78. onclick="resetForm()"
  79. class="bg-gray-600 hover:bg-gray-700 text-white font-medium py-3 px-6 rounded-xl transition-all duration-200"
  80. >
  81. <i class="fas fa-plus mr-2"></i>Acortar otro enlace
  82. </button>
  83. </div>
  84. </div>
  85. </div>
  86. </div>
  87. </div>
  88. <script>
  89. // Form submission handler
  90. document.getElementById('shortenForm').addEventListener('submit', async function(e) {
  91. e.preventDefault();
  92. const url = document.getElementById('url').value;
  93. const duration = document.getElementById('duration').value;
  94. const resultDiv = document.getElementById('result');
  95. const shortUrlInput = document.getElementById('shortUrl');
  96. try {
  97. const response = await fetch('/shorten', {
  98. method: 'POST',
  99. headers: {
  100. 'Content-Type': 'application/json',
  101. },
  102. body: JSON.stringify({ url: url, duration: duration })
  103. });
  104. const data = await response.json();
  105. if (data.success) {
  106. shortUrlInput.value = data.short_url;
  107. // Show success animation first
  108. showSuccessAnimation(() => {
  109. // Hide form and show result after animation
  110. document.getElementById('shortenForm').style.display = 'none';
  111. resultDiv.classList.remove('hidden');
  112. // Animate result appearance
  113. setTimeout(() => {
  114. resultDiv.style.transform = 'translateY(0)';
  115. resultDiv.style.opacity = '1';
  116. }, 50);
  117. showToast('¡Enlace acortado exitosamente!', 'success');
  118. });
  119. } else {
  120. showToast(data.error || 'Error al acortar el enlace', 'error');
  121. }
  122. } catch (error) {
  123. showToast('Error de conexión', 'error');
  124. }
  125. });
  126. // Function to show success animation
  127. function showSuccessAnimation(callback) {
  128. const successAnimation = document.getElementById('successAnimation');
  129. const checkmarkCircle = document.getElementById('checkmarkCircle');
  130. const checkmarkIcon = document.getElementById('checkmarkIcon');
  131. const successText = document.getElementById('successText');
  132. // Show animation container
  133. successAnimation.classList.remove('hidden');
  134. // Animate checkmark circle
  135. setTimeout(() => {
  136. checkmarkCircle.style.transform = 'scale(1)';
  137. }, 100);
  138. // Show checkmark icon
  139. setTimeout(() => {
  140. checkmarkIcon.style.opacity = '1';
  141. }, 400);
  142. // Show success text
  143. setTimeout(() => {
  144. successText.style.opacity = '1';
  145. }, 700);
  146. // Hide animation and show result
  147. setTimeout(() => {
  148. successAnimation.classList.add('hidden');
  149. // Reset animation states
  150. checkmarkCircle.style.transform = 'scale(0)';
  151. checkmarkIcon.style.opacity = '0';
  152. successText.style.opacity = '0';
  153. // Execute callback to show result
  154. if (callback) callback();
  155. }, 2000);
  156. }
  157. // Function to reset form
  158. function resetForm() {
  159. const resultDiv = document.getElementById('result');
  160. // Animate result disappearance
  161. resultDiv.style.transform = 'translateY(4px)';
  162. resultDiv.style.opacity = '0';
  163. setTimeout(() => {
  164. document.getElementById('shortenForm').style.display = 'block';
  165. resultDiv.classList.add('hidden');
  166. document.getElementById('url').value = '';
  167. document.getElementById('shortUrl').value = '';
  168. // Reset result animation state
  169. resultDiv.style.transform = 'translateY(4px)';
  170. resultDiv.style.opacity = '0';
  171. }, 300);
  172. }
  173. </script>