fix(nextjs): Auto save before slide rendered & some layout issues
This commit is contained in:
parent
f1480ecd70
commit
ba01fe1154
13 changed files with 75 additions and 46 deletions
|
|
@ -3,7 +3,8 @@ import React, { createContext, useContext, useEffect, useState, ReactNode } from
|
|||
import dynamic from 'next/dynamic';
|
||||
import { toast } from "sonner";
|
||||
import * as z from 'zod';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setLayoutLoading } from '@/store/slices/presentationGeneration';
|
||||
export interface LayoutInfo {
|
||||
id: string;
|
||||
name?: string;
|
||||
|
|
@ -60,6 +61,7 @@ export const LayoutProvider: React.FC<{ children: ReactNode }> = ({ children })
|
|||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isPreloading, setIsPreloading] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const buildData = async (groupedLayoutsData: GroupedLayoutsResponse[]) => {
|
||||
const layouts: LayoutInfo[] = [];
|
||||
|
|
@ -171,6 +173,7 @@ export const LayoutProvider: React.FC<{ children: ReactNode }> = ({ children })
|
|||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
dispatch(setLayoutLoading(true));
|
||||
|
||||
|
||||
const layoutResponse = await fetch('/api/layouts');
|
||||
|
|
@ -197,6 +200,7 @@ export const LayoutProvider: React.FC<{ children: ReactNode }> = ({ children })
|
|||
setError(errorMessage);
|
||||
console.error('💥 Error loading layouts:', err);
|
||||
} finally {
|
||||
dispatch(setLayoutLoading(false));
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export const useGroupLayouts = () => {
|
|||
// Render slide content with group validation, automatic Tiptap text editing, and editable images/icons
|
||||
const renderSlideContent = useMemo(() => {
|
||||
return (slide: any, isEditMode: boolean) => {
|
||||
console.time("renderSlideContent");
|
||||
const Layout = getGroupLayout(slide.layout, slide.layout_group);
|
||||
if (!Layout) {
|
||||
return (
|
||||
|
|
@ -76,7 +75,6 @@ export const useGroupLayouts = () => {
|
|||
</EditableLayoutWrapper>
|
||||
);
|
||||
}
|
||||
console.timeEnd("renderSlideContent");
|
||||
return <Layout data={slide.content} />;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const useAutoSave = ({
|
|||
debounceMs = 2000,
|
||||
enabled = true,
|
||||
}: UseAutoSaveOptions = {}) => {
|
||||
const { presentationData, isStreaming, isLoading } = useSelector(
|
||||
const { presentationData, isStreaming, isLoading, isLayoutLoading } = useSelector(
|
||||
(state: RootState) => state.presentationGeneration
|
||||
);
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ export const useAutoSave = ({
|
|||
|
||||
// Effect to trigger auto-save when presentation data changes
|
||||
useEffect(() => {
|
||||
if (!enabled || !presentationData || isStreaming || isLoading) return;
|
||||
if (!enabled || !presentationData || isStreaming || isLoading || isLayoutLoading) return;
|
||||
|
||||
// Trigger debounced save
|
||||
debouncedSave(presentationData);
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ export default function RootLayout({
|
|||
className={`${inter.variable} ${roboto.variable} ${instrument_sans.variable} antialiased`}
|
||||
>
|
||||
<Providers>
|
||||
|
||||
<LayoutProvider>
|
||||
{children}
|
||||
</LayoutProvider>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import Home from '../components/Home'
|
||||
import Home from "@/components/Home"
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
<Home />
|
||||
)
|
||||
return (
|
||||
<Home />
|
||||
)
|
||||
}
|
||||
|
||||
export default page
|
||||
export default page
|
||||
|
|
@ -62,9 +62,9 @@ const SettingsPage = () => {
|
|||
isDisabled: true,
|
||||
text: "Saving Configuration..."
|
||||
}));
|
||||
|
||||
|
||||
await handleSaveLLMConfig(llmConfig);
|
||||
|
||||
|
||||
if (llmConfig.LLM === "ollama" && llmConfig.OLLAMA_MODEL) {
|
||||
const isPulled = await checkIfSelectedOllamaModelIsPulled(llmConfig.OLLAMA_MODEL);
|
||||
if (!isPulled) {
|
||||
|
|
@ -72,7 +72,7 @@ const SettingsPage = () => {
|
|||
await handleModelDownload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toast.info("Configuration saved successfully");
|
||||
setIsLoading(false);
|
||||
setButtonState(prev => ({
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ interface Type10SlideLayoutProps {
|
|||
}
|
||||
|
||||
const Type10SlideLayout: React.FC<Type10SlideLayoutProps> = ({ data: slideData }) => {
|
||||
const { title, items, chartData, chartType = 'line', color = '#3b82f6', dataKey = 'value', categoryKey = 'name', showLegend = false, showTooltip = true } = slideData;
|
||||
|
||||
const { title, items, data, chartType = 'line', color = '#3b82f6', dataKey = 'value', categoryKey = 'name', showLegend = false, showTooltip = true } = slideData;
|
||||
const chartData = data || [];
|
||||
const renderChart = () => {
|
||||
const commonProps = {
|
||||
data: chartData,
|
||||
|
|
@ -171,7 +171,7 @@ const Type10SlideLayout: React.FC<Type10SlideLayoutProps> = ({ data: slideData }
|
|||
|
||||
case 'pie':
|
||||
return (
|
||||
<PieChart margin={{ top: 20, right: 30, left: 40, bottom: 60 }}>
|
||||
<PieChart margin={{ top: 10, right: 10, left: 10, bottom: 10 }}>
|
||||
{showTooltip && <ChartTooltip content={<ChartTooltipContent />} />}
|
||||
{showLegend && <ChartLegend content={<ChartLegendContent />} />}
|
||||
<Pie
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const Type2TimelineSlideLayout: React.FC<Type2TimelineSlideLayoutProps> = ({ dat
|
|||
key={`timeline-${index}`}
|
||||
className="relative z-10 w-12 h-12 rounded-full bg-blue-600 px-1 text-white flex items-center justify-center font-bold text-lg"
|
||||
>
|
||||
<span> `0${index + 1}`</span>
|
||||
<span> {index + 1}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ const Type4SlideLayout: React.FC<Type4SlideLayoutProps> = ({ data: slideData })
|
|||
|
||||
case 'pie':
|
||||
return (
|
||||
<PieChart margin={{ top: 20, right: 30, left: 40, bottom: 60 }}>
|
||||
<PieChart margin={{ top: 10, right: 10, left: 10, bottom: 10 }}>
|
||||
{showTooltip && <ChartTooltip content={<ChartTooltipContent />} />}
|
||||
{showLegend && <ChartLegend content={<ChartLegendContent />} />}
|
||||
<Pie
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ interface Type9SlideLayoutProps {
|
|||
}
|
||||
|
||||
const Type9SlideLayout: React.FC<Type9SlideLayoutProps> = ({ data: slideData }) => {
|
||||
const { title, items, chartData, chartType = 'line', color = '#3b82f6', dataKey = 'value', categoryKey = 'name', showLegend = false, showTooltip = true } = slideData;
|
||||
const { title, items, data, chartType = 'line', color = '#3b82f6', dataKey = 'value', categoryKey = 'name', showLegend = false, showTooltip = true } = slideData;
|
||||
const chartData = data || [];
|
||||
const renderChart = () => {
|
||||
const commonProps = {
|
||||
data: chartData,
|
||||
|
|
@ -159,23 +160,27 @@ const Type9SlideLayout: React.FC<Type9SlideLayoutProps> = ({ data: slideData })
|
|||
|
||||
case 'pie':
|
||||
return (
|
||||
<PieChart margin={{ top: 20, right: 30, left: 40, bottom: 60 }}>
|
||||
{showTooltip && <ChartTooltip content={<ChartTooltipContent />} />}
|
||||
{showLegend && <ChartLegend content={<ChartLegendContent />} />}
|
||||
<Pie
|
||||
data={chartData}
|
||||
cx="50%"
|
||||
cy="40%"
|
||||
outerRadius={70}
|
||||
fill={color}
|
||||
dataKey={dataKey}
|
||||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||
>
|
||||
{chartData.map((entry: any, index: number) => (
|
||||
<Cell key={`cell-${index}`} fill={CHART_COLORS[index % CHART_COLORS.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
<ResponsiveContainer >
|
||||
<PieChart margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
|
||||
{showTooltip && <ChartTooltip content={<ChartTooltipContent />} />}
|
||||
{showLegend && <ChartLegend content={<ChartLegendContent />} />}
|
||||
<Pie
|
||||
isAnimationActive={false}
|
||||
data={chartData}
|
||||
cx="50%"
|
||||
cy="40%"
|
||||
outerRadius={80}
|
||||
fill={color}
|
||||
dataKey={dataKey}
|
||||
|
||||
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
||||
>
|
||||
{chartData && chartData.map((entry: any, index: number) => (
|
||||
<Cell key={`cell-${index}`} fill={CHART_COLORS[index % CHART_COLORS.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
|
||||
case 'scatter':
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
import * as z from "zod";
|
||||
// Note:
|
||||
// If you want to use Image and Icon Must Use these Schemas.
|
||||
// Image and icons are only media support for PDF and PPTX.
|
||||
|
||||
import { ImageSchema, IconSchema } from "../defaultSchemes";
|
||||
|
||||
// Schema definition
|
||||
export const Schema = z.object({
|
||||
// Note:
|
||||
// Schema fields
|
||||
// Each fields must have a default value (this is important for Layout-preview)
|
||||
// Each fields must have a meta description
|
||||
// Each fields must have a min and max length, length must support the layout design.
|
||||
// Each Array fields must have a min and max length
|
||||
|
||||
|
||||
sectionTitle: z.string()
|
||||
.min(3)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@ export const Schema = z.object({
|
|||
description: "Supporting subtitle that describes the service approach or value proposition",
|
||||
}),
|
||||
|
||||
bulletPoints: z.array(z.string().min(10).max(40)).min(4).max(6)
|
||||
.default([
|
||||
"Customized solutions for your business",
|
||||
"Expert guidance and support",
|
||||
"Innovative technology solutions",
|
||||
"Comprehensive service portfolio"
|
||||
]).meta({
|
||||
description: "Bullet points to describe the services offered",
|
||||
}),
|
||||
|
||||
serviceHighlight: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1556761175-b413da4baf72?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Professional service delivery or team working on client solutions"
|
||||
|
|
@ -47,7 +57,7 @@ type SchemaType = z.infer<typeof Schema>;
|
|||
// Component definition
|
||||
const OurServiceSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
||||
|
||||
const { sectionTitle, sectionSubtitle, serviceHighlight, showVisualAccents, showColorBlocks } = data;
|
||||
const { sectionTitle, sectionSubtitle, serviceHighlight, showVisualAccents, showColorBlocks, bulletPoints } = data;
|
||||
|
||||
return (
|
||||
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
|
||||
|
|
@ -67,6 +77,18 @@ const OurServiceSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
</p>
|
||||
)}
|
||||
|
||||
<div className="mt-16 space-y-4">
|
||||
|
||||
{bulletPoints && bulletPoints.map((point, index) => (
|
||||
<div key={index} className="text-base font-semibold text-gray-800 tracking-wide">
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="w-8 h-8 bg-teal-700 rounded-full "></div>
|
||||
<p className="text-lg font-medium text-gray-800 tracking-wide">{point}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Visual Accents */}
|
||||
{showVisualAccents && (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@ interface PresentationGenerationState {
|
|||
error: string | null;
|
||||
presentationData: PresentationData | null;
|
||||
isSlidesRendered: boolean;
|
||||
isLayoutLoading: boolean;
|
||||
}
|
||||
|
||||
const initialState: PresentationGenerationState = {
|
||||
presentation_id: null,
|
||||
outlines: [],
|
||||
isSlidesRendered: false,
|
||||
isLayoutLoading: false,
|
||||
isLoading: false,
|
||||
isStreaming: null,
|
||||
error: null,
|
||||
|
|
@ -53,6 +55,9 @@ const presentationGenerationSlice = createSlice({
|
|||
setLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.isLoading = action.payload;
|
||||
},
|
||||
setLayoutLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.isLayoutLoading = action.payload;
|
||||
},
|
||||
// Presentation ID
|
||||
setPresentationId: (state, action: PayloadAction<string>) => {
|
||||
state.presentation_id = action.payload;
|
||||
|
|
@ -365,6 +370,7 @@ const presentationGenerationSlice = createSlice({
|
|||
export const {
|
||||
setStreaming,
|
||||
setLoading,
|
||||
setLayoutLoading,
|
||||
setPresentationId,
|
||||
setSlidesRendered,
|
||||
setError,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue