fix(Nextjs): Header import issue in setting & document preview page

This commit is contained in:
shiva raj badu 2025-08-04 14:19:35 +05:45
parent 339a378e7f
commit d9a46fdff3
No known key found for this signature in database
3 changed files with 111 additions and 74 deletions

View file

@ -1,13 +1,13 @@
/**
* DocumentPreviewPage Component
*
*
* A component that displays and manages document previews for presentation generation.
* Features:
* - Document content preview with markdown support
* - Sidebar navigation for documents
* - Document content editing and saving
* - Presentation generation workflow
*
*
* @component
*/
@ -27,7 +27,7 @@ import MarkdownRenderer from "./MarkdownRenderer";
import { getIconFromFile } from "../../utils/others";
import { ChevronRight, PanelRightOpen, X } from "lucide-react";
import ToolTip from "@/components/ToolTip";
import Header from "@/app/dashboard/components/Header";
import Header from "@/components/Header";
// Types
interface LoadingState {
@ -60,7 +60,9 @@ const DocumentsPreviewPage: React.FC = () => {
// Local state
const [textContents, setTextContents] = useState<TextContents>({});
const [selectedDocument, setSelectedDocument] = useState<string | null>(null);
const [downloadingDocuments, setDownloadingDocuments] = useState<string[]>([]);
const [downloadingDocuments, setDownloadingDocuments] = useState<string[]>(
[]
);
const [isOpen, setIsOpen] = useState(true);
const [showLoading, setShowLoading] = useState<LoadingState>({
message: "",
@ -72,17 +74,19 @@ const DocumentsPreviewPage: React.FC = () => {
// Memoized computed values
const fileItems: FileItem[] = useMemo(() => {
if (!files || !Array.isArray(files) || files.length === 0) return [];
return files.flat().filter((item: any) => item && item.name && item.file_path);
return files
.flat()
.filter((item: any) => item && item.name && item.file_path);
}, [files]);
const documentKeys = useMemo(() => {
return fileItems.map(file => file.name);
return fileItems.map((file) => file.name);
}, [fileItems]);
const updateSelectedDocument = (value: string) => {
setSelectedDocument(value);
if (textareaRef.current) {
textareaRef.current.value = textContents[value] || '';
textareaRef.current.value = textContents[value] || "";
}
};
@ -92,7 +96,7 @@ const DocumentsPreviewPage: React.FC = () => {
body: JSON.stringify({ filePath }),
});
return res.json();
}
};
const maintainDocumentTexts = async () => {
const newDocuments: string[] = [];
@ -102,7 +106,7 @@ const DocumentsPreviewPage: React.FC = () => {
documentKeys.forEach((key: string) => {
if (!(key in textContents)) {
newDocuments.push(key);
const fileItem = fileItems.find(item => item.name === key);
const fileItem = fileItems.find((item) => item.name === key);
if (fileItem) {
promises.push(readFile(fileItem.file_path));
}
@ -113,7 +117,7 @@ const DocumentsPreviewPage: React.FC = () => {
setDownloadingDocuments(newDocuments);
try {
const results = await Promise.all(promises);
setTextContents(prev => {
setTextContents((prev) => {
const newContents = { ...prev };
newDocuments.forEach((key, index) => {
newContents[key] = results[index].content || "";
@ -121,7 +125,7 @@ const DocumentsPreviewPage: React.FC = () => {
return newContents;
});
} catch (error) {
console.error('Error reading files:', error);
console.error("Error reading files:", error);
toast.error("Failed to read document content");
}
setDownloadingDocuments([]);
@ -130,7 +134,6 @@ const DocumentsPreviewPage: React.FC = () => {
const handleCreatePresentation = async () => {
try {
setShowLoading({
message: "Generating presentation outline...",
show: true,
@ -138,20 +141,23 @@ const DocumentsPreviewPage: React.FC = () => {
progress: true,
});
const documentPaths = fileItems.map((fileItem: FileItem) => fileItem.file_path);
const createResponse = await PresentationGenerationApi.createPresentation({
prompt: config?.prompt ?? "",
n_slides: config?.slides ? parseInt(config.slides) : null,
file_paths: documentPaths,
language: config?.language ?? "",
});
const documentPaths = fileItems.map(
(fileItem: FileItem) => fileItem.file_path
);
const createResponse = await PresentationGenerationApi.createPresentation(
{
prompt: config?.prompt ?? "",
n_slides: config?.slides ? parseInt(config.slides) : null,
file_paths: documentPaths,
language: config?.language ?? "",
}
);
dispatch(setPresentationId(createResponse.id));
router.push("/outline");
} catch (error: any) {
console.error("Error in radar presentation creation:", error);
toast.error('Error', {
toast.error("Error", {
description: error.message || "Error in radar presentation creation.",
});
setShowLoading({
@ -194,7 +200,9 @@ const DocumentsPreviewPage: React.FC = () => {
{downloadingDocuments.includes(selectedDocument) ? (
<Skeleton className="w-full h-full" />
) : (
<MarkdownRenderer content={textContents[selectedDocument] || ""} />
<MarkdownRenderer
content={textContents[selectedDocument] || ""}
/>
)}
</div>
</div>
@ -206,8 +214,10 @@ const DocumentsPreviewPage: React.FC = () => {
if (!isOpen) return null;
return (
<div className={`border-r border-gray-200 fixed xl:relative w-full z-50 xl:z-auto
transition-all duration-300 ease-in-out max-w-[200px] md:max-w-[300px] h-[85vh] rounded-md p-5`}>
<div
className={`border-r border-gray-200 fixed xl:relative w-full z-50 xl:z-auto
transition-all duration-300 ease-in-out max-w-[200px] md:max-w-[300px] h-[85vh] rounded-md p-5`}
>
<X
onClick={() => setIsOpen(false)}
className="text-black mb-4 ml-auto mr-0 cursor-pointer hover:text-gray-600"
@ -222,8 +232,9 @@ const DocumentsPreviewPage: React.FC = () => {
<div
key={key}
onClick={() => updateSelectedDocument(key)}
className={`${selectedDocument === key ? 'border border-blue-500' : ""
} flex p-2 rounded-sm gap-2 items-center cursor-pointer`}
className={`${
selectedDocument === key ? "border border-blue-500" : ""
} flex p-2 rounded-sm gap-2 items-center cursor-pointer`}
>
<img
className="h-6 w-6 border border-gray-200"

View file

@ -1,18 +1,16 @@
import Header from '@/app/dashboard/components/Header'
import { Skeleton } from '@/components/ui/skeleton'
import React from 'react'
import { Skeleton } from "@/components/ui/skeleton";
import React from "react";
const loading = () => {
return (
<div>
<Skeleton className="h-20 w-full mx-auto" />
<div className=' flex gap-14 pb-10 h-screen py-6'>
<Skeleton className="h-full w-[30%] mx-auto" />
<Skeleton className="h-full w-[70%] " />
return (
<div>
<Skeleton className="h-20 w-full mx-auto" />
<div className=" flex gap-14 pb-10 h-screen py-6">
<Skeleton className="h-full w-[30%] mx-auto" />
<Skeleton className="h-full w-[70%] " />
</div>
</div>
);
};
</div>
</div>
)
}
export default loading
export default loading;

View file

@ -1,6 +1,5 @@
"use client";
import React, { useState, useEffect } from "react";
import Header from "../dashboard/components/Header";
import { Loader2, Download, CheckCircle } from "lucide-react";
import { toast } from "sonner";
import { RootState } from "@/store/store";
@ -9,10 +8,11 @@ import { handleSaveLLMConfig } from "@/utils/storeHelpers";
import {
checkIfSelectedOllamaModelIsPulled,
pullOllamaModel,
LLMConfig
LLMConfig,
} from "@/utils/providerUtils";
import { useRouter } from "next/navigation";
import LLMProviderSelection from "@/components/LLMSelection";
import Header from "@/components/Header";
// Button state interface
interface ButtonState {
@ -27,14 +27,16 @@ interface ButtonState {
const SettingsPage = () => {
const router = useRouter();
const userConfigState = useSelector((state: RootState) => state.userConfig);
const [llmConfig, setLlmConfig] = useState<LLMConfig>(userConfigState.llm_config);
const [llmConfig, setLlmConfig] = useState<LLMConfig>(
userConfigState.llm_config
);
const canChangeKeys = userConfigState.can_change_keys;
const [isLoading, setIsLoading] = useState<boolean>(false);
const [buttonState, setButtonState] = useState<ButtonState>({
isLoading: false,
isDisabled: false,
text: "Save Configuration",
showProgress: false
showProgress: false,
});
const [downloadingModel, setDownloadingModel] = useState<{
@ -47,8 +49,14 @@ const SettingsPage = () => {
const [showDownloadModal, setShowDownloadModal] = useState<boolean>(false);
const downloadProgress = React.useMemo(() => {
if (downloadingModel && downloadingModel.downloaded !== null && downloadingModel.size !== null) {
return Math.round((downloadingModel.downloaded / downloadingModel.size) * 100);
if (
downloadingModel &&
downloadingModel.downloaded !== null &&
downloadingModel.size !== null
) {
return Math.round(
(downloadingModel.downloaded / downloadingModel.size) * 100
);
}
return 0;
}, [downloadingModel?.downloaded, downloadingModel?.size]);
@ -56,17 +64,19 @@ const SettingsPage = () => {
const handleSaveConfig = async () => {
try {
setIsLoading(true);
setButtonState(prev => ({
setButtonState((prev) => ({
...prev,
isLoading: true,
isDisabled: true,
text: "Saving Configuration..."
text: "Saving Configuration...",
}));
await handleSaveLLMConfig(llmConfig);
if (llmConfig.LLM === "ollama" && llmConfig.OLLAMA_MODEL) {
const isPulled = await checkIfSelectedOllamaModelIsPulled(llmConfig.OLLAMA_MODEL);
const isPulled = await checkIfSelectedOllamaModelIsPulled(
llmConfig.OLLAMA_MODEL
);
if (!isPulled) {
setShowDownloadModal(true);
await handleModelDownload();
@ -75,26 +85,24 @@ const SettingsPage = () => {
toast.info("Configuration saved successfully");
setIsLoading(false);
setButtonState(prev => ({
setButtonState((prev) => ({
...prev,
isLoading: false,
isDisabled: false,
text: "Save Configuration"
text: "Save Configuration",
}));
router.back();
} catch (error) {
console.error("Error:", error);
toast.info(
error instanceof Error
? error.message
: "Failed to save configuration"
error instanceof Error ? error.message : "Failed to save configuration"
);
setIsLoading(false);
setButtonState(prev => ({
setButtonState((prev) => ({
...prev,
isLoading: false,
isDisabled: false,
text: "Save Configuration"
text: "Save Configuration",
}));
}
};
@ -110,15 +118,21 @@ const SettingsPage = () => {
};
useEffect(() => {
if (downloadingModel && downloadingModel.downloaded !== null && downloadingModel.size !== null) {
const percentage = Math.round(((downloadingModel.downloaded / downloadingModel.size) * 100));
if (
downloadingModel &&
downloadingModel.downloaded !== null &&
downloadingModel.size !== null
) {
const percentage = Math.round(
(downloadingModel.downloaded / downloadingModel.size) * 100
);
setButtonState({
isLoading: true,
isDisabled: true,
text: `Downloading Model (${percentage}%)`,
showProgress: true,
progressPercentage: percentage,
status: downloadingModel.status
status: downloadingModel.status,
});
}
@ -162,10 +176,11 @@ const SettingsPage = () => {
<button
onClick={handleSaveConfig}
disabled={buttonState.isDisabled}
className={`w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${buttonState.isDisabled
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}
className={`w-full font-semibold py-3 px-4 rounded-lg transition-all duration-500 ${
buttonState.isDisabled
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}
>
{buttonState.isLoading ? (
<div className="flex items-center justify-center gap-2">
@ -196,7 +211,9 @@ const SettingsPage = () => {
{/* Title */}
<h3 className="text-lg font-semibold text-gray-900 mb-2">
{downloadingModel.done ? "Download Complete!" : "Downloading Model"}
{downloadingModel.done
? "Download Complete!"
: "Downloading Model"}
</h3>
{/* Model Name */}
@ -230,20 +247,31 @@ const SettingsPage = () => {
)}
{/* Status Message */}
{downloadingModel.status && downloadingModel.status !== "pulled" && (
<div className="text-xs text-gray-500">
{downloadingModel.status === "downloading" && "Downloading model files..."}
{downloadingModel.status === "verifying" && "Verifying model integrity..."}
{downloadingModel.status === "pulling" && "Pulling model from registry..."}
</div>
)}
{downloadingModel.status &&
downloadingModel.status !== "pulled" && (
<div className="text-xs text-gray-500">
{downloadingModel.status === "downloading" &&
"Downloading model files..."}
{downloadingModel.status === "verifying" &&
"Verifying model integrity..."}
{downloadingModel.status === "pulling" &&
"Pulling model from registry..."}
</div>
)}
{/* Download Info */}
{downloadingModel.downloaded && downloadingModel.size && (
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
<div className="flex justify-between text-xs text-gray-600">
<span>Downloaded: {(downloadingModel.downloaded / 1024 / 1024).toFixed(1)} MB</span>
<span>Total: {(downloadingModel.size / 1024 / 1024).toFixed(1)} MB</span>
<span>
Downloaded:{" "}
{(downloadingModel.downloaded / 1024 / 1024).toFixed(1)}{" "}
MB
</span>
<span>
Total: {(downloadingModel.size / 1024 / 1024).toFixed(1)}{" "}
MB
</span>
</div>
</div>
)}