2025-05-05 17:27:36 +08:00
|
|
|
'use client';
|
|
|
|
|
2025-05-07 04:14:50 +08:00
|
|
|
import React, { useState } from 'react';
|
2025-05-05 17:27:36 +08:00
|
|
|
import {
|
|
|
|
DropdownMenu,
|
|
|
|
DropdownMenuContent,
|
|
|
|
DropdownMenuItem,
|
|
|
|
DropdownMenuTrigger,
|
|
|
|
} from '@/components/ui/dropdown-menu';
|
|
|
|
import {
|
|
|
|
Tooltip,
|
|
|
|
TooltipContent,
|
|
|
|
TooltipProvider,
|
|
|
|
TooltipTrigger,
|
|
|
|
} from '@/components/ui/tooltip';
|
|
|
|
import { Button } from '@/components/ui/button';
|
2025-05-07 04:14:50 +08:00
|
|
|
import { Check, ChevronDown, LockIcon, ZapIcon } from 'lucide-react';
|
|
|
|
import { ModelOption, SubscriptionStatus } from './_use-model-selection';
|
|
|
|
import { PaywallDialog } from '@/components/payment/paywall-dialog';
|
2025-05-05 17:27:36 +08:00
|
|
|
|
|
|
|
interface ModelSelectorProps {
|
|
|
|
selectedModel: string;
|
|
|
|
onModelChange: (modelId: string) => void;
|
|
|
|
modelOptions: ModelOption[];
|
|
|
|
canAccessModel: (modelId: string) => boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
|
|
|
selectedModel,
|
|
|
|
onModelChange,
|
|
|
|
modelOptions,
|
|
|
|
canAccessModel,
|
|
|
|
}) => {
|
2025-05-07 04:14:50 +08:00
|
|
|
const [paywallOpen, setPaywallOpen] = useState(false);
|
|
|
|
const [lockedModel, setLockedModel] = useState<string | null>(null);
|
2025-05-05 17:27:36 +08:00
|
|
|
|
2025-05-07 04:14:50 +08:00
|
|
|
const selectedLabel =
|
|
|
|
modelOptions.find((o) => o.id === selectedModel)?.label || 'Select model';
|
|
|
|
|
|
|
|
const handleSelect = (id: string) => {
|
|
|
|
if (canAccessModel(id)) {
|
|
|
|
onModelChange(id);
|
|
|
|
} else {
|
|
|
|
setLockedModel(id);
|
|
|
|
setPaywallOpen(true);
|
2025-05-05 17:27:36 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-05-07 04:14:50 +08:00
|
|
|
const closeDialog = () => {
|
|
|
|
setPaywallOpen(false);
|
|
|
|
setLockedModel(null);
|
2025-05-05 17:27:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="relative">
|
|
|
|
<DropdownMenu>
|
|
|
|
<DropdownMenuTrigger asChild>
|
|
|
|
<Button
|
|
|
|
variant="ghost"
|
2025-05-07 04:14:50 +08:00
|
|
|
size="default"
|
|
|
|
className="h-8 rounded-md text-muted-foreground shadow-none border-none focus:ring-0 px-3"
|
2025-05-05 17:27:36 +08:00
|
|
|
>
|
2025-05-07 04:14:50 +08:00
|
|
|
<div className="flex items-center gap-1 text-sm font-medium">
|
|
|
|
<span>{selectedLabel}</span>
|
|
|
|
<ChevronDown className="h-3 w-3 opacity-50 ml-1" />
|
2025-05-05 17:27:36 +08:00
|
|
|
</div>
|
|
|
|
</Button>
|
|
|
|
</DropdownMenuTrigger>
|
|
|
|
|
2025-05-07 04:14:50 +08:00
|
|
|
<DropdownMenuContent align="end" className="w-64 p-1">
|
|
|
|
{modelOptions.map((opt) => {
|
|
|
|
const accessible = canAccessModel(opt.id);
|
2025-05-05 17:27:36 +08:00
|
|
|
return (
|
2025-05-07 04:14:50 +08:00
|
|
|
<TooltipProvider key={opt.id}>
|
2025-05-05 17:27:36 +08:00
|
|
|
<Tooltip>
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
<DropdownMenuItem
|
2025-05-07 04:14:50 +08:00
|
|
|
className="text-sm py-3 px-3 flex items-start cursor-pointer rounded-md"
|
|
|
|
onClick={() => handleSelect(opt.id)}
|
2025-05-05 17:27:36 +08:00
|
|
|
>
|
2025-05-07 04:14:50 +08:00
|
|
|
<div className="flex flex-col w-full">
|
|
|
|
<div className="flex items-center justify-between w-full">
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
{opt.id === 'sonnet-3.7' && (
|
|
|
|
<ZapIcon className="h-4 w-4 text-yellow-500" />
|
|
|
|
)}
|
|
|
|
<span className="font-medium">{opt.label}</span>
|
|
|
|
{!accessible && <LockIcon className="h-3 w-3 ml-1 text-gray-400" />}
|
|
|
|
</div>
|
|
|
|
{selectedModel === opt.id && (
|
|
|
|
<Check className="h-4 w-4 text-blue-500" />
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
|
|
{opt.description}
|
|
|
|
</div>
|
2025-05-05 17:27:36 +08:00
|
|
|
</div>
|
|
|
|
</DropdownMenuItem>
|
|
|
|
</TooltipTrigger>
|
2025-05-07 04:14:50 +08:00
|
|
|
{!accessible && (
|
2025-05-05 17:27:36 +08:00
|
|
|
<TooltipContent side="left" className="text-xs">
|
2025-05-07 04:14:50 +08:00
|
|
|
<p>Requires subscription to access</p>
|
2025-05-05 17:27:36 +08:00
|
|
|
</TooltipContent>
|
|
|
|
)}
|
|
|
|
</Tooltip>
|
|
|
|
</TooltipProvider>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</DropdownMenuContent>
|
|
|
|
</DropdownMenu>
|
2025-05-07 04:14:50 +08:00
|
|
|
|
|
|
|
{paywallOpen && (
|
|
|
|
<PaywallDialog
|
|
|
|
open={true}
|
|
|
|
onDialogClose={closeDialog}
|
|
|
|
title="Premium Model"
|
|
|
|
description={
|
|
|
|
lockedModel
|
|
|
|
? `Subscribe to access ${modelOptions.find(
|
|
|
|
(m) => m.id === lockedModel
|
|
|
|
)?.label}`
|
|
|
|
: 'Subscribe to access premium models'
|
|
|
|
}
|
|
|
|
ctaText="Subscribe Now"
|
|
|
|
cancelText="Maybe Later"
|
|
|
|
/>
|
|
|
|
)}
|
2025-05-05 17:27:36 +08:00
|
|
|
</div>
|
|
|
|
);
|
2025-05-07 04:14:50 +08:00
|
|
|
};
|