feat: Better mix panel analytics

This commit is contained in:
shiva raj badu 2026-04-13 21:04:09 +05:45
parent bbb3c835e5
commit fae796cd85
No known key found for this signature in database
5 changed files with 163 additions and 32 deletions

View file

@ -335,7 +335,7 @@ const ImageEditor = ({
<p className="text-sm text-gray-500">{promptContent}</p>
</div>
<div>
<div className="mx-0.5">
<h3 className="text-base font-medium mb-2">
{isStockImageProvider ? "Image Keyword" : "Image Description"}
</h3>
@ -347,7 +347,7 @@ const ImageEditor = ({
}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="min-h-[100px]"
className="min-h-[100px] "
/>
</div>
@ -444,7 +444,7 @@ const ImageEditor = ({
<div className="space-y-4">
<div
className={cn(
"border-2 border-dashed rounded-lg p-8 text-center transition-colors",
"border-2 border-dashed rounded-lg p-8 text-center transition-colors",
isUploading
? "border-gray-400 bg-gray-50"
: "border-gray-300 hover:border-blue-400"

View file

@ -389,9 +389,7 @@ const PresentationHeader = ({
) : (
titleBlock
)}
<a href={`/pdf-maker?id=${presentation_id}`}>
pdf-maker
</a>
<div className="flex items-center gap-2.5">
{isPresentationSaving && <div className="flex items-center gap-2">

View file

@ -29,8 +29,15 @@ import { ConfigurationSelects } from "./ConfigurationSelects";
import { RootState } from "@/store/store";
import { ImagesApi } from "../../services/api/images";
import CurrentConfig from "./CurrentConfig";
import { LLMConfig } from "@/types/llm_config";
const STOCK_IMAGE_PROVIDERS = new Set(["pexels", "pixabay"]);
const FILE_TYPE_WORD = new Set([".doc", ".docx", ".docm", ".odt", ".rtf"]);
const FILE_TYPE_PRESENTATION = new Set([".ppt", ".pptx", ".pptm", ".odp"]);
const FILE_TYPE_SPREADSHEET = new Set([".xls", ".xlsx", ".xlsm", ".ods", ".csv", ".tsv"]);
const FILE_TYPE_IMAGE = new Set([".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".webp", ".svg"]);
const FILE_TYPE_PDF = new Set([".pdf"]);
const FILE_TYPE_TEXT = new Set([".txt"]);
// Types for loading state
interface LoadingState {
@ -41,6 +48,50 @@ interface LoadingState {
extra_info?: string;
}
const getFileExtension = (fileName: string): string => {
const index = fileName.lastIndexOf(".");
if (index < 0) return "";
return fileName.slice(index).toLowerCase();
};
const getFileCategory = (file: File): string => {
const extension = getFileExtension(file.name || "");
if (FILE_TYPE_WORD.has(extension)) return "word";
if (FILE_TYPE_PRESENTATION.has(extension)) return "presentation";
if (FILE_TYPE_SPREADSHEET.has(extension)) return "spreadsheet";
if (FILE_TYPE_IMAGE.has(extension) || (file.type || "").startsWith("image/")) return "image";
if (FILE_TYPE_PDF.has(extension) || file.type === "application/pdf") return "pdf";
if (FILE_TYPE_TEXT.has(extension) || file.type === "text/plain") return "text";
return "other";
};
const getSelectedTextModel = (config?: LLMConfig): string => {
if (!config) return "";
switch (config.LLM) {
case "openai":
return config.OPENAI_MODEL || "";
case "google":
return config.GOOGLE_MODEL || "";
case "anthropic":
return config.ANTHROPIC_MODEL || "";
case "ollama":
return config.OLLAMA_MODEL || "";
case "custom":
return config.CUSTOM_MODEL || "";
case "codex":
return config.CODEX_MODEL || "";
default:
return "";
}
};
const getSelectedImageQuality = (config?: LLMConfig): string => {
if (!config) return "";
if (config.IMAGE_PROVIDER === "dall-e-3") return config.DALL_E_3_QUALITY || "";
if (config.IMAGE_PROVIDER === "gpt-image-1.5") return config.GPT_IMAGE_1_5_QUALITY || "";
return "";
};
const UploadPage = () => {
const router = useRouter();
const pathname = usePathname();
@ -68,6 +119,41 @@ const UploadPage = () => {
extra_info: "",
});
const getUploadSnapshotProps = () => {
const trimmedPrompt = config.prompt.trim();
const trimmedInstructions = (config.instructions || "").trim();
const attachmentCategories = Array.from(new Set(files.map(getFileCategory))).sort();
const imageGenerationEnabled = !llmConfig?.DISABLE_IMAGE_GENERATION;
const parsedSlides =
config.slides && /^\d+$/.test(config.slides) ? Number(config.slides) : null;
return {
pathname,
generation_path: files.length > 0 ? "documents" : "prompt_only",
slides_selected: parsedSlides,
slides_mode: config.slides ? "selected" : "auto",
language: config.language || "",
tone: config.tone,
verbosity: config.verbosity,
include_table_of_contents: !!config.includeTableOfContents,
include_title_slide: !!config.includeTitleSlide,
web_search: !!config.webSearch,
has_prompt: Boolean(trimmedPrompt),
prompt_char_count: trimmedPrompt.length,
prompt_word_count: trimmedPrompt ? trimmedPrompt.split(/\s+/).filter(Boolean).length : 0,
has_instructions: Boolean(trimmedInstructions),
instructions_char_count: trimmedInstructions.length,
has_attachments: files.length > 0,
attachments_count: files.length,
attachment_categories: attachmentCategories.join(","),
text_provider: llmConfig?.LLM || "",
text_model: getSelectedTextModel(llmConfig),
image_generation_enabled: imageGenerationEnabled,
image_provider: imageGenerationEnabled ? (llmConfig?.IMAGE_PROVIDER || "") : "disabled",
image_quality: imageGenerationEnabled ? getSelectedImageQuality(llmConfig) : "",
};
};
const handleConfigChange = (key: keyof PresentationConfig, value: unknown) => {
setConfig((prev) => ({ ...prev, [key]: value } as PresentationConfig));
};
@ -108,16 +194,28 @@ const UploadPage = () => {
*/
const validateConfiguration = (): boolean => {
if (!config.language) {
trackEvent(MixpanelEvent.Upload_Validation_Failed, {
...getUploadSnapshotProps(),
reason: "language_missing",
});
toast.error("Please select language");
return false;
}
if (files.length > 0 && config.language === LanguageType.Auto) {
trackEvent(MixpanelEvent.Upload_Validation_Failed, {
...getUploadSnapshotProps(),
reason: "language_auto_with_documents",
});
toast.error("Please choose a language before processing uploaded documents");
return false;
}
if (!config.prompt.trim() && files.length === 0) {
trackEvent(MixpanelEvent.Upload_Validation_Failed, {
...getUploadSnapshotProps(),
reason: "prompt_or_document_missing",
});
toast.error("No Prompt or Document Provided");
return false;
}
@ -130,8 +228,16 @@ const UploadPage = () => {
const handleGeneratePresentation = async () => {
if (!validateConfiguration()) return;
trackEvent(MixpanelEvent.Upload_GetStarted_Button_Clicked, getUploadSnapshotProps());
const isStockProviderReady = await ensureStockImageProviderReady();
if (!isStockProviderReady) return;
if (!isStockProviderReady) {
trackEvent(MixpanelEvent.Upload_Validation_Failed, {
...getUploadSnapshotProps(),
reason: "stock_image_provider_unreachable",
});
return;
}
try {
const hasUploadedAssets = files.length > 0;
@ -293,4 +399,4 @@ const UploadPage = () => {
);
};
export default UploadPage;
export default UploadPage;

View file

@ -1,7 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react'
import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { Button } from '../ui/button';
import { Check, CheckCircle, ChevronLeft, ChevronRight, ChevronUp, Download, Eye, EyeOff, Loader2 } from 'lucide-react';
import { Check, CheckCircle, ChevronLeft, ChevronUp, Download, Eye, EyeOff, Loader2 } from 'lucide-react';
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../ui/command';
import { DALLE_3_QUALITY_OPTIONS, GPT_IMAGE_1_5_QUALITY_OPTIONS, IMAGE_PROVIDERS, LLM_PROVIDERS } from '@/utils/providerConstants';
import { cn } from '@/lib/utils';
@ -13,7 +13,7 @@ import ToolTip from '../ToolTip';
import { Switch } from '../ui/switch';
import { Select, SelectItem, SelectContent, SelectValue, SelectTrigger } from '../ui/select';
import { MixpanelEvent, trackEvent } from '@/utils/mixpanel';
import { usePathname, useRouter } from 'next/navigation';
import { usePathname } from 'next/navigation';
import { handleSaveLLMConfig } from '@/utils/storeHelpers';
import { checkIfSelectedOllamaModelIsPulled, pullOllamaModel } from '@/utils/providerUtils';
import { getApiUrl } from '@/utils/api';
@ -21,7 +21,6 @@ import CodexConfig, { CHATGPT_MODELS } from '../CodexConfig';
const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep: (step: number) => void }) => {
const pathname = usePathname();
const router = useRouter();
const [openProviderSelect, setOpenProviderSelect] = useState(false);
const [openImageProviderSelect, setOpenImageProviderSelect] = useState(false);
const userConfigState = useSelector((state: RootState) => state.userConfig);
@ -45,7 +44,6 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
} | null>(null);
const handleProviderChange = (provider: string) => {
setLlmConfig(prev => ({
...prev,
LLM: provider
@ -104,12 +102,36 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
const currentOllamaUrl = llmConfig.OLLAMA_URL || '';
const useCustomOllamaUrl = !!llmConfig.USE_CUSTOM_URL;
const getSelectedTextModel = (config: LLMConfig): string => {
switch (config.LLM) {
case 'openai':
return config.OPENAI_MODEL || '';
case 'google':
return config.GOOGLE_MODEL || '';
case 'anthropic':
return config.ANTHROPIC_MODEL || '';
case 'ollama':
return config.OLLAMA_MODEL || '';
case 'custom':
return config.CUSTOM_MODEL || '';
case 'codex':
return config.CODEX_MODEL || '';
default:
return '';
}
};
const getSelectedImageQuality = (config: LLMConfig): string => {
if (config.IMAGE_PROVIDER === 'dall-e-3') return config.DALL_E_3_QUALITY || '';
if (config.IMAGE_PROVIDER === 'gpt-image-1.5') return config.GPT_IMAGE_1_5_QUALITY || '';
return '';
};
const fetchAvailableModels = async () => {
if (llmConfig.LLM === 'openai' && !currentApiKey) return;
if (llmConfig.LLM === 'google' && !currentApiKey) return;
if (llmConfig.LLM === 'anthropic' && !currentApiKey) return;
if (llmConfig.LLM === 'custom' && !llmConfig.CUSTOM_LLM_URL) return;
setModelsLoading(true);
try {
let response: Response;
@ -270,31 +292,39 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
setShowDownloadModal(false);
}
};
const handleSaveConfig = async () => {
trackEvent(MixpanelEvent.Home_SaveConfiguration_Button_Clicked, { pathname });
try {
setSavingConfig(true);
// API: save config
trackEvent(MixpanelEvent.Home_SaveConfiguration_API_Call);
// API CALL: save config
await handleSaveLLMConfig(llmConfig);
if (llmConfig.LLM === "ollama" && llmConfig.OLLAMA_MODEL) {
// API: check model pulled
trackEvent(MixpanelEvent.Home_CheckOllamaModelPulled_API_Call);
const isPulled = await checkIfSelectedOllamaModelIsPulled(llmConfig.OLLAMA_MODEL);
if (!isPulled) {
setShowDownloadModal(true);
// API: download model
trackEvent(MixpanelEvent.Home_DownloadOllamaModel_API_Call);
await handleModelDownload();
}
}
const textProvider = llmConfig.LLM || '';
const textModel = getSelectedTextModel(llmConfig);
const imageGenerationEnabled = !llmConfig.DISABLE_IMAGE_GENERATION;
const imageProvider = imageGenerationEnabled ? (llmConfig.IMAGE_PROVIDER || '') : 'disabled';
trackEvent(MixpanelEvent.Onboarding_Providers_Models_Selected, {
pathname,
text_provider: textProvider,
text_provider_label: LLM_PROVIDERS[textProvider]?.label || textProvider || '',
text_model: textModel,
uses_chatgpt_login: textProvider === 'codex',
image_generation_enabled: imageGenerationEnabled,
image_provider: imageProvider,
image_provider_label: imageGenerationEnabled
? (IMAGE_PROVIDERS[imageProvider]?.label || imageProvider || '')
: 'Image generation disabled',
image_quality: imageGenerationEnabled ? getSelectedImageQuality(llmConfig) : ''
});
toast.info("Configuration saved successfully");
// Track navigation from -> to
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/final onboarding step" });
setStep(3)
// router.push("/upload");
} catch (error) {
@ -315,7 +345,7 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
useEffect(() => {
if (llmConfig.LLM === 'ollama' && !modelsChecked && !modelsLoading) {
fetchAvailableModels();
void fetchAvailableModels();
}
}, [llmConfig.LLM, modelsChecked, modelsLoading]);

View file

@ -7,10 +7,7 @@ const MIXPANEL_TOKEN = '4ebfc788c739c72a9565c489a7cc2eac';
export enum MixpanelEvent {
PageView = 'Page View',
Navigation = 'Navigation',
Home_SaveConfiguration_Button_Clicked = 'Home Save Configuration Button Clicked',
Home_SaveConfiguration_API_Call = 'Home Save Configuration API Call',
Home_CheckOllamaModelPulled_API_Call = 'Home Check Ollama Model Pulled API Call',
Home_DownloadOllamaModel_API_Call = 'Home Download Ollama Model API Call',
Onboarding_Providers_Models_Selected = 'Onboarding Providers Models Selected',
Codex_SignIn_API_Call = 'Codex Sign In API Call',
Outline_Generate_Presentation_Button_Clicked = 'Outline Generate Presentation Button Clicked',
Outline_Select_Template_Button_Clicked = 'Outline Select Template Button Clicked',
@ -39,6 +36,8 @@ export enum MixpanelEvent {
Upload_Upload_Documents_API_Call = 'Upload Upload Documents API Call',
Upload_Decompose_Documents_API_Call = 'Upload Decompose Documents API Call',
Upload_Create_Presentation_API_Call = 'Upload Create Presentation API Call',
Upload_GetStarted_Button_Clicked = 'Upload Get Started Button Clicked',
Upload_Validation_Failed = 'Upload Validation Failed',
DocumentsPreview_Create_Presentation_API_Call = 'Documents Preview Create Presentation API Call',
DocumentsPreview_Next_Button_Clicked = 'Documents Preview Next Button Clicked',
Settings_SaveConfiguration_Button_Clicked = 'Settings Save Configuration Button Clicked',
@ -209,5 +208,3 @@ export default {
resetTelemetryCache,
setTelemetryEnabled,
};