feat: add loading state and full-page loader component; update payment page and application layout to improve user experience
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 7m23s

This commit is contained in:
2025-05-31 12:37:46 +05:00
parent c705addccc
commit bed426a6b4
14 changed files with 84 additions and 18 deletions

View File

@ -16,6 +16,7 @@ import { tryCatch } from "@/utils/tryCatch";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { AccountPopover } from "./account-popver";
import { WelcomeBanner } from "../welcome-banner";
export async function ApplicationLayout({
children,
@ -31,7 +32,6 @@ export async function ApplicationLayout({
return (
<SidebarProvider>
<AppSidebar />
<DeviceCartDrawer />
<SidebarInset>
<header className="flex justify-between sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4 z-10">
<div className="flex items-center gap-2 ">
@ -45,13 +45,14 @@ export async function ApplicationLayout({
<AccountPopover />
</div>
</header>
<div className="text-sm font-mono px-2 p-1 bg-green-500/10 text-green-900 dark:text-green-400">
Welcome,{" "}
<span className="font-semibold">
{session?.user?.first_name} {session?.user?.last_name}
</span>
<WelcomeBanner
firstName={session?.user?.first_name}
lastName={session?.user?.last_name}
/>
<DeviceCartDrawer />
<div className="p-4 flex flex-col flex-1 rounded-lg bg-background">
{children}
</div>
<div className="p-4 flex flex-col flex-1">{children}</div>
</SidebarInset>
</SidebarProvider>
);

View File

@ -20,7 +20,7 @@ export default function ClickableRow({
key={device.id}
className={cn(
(parentalControl === false && device.blocked) || device.is_active
? "cursor-not-allowed bg-accent-foreground/10 hover:bg-accent-foreground/10"
? "cursor-not-allowed hover:bg-accent-foreground/10"
: "cursor-pointer hover:bg-muted-foreground/10",
)}
onClick={() => {

View File

@ -19,7 +19,7 @@ export default function DeviceCard({
return (
<div
onKeyUp={() => {}}
onKeyUp={() => { }}
onClick={() => {
if (device.blocked) return;
if (device.is_active === true) return;
@ -36,9 +36,9 @@ export default function DeviceCard({
<div
className={cn(
"flex text-sm justify-between items-center my-2 p-4 border rounded-md",
isChecked ? "bg-accent" : "bg-",
isChecked ? "bg-accent" : "",
device.is_active
? "cursor-not-allowed bg-accent-foreground/10 hover:bg-accent-foreground/10"
? "cursor-not-allowed text-green-600 hover:bg-accent-foreground/10"
: "cursor-pointer hover:bg-muted-foreground/10",
)}
>

View File

@ -10,7 +10,6 @@ export function DeviceCartDrawer() {
const pathname = usePathname();
const devices = useAtomValue(deviceCartAtom);
const router = useRouter();
if (pathname === "/payment" || pathname === "/devices-to-pay") {
return null;
}
@ -19,7 +18,7 @@ export function DeviceCartDrawer() {
return (
<Button
size={"lg"}
className="bg-sarLinkOrange fixed bottom-20 w-80 uppercase h-12 z-20 left-1/2 transform -translate-x-1/2"
className="bg-sarLinkOrange dark:hover:bg-orange-900 fixed bottom-20 w-80 uppercase h-12 z-20 left-1/2 transform -translate-x-1/2 hover:ring-2 hover:ring-sarLinkOrange transition-all duration-200"
onClick={() => router.push("/devices-to-pay")}
variant="outline"
>

View File

@ -12,6 +12,7 @@ import { CircleDollarSign, Loader2 } from "lucide-react";
import { redirect, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import FullPageLoader from "./full-page-loader";
export default function DevicesForPayment() {
const baseAmount = 100;
const discountPercentage = 75;
@ -45,7 +46,7 @@ export default function DevicesForPayment() {
};
if (disabled) {
return "Please wait...";
return <FullPageLoader />
}
return (
<div className="max-w-lg mx-auto space-y-4 px-4">

View File

@ -0,0 +1,9 @@
import React from 'react'
import { Loader2 } from 'lucide-react'
export default function FullPageLoader() {
return (
<div className='flex items-center justify-center h-screen'>
<Loader2 className='animate-spin' />
</div>
)
}

View File

@ -101,7 +101,7 @@ export default function AddDeviceDialogForm({ user_id }: { user_id?: string }) {
Device Name
</Label>
<Input
placeholder="eg: Iphone X"
placeholder="eg: iPhone X"
type="text"
{...register("name")}
id="device_name"

View File

@ -0,0 +1,33 @@
"use client";
import { useEffect, useState } from "react";
interface WelcomeBannerProps {
firstName?: string | null;
lastName?: string | null;
}
export function WelcomeBanner({ firstName, lastName }: WelcomeBannerProps) {
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setIsVisible(false);
}, 3000);
return () => clearTimeout(timer);
}, []);
if (!isVisible) {
return null;
}
return (
<div className="text-sm font-mono px-2 p-1 fade-out-10 bg-green-500/10 text-green-900 dark:text-green-400">
Welcome,{" "}
<span className="font-semibold">
{firstName} {lastName}
</span>
</div>
);
}