Files
vberp/src/routes/login/+page.svelte
AJISETIAJI 582284230a fix
2025-07-28 09:10:34 +07:00

321 lines
12 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import { supabase } from "$lib/supabaseClient";
import { goto } from "$app/navigation";
import logo from "$lib/images/villa.webp"; // Confirm this path and image is suitable
let email = "";
let password = "";
let error = "";
let isLoading = false;
let showPassword = false; // New state for password visibility
const togglePasswordVisibility = () => {
showPassword = !showPassword;
};
const handleLogin = async () => {
error = "";
isLoading = true;
try {
const { data, error: loginError } =
await supabase.auth.signInWithPassword({ email, password });
if (loginError) {
if (
loginError.message.includes("Invalid login credentials") ||
loginError.message.includes("Email not confirmed")
) {
error = "Incorrect email or password. Please try again.";
} else if (loginError.message.includes("network")) {
error =
"Network error. Please check your internet connection and try again.";
} else {
error = `Login failed: ${loginError.message}`;
}
} else {
const { data: dataUser, error: userDataError } = await supabase
.from("vb_users")
.select("*")
.eq("id", data.user.id)
.single();
if (userDataError) {
error =
"Failed to fetch user data. Please contact support if the issue persists.";
console.error("User data fetch error:", userDataError);
return; // Stop execution if user data fetch fails
} else {
localStorage.setItem("user", JSON.stringify(dataUser));
}
goto("/backoffice");
}
} catch (err) {
error = "An unexpected error occurred. Please try again.";
console.error("Login process error:", err);
} finally {
isLoading = false;
}
};
</script>
<div
class="min-h-screen flex flex-col md:flex-row items-center justify-center bg-gradient-to-br from-blue-50 to-purple-100 px-6 py-12 animate-fade-in"
>
<div class="hidden md:flex w-1/2 justify-center items-center p-8">
<img
src={logo}
alt="Login Illustration - Your App's Purpose"
class="w-full max-w-sm object-contain animate-float"
/>
</div>
<div
class="w-full max-w-md bg-white rounded-3xl shadow-2xl p-10 space-y-7 transform transition-transform duration-500 ease-out animate-slide-up"
>
<h2
class="text-4xl font-extrabold text-center text-gray-900 leading-tight"
>
Welcome Back <span class="wave-emoji">👋</span>
</h2>
<p class="text-center text-md text-gray-600">
Securely access your account to manage everything.
</p>
{#if error}
<div
role="alert"
aria-live="assertive"
class="bg-red-50 border border-red-300 text-red-700 text-sm p-4 rounded-xl transition-all duration-300 ease-in-out transform opacity-100 scale-100 shadow-sm"
>
<div class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 mr-2"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
{error}
</div>
</div>
{/if}
<form on:submit|preventDefault={handleLogin} class="space-y-6">
<div>
<label
for="email"
class="block text-sm font-medium text-gray-700 mb-2"
>Email Address</label
>
<input
id="email"
type="email"
bind:value={email}
placeholder="your.email@example.com"
class="w-full px-5 py-3 border border-gray-300 rounded-xl focus:ring-3 focus:ring-blue-400 focus:outline-none shadow-sm transition-all duration-200"
required
autocomplete="email"
/>
</div>
<div>
<label
for="password"
class="block text-sm font-medium text-gray-700 mb-2"
>Password</label
>
<div class="relative">
<input
id="password"
type={showPassword ? "text" : "password"}
bind:value={password}
placeholder="••••••••"
class="w-full px-5 py-3 border border-gray-300 rounded-xl focus:ring-3 focus:ring-blue-400 focus:outline-none shadow-sm transition-all duration-200 pr-12"
required
autocomplete="current-password"
/>
<button
type="button"
class="absolute inset-y-0 right-0 pr-4 flex items-center text-gray-500 hover:text-gray-700 transition"
on:click={togglePasswordVisibility}
aria-label={showPassword
? "Hide password"
: "Show password"}
>
{#if showPassword}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.249-.756A10.05 10.05 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.158 5.447M12 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
{/if}
</button>
</div>
<a
href="/forgot-password"
class="text-sm text-blue-600 hover:underline font-medium mt-2 block text-right"
>Forgot password?</a
>
</div>
<button
type="submit"
class="w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-xl transition duration-300 shadow-lg hover:shadow-xl flex items-center justify-center transform hover:-translate-y-0.5"
disabled={isLoading}
>
{#if isLoading}
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Authenticating...
{:else}
Login
{/if}
</button>
</form>
<!-- <p class="text-center text-sm text-gray-500">
Dont have an account?
<a
href="/register"
class="text-blue-600 hover:underline font-bold ml-1"
>Sign Up Now</a
>
</p> -->
</div>
</div>
<style>
/* Tailwind CSS animations or custom keyframes */
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slide-up {
from {
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes float {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
100% {
transform: translateY(0px);
}
}
@keyframes wave {
0% {
transform: rotate(0deg);
}
10% {
transform: rotate(14deg);
}
20% {
transform: rotate(-8deg);
}
30% {
transform: rotate(14deg);
}
40% {
transform: rotate(-4deg);
}
50% {
transform: rotate(10deg);
}
60% {
transform: rotate(0deg);
}
100% {
transform: rotate(0deg);
}
}
.animate-fade-in {
animation: fade-in 0.8s ease-out forwards;
}
.animate-slide-up {
animation: slide-up 0.7s ease-out forwards;
animation-delay: 0.2s; /* Delay form animation slightly */
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
.wave-emoji {
display: inline-block;
animation: wave 2.5s infinite;
transform-origin: 70% 70%;
}
</style>