Skip to main content

Command Palette

Search for a command to run...

Split Login page using tailwind css & shadcn

Published
3 min read
import { useState } from "react"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
import { Link, useNavigate } from "react-router-dom"
import { toast } from "sonner"
import { Loader2, Eye, EyeOff, CheckCircle2, Zap } from "lucide-react"
import { Button } from "@hms/shared/components/ui/button"
import { Input } from "@hms/shared/components/ui/input"
import { Label } from "@hms/shared/components/ui/label"

const loginSchema = z.object({
  email: z.string().email("Please enter a valid email address"),
  password: z.string().min(6, "Password must be at least 6 characters"),
})

type LoginFormValues = z.infer<typeof loginSchema>

export default function SplitLogin() {
  const navigate = useNavigate()
  const [showPassword, setShowPassword] = useState(false)

  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<LoginFormValues>({
    resolver: zodResolver(loginSchema),
  })

  async function onSubmit(data: LoginFormValues) {
    await new Promise((r) => setTimeout(r, 1000))
    localStorage.setItem("access_token", "mock-token")
    localStorage.setItem(
      "user",
      JSON.stringify({ email: data.email, name: data.email.split("@")[0], role: "PATIENT" })
    )
    toast.success("Welcome back!")
    navigate("/dashboard")
  }

  return (
    <div className="flex min-h-svh">
      {/* Left Panel - Dark Branding */}
      <div className="hidden lg:flex lg:w-1/2 bg-zinc-950 text-white flex-col justify-between p-10">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Welcome Back</h1>
          <p className="mt-2 text-zinc-400">Sign in to continue to your workspace</p>
        </div>

        <div className="space-y-6">
          <div className="flex items-start gap-4">
            <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-zinc-800">
              <CheckCircle2 className="h-5 w-5 text-zinc-300" />
            </div>
            <div>
              <h3 className="font-semibold">Secure Access</h3>
              <p className="text-sm text-zinc-400">
                Your data is protected with enterprise-grade security
              </p>
            </div>
          </div>
          <div className="flex items-start gap-4">
            <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-zinc-800">
              <Zap className="h-5 w-5 text-zinc-300" />
            </div>
            <div>
              <h3 className="font-semibold">Fast Performance</h3>
              <p className="text-sm text-zinc-400">
                Lightning-fast access to all your resources
              </p>
            </div>
          </div>
        </div>

        <p className="text-sm text-zinc-500">
          &copy; {new Date().getFullYear()} Desk. All rights reserved.
        </p>
      </div>

      {/* Right Panel - Login Form */}
      <div className="flex w-full lg:w-1/2 items-center justify-center bg-white p-6 sm:p-10">
        <div className="w-full max-w-md space-y-8">
          <div>
            <h2 className="text-2xl font-bold text-zinc-900">Sign In</h2>
            <p className="mt-1 text-sm text-zinc-500">
              Enter your credentials to access your account
            </p>
          </div>

          <form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
            <div className="space-y-2">
              <Label htmlFor="email" className="text-zinc-700">
                Email
              </Label>
              <Input
                id="email"
                type="email"
                placeholder="name@example.com"
                autoComplete="email"
                className="h-11 rounded-lg border-zinc-300 bg-white text-zinc-900 placeholder:text-zinc-400 focus-visible:border-zinc-900 focus-visible:ring-zinc-900/20"
                aria-invalid={!!errors.email}
                {...register("email")}
              />
              {errors.email && (
                <p className="text-sm text-destructive">{errors.email.message}</p>
              )}
            </div>

            <div className="space-y-2">
              <Label htmlFor="password" className="text-zinc-700">
                Password
              </Label>
              <div className="relative">
                <Input
                  id="password"
                  type={showPassword ? "text" : "password"}
                  placeholder="Enter your password"
                  autoComplete="current-password"
                  className="h-11 rounded-lg border-zinc-300 bg-white pr-10 text-zinc-900 placeholder:text-zinc-400 focus-visible:border-zinc-900 focus-visible:ring-zinc-900/20"
                  aria-invalid={!!errors.password}
                  {...register("password")}
                />
                <button
                  type="button"
                  onClick={() => setShowPassword(!showPassword)}
                  className="absolute right-3 top-1/2 -translate-y-1/2 text-zinc-400 hover:text-zinc-600"
                  tabIndex={-1}
                  aria-label={showPassword ? "Hide password" : "Show password"}
                >
                  {showPassword ? (
                    <EyeOff className="h-4 w-4" />
                  ) : (
                    <Eye className="h-4 w-4" />
                  )}
                </button>
              </div>
              {errors.password && (
                <p className="text-sm text-destructive">{errors.password.message}</p>
              )}
            </div>

            <Button
              type="submit"
              size="lg"
              className="w-full h-11 rounded-lg bg-zinc-900 text-white hover:bg-zinc-800"
              disabled={isSubmitting}
            >
              {isSubmitting && <Loader2 className="animate-spin" />}
              {isSubmitting ? "Signing in..." : "Sign In"}
            </Button>
          </form>

          <p className="text-sm text-zinc-500 text-center">
            Don&apos;t have an account?{" "}
            <Link to="/auth/register" className="text-zinc-900 font-medium hover:underline">
              Register
            </Link>
          </p>
        </div>
      </div>
    </div>
  )
}

Example output