Feat: Implement link creation form UI and logic

This commit is contained in:
2025-09-28 11:17:00 -07:00
parent 234670de33
commit 664b34c409

View File

@@ -31,22 +31,40 @@
<main class="flex-grow">
<div class="max-w-5xl mx-auto p-4 sm:p-6 lg:p-8">
<div class="max-w-2xl mx-auto">
<div class="bg-white p-8 rounded-xl shadow-sm border border-slate-200">
<div x-data="linkForm()" class="bg-white p-8 rounded-xl shadow-sm border border-slate-200">
<h1 class="text-2xl font-bold mb-1">Create a new link</h1>
<p class="text-slate-500 mb-6">Shorten a long URL into a memorable link.</p>
<form class="space-y-4">
<template x-if="result.url">
<div class="bg-green-100 border border-green-300 text-green-800 p-3.5 rounded-md mb-6" role="alert">
<p class="font-semibold">Success! Your link is ready:</p>
<div class="flex items-center gap-2 mt-2">
<a :href="result.url" x-text="result.url" target="_blank" class="font-mono text-indigo-600 hover:underline"></a>
<button @click="navigator.clipboard.writeText(result.url); copied = true; setTimeout(() => copied = false, 2000)" class="text-slate-500 hover:text-indigo-600">
<i data-lucide="copy" class="w-4 h-4" x-show="!copied"></i>
<i data-lucide="check" class="w-4 h-4 text-green-600" x-show="copied" style="display: none;"></i>
</button>
</div>
</div>
</template>
<form @submit.prevent="createLink" class="space-y-4">
<div>
<label for="longUrl" class="block text-sm font-medium text-slate-700 mb-1">Destination URL</label>
<input type="url" id="longUrl" placeholder="https://example.com/very-long-url-to-shorten" required class="w-full p-3 bg-slate-50 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition">
<input x-model="destination_url" type="url" id="longUrl" placeholder="https://example.com/very-long-url-to-shorten" required class="w-full p-3 bg-slate-50 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition">
</div>
<div>
<label for="customSlug" class="block text-sm font-medium text-slate-700 mb-1">Custom slug (optional)</label>
<div class="flex items-center">
<span class="p-3 bg-slate-100 border border-r-0 border-slate-300 rounded-l-md text-slate-500">4ev.link/</span>
<input type="text" id="customSlug" placeholder="my-awesome-link" class="w-full p-3 bg-slate-50 border border-slate-300 rounded-r-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition">
<input x-model="slug" type="text" id="customSlug" placeholder="my-awesome-link" class="w-full p-3 bg-slate-50 border border-slate-300 rounded-r-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition">
</div>
</div>
<button type="submit" class="w-full py-3 font-semibold rounded-lg text-white bg-indigo-600 hover:bg-indigo-700 transition-colors">Create Short Link</button>
<p x-text="error" x-show="error" class="text-rose-500 text-sm h-5"></p>
<button type="submit" :disabled="loading" class="w-full py-3 font-semibold rounded-lg text-white bg-indigo-600 hover:bg-indigo-700 transition-colors flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed">
<span x-show="!loading">Create Short Link</span>
<i x-show="loading" data-lucide="loader-2" class="animate-spin w-6 h-6"></i>
</button>
</form>
</div>
</div>
@@ -54,6 +72,9 @@
</main>
</div>
<script>lucide.createIcons();</script>
<script>
function linkForm(){return{destination_url:"",slug:"",loading:!1,error:"",result:{},copied:!1,async createLink(){this.loading=!0,this.error="",this.result={};try{const e=sessionStorage.getItem("username"),s=sessionStorage.getItem("pass_hash");if(!e||!s)throw new Error("Authentication error. Please log in again.");const t=await fetch("/api/links/create",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({destination_url:this.destination_url,slug:this.slug||null,username:e,pass_hash:s})});if(!t.ok)throw new Error(await t.text()||"Failed to create link.");const i=await t.json(),o=window.location.host;this.result={...i,url:`https://${o}/${i.slug}`},this.destination_url="",this.slug="",this.$nextTick(()=>lucide.createIcons())}catch(e){this.error=e.message}finally{this.loading=!1}}}}
lucide.createIcons();
</script>
</body>
</html>