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:
parent
e07ddf8669
commit
7373b2ccee
11 changed files with 352 additions and 162 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue