feat(fastapi): converts schema to pydantic model for structured response, fix: changes icon and image schema for pydantic model support
This commit is contained in:
parent
c63944b7cf
commit
bb6abf1890
56 changed files with 440 additions and 377 deletions
|
|
@ -8,4 +8,5 @@ build
|
|||
.gitignore
|
||||
tmp
|
||||
debug
|
||||
.fastembed_cache
|
||||
.fastembed_cache
|
||||
generated_models
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -11,4 +11,5 @@ app_data
|
|||
tmp
|
||||
debug
|
||||
.fastembed_cache
|
||||
my-doc.txt
|
||||
my-doc.txt
|
||||
generated_models
|
||||
|
|
@ -2,10 +2,11 @@ import asyncio
|
|||
import json
|
||||
import os
|
||||
import random
|
||||
import importlib
|
||||
from typing import Annotated, List, Literal, Optional
|
||||
from fastapi import APIRouter, Body, Depends, File, HTTPException, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy import String, cast, delete
|
||||
from sqlalchemy import delete
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import select
|
||||
from constants.documents import UPLOAD_ACCEPTED_FILE_TYPES
|
||||
|
|
@ -27,7 +28,7 @@ from utils.export_utils import export_presentation
|
|||
from utils.llm_calls.generate_presentation_outlines import generate_ppt_outline
|
||||
from models.sql.slide import SlideModel
|
||||
from models.sse_response import SSECompleteResponse, SSEResponse
|
||||
from services import TEMP_FILE_SERVICE
|
||||
from services import SCHEMA_TO_MODEL_SERVICE, TEMP_FILE_SERVICE
|
||||
from services.database import get_async_session
|
||||
from services.documents_loader import DocumentsLoader
|
||||
from models.sql.presentation import PresentationModel
|
||||
|
|
@ -42,6 +43,7 @@ from utils.llm_calls.generate_slide_content import (
|
|||
)
|
||||
from utils.process_slides import process_slide_and_fetch_assets
|
||||
from utils.randomizers import get_random_uuid
|
||||
from utils.schema_utils import remove_fields_from_schema
|
||||
from utils.validators import validate_files
|
||||
|
||||
PRESENTATION_ROUTER = APIRouter(prefix="/presentation", tags=["Presentation"])
|
||||
|
|
@ -217,9 +219,23 @@ async def stream_presentation(
|
|||
).to_string()
|
||||
for i, slide_layout_index in enumerate(structure.slides):
|
||||
slide_layout = layout.slides[slide_layout_index]
|
||||
slide_content = await get_slide_content_from_type_and_outline(
|
||||
slide_layout, outline.slides[i], presentation.language
|
||||
|
||||
# Generate Pydantic model from slide layout schema
|
||||
schema_model_id = f"{layout.name}/{slide_layout.id}"
|
||||
response_schema = remove_fields_from_schema(
|
||||
slide_layout.json_schema, ["image_url_", "icon_url_"]
|
||||
)
|
||||
schema_model_path = (
|
||||
await SCHEMA_TO_MODEL_SERVICE.get_pydantic_model_path_from_schema(
|
||||
schema_model_id, response_schema
|
||||
)
|
||||
)
|
||||
module = importlib.import_module(schema_model_path)
|
||||
response_model = module.GeneratedModel
|
||||
slide_content = await get_slide_content_from_type_and_outline(
|
||||
response_model, outline.slides[i], presentation.language
|
||||
)
|
||||
|
||||
slide = SlideModel(
|
||||
presentation=presentation_id,
|
||||
layout_group=layout.name,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import importlib
|
||||
from typing import Annotated, Optional
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from models.sql.presentation import PresentationModel
|
||||
from models.sql.slide import SlideModel
|
||||
from services import SCHEMA_TO_MODEL_SERVICE
|
||||
from services.database import get_async_session
|
||||
from services.icon_finder_service import IconFinderService
|
||||
from services.image_generation_service import ImageGenerationService
|
||||
|
|
@ -13,6 +15,7 @@ from utils.llm_calls.edit_slide_html import get_edited_slide_html
|
|||
from utils.llm_calls.select_slide_type_on_edit import get_slide_layout_from_prompt
|
||||
from utils.process_slides import process_old_and_new_slides_and_fetch_assets
|
||||
from utils.randomizers import get_random_uuid
|
||||
from utils.schema_utils import remove_fields_from_schema
|
||||
|
||||
|
||||
SLIDE_ROUTER = APIRouter(prefix="/slide", tags=["Slide"])
|
||||
|
|
@ -36,8 +39,21 @@ async def edit_slide(
|
|||
slide_layout = await get_slide_layout_from_prompt(
|
||||
prompt, presentation_layout, slide
|
||||
)
|
||||
|
||||
# Generate Pydantic model from slide layout schema
|
||||
schema_model_id = f"{presentation_layout.name}/{slide_layout.id}"
|
||||
response_schema = remove_fields_from_schema(
|
||||
slide_layout.json_schema, ["image_url_", "icon_url_"]
|
||||
)
|
||||
schema_model_path = (
|
||||
await SCHEMA_TO_MODEL_SERVICE.get_pydantic_model_path_from_schema(
|
||||
schema_model_id, response_schema
|
||||
)
|
||||
)
|
||||
module = importlib.import_module(schema_model_path)
|
||||
response_model = module.GeneratedModel
|
||||
edited_slide_content = await get_edited_slide_content(
|
||||
prompt, slide_layout, slide, presentation.language
|
||||
prompt, slide, presentation.language, response_model
|
||||
)
|
||||
|
||||
image_generation_service = ImageGenerationService(get_images_directory())
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -17,8 +17,8 @@ class ContactInfoModel(BaseModel):
|
|||
|
||||
|
||||
class ImageModel(BaseModel):
|
||||
__image_url__: str = Field(description="Image URL")
|
||||
__image_prompt__: str = Field(description="Image prompt")
|
||||
image_url_: str = Field(description="Image URL")
|
||||
image_prompt_: str = Field(description="Image prompt")
|
||||
|
||||
|
||||
# First Slide Layout
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class SlideLayoutModel(BaseModel):
|
|||
|
||||
|
||||
class PresentationLayoutModel(BaseModel):
|
||||
name: Optional[str] = None
|
||||
name: str
|
||||
ordered: bool = Field(default=False)
|
||||
slides: List[SlideLayoutModel]
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ aiosqlite==0.21.0
|
|||
annotated-types==0.7.0
|
||||
anthropic==0.60.0
|
||||
anyio==4.9.0
|
||||
argcomplete==3.6.2
|
||||
async-timeout==5.0.1
|
||||
asyncpg==0.30.0
|
||||
attrs==25.3.0
|
||||
backoff==2.2.1
|
||||
bcrypt==4.3.0
|
||||
black==25.1.0
|
||||
build==1.2.2.post1
|
||||
cachetools==5.5.2
|
||||
certifi==2025.7.14
|
||||
|
|
@ -20,6 +22,7 @@ chromadb==1.0.15
|
|||
click==8.2.1
|
||||
coloredlogs==15.0.1
|
||||
cryptography==45.0.5
|
||||
datamodel-code-generator==0.32.0
|
||||
distro==1.9.0
|
||||
dnspython==2.7.0
|
||||
durationpy==0.10
|
||||
|
|
@ -32,6 +35,7 @@ filelock==3.18.0
|
|||
flatbuffers==25.2.10
|
||||
frozenlist==1.7.0
|
||||
fsspec==2025.7.0
|
||||
genson==1.3.0
|
||||
google-auth==2.40.3
|
||||
google-genai==1.25.0
|
||||
googleapis-common-protos==1.70.0
|
||||
|
|
@ -50,7 +54,9 @@ hyperframe==6.1.0
|
|||
idna==3.10
|
||||
importlib_metadata==8.7.0
|
||||
importlib_resources==6.5.2
|
||||
inflect==7.5.0
|
||||
iniconfig==2.1.0
|
||||
isort==6.0.1
|
||||
Jinja2==3.1.6
|
||||
jiter==0.10.0
|
||||
jsonschema==4.25.0
|
||||
|
|
@ -62,8 +68,10 @@ markdown-it-py==3.0.0
|
|||
MarkupSafe==3.0.2
|
||||
mdurl==0.1.2
|
||||
mmh3==5.1.0
|
||||
more-itertools==10.7.0
|
||||
mpmath==1.3.0
|
||||
multidict==6.6.3
|
||||
mypy_extensions==1.1.0
|
||||
numpy==2.3.2
|
||||
oauthlib==3.3.1
|
||||
onnxruntime==1.22.1
|
||||
|
|
@ -77,10 +85,12 @@ opentelemetry-semantic-conventions==0.56b0
|
|||
orjson==3.11.1
|
||||
overrides==7.7.0
|
||||
packaging==25.0
|
||||
pathspec==0.12.1
|
||||
pathvalidate==3.3.1
|
||||
pdfminer.six==20250506
|
||||
pdfplumber==0.11.7
|
||||
pillow==11.3.0
|
||||
platformdirs==4.3.8
|
||||
pluggy==1.6.0
|
||||
portalocker==3.2.0
|
||||
posthog==5.4.0
|
||||
|
|
@ -123,7 +133,9 @@ starlette==0.47.1
|
|||
sympy==1.14.0
|
||||
tenacity==8.5.0
|
||||
tokenizers==0.21.2
|
||||
tomli==2.2.1
|
||||
tqdm==4.67.1
|
||||
typeguard==4.4.4
|
||||
typer==0.16.0
|
||||
typing-inspection==0.4.1
|
||||
typing_extensions==4.14.1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from services.redis_service import RedisService
|
||||
from services.schema_to_model_service import SchemaToModelService
|
||||
from services.temp_file_service import TempFileService
|
||||
|
||||
|
||||
TEMP_FILE_SERVICE = TempFileService()
|
||||
REDIS_SERVICE = RedisService()
|
||||
SCHEMA_TO_MODEL_SERVICE = SchemaToModelService(TEMP_FILE_SERVICE)
|
||||
|
|
|
|||
|
|
@ -205,14 +205,13 @@ class LLMClient:
|
|||
)
|
||||
content = response.choices[0].message.parsed
|
||||
if content:
|
||||
return content
|
||||
return content.model_dump(mode="json")
|
||||
return None
|
||||
|
||||
async def _generate_google_structured(
|
||||
self, model: str, messages: List[LLMMessage], response_format: BaseModel | dict
|
||||
):
|
||||
client: genai.Client = self._client
|
||||
is_response_format_dict = isinstance(response_format, dict)
|
||||
response = await asyncio.to_thread(
|
||||
client.models.generate_content,
|
||||
model=model,
|
||||
|
|
@ -228,9 +227,6 @@ class LLMClient:
|
|||
if response.text:
|
||||
content = json.loads(response.text)
|
||||
|
||||
# If response format is Pydantic model, return the model instance
|
||||
if content and not is_response_format_dict:
|
||||
return response_format(**content)
|
||||
return content
|
||||
|
||||
async def _generate_anthropic_structured(
|
||||
|
|
@ -263,9 +259,6 @@ class LLMClient:
|
|||
if content_block.type == "tool_use":
|
||||
content = content_block.input
|
||||
|
||||
# If response format is Pydantic model, return the model instance
|
||||
if content and not is_response_format_dict:
|
||||
return response_format(**content)
|
||||
return content
|
||||
|
||||
async def _generate_ollama_structured(
|
||||
|
|
@ -280,7 +273,7 @@ class LLMClient:
|
|||
|
||||
async def generate_structured(
|
||||
self, model: str, messages: List[LLMMessage], response_format: BaseModel | dict
|
||||
):
|
||||
) -> dict:
|
||||
content = None
|
||||
match self.llm_provider:
|
||||
case LLMProvider.OPENAI:
|
||||
|
|
|
|||
|
|
@ -1,39 +1,78 @@
|
|||
import asyncio
|
||||
import json
|
||||
import tempfile
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from fastapi import HTTPException
|
||||
from datamodel_code_generator import generate, InputFileType, DataModelType
|
||||
|
||||
from services import TEMP_FILE_SERVICE
|
||||
from services.temp_file_service import TempFileService
|
||||
from utils.randomizers import get_random_uuid
|
||||
|
||||
|
||||
class SchemaToModelService:
|
||||
def __init__(self):
|
||||
self.temp_dir = TEMP_FILE_SERVICE.create_temp_dir()
|
||||
self._records = {}
|
||||
def __init__(self, temp_file_service: TempFileService):
|
||||
self.temp_file_service = temp_file_service
|
||||
self.temp_dir = self.temp_file_service.create_temp_dir()
|
||||
|
||||
def convert(self, schema: dict, identifier: str) -> BaseModel:
|
||||
return BaseModel.model_validate(schema)
|
||||
self.generated_models_dir = "generated_models"
|
||||
if os.path.exists(self.generated_models_dir):
|
||||
for file in os.listdir(self.generated_models_dir):
|
||||
if file.endswith(".py"):
|
||||
os.remove(os.path.join(self.generated_models_dir, file))
|
||||
os.makedirs(self.generated_models_dir, exist_ok=True)
|
||||
|
||||
def schema_to_pydantic_model(self, schema: dict, class_name: str):
|
||||
schema_path = TEMP_FILE_SERVICE.create_temp_file_path(
|
||||
get_random_uuid() + ".json", self.temp_dir
|
||||
self._records: Dict[str, str] = {}
|
||||
self._fetch_locks: Dict[str, asyncio.Lock] = {}
|
||||
|
||||
def convert_path_to_module_path(self, path: str):
|
||||
return path.replace("/", ".").replace("\\", ".").replace(".py", "")
|
||||
|
||||
async def get_pydantic_model_path_from_schema(
|
||||
self, identifier: str, schema: dict
|
||||
) -> str:
|
||||
if identifier in self._fetch_locks:
|
||||
async with self._fetch_locks[identifier]:
|
||||
return self._records[identifier]
|
||||
else:
|
||||
async_lock = asyncio.Lock()
|
||||
await async_lock.acquire()
|
||||
self._fetch_locks[identifier] = async_lock
|
||||
model_path = await self.generate_pydantic_model_from_schema_async(schema)
|
||||
model_path = self.convert_path_to_module_path(model_path)
|
||||
self._records[identifier] = model_path
|
||||
async_lock.release()
|
||||
return model_path
|
||||
|
||||
async def generate_pydantic_model_from_schema_async(self, schema: dict):
|
||||
return await asyncio.to_thread(self.generate_pydantic_model_from_schema, schema)
|
||||
|
||||
def generate_pydantic_model_from_schema(self, schema: dict):
|
||||
generated_model_path = os.path.join(
|
||||
self.generated_models_dir, get_random_uuid() + ".py"
|
||||
)
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema, f)
|
||||
try:
|
||||
schema_path = self.temp_file_service.create_temp_file_path(
|
||||
get_random_uuid() + ".json", self.temp_dir
|
||||
)
|
||||
with open(schema_path, "w") as f:
|
||||
json.dump(schema, f)
|
||||
|
||||
generated_model_path = TEMP_FILE_SERVICE.create_temp_file_path(
|
||||
get_random_uuid() + ".py", self.temp_dir
|
||||
)
|
||||
# generate(
|
||||
# input_=Path(schema_path),
|
||||
# input_file_type=InputFileType.JsonSchema,
|
||||
# output=Path(output_file),
|
||||
# output_model_type=DataModelType.PydanticV2BaseModel,
|
||||
# class_name=class_name,
|
||||
# use_annotated=False,
|
||||
# field_constraints=True,
|
||||
# )
|
||||
|
||||
# Path(schema_file).unlink(missing_ok=True)
|
||||
generate(
|
||||
input_=Path(schema_path),
|
||||
input_file_type=InputFileType.JsonSchema,
|
||||
output=Path(generated_model_path),
|
||||
output_model_type=DataModelType.PydanticV2BaseModel,
|
||||
class_name="GeneratedModel",
|
||||
use_annotated=False,
|
||||
field_constraints=True,
|
||||
extra_fields="ignore",
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=500, detail="Failed to generate Pydantic model from schema"
|
||||
)
|
||||
finally:
|
||||
self.temp_file_service.cleanup_temp_file(schema_path)
|
||||
|
||||
return generated_model_path
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from pydantic import BaseModel
|
||||
from models.llm_message import LLMMessage
|
||||
from models.presentation_layout import SlideLayoutModel
|
||||
from models.sql.slide import SlideModel
|
||||
|
|
@ -54,19 +55,16 @@ def get_messages(
|
|||
|
||||
async def get_edited_slide_content(
|
||||
prompt: str,
|
||||
slide_layout: SlideLayoutModel,
|
||||
slide: SlideModel,
|
||||
language: str,
|
||||
response_model: BaseModel,
|
||||
):
|
||||
model = get_large_model()
|
||||
response_schema = remove_fields_from_schema(
|
||||
slide_layout.json_schema, ["__image_url__", "__icon_url__"]
|
||||
)
|
||||
|
||||
client = LLMClient()
|
||||
response = await client.generate_structured(
|
||||
model=model,
|
||||
messages=get_messages(prompt, slide.content, language),
|
||||
response_format=response_schema,
|
||||
response_format=response_model,
|
||||
)
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -76,4 +76,4 @@ async def generate_presentation_structure(
|
|||
),
|
||||
response_format=response_model,
|
||||
)
|
||||
return response
|
||||
return PresentationStructureModel(**response)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,8 @@
|
|||
import asyncio
|
||||
import json
|
||||
from google.genai.types import GenerateContentConfig
|
||||
from pydantic import BaseModel
|
||||
from models.llm_message import LLMMessage
|
||||
from models.presentation_layout import SlideLayoutModel
|
||||
from models.presentation_outline_model import SlideOutlineModel
|
||||
from services.llm_client import LLMClient
|
||||
from utils.llm_provider import (
|
||||
get_anthropic_llm_client,
|
||||
get_google_llm_client,
|
||||
get_large_model,
|
||||
get_llm_client,
|
||||
is_anthropic_selected,
|
||||
is_google_selected,
|
||||
)
|
||||
from utils.schema_utils import remove_fields_from_schema
|
||||
from utils.llm_provider import get_large_model
|
||||
|
||||
system_prompt = """
|
||||
Generate structured slide based on provided title and outline, follow mentioned steps and notes and provide structured output.
|
||||
|
|
@ -25,8 +14,8 @@ system_prompt = """
|
|||
# Notes
|
||||
- Slide body should not use words like "This slide", "This presentation".
|
||||
- Rephrase the slide body to make it flow naturally.
|
||||
- Provide prompt to generate image on "__image_prompt__" property.
|
||||
- Provide query to search icon on "__icon_query__" property.
|
||||
- Provide prompt to generate image on "image_prompt_" property.
|
||||
- Provide query to search icon on "icon_query_" property.
|
||||
- Do not use markdown formatting in slide body.
|
||||
- Make sure to follow language guidelines.
|
||||
**Strictly follow the max and min character limit for every property in the slide.**
|
||||
|
|
@ -64,15 +53,11 @@ def get_messages(title: str, outline: str, language: str):
|
|||
|
||||
|
||||
async def get_slide_content_from_type_and_outline(
|
||||
slide_layout: SlideLayoutModel, outline: SlideOutlineModel, language: str
|
||||
response_model: BaseModel, outline: SlideOutlineModel, language: str
|
||||
):
|
||||
client = LLMClient()
|
||||
model = get_large_model()
|
||||
|
||||
response_schema = remove_fields_from_schema(
|
||||
slide_layout.json_schema, ["__image_url__", "__icon_url__"]
|
||||
)
|
||||
|
||||
response = await client.generate_structured(
|
||||
model=model,
|
||||
messages=get_messages(
|
||||
|
|
@ -80,6 +65,6 @@ async def get_slide_content_from_type_and_outline(
|
|||
outline.body,
|
||||
language,
|
||||
),
|
||||
response_format=response_schema,
|
||||
response_format=response_model,
|
||||
)
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ async def get_slide_layout_from_prompt(
|
|||
|
||||
slide_layout_ids = list(map(lambda x: x.id, layout.slides))
|
||||
|
||||
response: SlideLayoutIndex = await client.generate_structured(
|
||||
response = await client.generate_structured(
|
||||
model=model,
|
||||
messages=get_messages(
|
||||
prompt,
|
||||
|
|
@ -58,5 +58,5 @@ async def get_slide_layout_from_prompt(
|
|||
),
|
||||
response_format=SlideLayoutIndex,
|
||||
)
|
||||
index = response.index
|
||||
index = SlideLayoutIndex(**response).index
|
||||
return layout.slides[index]
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ def get_anthropic_llm_client():
|
|||
def get_large_model():
|
||||
selected_llm = get_llm_provider()
|
||||
if selected_llm == LLMProvider.OPENAI:
|
||||
return "gpt-4.1-nano"
|
||||
return "gpt-4.1"
|
||||
elif selected_llm == LLMProvider.GOOGLE:
|
||||
return "gemini-2.0-flash"
|
||||
elif selected_llm == LLMProvider.ANTHROPIC:
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@ async def process_slide_and_fetch_assets(
|
|||
|
||||
async_tasks = []
|
||||
|
||||
image_paths = get_dict_paths_with_key(slide.content, "__image_prompt__")
|
||||
icon_paths = get_dict_paths_with_key(slide.content, "__icon_query__")
|
||||
image_paths = get_dict_paths_with_key(slide.content, "image_prompt_")
|
||||
icon_paths = get_dict_paths_with_key(slide.content, "icon_query_")
|
||||
|
||||
for image_path in image_paths:
|
||||
image_prompt_parent = get_dict_at_path(slide.content, image_path)
|
||||
async_tasks.append(
|
||||
image_generation_service.generate_image(
|
||||
ImagePrompt(
|
||||
prompt=image_prompt_parent["__image_prompt__"],
|
||||
prompt=image_prompt_parent["image_prompt_"],
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -33,7 +33,7 @@ async def process_slide_and_fetch_assets(
|
|||
for icon_path in icon_paths:
|
||||
icon_query_parent = get_dict_at_path(slide.content, icon_path)
|
||||
async_tasks.append(
|
||||
icon_finder_service.search_icons(icon_query_parent["__icon_query__"])
|
||||
icon_finder_service.search_icons(icon_query_parent["icon_query_"])
|
||||
)
|
||||
|
||||
results = await asyncio.gather(*async_tasks)
|
||||
|
|
@ -45,14 +45,14 @@ async def process_slide_and_fetch_assets(
|
|||
result = results.pop()
|
||||
if isinstance(result, ImageAsset):
|
||||
return_assets.append(result)
|
||||
image_dict["__image_url__"] = result.path
|
||||
image_dict["image_url_"] = result.path
|
||||
else:
|
||||
image_dict["__image_url__"] = result
|
||||
image_dict["image_url_"] = result
|
||||
set_dict_at_path(slide.content, image_path, image_dict)
|
||||
|
||||
for icon_path in icon_paths:
|
||||
icon_dict = get_dict_at_path(slide.content, icon_path)
|
||||
icon_dict["__icon_url__"] = results.pop()[0]
|
||||
icon_dict["icon_url_"] = results.pop()[0]
|
||||
set_dict_at_path(slide.content, icon_path, icon_dict)
|
||||
|
||||
return return_assets
|
||||
|
|
@ -66,34 +66,34 @@ async def process_old_and_new_slides_and_fetch_assets(
|
|||
) -> List[ImageAsset]:
|
||||
# Finds all old images
|
||||
old_image_dict_paths = get_dict_paths_with_key(
|
||||
old_slide_content, "__image_prompt__"
|
||||
old_slide_content, "image_prompt_"
|
||||
)
|
||||
old_image_dicts = [
|
||||
get_dict_at_path(old_slide_content, path) for path in old_image_dict_paths
|
||||
]
|
||||
old_image_prompts = [
|
||||
old_image_dict["__image_prompt__"] for old_image_dict in old_image_dicts
|
||||
old_image_dict["image_prompt_"] for old_image_dict in old_image_dicts
|
||||
]
|
||||
|
||||
# Finds all old icons
|
||||
old_icon_dict_paths = get_dict_paths_with_key(old_slide_content, "__icon_query__")
|
||||
old_icon_dict_paths = get_dict_paths_with_key(old_slide_content, "icon_query_")
|
||||
old_icon_dicts = [
|
||||
get_dict_at_path(old_slide_content, path) for path in old_icon_dict_paths
|
||||
]
|
||||
old_icon_queries = [
|
||||
old_icon_dict["__icon_query__"] for old_icon_dict in old_icon_dicts
|
||||
old_icon_dict["icon_query_"] for old_icon_dict in old_icon_dicts
|
||||
]
|
||||
|
||||
# Finds all new images
|
||||
new_image_dict_paths = get_dict_paths_with_key(
|
||||
new_slide_content, "__image_prompt__"
|
||||
new_slide_content, "image_prompt_"
|
||||
)
|
||||
new_image_dicts = [
|
||||
get_dict_at_path(new_slide_content, path) for path in new_image_dict_paths
|
||||
]
|
||||
|
||||
# Finds all new icons
|
||||
new_icon_dict_paths = get_dict_paths_with_key(new_slide_content, "__icon_query__")
|
||||
new_icon_dict_paths = get_dict_paths_with_key(new_slide_content, "icon_query_")
|
||||
new_icon_dicts = [
|
||||
get_dict_at_path(new_slide_content, path) for path in new_icon_dict_paths
|
||||
]
|
||||
|
|
@ -109,18 +109,18 @@ async def process_old_and_new_slides_and_fetch_assets(
|
|||
# Creates async tasks for fetching new images
|
||||
# Use old image url if prompt is same
|
||||
for new_image in new_image_dicts:
|
||||
if new_image["__image_prompt__"] in old_image_prompts:
|
||||
if new_image["image_prompt_"] in old_image_prompts:
|
||||
old_image_url = old_image_dicts[
|
||||
old_image_prompts.index(new_image["__image_prompt__"])
|
||||
]["__image_url__"]
|
||||
new_image["__image_url__"] = old_image_url
|
||||
old_image_prompts.index(new_image["image_prompt_"])
|
||||
]["image_url_"]
|
||||
new_image["image_url_"] = old_image_url
|
||||
new_images_fetch_status.append(False)
|
||||
continue
|
||||
|
||||
async_image_fetch_tasks.append(
|
||||
image_generation_service.generate_image(
|
||||
ImagePrompt(
|
||||
prompt=new_image["__image_prompt__"],
|
||||
prompt=new_image["image_prompt_"],
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -129,16 +129,16 @@ async def process_old_and_new_slides_and_fetch_assets(
|
|||
# Creates async tasks for fetching new icons
|
||||
# Use old icon url if query is same
|
||||
for new_icon in new_icon_dicts:
|
||||
if new_icon["__icon_query__"] in old_icon_queries:
|
||||
if new_icon["icon_query_"] in old_icon_queries:
|
||||
old_icon_url = old_icon_dicts[
|
||||
old_icon_queries.index(new_icon["__icon_query__"])
|
||||
]["__icon_url__"]
|
||||
new_icon["__icon_url__"] = old_icon_url
|
||||
old_icon_queries.index(new_icon["icon_query_"])
|
||||
]["icon_url_"]
|
||||
new_icon["icon_url_"] = old_icon_url
|
||||
new_icons_fetch_status.append(False)
|
||||
continue
|
||||
|
||||
async_icon_fetch_tasks.append(
|
||||
icon_finder_service.search_icons(new_icon["__icon_query__"])
|
||||
icon_finder_service.search_icons(new_icon["icon_query_"])
|
||||
)
|
||||
new_icons_fetch_status.append(True)
|
||||
|
||||
|
|
@ -157,11 +157,11 @@ async def process_old_and_new_slides_and_fetch_assets(
|
|||
image_url = fetched_image.path
|
||||
else:
|
||||
image_url = fetched_image
|
||||
new_image_dicts[i]["__image_url__"] = image_url
|
||||
new_image_dicts[i]["image_url_"] = image_url
|
||||
|
||||
for i, new_icon in enumerate(new_icons):
|
||||
if new_icons_fetch_status[i]:
|
||||
new_icon_dicts[i]["__icon_url__"] = new_icons[i][0]
|
||||
new_icon_dicts[i]["icon_url_"] = new_icons[i][0]
|
||||
|
||||
for i, new_image_dict in enumerate(new_image_dicts):
|
||||
set_dict_at_path(new_slide_content, new_image_dict_paths[i], new_image_dict)
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
|
||||
const matches: { path: string; type: 'image' | 'icon'; data: any }[] = [];
|
||||
|
||||
// Check current level for __image_url__ or __icon_url__
|
||||
if (data.__image_url__ && targetUrl.includes(data.__image_url__)) {
|
||||
// Check current level for image_url_ or icon_url_
|
||||
if (data.image_url_ && targetUrl.includes(data.image_url_)) {
|
||||
matches.push({ path, type: 'image', data });
|
||||
}
|
||||
|
||||
if (data.__icon_url__ && targetUrl.includes(data.__icon_url__)) {
|
||||
if (data.icon_url_ && targetUrl.includes(data.icon_url_)) {
|
||||
matches.push({ path, type: 'icon', data });
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +308,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
slideIndex,
|
||||
dataPath: activeEditor.dataPath,
|
||||
imageUrl: newImageUrl,
|
||||
prompt: prompt || activeEditor.data?.__image_prompt__ || ''
|
||||
prompt: prompt || activeEditor.data?.image_prompt_ || ''
|
||||
}));
|
||||
setActiveEditor(null);
|
||||
}
|
||||
|
|
@ -326,7 +326,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
slideIndex,
|
||||
dataPath: activeEditor.dataPath,
|
||||
iconUrl: newIconUrl,
|
||||
query: query || activeEditor.data?.__icon_query__ || ''
|
||||
query: query || activeEditor.data?.icon_query_ || ''
|
||||
}));
|
||||
|
||||
|
||||
|
|
@ -361,7 +361,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
<ImageEditor
|
||||
initialImage={activeEditor.src}
|
||||
slideIndex={slideIndex}
|
||||
promptContent={activeEditor.data?.__image_prompt__ || ''}
|
||||
promptContent={activeEditor.data?.image_prompt_ || ''}
|
||||
imageIdx={0}
|
||||
properties={null}
|
||||
onClose={handleEditorClose}
|
||||
|
|
@ -374,7 +374,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
{/* Render IconsEditor when an icon is being edited */}
|
||||
{activeEditor && activeEditor.type === 'icon' && (
|
||||
<IconsEditor
|
||||
icon_prompt={activeEditor.data?.__icon_query__ ? [activeEditor.data.__icon_query__] : []}
|
||||
icon_prompt={activeEditor.data?.icon_query_ ? [activeEditor.data.icon_query_] : []}
|
||||
onClose={handleEditorClose}
|
||||
onIconChange={handleIconChange}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -31,15 +31,15 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
chartImage: ImageSchema.default({
|
||||
__image_url__: "https://example.com/quarterly-chart.png",
|
||||
__image_prompt__: "Quarterly performance chart showing upward trend"
|
||||
image_url_: "https://example.com/quarterly-chart.png",
|
||||
image_prompt_: "Quarterly performance chart showing upward trend"
|
||||
}).meta({
|
||||
description: "Main performance chart",
|
||||
}),
|
||||
|
||||
trendIcon: IconSchema.default({
|
||||
__icon_url__: "/static/icons/placeholder.png",
|
||||
__icon_query__: "upward trend arrow icon"
|
||||
icon_url_: "/static/icons/placeholder.png",
|
||||
icon_query_: "upward trend arrow icon"
|
||||
}).meta({
|
||||
description: "Trend indicator icon",
|
||||
}),
|
||||
|
|
@ -57,11 +57,11 @@ export default function ExampleSlideLayout({ data }: { data: SchemaType }) {
|
|||
</header>
|
||||
|
||||
<main className="slide-content flex-1 flex">
|
||||
{chartImage?.__image_url__ && (
|
||||
{chartImage?.image_url_ && (
|
||||
<div className="chart-section flex-1">
|
||||
<img
|
||||
src={chartImage.__image_url__}
|
||||
alt={chartImage.__image_prompt__}
|
||||
src={chartImage.image_url_}
|
||||
alt={chartImage.image_prompt_}
|
||||
className="w-full h-auto max-h-96 object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -74,9 +74,9 @@ export default function ExampleSlideLayout({ data }: { data: SchemaType }) {
|
|||
<div key={index} className="metric-item mb-4 p-3 bg-gray-50 rounded">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{metric.label}</span>
|
||||
{trendIcon?.__icon_url__ && (
|
||||
{trendIcon?.icon_url_ && (
|
||||
<img
|
||||
src={trendIcon.__icon_url__}
|
||||
src={trendIcon.icon_url_}
|
||||
alt={metric.trend}
|
||||
className="w-6 h-6"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ const type10SlideSchema = z.object({
|
|||
})).min(2).max(3).default(() => [
|
||||
{
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'A beautiful road in the mountains'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'A beautiful road in the mountains'
|
||||
},
|
||||
heading: 'First Key Point',
|
||||
description: 'Detailed explanation of the first important point that supports the main topic'
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'A beautiful road in the mountains'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'A beautiful road in the mountains'
|
||||
},
|
||||
heading: 'Second Key Point',
|
||||
description: 'Detailed explanation of the second important point with relevant information'
|
||||
|
|
@ -240,9 +240,9 @@ const Type10SlideLayout: React.FC<Type10SlideLayoutProps> = ({ data: slideData }
|
|||
<div className="flex gap-6">
|
||||
<div className="w-[48px] h-[48px]">
|
||||
<div className="w-full h-full bg-blue-600 rounded-lg flex items-center justify-center overflow-hidden">
|
||||
{item.icon?.__icon_url__ && <img
|
||||
src={item.icon?.__icon_url__ || ''}
|
||||
alt={item.icon?.__icon_query__ || item.heading}
|
||||
{item.icon?.icon_url_ && <img
|
||||
src={item.icon?.icon_url_ || ''}
|
||||
alt={item.icon?.icon_query_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ const type1SlideSchema = z.object({
|
|||
description: "Main description text",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg',
|
||||
__image_prompt__: 'A beautiful road in the mountains'
|
||||
image_url_: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg',
|
||||
image_prompt_: 'A beautiful road in the mountains'
|
||||
}).meta({
|
||||
description: "Main slide image",
|
||||
})
|
||||
|
|
@ -52,8 +52,8 @@ const Type1SlideLayout: React.FC<Type1SlideLayoutProps> = ({ data: slideData })
|
|||
{/* Image */}
|
||||
<div className="w-full max-h-[600px]">
|
||||
{image && <img
|
||||
src={image?.__image_url__ || ''}
|
||||
alt={image?.__image_prompt__ || title || ''}
|
||||
src={image?.image_url_ || ''}
|
||||
alt={image?.image_prompt_ || title || ''}
|
||||
className="w-full max-h-full object-cover rounded-lg shadow-md"
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,24 +25,24 @@ const type3SlideSchema = z.object({
|
|||
heading: 'First Feature',
|
||||
description: 'Description for the first featured item with detailed information',
|
||||
image: {
|
||||
__image_url__: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg',
|
||||
__image_prompt__: 'A beautiful road in the mountains'
|
||||
image_url_: 'https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg',
|
||||
image_prompt_: 'A beautiful road in the mountains'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Second Feature',
|
||||
description: 'Description for the second featured item with relevant details',
|
||||
image: {
|
||||
__image_url__: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg',
|
||||
__image_prompt__: 'Modern office workspace'
|
||||
image_url_: 'https://cdn.pixabay.com/photo/2016/02/19/11/19/office-1209640_1280.jpg',
|
||||
image_prompt_: 'Modern office workspace'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Third Feature',
|
||||
description: 'Description for the third featured item with important points',
|
||||
image: {
|
||||
__image_url__: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg',
|
||||
__image_prompt__: 'Laptop with code on screen'
|
||||
image_url_: 'https://cdn.pixabay.com/photo/2017/08/10/08/47/laptop-2619235_1280.jpg',
|
||||
image_prompt_: 'Laptop with code on screen'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -94,8 +94,8 @@ const Type3SlideLayout: React.FC<Type3SlideLayoutProps> = ({ data: slideData })
|
|||
{/* Image */}
|
||||
<div className="max-md:h-[140px] max-lg:h-[180px] h-48 w-full">
|
||||
<img
|
||||
src={item.image?.__image_url__ || ''}
|
||||
alt={item.image?.__image_prompt__ || item.heading}
|
||||
src={item.image?.image_url_ || ''}
|
||||
alt={item.image?.image_prompt_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,32 +23,32 @@ const type6SlideSchema = z.object({
|
|||
heading: 'Professional Service',
|
||||
description: 'High-quality professional services tailored to your specific needs and requirements',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Professional Service'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Professional Service'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Expert Consultation',
|
||||
description: 'Expert advice and consultation from experienced professionals in the field',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Expert Consultation'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Expert Consultation'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Quality Assurance',
|
||||
description: 'Comprehensive quality assurance processes to ensure excellent results',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Quality Assurance'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Quality Assurance'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Customer Support',
|
||||
description: 'Dedicated customer support available to assist you throughout the process',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Customer Support'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Customer Support'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -94,7 +94,7 @@ const Type6SlideLayout: React.FC<Type6SlideLayoutProps> = ({ data: slideData })
|
|||
<div className="flex items-start gap-2 mg:gap-4">
|
||||
<div className="flex-shrink-0 lg:w-16">
|
||||
<div className="w-12 h-12 lg:w-16 lg:h-16 bg-blue-600 rounded-lg flex items-center justify-center text-white text-xl lg:text-2xl">
|
||||
<img src={item.icon.__icon_url__} className='w-full h-full object-contain' alt={item.icon.__icon_query__} />
|
||||
<img src={item.icon.icon_url_} className='w-full h-full object-contain' alt={item.icon.icon_query_} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -125,7 +125,7 @@ const Type6SlideLayout: React.FC<Type6SlideLayoutProps> = ({ data: slideData })
|
|||
>
|
||||
<div className="text-center mb-4">
|
||||
<div className="w-16 h-16 lg:w-20 lg:h-20 bg-blue-600 rounded-lg flex items-center justify-center text-white text-2xl lg:text-3xl mx-auto mb-4">
|
||||
<img src={item.icon.__icon_url__} className='w-full h-full object-contain' alt={item.icon.__icon_query__} />
|
||||
<img src={item.icon.icon_url_} className='w-full h-full object-contain' alt={item.icon.icon_query_} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:space-y-4 mt-2 lg:mt-4">
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ const type7SlideSchema = z.object({
|
|||
description: "Item description",
|
||||
}),
|
||||
icon: IconSchema.default({
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Default icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Default icon'
|
||||
}).meta({
|
||||
description: "Icon for the item",
|
||||
})
|
||||
|
|
@ -28,32 +28,32 @@ const type7SlideSchema = z.object({
|
|||
heading: 'Professional Service',
|
||||
description: 'High-quality professional services tailored to your specific needs and requirements',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Professional service icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Professional service icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Expert Consultation',
|
||||
description: 'Expert advice and consultation from experienced professionals in the field',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Expert consultation icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Expert consultation icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Quality Assurance',
|
||||
description: 'Comprehensive quality assurance processes to ensure excellent results',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Quality assurance icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Quality assurance icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Customer Support',
|
||||
description: 'Dedicated customer support available to assist you throughout the process',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Customer support icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Customer support icon'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -100,8 +100,8 @@ const Type7SlideLayout: React.FC<Type7SlideLayoutProps> = ({ data: slideData })
|
|||
<div className="flex-shrink-0 lg:w-16">
|
||||
<div className="w-12 h-12 lg:w-16 lg:h-16 bg-blue-600 rounded-lg flex items-center justify-center overflow-hidden">
|
||||
<img
|
||||
src={item.icon?.__icon_url__ || ''}
|
||||
alt={item.icon?.__icon_query__ || item.heading}
|
||||
src={item.icon?.icon_url_ || ''}
|
||||
alt={item.icon?.icon_query_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -134,9 +134,9 @@ const Type7SlideLayout: React.FC<Type7SlideLayoutProps> = ({ data: slideData })
|
|||
>
|
||||
<div className="text-center mb-4">
|
||||
<div className="w-16 h-16 lg:w-20 lg:h-20 bg-blue-600 rounded-lg flex items-center justify-center mx-auto mb-4 overflow-hidden">
|
||||
{item.icon?.__icon_url__ && <img
|
||||
src={item.icon?.__icon_url__ || ''}
|
||||
alt={item.icon?.__icon_query__ || item.heading}
|
||||
{item.icon?.icon_url_ && <img
|
||||
src={item.icon?.icon_url_ || ''}
|
||||
alt={item.icon?.icon_query_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ const type8SlideSchema = z.object({
|
|||
description: "Item description",
|
||||
}),
|
||||
icon: IconSchema.default({
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Default icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Default icon'
|
||||
}).meta({
|
||||
description: "Icon for the item",
|
||||
})
|
||||
|
|
@ -31,24 +31,24 @@ const type8SlideSchema = z.object({
|
|||
heading: 'Advanced Features',
|
||||
description: 'Cutting-edge functionality designed to enhance productivity and user experience',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Advanced features icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Advanced features icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Reliable Performance',
|
||||
description: 'Consistent and dependable performance across all platforms and devices',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Reliable performance icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Reliable performance icon'
|
||||
}
|
||||
},
|
||||
{
|
||||
heading: 'Secure Environment',
|
||||
description: 'Enterprise-grade security measures to protect your data and privacy',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'Secure environment icon'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'Secure environment icon'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -82,9 +82,9 @@ const Type8SlideLayout: React.FC<Type8SlideLayoutProps> = ({ data: slideData })
|
|||
>
|
||||
<div className="text-center mb-4">
|
||||
<div className="w-16 h-16 lg:w-20 lg:h-20 bg-blue-600 rounded-lg flex items-center justify-center mx-auto mb-4 overflow-hidden">
|
||||
{item.icon?.__icon_url__ && <img
|
||||
src={item.icon?.__icon_url__ || ''}
|
||||
alt={item.icon?.__icon_query__ || item.heading}
|
||||
{item.icon?.icon_url_ && <img
|
||||
src={item.icon?.icon_url_ || ''}
|
||||
alt={item.icon?.icon_query_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>}
|
||||
</div>
|
||||
|
|
@ -116,9 +116,9 @@ const Type8SlideLayout: React.FC<Type8SlideLayoutProps> = ({ data: slideData })
|
|||
<div className="flex items-start gap-4">
|
||||
<div className="w-[64px] h-[64px]">
|
||||
<div className="w-full h-full bg-blue-600 rounded-lg flex items-center justify-center overflow-hidden">
|
||||
{item.icon?.__icon_url__ && <img
|
||||
src={item.icon?.__icon_url__ || ''}
|
||||
alt={item.icon?.__icon_query__ || item.heading}
|
||||
{item.icon?.icon_url_ && <img
|
||||
src={item.icon?.icon_url_ || ''}
|
||||
alt={item.icon?.icon_query_ || item.heading}
|
||||
className="w-full h-full object-cover"
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import * as z from "zod";
|
||||
|
||||
export const ImageSchema = z.object({
|
||||
__image_url__: z.url().meta({
|
||||
image_url_: z.url().meta({
|
||||
description: "URL to image",
|
||||
}),
|
||||
__image_prompt__: z.string().meta({
|
||||
image_prompt_: z.string().meta({
|
||||
description: "Prompt used to generate the image",
|
||||
}).min(10).max(50),
|
||||
})
|
||||
|
||||
export const IconSchema = z.object({
|
||||
__icon_url__: z.string().meta({
|
||||
icon_url_: z.string().meta({
|
||||
description: "URL to icon",
|
||||
}),
|
||||
__icon_query__: z.string().meta({
|
||||
icon_query_: z.string().meta({
|
||||
description: "Query used to search the icon",
|
||||
}).min(5).max(20),
|
||||
})
|
||||
|
|
@ -14,8 +14,8 @@ const basicInfoSlideSchema = z.object({
|
|||
description: "Main description text content",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
image_url_: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
|
|
@ -54,8 +54,8 @@ const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slide
|
|||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ const bulletIconsOnlySlideSchema = z.object({
|
|||
description: "Main title of the slide",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business professionals collaborating and discussing solutions'
|
||||
image_url_: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Business professionals collaborating and discussing solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
|
|
@ -29,32 +29,32 @@ const bulletIconsOnlySlideSchema = z.object({
|
|||
title: 'Custom Software',
|
||||
subtitle: 'We create tailored software to optimize processes and boost efficiency.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'code software development'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'code software development'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Digital Consulting',
|
||||
subtitle: 'Our consultants guide organizations in leveraging the latest technologies.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'users consulting team'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'users consulting team'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Support Services',
|
||||
subtitle: 'We provide ongoing support to help businesses adapt and maintain performance.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'headphones support service'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'headphones support service'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Scalable Marketing',
|
||||
subtitle: 'Our data-driven strategies help businesses expand their reach and engagement.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'trending up marketing growth'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'trending up marketing growth'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -131,8 +131,8 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
|
|||
{/* Icon */}
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-purple-600 rounded-full flex items-center justify-center">
|
||||
<img
|
||||
src={bullet.icon.__icon_url__}
|
||||
alt={bullet.icon.__icon_query__}
|
||||
src={bullet.icon.icon_url_}
|
||||
alt={bullet.icon.icon_query_}
|
||||
className="w-6 h-6 object-contain brightness-0 invert"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -176,8 +176,8 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
|
|||
{/* Main Image */}
|
||||
<div className="w-full h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ const bulletWithIconsSlideSchema = z.object({
|
|||
description: "Main description text explaining the problem or topic",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business people analyzing documents and charts in office'
|
||||
image_url_: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Business people analyzing documents and charts in office'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
|
|
@ -32,16 +32,16 @@ const bulletWithIconsSlideSchema = z.object({
|
|||
title: 'Inefficiency',
|
||||
description: 'Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'warning alert inefficiency'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'warning alert inefficiency'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'High Costs',
|
||||
description: 'Outdated systems increase expenses, while small businesses struggle to expand their market reach.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'trending up costs chart'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'trending up costs chart'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -105,8 +105,8 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
|
|||
<div className="relative z-10 h-full flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -134,8 +134,8 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
|
|||
{/* Icon */}
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-white rounded-lg shadow-md flex items-center justify-center">
|
||||
<img
|
||||
src={bullet.icon.__icon_url__}
|
||||
alt={bullet.icon.__icon_query__}
|
||||
src={bullet.icon.icon_url_}
|
||||
alt={bullet.icon.icon_query_}
|
||||
className="w-6 h-6 object-contain text-gray-700"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -64,24 +64,24 @@ const chartWithBulletsSlideSchema = z.object({
|
|||
title: 'Total Addressable Market',
|
||||
description: 'Companies can use TAM to plan future expansion and investment.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'target market scope'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'target market scope'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Serviceable Available Market',
|
||||
description: 'Indicates more measurable market segments for sales efforts.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'pie chart analysis'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'pie chart analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Serviceable Obtainable Market',
|
||||
description: 'Help companies plan development strategies according to the market.',
|
||||
icon: {
|
||||
__icon_url__: '/static/icons/placeholder.png',
|
||||
__icon_query__: 'trending up growth'
|
||||
icon_url_: '/static/icons/placeholder.png',
|
||||
icon_query_: 'trending up growth'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -270,8 +270,8 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
|
|||
<div className="flex items-center space-x-3 mb-3">
|
||||
<div className="w-8 h-8 bg-white/20 rounded-lg flex items-center justify-center">
|
||||
<img
|
||||
src={bullet.icon.__icon_url__}
|
||||
alt={bullet.icon.__icon_query__}
|
||||
src={bullet.icon.icon_url_}
|
||||
alt={bullet.icon.icon_query_}
|
||||
className="w-5 h-5 object-contain brightness-0 invert"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ const introSlideSchema = z.object({
|
|||
description: "Date of the presentation",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business team in meeting room discussing product features and solutions'
|
||||
image_url_: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Business team in meeting room discussing product features and solutions'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
})
|
||||
|
|
@ -64,8 +64,8 @@ const IntroSlideLayout: React.FC<IntroSlideLayoutProps> = ({ data: slideData })
|
|||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-80 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ const metricsWithImageSlideSchema = z.object({
|
|||
description: "Description text below the title",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Person holding tablet with analytics dashboard and charts'
|
||||
image_url_: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Person holding tablet with analytics dashboard and charts'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
|
|
@ -86,8 +86,8 @@ const MetricsWithImageSlideLayout: React.FC<MetricsWithImageSlideLayoutProps> =
|
|||
<div className="flex-1 flex items-center justify-center pr-8">
|
||||
<div className="w-full max-w-lg h-96 rounded-2xl overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ const numberedBulletsSlideSchema = z.object({
|
|||
description: "Main title of the slide",
|
||||
}),
|
||||
image: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
__image_prompt__: 'Business people analyzing charts and data on wall'
|
||||
image_url_: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80',
|
||||
image_prompt_: 'Business people analyzing charts and data on wall'
|
||||
}).meta({
|
||||
description: "Supporting image for the slide",
|
||||
}),
|
||||
|
|
@ -87,8 +87,8 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
|
|||
{/* Image Section */}
|
||||
<div className="flex-shrink-0 w-80 h-48">
|
||||
<img
|
||||
src={slideData?.image?.__image_url__ || ''}
|
||||
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
|
||||
src={slideData?.image?.image_url_ || ''}
|
||||
alt={slideData?.image?.image_prompt_ || slideData?.title || ''}
|
||||
className="w-full h-full object-cover rounded-lg shadow-md"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ const quoteSlideSchema = z.object({
|
|||
description: "Author of the quote",
|
||||
}),
|
||||
backgroundImage: ImageSchema.default({
|
||||
__image_url__: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2000&q=80',
|
||||
__image_prompt__: 'Inspirational mountain landscape with dramatic sky and clouds'
|
||||
image_url_: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2000&q=80',
|
||||
image_prompt_: 'Inspirational mountain landscape with dramatic sky and clouds'
|
||||
}).meta({
|
||||
description: "Background image for the slide",
|
||||
})
|
||||
|
|
@ -51,7 +51,7 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
|
|||
<div
|
||||
className="absolute inset-0 w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
style={{
|
||||
backgroundImage: `url('${slideData?.backgroundImage?.__image_url__ || ''}')`,
|
||||
backgroundImage: `url('${slideData?.backgroundImage?.image_url_ || ''}')`,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ const teamSlideSchema = z.object({
|
|||
position: 'CEO',
|
||||
description: 'Strategic leader with 15+ years experience in digital transformation and business growth.',
|
||||
image: {
|
||||
__image_url__: 'https://images.unsplash.com/photo-1494790108755-2616b612994a?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
__image_prompt__: 'Professional businesswoman CEO headshot'
|
||||
image_url_: 'https://images.unsplash.com/photo-1494790108755-2616b612994a?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
image_prompt_: 'Professional businesswoman CEO headshot'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -41,8 +41,8 @@ const teamSlideSchema = z.object({
|
|||
position: 'CTO',
|
||||
description: 'Technology expert specializing in scalable solutions and innovative software architecture.',
|
||||
image: {
|
||||
__image_url__: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
__image_prompt__: 'Professional businessman CTO headshot'
|
||||
image_url_: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
image_prompt_: 'Professional businessman CTO headshot'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -50,8 +50,8 @@ const teamSlideSchema = z.object({
|
|||
position: 'COO',
|
||||
description: 'Operations leader focused on efficiency, process optimization, and team development.',
|
||||
image: {
|
||||
__image_url__: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
__image_prompt__: 'Professional businessman COO headshot'
|
||||
image_url_: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
image_prompt_: 'Professional businessman COO headshot'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -59,8 +59,8 @@ const teamSlideSchema = z.object({
|
|||
position: 'CMO',
|
||||
description: 'Marketing strategist with expertise in brand development and customer engagement.',
|
||||
image: {
|
||||
__image_url__: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
__image_prompt__: 'Professional businesswoman CMO headshot'
|
||||
image_url_: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
|
||||
image_prompt_: 'Professional businesswoman CMO headshot'
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -138,8 +138,8 @@ const TeamSlideLayout: React.FC<TeamSlideLayoutProps> = ({ data: slideData }) =>
|
|||
{/* Member Photo */}
|
||||
<div className="w-32 h-32 mx-auto rounded-lg overflow-hidden shadow-md">
|
||||
<img
|
||||
src={member.image.__image_url__ || ''}
|
||||
alt={member.image.__image_prompt__ || member.name}
|
||||
src={member.image.image_url_ || ''}
|
||||
alt={member.image.image_prompt_ || member.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ const AboutCompanySlideLayout: React.FC<AboutCompanySlideLayoutProps> = ({
|
|||
<div className="w-full h-96 overflow-hidden">
|
||||
{slideData?.image ? (
|
||||
<img
|
||||
src={slideData.image.__image_url__}
|
||||
alt={slideData.image.__image_prompt__}
|
||||
src={slideData.image.image_url_}
|
||||
alt={slideData.image.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ const problemStatementSlideSchema = z.object({
|
|||
description:
|
||||
"Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.",
|
||||
icon: {
|
||||
__icon_url__:
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
__icon_query__: "warning alert inefficiency",
|
||||
icon_query_: "warning alert inefficiency",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -53,9 +53,9 @@ const problemStatementSlideSchema = z.object({
|
|||
description:
|
||||
"Outdated systems increase expenses, while small businesses struggle to expand their market reach.",
|
||||
icon: {
|
||||
__icon_url__:
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
__icon_query__: "trending up costs chart",
|
||||
icon_query_: "trending up costs chart",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -63,9 +63,9 @@ const problemStatementSlideSchema = z.object({
|
|||
description:
|
||||
"Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.",
|
||||
icon: {
|
||||
__icon_url__:
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
__icon_query__: "warning alert inefficiency",
|
||||
icon_query_: "warning alert inefficiency",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -73,9 +73,9 @@ const problemStatementSlideSchema = z.object({
|
|||
description:
|
||||
"Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.",
|
||||
icon: {
|
||||
__icon_url__:
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
__icon_query__: "warning alert inefficiency",
|
||||
icon_query_: "warning alert inefficiency",
|
||||
},
|
||||
},
|
||||
])
|
||||
|
|
@ -150,10 +150,10 @@ const ProblemStatementSlideLayout: React.FC<
|
|||
className="flex items-start gap-5 bg-white bg-opacity-5 rounded-lg p-5"
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
{category.icon?.__icon_url__ && (
|
||||
{category.icon?.icon_url_ && (
|
||||
<img
|
||||
src={category.icon?.__icon_url__}
|
||||
alt={category.icon?.__icon_query__}
|
||||
src={category.icon?.icon_url_}
|
||||
alt={category.icon?.icon_query_}
|
||||
className="w-12 h-12"
|
||||
style={{ filter: "invert(1)" }}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ const solutionSlideSchema = z.object({
|
|||
description:
|
||||
"Innovative and widely accepted. Innovative and widely accepted. Innovative and widely accepted.",
|
||||
icon: {
|
||||
__icon_query__: "market innovation",
|
||||
__icon_url__:
|
||||
icon_query_: "market innovation",
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
},
|
||||
},
|
||||
|
|
@ -58,8 +58,8 @@ const solutionSlideSchema = z.object({
|
|||
title: "Industry",
|
||||
description: "Based on sound market decisions.",
|
||||
icon: {
|
||||
__icon_query__: "industry building",
|
||||
__icon_url__:
|
||||
icon_query_: "industry building",
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
},
|
||||
},
|
||||
|
|
@ -67,8 +67,8 @@ const solutionSlideSchema = z.object({
|
|||
title: "SEM",
|
||||
description: "Driven by precise data and analysis.",
|
||||
icon: {
|
||||
__icon_query__: "SEM data analysis",
|
||||
__icon_url__:
|
||||
icon_query_: "SEM data analysis",
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
},
|
||||
},
|
||||
|
|
@ -76,8 +76,8 @@ const solutionSlideSchema = z.object({
|
|||
title: "End User",
|
||||
description: "Focused on real user impact.",
|
||||
icon: {
|
||||
__icon_query__: "end user impact",
|
||||
__icon_url__:
|
||||
icon_query_: "end user impact",
|
||||
icon_url_:
|
||||
"/static/icons/placeholder.png",
|
||||
},
|
||||
},
|
||||
|
|
@ -140,10 +140,10 @@ const SolutionSlideLayout: React.FC<SolutionSlideLayoutProps> = ({
|
|||
className="flex flex-col items-center text-center bg-[#F5F8FE] rounded-lg shadow px-3 py-4 "
|
||||
>
|
||||
<div className="mb-2">
|
||||
{section?.icon?.__icon_url__ && (
|
||||
{section?.icon?.icon_url_ && (
|
||||
<img
|
||||
src={section.icon.__icon_url__}
|
||||
alt={section.icon.__icon_query__}
|
||||
src={section.icon.icon_url_}
|
||||
alt={section.icon.icon_query_}
|
||||
className="w-12 h-12 mb-2"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ const productOverviewSlideSchema = z.object({
|
|||
description:
|
||||
"Detail and explain each product. Our examination of community and market issues increases with additional products/services.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=300&h=200&fit=crop",
|
||||
__image_prompt__: "Person working on electronics with headphones",
|
||||
image_prompt_: "Person working on electronics with headphones",
|
||||
},
|
||||
isBlueBackground: true,
|
||||
},
|
||||
|
|
@ -63,9 +63,9 @@ const productOverviewSlideSchema = z.object({
|
|||
description:
|
||||
"Our alternate product category is available. Our products must work together to solve social and economic issues.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://images.unsplash.com/photo-1573164713988-8665fc963095?w=300&h=200&fit=crop",
|
||||
__image_prompt__:
|
||||
image_prompt_:
|
||||
"Woman working at computer with technical equipment",
|
||||
},
|
||||
isBlueBackground: true,
|
||||
|
|
@ -163,9 +163,9 @@ const ProductOverviewSlideLayout: React.FC<ProductOverviewSlideLayoutProps> = ({
|
|||
style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }}
|
||||
>
|
||||
<img
|
||||
src={products[0].image.__image_url__}
|
||||
src={products[0].image.image_url_}
|
||||
alt={
|
||||
products[0].image.__image_prompt__ || products[0].title
|
||||
products[0].image.image_prompt_ || products[0].title
|
||||
}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
@ -189,9 +189,9 @@ const ProductOverviewSlideLayout: React.FC<ProductOverviewSlideLayoutProps> = ({
|
|||
style={{ height: `${IMAGE_SECTION_HEIGHT + 28}px` }}
|
||||
>
|
||||
<img
|
||||
src={products[1].image.__image_url__}
|
||||
src={products[1].image.image_url_}
|
||||
alt={
|
||||
products[1].image.__image_prompt__ || products[1].title
|
||||
products[1].image.image_prompt_ || products[1].title
|
||||
}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ const marketSizeSlideSchema = z.object({
|
|||
description: "Today Date displayed in header",
|
||||
}),
|
||||
mapImage: ImageSchema.default({
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://upload.wikimedia.org/wikipedia/commons/8/80/World_map_-_low_resolution.svg", // You can quickly find a world map image via a Google search or use a free resource like Wikimedia Commons
|
||||
__image_prompt__: "World map with location pins or points",
|
||||
image_prompt_: "World map with location pins or points",
|
||||
}),
|
||||
marketStats: z
|
||||
.array(
|
||||
|
|
@ -111,9 +111,9 @@ const MarketSizeSlideLayout: React.FC<MarketSizeSlideProps> = ({
|
|||
{slideData?.title || "Market Size"}
|
||||
</h1>
|
||||
<div className="w-full bg-[#CBE3CC] rounded-md mb-8 flex items-center justify-center">
|
||||
{slideData?.mapImage?.__image_url__ && (
|
||||
{slideData?.mapImage?.image_url_ && (
|
||||
<img
|
||||
src={slideData?.mapImage?.__image_url__}
|
||||
src={slideData?.mapImage?.image_url_}
|
||||
alt="Market World Map with Points"
|
||||
className="w-full object-contain rounded-md"
|
||||
style={{ maxHeight: 220 }}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ const modernTeamSlideSchema = z.object({
|
|||
description:
|
||||
"Strategic leader with 15+ years experience in technology and business development. Former VP at Fortune 500 company.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://plus.unsplash.com/premium_photo-1661589856899-6dd0871f9db6?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NXx8YnVzaW5lc3N3b21lbnxlbnwwfHwwfHx8MA%3D%3D",
|
||||
__image_prompt__: "Professional businesswoman CEO headshot",
|
||||
image_prompt_: "Professional businesswoman CEO headshot",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -52,9 +52,9 @@ const modernTeamSlideSchema = z.object({
|
|||
description:
|
||||
"Technology expert specializing in scalable architecture and AI solutions. PhD in Computer Science from MIT.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional businessman CTO headshot",
|
||||
image_prompt_: "Professional businessman CTO headshot",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -63,9 +63,9 @@ const modernTeamSlideSchema = z.object({
|
|||
description:
|
||||
"Sales leader with proven track record of building high-performing teams and driving revenue growth in B2B markets.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional businesswoman VP headshot",
|
||||
image_prompt_: "Professional businesswoman VP headshot",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -74,9 +74,9 @@ const modernTeamSlideSchema = z.object({
|
|||
description:
|
||||
"Product strategist focused on user experience and market-driven solutions. Former product manager at leading tech companies.",
|
||||
image: {
|
||||
__image_url__:
|
||||
image_url_:
|
||||
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional businessman product manager headshot",
|
||||
image_prompt_: "Professional businessman product manager headshot",
|
||||
},
|
||||
},
|
||||
])
|
||||
|
|
@ -145,10 +145,10 @@ const ModernTeamSlideLayout: React.FC<ModernTeamSlideLayoutProps> = ({
|
|||
>
|
||||
{/* Photo */}
|
||||
<div className="relative w-28 h-28 mb-4 rounded overflow-hidden bg-white border-2 border-blue-100 flex items-center justify-center">
|
||||
{member.image.__image_url__ && (
|
||||
{member.image.image_url_ && (
|
||||
<img
|
||||
src={member.image.__image_url__}
|
||||
alt={member.image.__image_prompt__ || member.name}
|
||||
src={member.image.image_url_}
|
||||
alt={member.image.image_prompt_ || member.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
featuredImage: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Professional business team analyzing data and working collaboratively"
|
||||
image_url_: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Professional business team analyzing data and working collaboratively"
|
||||
}).meta({
|
||||
description: "Primary visual that represents the organization's work or environment",
|
||||
}),
|
||||
|
|
@ -127,11 +127,11 @@ const AboutUsSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
)}
|
||||
|
||||
{/* Business Image - Left positioned */}
|
||||
{featuredImage?.__image_url__ && (
|
||||
{featuredImage?.image_url_ && (
|
||||
<div className="absolute right-36 top-1/2 -translate-y-1/2 h-[500px] w-[350px] z-20 shadow-lg">
|
||||
<img
|
||||
src={featuredImage.__image_url__}
|
||||
alt={featuredImage.__image_prompt__}
|
||||
src={featuredImage.image_url_}
|
||||
alt={featuredImage.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
headerVisual: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Business strategy meeting with charts, graphs and team collaboration"
|
||||
image_url_: "https://images.unsplash.com/photo-1559136555-9303baea8ebd?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Business strategy meeting with charts, graphs and team collaboration"
|
||||
}).meta({
|
||||
description: "Header visual representing the topic area - ADAPT the image prompt to match presentation topic (e.g., 'Climate scientists analyzing global warming data', 'Medical team reviewing patient care protocols', 'Teachers planning educational curriculum')",
|
||||
}),
|
||||
|
|
@ -92,11 +92,11 @@ const BusinessModelSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
return (
|
||||
<div className="aspect-video max-w-[1280px] w-full bg-white relative overflow-hidden">
|
||||
{/* Header Image Section */}
|
||||
{headerVisual?.__image_url__ && (
|
||||
{headerVisual?.image_url_ && (
|
||||
<div className="h-32 w-full relative">
|
||||
<img
|
||||
src={headerVisual.__image_url__}
|
||||
alt={headerVisual.__image_prompt__}
|
||||
src={headerVisual.image_url_}
|
||||
alt={headerVisual.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
visualRepresentation: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1597149962419-0d900ac2b46c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "World map showing global market reach and geographic distribution"
|
||||
image_url_: "https://images.unsplash.com/photo-1597149962419-0d900ac2b46c?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "World map showing global market reach and geographic distribution"
|
||||
}).meta({
|
||||
description: "Visual that represents market scope - could be a world map, chart, or geographic visualization",
|
||||
}),
|
||||
|
|
@ -135,11 +135,11 @@ const MarketSizeSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
)}
|
||||
|
||||
{/* Visual Representation */}
|
||||
{visualRepresentation?.__image_url__ && (
|
||||
{visualRepresentation?.image_url_ && (
|
||||
<div className="absolute inset-8 shadow-lg">
|
||||
<img
|
||||
src={visualRepresentation.__image_url__}
|
||||
alt={visualRepresentation.__image_prompt__}
|
||||
src={visualRepresentation.image_url_}
|
||||
alt={visualRepresentation.image_prompt_}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
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"
|
||||
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"
|
||||
}).meta({
|
||||
description: "Visual that represents service delivery, expertise, or client collaboration",
|
||||
}),
|
||||
|
|
@ -102,11 +102,11 @@ const OurServiceSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
{/* Right - Service Highlight */}
|
||||
<div className="w-1/2 relative">
|
||||
{/* Service Highlight Image */}
|
||||
{serviceHighlight?.__image_url__ && (
|
||||
{serviceHighlight?.image_url_ && (
|
||||
<div className="h-full w-full">
|
||||
<img
|
||||
src={serviceHighlight.__image_url__}
|
||||
alt={serviceHighlight.__image_prompt__}
|
||||
src={serviceHighlight.image_url_}
|
||||
alt={serviceHighlight.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
supportingVisual: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Professional workspace showing analysis and problem-solving activities"
|
||||
image_url_: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Professional workspace showing analysis and problem-solving activities"
|
||||
}).meta({
|
||||
description: "Visual that supports the problem discussion - could show analysis, challenges, or work environment",
|
||||
}),
|
||||
|
|
@ -129,11 +129,11 @@ const ProblemsSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
)}
|
||||
|
||||
{/* Supporting Visual */}
|
||||
{supportingVisual?.__image_url__ && (
|
||||
{supportingVisual?.image_url_ && (
|
||||
<div className="absolute top-8 right-8 bottom-20 left-4 shadow-lg">
|
||||
<img
|
||||
src={supportingVisual.__image_url__}
|
||||
alt={supportingVisual.__image_prompt__}
|
||||
src={supportingVisual.image_url_}
|
||||
alt={supportingVisual.image_prompt_}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,15 +46,15 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
primaryVisual: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1560472354-b33ff0c44a43?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Modern workspace with team collaboration and strategic planning"
|
||||
image_url_: "https://images.unsplash.com/photo-1560472354-b33ff0c44a43?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Modern workspace with team collaboration and strategic planning"
|
||||
}).meta({
|
||||
description: "Primary visual representing teamwork, strategy, or solution implementation",
|
||||
}),
|
||||
|
||||
brandingVisual: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/150x80/22C55E/FFFFFF?text=LOGO",
|
||||
__image_prompt__: "Organization logo or brand mark"
|
||||
image_url_: "https://via.placeholder.com/150x80/22C55E/FFFFFF?text=LOGO",
|
||||
image_prompt_: "Organization logo or brand mark"
|
||||
}).meta({
|
||||
description: "Logo or branding element to maintain visual identity",
|
||||
}),
|
||||
|
|
@ -93,11 +93,11 @@ const SolutionsSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
{/* Left Side - Images and Branding */}
|
||||
<div className="w-2/5 relative bg-gray-100 flex flex-col">
|
||||
{/* Top Image Area */}
|
||||
{primaryVisual?.__image_url__ && (
|
||||
{primaryVisual?.image_url_ && (
|
||||
<div className="flex-1 relative">
|
||||
<img
|
||||
src={primaryVisual.__image_url__}
|
||||
alt={primaryVisual.__image_prompt__}
|
||||
src={primaryVisual.image_url_}
|
||||
alt={primaryVisual.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -105,10 +105,10 @@ const SolutionsSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
|
||||
{/* Bottom Branding Area */}
|
||||
<div className="h-24 bg-white flex items-center justify-center px-8">
|
||||
{brandingVisual?.__image_url__ && (
|
||||
{brandingVisual?.image_url_ && (
|
||||
<img
|
||||
src={brandingVisual.__image_url__}
|
||||
alt={brandingVisual.__image_prompt__}
|
||||
src={brandingVisual.image_url_}
|
||||
alt={brandingVisual.image_prompt_}
|
||||
className="h-12 object-contain"
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
__image_prompt__: "Professional organization logo - clean and modern design"
|
||||
image_url_: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
image_prompt_: "Professional organization logo - clean and modern design"
|
||||
}).meta({
|
||||
description: "Logo or brand mark representing the organization",
|
||||
}),
|
||||
|
|
@ -67,8 +67,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
companyLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/FFFFFF/1D9A8A?text=C",
|
||||
__image_prompt__: "Clean modern company logo icon in white"
|
||||
image_url_: "https://via.placeholder.com/40x40/FFFFFF/1D9A8A?text=C",
|
||||
image_prompt_: "Clean modern company logo icon in white"
|
||||
}).meta({
|
||||
description: "Company logo icon",
|
||||
}),
|
||||
|
|
@ -128,11 +128,11 @@ const StatisticCircularSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
{/* Right - Company Branding */}
|
||||
<div className="w-1/2 bg-teal-600 px-16 py-8 flex items-center justify-end">
|
||||
<div className="flex items-center space-x-3">
|
||||
{companyLogo?.__image_url__ && (
|
||||
{companyLogo?.image_url_ && (
|
||||
<div className="w-10 h-10">
|
||||
<img
|
||||
src={companyLogo.__image_url__}
|
||||
alt={companyLogo.__image_prompt__}
|
||||
src={companyLogo.image_url_}
|
||||
alt={companyLogo.image_prompt_}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
__image_prompt__: "Professional organization logo - clean and modern design"
|
||||
image_url_: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
image_prompt_: "Professional organization logo - clean and modern design"
|
||||
}).meta({
|
||||
description: "Logo or brand mark representing the organization",
|
||||
}),
|
||||
|
|
@ -130,11 +130,11 @@ const StatisticDualChartSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
|
||||
{/* Company Branding */}
|
||||
<div className="flex items-center space-x-3">
|
||||
{brandLogo?.__image_url__ && (
|
||||
{brandLogo?.image_url_ && (
|
||||
<div className="w-8 h-8">
|
||||
<img
|
||||
src={brandLogo.__image_url__}
|
||||
alt={brandLogo.__image_prompt__}
|
||||
src={brandLogo.image_url_}
|
||||
alt={brandLogo.image_prompt_}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
supportingVisual: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Business analytics dashboard with charts and data visualization"
|
||||
image_url_: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Business analytics dashboard with charts and data visualization"
|
||||
}).meta({
|
||||
description: "ADAPT the image prompt to match the presentation topic: For global warming: 'Climate monitoring station with temperature sensors and weather equipment', 'Scientists analyzing ice core data in Arctic research facility'. For healthcare: 'Medical monitoring equipment displaying patient vital signs', 'Healthcare analytics dashboard showing treatment outcomes'. For education: 'Educational assessment data on computer screens', 'Students using digital learning platforms'.",
|
||||
}),
|
||||
|
|
@ -148,12 +148,12 @@ const StatisticSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
</div>
|
||||
|
||||
{/* Business Image */}
|
||||
{supportingVisual?.__image_url__ && (
|
||||
{supportingVisual?.image_url_ && (
|
||||
<div className="flex-1 flex items-end">
|
||||
<div className="w-full h-48">
|
||||
<img
|
||||
src={supportingVisual.__image_url__}
|
||||
alt={supportingVisual.__image_prompt__}
|
||||
src={supportingVisual.image_url_}
|
||||
alt={supportingVisual.image_prompt_}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandingVisual: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/200x100/22C55E/FFFFFF?text=BRAND",
|
||||
__image_prompt__: "Organization logo or brand visual element"
|
||||
image_url_: "https://via.placeholder.com/200x100/22C55E/FFFFFF?text=BRAND",
|
||||
image_prompt_: "Organization logo or brand visual element"
|
||||
}).meta({
|
||||
description: "Logo or branding element displayed prominently for visual identity",
|
||||
}),
|
||||
|
|
@ -146,11 +146,11 @@ const TableOfContentsSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
{/* Right Side - Branding and Visual Elements */}
|
||||
<div className="w-2/5 relative bg-gray-50 flex items-center justify-center">
|
||||
{/* Branding Visual */}
|
||||
{brandingVisual?.__image_url__ && (
|
||||
{brandingVisual?.image_url_ && (
|
||||
<div className="text-center">
|
||||
<img
|
||||
src={brandingVisual.__image_url__}
|
||||
alt={brandingVisual.__image_prompt__}
|
||||
src={brandingVisual.image_url_}
|
||||
alt={brandingVisual.image_prompt_}
|
||||
className="max-w-64 max-h-32 object-contain mx-auto"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
__image_prompt__: "Professional organization logo - clean and modern design"
|
||||
image_url_: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
image_prompt_: "Professional organization logo - clean and modern design"
|
||||
}).meta({
|
||||
description: "Logo or brand mark representing the organization",
|
||||
}),
|
||||
|
|
@ -43,8 +43,8 @@ export const Schema = z.object({
|
|||
testimonialText: "Working with this team has been transformative for our business. Their expertise, dedication, and innovative approach exceeded our expectations and delivered remarkable results.",
|
||||
rating: 5,
|
||||
clientPhoto: {
|
||||
__image_url__: "https://images.unsplash.com/photo-1494790108755-2616b612b830?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional businesswoman headshot"
|
||||
image_url_: "https://images.unsplash.com/photo-1494790108755-2616b612b830?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
image_prompt_: "Professional businesswoman headshot"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -54,8 +54,8 @@ export const Schema = z.object({
|
|||
testimonialText: "The level of professionalism and quality of service provided was outstanding. They understood our needs perfectly and delivered solutions that truly made a difference.",
|
||||
rating: 5,
|
||||
clientPhoto: {
|
||||
__image_url__: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional businessman headshot"
|
||||
image_url_: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
image_prompt_: "Professional businessman headshot"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -65,8 +65,8 @@ export const Schema = z.object({
|
|||
testimonialText: "Exceptional service and results that spoke for themselves. The team's attention to detail and commitment to excellence made our collaboration highly successful.",
|
||||
rating: 5,
|
||||
clientPhoto: {
|
||||
__image_url__: "https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
__image_prompt__: "Professional woman headshot"
|
||||
image_url_: "https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80",
|
||||
image_prompt_: "Professional woman headshot"
|
||||
}
|
||||
}
|
||||
]).meta({
|
||||
|
|
@ -121,11 +121,11 @@ const TestimonialSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
|
||||
{/* Company Branding */}
|
||||
<div className="flex items-center space-x-3">
|
||||
{brandLogo?.__image_url__ && (
|
||||
{brandLogo?.image_url_ && (
|
||||
<div className="w-8 h-8">
|
||||
<img
|
||||
src={brandLogo.__image_url__}
|
||||
alt={brandLogo.__image_prompt__}
|
||||
src={brandLogo.image_url_}
|
||||
alt={brandLogo.image_prompt_}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -164,11 +164,11 @@ const TestimonialSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
{/* Client Info */}
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* Client Photo */}
|
||||
{showClientPhotos && item.clientPhoto?.__image_url__ && (
|
||||
{showClientPhotos && item.clientPhoto?.image_url_ && (
|
||||
<div className="w-12 h-12 rounded-full overflow-hidden flex-shrink-0">
|
||||
<img
|
||||
src={item.clientPhoto.__image_url__}
|
||||
alt={item.clientPhoto.__image_prompt__}
|
||||
src={item.clientPhoto.image_url_}
|
||||
alt={item.clientPhoto.image_prompt_}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
__image_prompt__: "Professional organization logo - clean and modern design"
|
||||
image_url_: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
image_prompt_: "Professional organization logo - clean and modern design"
|
||||
}).meta({
|
||||
description: "Logo or brand mark representing the presenting organization",
|
||||
}),
|
||||
|
|
@ -89,11 +89,11 @@ const ThankYouSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
|
||||
{/* Company Logo and Name */}
|
||||
<div className="flex items-center space-x-3">
|
||||
{brandLogo?.__image_url__ && (
|
||||
{brandLogo?.image_url_ && (
|
||||
<div className="w-10 h-10">
|
||||
<img
|
||||
src={brandLogo.__image_url__}
|
||||
alt={brandLogo.__image_prompt__}
|
||||
src={brandLogo.image_url_}
|
||||
alt={brandLogo.image_prompt_}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
brandLogo: ImageSchema.default({
|
||||
__image_url__: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
__image_prompt__: "Professional organization logo - clean and modern design"
|
||||
image_url_: "https://via.placeholder.com/40x40/22C55E/FFFFFF?text=L",
|
||||
image_prompt_: "Professional organization logo - clean and modern design"
|
||||
}).meta({
|
||||
description: "Logo or brand mark representing the presenting organization",
|
||||
}),
|
||||
|
|
@ -84,11 +84,11 @@ const ThynkTitleSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
<div className="absolute top-0 left-0 right-0 px-16 py-8 flex justify-between items-center z-20">
|
||||
{/* Company Logo and Name */}
|
||||
<div className="flex items-center space-x-3">
|
||||
{brandLogo?.__image_url__ && (
|
||||
{brandLogo?.image_url_ && (
|
||||
<div className="w-10 h-10">
|
||||
<img
|
||||
src={brandLogo.__image_url__}
|
||||
alt={brandLogo.__image_prompt__}
|
||||
src={brandLogo.image_url_}
|
||||
alt={brandLogo.image_prompt_}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ export const Schema = z.object({
|
|||
}),
|
||||
|
||||
supportingVisual: ImageSchema.default({
|
||||
__image_url__: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
__image_prompt__: "Diverse team collaborating and planning together in modern workspace"
|
||||
image_url_: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80",
|
||||
image_prompt_: "Diverse team collaborating and planning together in modern workspace"
|
||||
}).meta({
|
||||
description: "Visual that represents collaboration, vision, or organizational culture",
|
||||
}),
|
||||
|
|
@ -73,11 +73,11 @@ const WhatWeBelieveSlide = ({ data }: { data: Partial<SchemaType> }) => {
|
|||
<div className="h-full flex">
|
||||
{/* Left Side - Image */}
|
||||
<div className="w-2/5 relative">
|
||||
{supportingVisual?.__image_url__ && (
|
||||
{supportingVisual?.image_url_ && (
|
||||
<div className="absolute inset-8 shadow-lg">
|
||||
<img
|
||||
src={supportingVisual.__image_url__}
|
||||
alt={supportingVisual.__image_prompt__}
|
||||
src={supportingVisual.image_url_}
|
||||
alt={supportingVisual.image_prompt_}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -260,8 +260,8 @@ const presentationGenerationSlice = createSlice({
|
|||
// Preserve existing properties if the target already exists
|
||||
const updatedValue = {
|
||||
...(target && typeof target === 'object' ? target : {}),
|
||||
__image_url__: url,
|
||||
__image_prompt__: promptText || (target?.__image_prompt__) || ''
|
||||
image_url_: url,
|
||||
image_prompt_: promptText || (target?.image_prompt_) || ''
|
||||
};
|
||||
|
||||
if (isNaN(Number(finalKey))) {
|
||||
|
|
@ -359,8 +359,8 @@ const presentationGenerationSlice = createSlice({
|
|||
// Preserve existing properties if the target already exists
|
||||
const updatedValue = {
|
||||
...(target && typeof target === 'object' ? target : {}),
|
||||
__icon_url__: url,
|
||||
__icon_query__: queryText || (target?.__icon_query__) || ''
|
||||
icon_url_: url,
|
||||
icon_query_: queryText || (target?.icon_query_) || ''
|
||||
};
|
||||
|
||||
if (isNaN(Number(finalKey))) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue