style(nextjs): makes outline page look better and changes loading shimmer for Home and Settings to match the actual layout

This commit is contained in:
sauravniraula 2025-07-23 19:25:10 +05:45
parent e07ddf8669
commit 7373b2ccee
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
11 changed files with 352 additions and 162 deletions

View file

@ -2,24 +2,50 @@ import React from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import Wrapper from "@/components/Wrapper";
import { FileText, Plus, ArrowRight } from "lucide-react";
const EmptyStateView: React.FC = () => {
const router = useRouter();
return (
<Wrapper>
<div className="max-w-[1000px] min-h-screen flex justify-center items-center mx-auto px-4 sm:px-6 pb-6">
<div className="mt-4 sm:mt-8 font-instrument_sans text-center">
<h4 className="text-lg sm:text-xl font-medium mb-4">
No Presentation ID Found
</h4>
<p className="text-gray-600 mb-4">Please start a new presentation.</p>
<Button
onClick={() => router.push("/upload")}
className="bg-[#5146E5] w-full rounded-xl text-base sm:text-lg py-4 sm:py-6 font-roboto font-semibold hover:bg-[#5146E5]/80 text-white mt-4 disabled:opacity-50 disabled:cursor-not-allowed"
>
Start New Presentation
</Button>
<div className="max-w-[800px] h-[calc(100vh-72px)] flex justify-center items-center mx-auto px-4 sm:px-6 pb-6">
<div className="text-center space-y-8">
{/* Icon */}
<div className="flex justify-center">
<div className="relative">
<div className="w-24 h-24 bg-gradient-to-br from-blue-50 to-indigo-100 rounded-full flex items-center justify-center">
<FileText className="w-12 h-12 text-indigo-600" />
</div>
<div className="absolute -top-2 -right-2 w-8 h-8 bg-red-100 rounded-full flex items-center justify-center">
<Plus className="w-4 h-4 text-red-600" />
</div>
</div>
</div>
{/* Content */}
<div className="space-y-4">
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 font-instrument_sans">
No Presentation Found
</h1>
<p className="text-lg text-gray-600 max-w-md mx-auto leading-relaxed">
It looks like the presentation you are looking for is not found.
Let's create a brand new presentation!
</p>
</div>
{/* Action Button */}
<div className="pt-4">
<Button
onClick={() => router.push("/upload")}
className="group bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white px-8 py-4 rounded-xl text-lg font-semibold shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105"
>
<Plus className="w-5 h-5 mr-2" />
Create New Presentation
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform duration-200" />
</Button>
</div>
</div>
</div>
</Wrapper>

View file

@ -31,40 +31,39 @@ const GenerateButton: React.FC<GenerateButtonProps> = ({
};
return (
<div className="mt-8 pt-6 border-t border-gray-200">
<Button
disabled={isDisabled}
onClick={onSubmit}
className="bg-[#5146E5] w-full rounded-lg text-base sm:text-lg py-4 sm:py-6 font-instrument_sans font-semibold hover:bg-[#5146E5]/80 text-white disabled:opacity-50 disabled:cursor-not-allowed"
<Button
disabled={isDisabled}
onClick={onSubmit}
className="bg-[#5146E5] w-full rounded-lg text-base sm:text-lg py-4 sm:py-6 font-instrument_sans font-semibold hover:bg-[#5146E5]/80 text-white disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg
className="mr-2"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 25 25"
fill="none"
>
<svg
className="mr-2"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 25 25"
fill="none"
>
<g clipPath="url(#clip0_1960_939)">
<path
d="M21.217 9.57008L21.463 9.00408C21.8955 8.0028 22.6876 7.2 23.683 6.75408L24.442 6.41508C24.5341 6.37272 24.6121 6.30485 24.6668 6.21951C24.7214 6.13417 24.7505 6.03494 24.7505 5.93358C24.7505 5.83222 24.7214 5.73299 24.6668 5.64765C24.6121 5.56231 24.5341 5.49444 24.442 5.45208L23.725 5.13308C22.7046 4.67446 21.8989 3.84196 21.474 2.80708L21.221 2.19608C21.1838 2.10144 21.119 2.02018 21.035 1.96291C20.951 1.90563 20.8517 1.875 20.75 1.875C20.6483 1.875 20.549 1.90563 20.465 1.96291C20.381 2.02018 20.3162 2.10144 20.279 2.19608L20.026 2.80608C19.6015 3.84116 18.7962 4.67401 17.776 5.13308L17.058 5.45308C16.9662 5.49556 16.8885 5.56342 16.834 5.64865C16.7795 5.73389 16.7506 5.83293 16.7506 5.93408C16.7506 6.03523 16.7795 6.13428 16.834 6.21951C16.8885 6.30474 16.9662 6.3726 17.058 6.41508L17.818 6.75308C18.8132 7.19945 19.6049 8.00261 20.037 9.00408L20.283 9.57008C20.463 9.98408 21.036 9.98408 21.217 9.57008ZM6.55 16.8761H8.704L9.304 15.3761H12.196L12.796 16.8761H14.95L11.75 8.87608H9.75L6.55 16.8761ZM10.75 11.7611L11.396 13.3761H10.104L10.75 11.7611ZM15.75 16.8761V8.87608H17.75V16.8761H15.75ZM3.75 3.87608C3.48478 3.87608 3.23043 3.98144 3.04289 4.16897C2.85536 4.35651 2.75 4.61086 2.75 4.87608V20.8761C2.75 21.1413 2.85536 21.3957 3.04289 21.5832C3.23043 21.7707 3.48478 21.8761 3.75 21.8761H21.75C22.0152 21.8761 22.2696 21.7707 22.4571 21.5832C22.6446 21.3957 22.75 21.1413 22.75 20.8761V11.8761H20.75V19.8761H4.75V5.87608H14.75V3.87608H3.75Z"
<g clipPath="url(#clip0_1960_939)">
<path
d="M21.217 9.57008L21.463 9.00408C21.8955 8.0028 22.6876 7.2 23.683 6.75408L24.442 6.41508C24.5341 6.37272 24.6121 6.30485 24.6668 6.21951C24.7214 6.13417 24.7505 6.03494 24.7505 5.93358C24.7505 5.83222 24.7214 5.73299 24.6668 5.64765C24.6121 5.56231 24.5341 5.49444 24.442 5.45208L23.725 5.13308C22.7046 4.67446 21.8989 3.84196 21.474 2.80708L21.221 2.19608C21.1838 2.10144 21.119 2.02018 21.035 1.96291C20.951 1.90563 20.8517 1.875 20.75 1.875C20.6483 1.875 20.549 1.90563 20.465 1.96291C20.381 2.02018 20.3162 2.10144 20.279 2.19608L20.026 2.80608C19.6015 3.84116 18.7962 4.67401 17.776 5.13308L17.058 5.45308C16.9662 5.49556 16.8885 5.56342 16.834 5.64865C16.7795 5.73389 16.7506 5.83293 16.7506 5.93408C16.7506 6.03523 16.7795 6.13428 16.834 6.21951C16.8885 6.30474 16.9662 6.3726 17.058 6.41508L17.818 6.75308C18.8132 7.19945 19.6049 8.00261 20.037 9.00408L20.283 9.57008C20.463 9.98408 21.036 9.98408 21.217 9.57008ZM6.55 16.8761H8.704L9.304 15.3761H12.196L12.796 16.8761H14.95L11.75 8.87608H9.75L6.55 16.8761ZM10.75 11.7611L11.396 13.3761H10.104L10.75 11.7611ZM15.75 16.8761V8.87608H17.75V16.8761H15.75ZM3.75 3.87608C3.48478 3.87608 3.23043 3.98144 3.04289 4.16897C2.85536 4.35651 2.75 4.61086 2.75 4.87608V20.8761C2.75 21.1413 2.85536 21.3957 3.04289 21.5832C3.23043 21.7707 3.48478 21.8761 3.75 21.8761H21.75C22.0152 21.8761 22.2696 21.7707 22.4571 21.5832C22.6446 21.3957 22.75 21.1413 22.75 20.8761V11.8761H20.75V19.8761H4.75V5.87608H14.75V3.87608H3.75Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_1960_939">
<rect
width="24"
height="24"
fill="white"
transform="translate(0.75 0.876953)"
/>
</g>
<defs>
<clipPath id="clip0_1960_939">
<rect
width="24"
height="24"
fill="white"
transform="translate(0.75 0.876953)"
/>
</clipPath>
</defs>
</svg>
{getButtonText()}
</Button>
</div>
</clipPath>
</defs>
</svg>
{getButtonText()}
</Button>
);
};

View file

@ -101,7 +101,7 @@ const LayoutSelection: React.FC<LayoutSelectionProps> = ({
}
return (
<div className="space-y-6">
<div className="space-y-6 mb-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{layoutGroups.map((group) => (
<GroupLayouts

View file

@ -42,7 +42,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
return (
<div className="space-y-6 font-instrument_sans">
<div className="flex items-center justify-between">
{/* <div className="flex items-center justify-between">
<h5 className="text-lg font-medium">
Presentation Outline
</h5>
@ -52,7 +52,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
Generating outlines...
</div>
)}
</div>
</div> */}
{/* Skeleton loading state */}
{isLoading && (
<div className="space-y-4">
@ -77,7 +77,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
{/* Outlines content */}
{outlines && outlines.length > 0 && (
<div className="border rounded-lg p-4 bg-white">
<div>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
@ -102,7 +102,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
variant="outline"
onClick={onAddSlide}
disabled={isLoading || isStreaming}
className="w-full mt-4 text-blue-600 border-blue-200"
className="w-full my-4 text-blue-600 border-blue-200"
>
+ Add Slide
</Button>

View file

@ -72,26 +72,26 @@ export function OutlineItem({
}
return (
<div className="mb-2 bg-[#F9F9F9]">
<div className="mb-2">
{/* Main Title Row */}
<div
ref={setNodeRef}
style={style}
className={`flex items-start gap-2 md:gap-4 p-2 sm:pr-4 bg-[#F9F9F9] rounded-[8px] ${isDragging ? "opacity-50" : ""}`}
className={`flex items-start gap-2 md:gap-4 p-2 sm:pr-4 border border-black/10 bg-purple-100/10 rounded-[8px] ${isDragging ? "opacity-50" : ""}`}
>
{/* Drag Handle with Number - Make it smaller on mobile */}
<div
{...attributes}
{...listeners}
className="min-w-8 sm:min-w-10 w-10 sm:w-14 h-10 sm:h-14 bg-[#E9E8F8] rounded-[8px] flex items-center justify-center relative cursor-grab"
className="min-w-8 sm:min-w-10 w-10 sm:w-14 h-10 sm:h-14 bg-blue-400/10 rounded-[8px] flex items-center justify-center relative cursor-grab"
>
<div className="grid grid-cols-2 gap-[2px]">
<div className="w-[3px] h-[3px] bg-[#5146E5] rounded-full" />
<div className="w-[3px] h-[3px] bg-[#5146E5] rounded-full" />
<div className="w-[3px] h-[3px] bg-[#5146E5] rounded-full" />
<div className="w-[3px] h-[3px] bg-[#5146E5] rounded-full" />
<div className="w-[3px] h-[3px] bg-black/80 rounded-full" />
<div className="w-[3px] h-[3px] bg-black/80 rounded-full" />
<div className="w-[3px] h-[3px] bg-black/80 rounded-full" />
<div className="w-[3px] h-[3px] bg-black/80 rounded-full" />
</div>
<span className="text-[#5146E5] text-sm sm:text-base font-medium ml-1">{index}</span>
<span className="text-black/80 text-md sm:text-lg font-medium ml-1">{index}</span>
</div>
{/* Main Title Input - Add onFocus handler */}
@ -100,7 +100,7 @@ export function OutlineItem({
type="text"
defaultValue={slideOutline.title || ''}
onBlur={(e) => handleSlideChange({ ...slideOutline, title: e.target.value })}
className="text-md sm:text-lg flex-1 font-semibold bg-transparent outline-none"
className="text-lg mt-4 sm:text-xl flex-1 font-semibold bg-transparent outline-none"
placeholder="Title goes here"
/>
@ -124,9 +124,9 @@ export function OutlineItem({
<ToolTip content="Delete Slide">
<button
onClick={handleSlideDelete}
className="p-1.5 sm:p-2 bg-[#EDEDED] hover:bg-[#E9E8F8] rounded-lg transition-colors"
className="p-1.5 sm:p-2 bg-gray-200/50 hover:bg-gray-200 rounded-lg transition-colors"
>
<Trash2 className="w-4 h-4 sm:w-5 sm:h-5 text-gray-500" />
<Trash2 className="w-4 h-4 sm:w-5 sm:h-5 text-black/70" />
</button>
</ToolTip>
</div>

View file

@ -40,7 +40,7 @@ const OutlinePage: React.FC = () => {
return (
<Wrapper>
<div className="h-[calc(100vh-72px)]">
<OverlayLoader
show={loadingState.isLoading}
text={loadingState.message}
@ -48,44 +48,53 @@ const OutlinePage: React.FC = () => {
duration={loadingState.duration}
/>
<div className="max-w-[1200px] font-instrument_sans mx-auto px-4 sm:px-6 pb-6">
<div className="mt-4 sm:mt-8">
<PageHeader />
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-[50%] mx-auto grid-cols-2">
<Wrapper className="h-full flex flex-col w-full">
<div className="flex-grow overflow-y-hidden w-[1200px] mx-auto">
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
<TabsList className="grid w-[50%] mx-auto my-4 grid-cols-2">
<TabsTrigger value={TABS.OUTLINE}>Outline & Content</TabsTrigger>
<TabsTrigger value={TABS.LAYOUTS}>Layout Style</TabsTrigger>
</TabsList>
<TabsContent value={TABS.OUTLINE} className="mt-6">
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</TabsContent>
<div className="flex-grow w-full overflow-y-auto custom_scrollbar">
<TabsContent value={TABS.OUTLINE}>
<div>
<OutlineContent
outlines={outlines}
isLoading={streamState.isLoading}
isStreaming={streamState.isStreaming}
onDragEnd={handleDragEnd}
onAddSlide={handleAddSlide}
/>
</div>
</TabsContent>
<TabsContent value={TABS.LAYOUTS} className="mt-6">
<LayoutSelection
selectedLayoutGroup={selectedLayoutGroup}
onSelectLayoutGroup={setSelectedLayoutGroup}
/>
</TabsContent>
<TabsContent value={TABS.LAYOUTS}>
<div>
<LayoutSelection
selectedLayoutGroup={selectedLayoutGroup}
onSelectLayoutGroup={setSelectedLayoutGroup}
/>
</div>
</TabsContent>
</div>
</Tabs>
<GenerateButton
loadingState={loadingState}
streamState={streamState}
outlines={outlines}
selectedLayoutGroup={selectedLayoutGroup}
onSubmit={handleSubmit}
/>
</div>
</div>
</Wrapper>
{/* Fixed Button */}
<div className="py-4 border-t border-gray-200">
<div className="max-w-[1200px] mx-auto">
<GenerateButton
loadingState={loadingState}
streamState={streamState}
outlines={outlines}
selectedLayoutGroup={selectedLayoutGroup}
onSubmit={handleSubmit}
/>
</div>
</div>
</Wrapper>
</div>
);
};

View file

@ -2,12 +2,12 @@ import React from "react";
const PageHeader: React.FC = () => (
<div className="mb-8">
<h4 className="text-2xl font-bold mb-2 text-gray-900">
{/* <h4 className="text-2xl font-bold mb-2 text-gray-900">
Customize Your Presentation
</h4>
<p className="text-gray-600">
</h4> */}
{/* <p className="text-gray-600">
Review your outline and select a layout style for your presentation.
</p>
</p> */}
</div>
);

View file

@ -249,15 +249,27 @@ thead {
}
.markdown-content h1 {
@apply text-3xl font-bold mb-6 text-gray-900;
@apply text-xl font-bold mb-4 text-gray-900;
}
.markdown-content h2 {
@apply text-2xl font-bold mb-4 text-gray-900;
@apply text-lg font-bold mb-3 text-gray-900;
}
.markdown-content h3 {
@apply text-xl font-bold mb-3 text-gray-900;
@apply text-base font-bold mb-2 text-gray-900;
}
.markdown-content h4 {
@apply text-sm font-bold mb-2 text-gray-900;
}
.markdown-content h5 {
@apply text-xs font-bold mb-1 text-gray-900;
}
.markdown-content h6 {
@apply text-xs font-semibold mb-1 text-gray-900;
}
.markdown-content p {
@ -313,6 +325,49 @@ thead {
@apply border border-gray-300 px-4 py-2;
}
/* Override Tailwind Typography prose heading sizes for markdown editor */
.prose h1 {
font-size: 18px !important;
font-weight: bold !important;
margin-bottom: 1rem !important;
color: rgb(17 24 39) !important;
}
.prose h2 {
font-size: 16px !important;
font-weight: bold !important;
margin-bottom: 0.75rem !important;
color: rgb(17 24 39) !important;
}
.prose h3 {
font-size: 14px !important;
font-weight: bold !important;
margin-bottom: 0.5rem !important;
color: rgb(17 24 39) !important;
}
.prose h4 {
font-size: 12px !important;
font-weight: bold !important;
margin-bottom: 0.5rem !important;
color: rgb(17 24 39) !important;
}
.prose h5 {
font-size: 11px !important;
font-weight: bold !important;
margin-bottom: 0.25rem !important;
color: rgb(17 24 39) !important;
}
.prose h6 {
font-size: 10px !important;
font-weight: 600 !important;
margin-bottom: 0.25rem !important;
color: rgb(17 24 39) !important;
}
/* MDXEditor styles */
.mdxeditor-toolbar-group {
@apply flex items-center gap-1 p-1;

View file

@ -1,12 +1,73 @@
import { Loader2 } from 'lucide-react'
import React from 'react'
import { Card } from "@/components/ui/card";
const loading = () => {
return (
<div className="h-screen w-screen flex justify-center items-center">
<Loader2 className="h-10 w-10 animate-spin" />
</div>
)
}
<div className="h-screen bg-gradient-to-b font-instrument_sans from-gray-50 to-white flex flex-col overflow-hidden">
<main className="flex-1 container mx-auto px-4 max-w-3xl overflow-hidden flex flex-col">
{/* Branding Header Skeleton */}
<div className="text-center mb-2 mt-4 flex-shrink-0">
<div className="flex items-center justify-center gap-3 mb-2">
<div className="h-12 w-12 bg-gray-200 animate-pulse rounded-md" />
</div>
<div className="h-4 w-64 bg-gray-200 animate-pulse rounded-md mx-auto" />
</div>
export default loading
{/* Main Configuration Content Skeleton */}
<div className="flex-1 overflow-hidden">
<div className="space-y-6 p-6">
{/* Page Title */}
<div className="space-y-2">
<div className="h-8 w-48 bg-gray-200 animate-pulse rounded-md" />
<div className="h-5 w-72 bg-gray-200 animate-pulse rounded-md" />
</div>
{/* LLM Provider Cards */}
<div className="space-y-4">
{[...Array(3)].map((_, index) => (
<Card key={index} className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<div className="h-10 w-10 bg-gray-200 animate-pulse rounded-md" />
<div className="space-y-1">
<div className="h-5 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="h-4 w-48 bg-gray-200 animate-pulse rounded-md" />
</div>
</div>
<div className="h-6 w-6 bg-gray-200 animate-pulse rounded-full" />
</div>
{/* Configuration Fields */}
<div className="space-y-4">
{[...Array(2)].map((_, fieldIndex) => (
<div key={fieldIndex} className="space-y-2">
<div className="h-4 w-24 bg-gray-200 animate-pulse rounded-md" />
<div className="h-10 w-full bg-gray-200 animate-pulse rounded-md" />
</div>
))}
</div>
</Card>
))}
</div>
{/* Model Selection */}
<Card className="p-6">
<div className="space-y-4">
<div className="h-5 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="h-10 w-full bg-gray-200 animate-pulse rounded-md" />
</div>
</Card>
</div>
</div>
</main>
{/* Fixed Bottom Button Skeleton */}
<div className="flex-shrink-0 bg-white border-t border-gray-200 p-4">
<div className="container mx-auto max-w-3xl">
<div className="h-12 w-full bg-gray-200 animate-pulse rounded-lg" />
</div>
</div>
</div>
);
};
export default loading;

View file

@ -2,71 +2,75 @@ import { Card } from "@/components/ui/card";
export default function LoadingProfile() {
return (
<div className="container max-w-7xl mx-auto p-6">
<div className="space-y-8">
{/* Header Section Skeleton */}
<div>
<div className="h-9 w-48 bg-gray-200 animate-pulse rounded-md" />
<div className="h-5 w-72 bg-gray-200 animate-pulse rounded-md mt-2" />
<div className="h-screen bg-gradient-to-b font-instrument_sans from-gray-50 to-white flex flex-col overflow-hidden">
{/* Header Skeleton */}
<div className="flex-shrink-0 bg-white border-b border-gray-200 p-4">
<div className="container mx-auto max-w-3xl">
<div className="flex items-center justify-between">
<div className="h-8 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="flex items-center gap-4">
<div className="h-8 w-8 bg-gray-200 animate-pulse rounded-full" />
<div className="h-8 w-24 bg-gray-200 animate-pulse rounded-md" />
</div>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* Subscription Card Skeleton */}
<Card className="col-span-2 p-6 space-y-6">
<div className="flex justify-between items-start">
<div>
<div className="h-8 w-40 bg-gray-200 animate-pulse rounded-md" />
<div className="h-5 w-56 bg-gray-200 animate-pulse rounded-md mt-1" />
</div>
<div className="h-10 w-32 bg-gray-200 animate-pulse rounded-md" />
{/* Main Content Skeleton */}
<main className="flex-1 container mx-auto px-4 max-w-3xl overflow-hidden flex flex-col">
<div className="flex-1 overflow-hidden">
{/* LLM Selection Content Skeleton */}
<div className="space-y-6 p-6">
{/* Page Title */}
<div className="space-y-2">
<div className="h-8 w-48 bg-gray-200 animate-pulse rounded-md" />
<div className="h-5 w-72 bg-gray-200 animate-pulse rounded-md" />
</div>
{/* Current Plan Details Skeleton */}
<div className="bg-gradient-to-r from-gray-50 to-gray-100 rounded-lg p-6">
<div className="flex items-center justify-between">
<div className="space-y-2">
<div className="h-6 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="h-4 w-20 bg-gray-200 animate-pulse rounded-md" />
</div>
<div className="text-right">
<div className="h-8 w-20 bg-gray-200 animate-pulse rounded-md" />
<div className="h-4 w-16 bg-gray-200 animate-pulse rounded-md mt-1" />
</div>
</div>
</div>
{/* Usage Stats Skeleton */}
{/* LLM Provider Cards */}
<div className="space-y-4">
<div className="h-6 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="space-y-6">
{[...Array(3)].map((_, index) => (
<div key={index} className="space-y-2">
<div className="flex justify-between">
<div className="h-4 w-40 bg-gray-200 animate-pulse rounded-md" />
<div className="h-4 w-12 bg-gray-200 animate-pulse rounded-md" />
{[...Array(3)].map((_, index) => (
<Card key={index} className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<div className="h-10 w-10 bg-gray-200 animate-pulse rounded-md" />
<div className="space-y-1">
<div className="h-5 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="h-4 w-48 bg-gray-200 animate-pulse rounded-md" />
</div>
</div>
<div className="h-2 w-full bg-gray-200 animate-pulse rounded-full" />
<div className="h-6 w-6 bg-gray-200 animate-pulse rounded-full" />
</div>
))}
</div>
{/* Configuration Fields */}
<div className="space-y-4">
{[...Array(2)].map((_, fieldIndex) => (
<div key={fieldIndex} className="space-y-2">
<div className="h-4 w-24 bg-gray-200 animate-pulse rounded-md" />
<div className="h-10 w-full bg-gray-200 animate-pulse rounded-md" />
</div>
))}
</div>
</Card>
))}
</div>
</Card>
{/* Quick Actions Skeleton */}
<div className="space-y-6">
{/* Model Selection */}
<Card className="p-6">
<div className="h-6 w-40 bg-gray-200 animate-pulse rounded-md mb-4" />
<div className="space-y-3">
{[...Array(4)].map((_, index) => (
<div key={index} className="flex items-center">
<div className="w-1.5 h-1.5 rounded-full bg-gray-200 mr-2" />
<div className="h-4 w-full bg-gray-200 animate-pulse rounded-md" />
</div>
))}
<div className="space-y-4">
<div className="h-5 w-32 bg-gray-200 animate-pulse rounded-md" />
<div className="h-10 w-full bg-gray-200 animate-pulse rounded-md" />
</div>
</Card>
</div>
</div>
</main>
{/* Fixed Bottom Button Skeleton */}
<div className="flex-shrink-0 bg-white border-t border-gray-200 p-4">
<div className="container mx-auto max-w-3xl">
<div className="h-12 w-full bg-gray-200 animate-pulse rounded-lg" />
</div>
</div>
</div>
);

View file

@ -2,7 +2,6 @@
import { useEffect, useState } from 'react';
import { setCanChangeKeys, setLLMConfig } from '@/store/slices/userConfig';
import { Loader2 } from 'lucide-react';
import { hasValidLLMConfig } from '@/utils/storeHelpers';
import { usePathname, useRouter } from 'next/navigation';
import { useDispatch } from 'react-redux';
@ -103,10 +102,47 @@ export function StoreInitializer({ children }: { children: React.ReactNode }) {
if (isLoading) {
return (
<div className="min-h-screen bg-[#E9E8F8] flex items-center justify-center">
<div className="text-center">
<Loader2 className="w-8 h-8 animate-spin text-blue-600 mx-auto mb-4" />
<p className="text-gray-600">Loading configuration...</p>
<div className="min-h-screen bg-gradient-to-br from-[#E9E8F8] via-[#F5F4FF] to-[#E0DFF7] flex items-center justify-center p-4">
<div className="max-w-md w-full">
<div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl border border-white/20 p-8 text-center">
{/* Logo/Branding */}
<div className="mb-6">
<img
src="/Logo.png"
alt="PresentOn"
className="h-12 mx-auto mb-4 opacity-90"
/>
<div className="w-16 h-1 bg-gradient-to-r from-blue-500 to-purple-600 mx-auto rounded-full"></div>
</div>
{/* Loading Animation */}
<div className="mb-6">
<img
src="/loading.gif"
alt="Loading"
className="w-24 h-24 mx-auto rounded-lg shadow-lg"
/>
</div>
{/* Loading Text */}
<div className="space-y-2">
<h3 className="text-lg font-semibold text-gray-800 font-inter">
Initializing Application
</h3>
<p className="text-sm text-gray-600 font-inter">
Loading configuration and checking model availability...
</p>
</div>
{/* Progress Indicator */}
<div className="mt-6">
<div className="flex space-x-1 justify-center">
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
<div className="w-2 h-2 bg-purple-500 rounded-full animate-pulse" style={{ animationDelay: '0.2s' }}></div>
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0.4s' }}></div>
</div>
</div>
</div>
</div>
</div>
);