feat: upload fonts to slide and be able to retrieve deduplicated list for all in template

This commit is contained in:
Suraj Jha 2025-08-10 00:55:25 +05:45
parent 353f8e41d1
commit 2cd5eef92f
No known key found for this signature in database
GPG key ID: 5AC6C16355CE2C14
5 changed files with 40 additions and 12 deletions

View file

@ -76,6 +76,7 @@ class GetLayoutsResponse(BaseModel):
layouts: list[LayoutData]
message: Optional[str] = None
template: Optional[dict] = None
fonts: Optional[List[str]] = None
class PresentationSummary(BaseModel):
@ -820,6 +821,13 @@ async def get_layouts(
for layout in layouts_db
]
# Aggregate unique fonts across all layouts
aggregated_fonts: set[str] = set()
for layout in layouts_db:
if layout.fonts:
aggregated_fonts.update([f for f in layout.fonts if isinstance(f, str)])
fonts_list = sorted(list(aggregated_fonts)) if aggregated_fonts else None
# Fetch template meta
template_meta = await session.get(TemplateModel, presentation_id)
template = None
@ -836,6 +844,7 @@ async def get_layouts(
layouts=layouts,
message=f"Retrieved {len(layouts)} layout(s) for presentation {presentation_id}",
template=template,
fonts=fonts_list,
)
except HTTPException:

View file

@ -0,0 +1,13 @@
from datetime import datetime
from typing import Optional
from sqlalchemy import Column, DateTime
from sqlmodel import SQLModel, Field
class TemplateModel(SQLModel, table=True):
__tablename__ = "templates"
id: str = Field(primary_key=True, description="UUID for the template (matches presentation_id)")
name: str = Field(description="Human friendly template name")
description: Optional[str] = Field(default=None, description="Optional template description")
created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))

View file

@ -2,11 +2,12 @@ import { useState, useCallback } from "react";
import { toast } from "sonner";
import { v4 as uuidv4 } from "uuid";
import { ApiResponseHandler } from "@/app/(presentation-generator)/services/api/api-error-handler";
import { ProcessedSlide, UploadedFont } from "../types";
import { ProcessedSlide, UploadedFont, FontData } from "../types";
export const useLayoutSaving = (
slides: ProcessedSlide[],
UploadedFonts: UploadedFont[],
fontsData: FontData | null,
refetch: () => void,
setSlides: React.Dispatch<React.SetStateAction<ProcessedSlide[]>>
) => {
@ -90,8 +91,10 @@ export const useLayoutSaving = (
const reactComponents: any[] = [];
const presentationId = uuidv4();
// Get all uploaded font URLs
const FontUrls = UploadedFonts.map((font) => font.fontUrl);
// Collect uploaded font URLs and Google Fonts CSS URLs
const uploadedFontUrls = UploadedFonts.map((font) => font.fontUrl);
const googleFontCssUrls = fontsData?.internally_supported_fonts?.map(f => f.google_fonts_url).filter(Boolean) || [];
const FontUrls = Array.from(new Set([...(uploadedFontUrls || []), ...googleFontCssUrls]));
console.log("FontUrls", FontUrls);
for (let i = 0; i < slides.length; i++) {
@ -186,7 +189,7 @@ export const useLayoutSaving = (
} finally {
setIsSavingLayout(false);
}
}, [slides, UploadedFonts, refetch, closeSaveModal, setSlides]);
}, [slides, UploadedFonts, fontsData, refetch, closeSaveModal, setSlides]);
return {
isSavingLayout,

View file

@ -35,6 +35,7 @@ const CustomTemplatePage = () => {
const { isSavingLayout, isModalOpen, openSaveModal, closeSaveModal, saveLayout } = useLayoutSaving(
slides,
UploadedFonts,
fontsData,
refetch,
setSlides
);

View file

@ -33,6 +33,7 @@ const GroupLayoutPreview = () => {
const [currentFonts, setCurrentFonts] = useState<string[] | undefined>(undefined);
const [isSaving, setIsSaving] = useState(false);
const [layoutsMap, setLayoutsMap] = useState<Record<string, { layout_id: string; layout_name: string; layout_code: string; fonts?: string[] }>>({});
const [templateMeta, setTemplateMeta] = useState<{ name?: string; description?: string } | null>(null);
const injectFonts = (fontUrls: string[]) => {
fontUrls.forEach((fontUrl) => {
@ -55,7 +56,7 @@ const GroupLayoutPreview = () => {
const loadCustomLayouts = async () => {
if (!isCustom) return;
try {
const res = await fetch(`/api/v1/ppt/layout-management/get-layouts/${presentationId}`);
const res = await fetch(`/api/v1/ppt/template-management/get-templates/${presentationId}`);
if (!res.ok) return;
const data = await res.json();
const map: Record<string, { layout_id: string; layout_name: string; layout_code: string; fonts?: string[] }> = {};
@ -68,12 +69,13 @@ const GroupLayoutPreview = () => {
};
}
setLayoutsMap(map);
// Inject all fonts used by this custom group's layouts
// const allFonts: string[] = [];
// Object.values(map).forEach((entry) => {
// (entry.fonts || []).forEach((f) => allFonts.push(f));
// });
injectFonts(map[0].fonts || []);
// Set template meta and inject aggregated fonts if provided
if (data?.template) {
setTemplateMeta({ name: data.template.name, description: data.template.description });
}
if (Array.isArray(data?.fonts) && data.fonts.length) {
injectFonts(data.fonts);
}
} catch (e) {
// noop
}
@ -157,7 +159,7 @@ const GroupLayoutPreview = () => {
},
],
};
const res = await fetch(`/api/v1/ppt/layout-management/save-layouts`, {
const res = await fetch(`/api/v1/ppt/template-management/save-templates`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),