Commit 77d31575 by QuentinHsu

fix(model-pricing): commit visual pricing drafts on save

- Commit the open visual editor draft before saving model pricing settings
- Show unsaved draft differences against persisted model pricing values
- Move model pricing actions into the editor toolbar and refine the visual editor layout
parent 39e05118
...@@ -37,7 +37,6 @@ import { ...@@ -37,7 +37,6 @@ import {
SettingsSwitchContent, SettingsSwitchContent,
SettingsSwitchItem, SettingsSwitchItem,
} from '../components/settings-form-layout' } from '../components/settings-form-layout'
import { SettingsPageActionsPortal } from '../components/settings-page-context'
import { import {
ModelRatioVisualEditor, ModelRatioVisualEditor,
type ModelRatioVisualEditorHandle, type ModelRatioVisualEditorHandle,
...@@ -59,6 +58,7 @@ type ModelFormValues = { ...@@ -59,6 +58,7 @@ type ModelFormValues = {
type ModelRatioFormProps = { type ModelRatioFormProps = {
form: UseFormReturn<ModelFormValues> form: UseFormReturn<ModelFormValues>
savedValues: ModelFormValues
onSave: (values: ModelFormValues) => Promise<void> onSave: (values: ModelFormValues) => Promise<void>
onReset: () => void onReset: () => void
isSaving: boolean isSaving: boolean
...@@ -67,6 +67,7 @@ type ModelRatioFormProps = { ...@@ -67,6 +67,7 @@ type ModelRatioFormProps = {
export const ModelRatioForm = memo(function ModelRatioForm({ export const ModelRatioForm = memo(function ModelRatioForm({
form, form,
savedValues,
onSave, onSave,
onReset, onReset,
isSaving, isSaving,
...@@ -101,7 +102,24 @@ export const ModelRatioForm = memo(function ModelRatioForm({ ...@@ -101,7 +102,24 @@ export const ModelRatioForm = memo(function ModelRatioForm({
return ( return (
<div className='space-y-6'> <div className='space-y-6'>
<div className='flex justify-end'> <div className='flex flex-wrap justify-end gap-2'>
<Button
type='button'
variant='destructive'
size='sm'
onClick={onReset}
disabled={isResetting}
>
{t('Reset prices')}
</Button>
<Button
type='button'
size='sm'
onClick={handleSave}
disabled={isSaving}
>
{isSaving ? t('Saving...') : t('Save model prices')}
</Button>
<Button variant='outline' size='sm' onClick={toggleEditMode}> <Button variant='outline' size='sm' onClick={toggleEditMode}>
{editMode === 'visual' ? ( {editMode === 'visual' ? (
<> <>
...@@ -118,29 +136,20 @@ export const ModelRatioForm = memo(function ModelRatioForm({ ...@@ -118,29 +136,20 @@ export const ModelRatioForm = memo(function ModelRatioForm({
</div> </div>
<Form {...form}> <Form {...form}>
<SettingsPageActionsPortal>
<Button
type='button'
variant='destructive'
size='sm'
onClick={onReset}
disabled={isResetting}
>
{t('Reset prices')}
</Button>
<Button
type='button'
size='sm'
onClick={handleSave}
disabled={isSaving}
>
{isSaving ? t('Saving...') : t('Save model prices')}
</Button>
</SettingsPageActionsPortal>
{editMode === 'visual' ? ( {editMode === 'visual' ? (
<div className='space-y-6'> <div className='space-y-6'>
<ModelRatioVisualEditor <ModelRatioVisualEditor
ref={visualEditorRef} ref={visualEditorRef}
savedModelPrice={savedValues.ModelPrice}
savedModelRatio={savedValues.ModelRatio}
savedCacheRatio={savedValues.CacheRatio}
savedCreateCacheRatio={savedValues.CreateCacheRatio}
savedCompletionRatio={savedValues.CompletionRatio}
savedImageRatio={savedValues.ImageRatio}
savedAudioRatio={savedValues.AudioRatio}
savedAudioCompletionRatio={savedValues.AudioCompletionRatio}
savedBillingMode={savedValues.BillingMode}
savedBillingExpr={savedValues.BillingExpr}
modelPrice={form.watch('ModelPrice')} modelPrice={form.watch('ModelPrice')}
modelRatio={form.watch('ModelRatio')} modelRatio={form.watch('ModelRatio')}
cacheRatio={form.watch('CacheRatio')} cacheRatio={form.watch('CacheRatio')}
......
...@@ -250,6 +250,9 @@ export function RatioSettingsCard({ ...@@ -250,6 +250,9 @@ export function RatioSettingsCard({
BillingMode: normalizeJsonString(modelDefaults.BillingMode), BillingMode: normalizeJsonString(modelDefaults.BillingMode),
BillingExpr: normalizeJsonString(modelDefaults.BillingExpr), BillingExpr: normalizeJsonString(modelDefaults.BillingExpr),
}) })
const [savedModelValues, setSavedModelValues] = useState(
modelNormalizedDefaults.current
)
const groupNormalizedDefaults = useRef({ const groupNormalizedDefaults = useRef({
GroupRatio: normalizeJsonString(groupDefaults.GroupRatio), GroupRatio: normalizeJsonString(groupDefaults.GroupRatio),
...@@ -315,6 +318,7 @@ export function RatioSettingsCard({ ...@@ -315,6 +318,7 @@ export function RatioSettingsCard({
BillingMode: normalizeJsonString(modelDefaults.BillingMode), BillingMode: normalizeJsonString(modelDefaults.BillingMode),
BillingExpr: normalizeJsonString(modelDefaults.BillingExpr), BillingExpr: normalizeJsonString(modelDefaults.BillingExpr),
} }
setSavedModelValues(modelNormalizedDefaults.current)
modelForm.reset({ modelForm.reset({
...modelDefaults, ...modelDefaults,
...@@ -395,6 +399,9 @@ export function RatioSettingsCard({ ...@@ -395,6 +399,9 @@ export function RatioSettingsCard({
const apiKey = apiKeyMap[key as string] || (key as string) const apiKey = apiKeyMap[key as string] || (key as string)
await updateOption.mutateAsync({ key: apiKey, value: normalized[key] }) await updateOption.mutateAsync({ key: apiKey, value: normalized[key] })
} }
modelNormalizedDefaults.current = normalized
setSavedModelValues(normalized)
}, },
[t, updateOption] [t, updateOption]
) )
...@@ -462,6 +469,7 @@ export function RatioSettingsCard({ ...@@ -462,6 +469,7 @@ export function RatioSettingsCard({
return ( return (
<ModelRatioForm <ModelRatioForm
form={modelForm} form={modelForm}
savedValues={savedModelValues}
onSave={saveModelRatios} onSave={saveModelRatios}
onReset={handleResetRatios} onReset={handleResetRatios}
isSaving={updateOption.isPending} isSaving={updateOption.isPending}
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "Double check the configuration below. Your system will be locked until initialization is complete.", "Double check the configuration below. Your system will be locked until initialization is complete.": "Double check the configuration below. Your system will be locked until initialization is complete.",
"Download": "Download", "Download": "Download",
"Draft": "Draft",
"Draw": "Draw", "Draw": "Draw",
"Drawing": "Drawing", "Drawing": "Drawing",
"Drawing logs": "Drawing logs", "Drawing logs": "Drawing logs",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "We could not load the setup status.", "We could not load the setup status.": "We could not load the setup status.",
"We will prompt your device to confirm using biometrics or your hardware key.": "We will prompt your device to confirm using biometrics or your hardware key.", "We will prompt your device to confirm using biometrics or your hardware key.": "We will prompt your device to confirm using biometrics or your hardware key.",
"We'll be back online shortly.": "We'll be back online shortly.", "We'll be back online shortly.": "We'll be back online shortly.",
"Will be removed": "Will be removed",
"Web search": "Web search", "Web search": "Web search",
"Web Search": "Web Search", "Web Search": "Web Search",
"Webhook Configuration:": "Webhook Configuration:", "Webhook Configuration:": "Webhook Configuration:",
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "Vérifiez la configuration ci-dessous. Votre système sera verrouillé jusqu'à ce que l'initialisation soit terminée.", "Double check the configuration below. Your system will be locked until initialization is complete.": "Vérifiez la configuration ci-dessous. Votre système sera verrouillé jusqu'à ce que l'initialisation soit terminée.",
"Download": "Télécharger", "Download": "Télécharger",
"Draft": "Brouillon",
"Draw": "Dessin", "Draw": "Dessin",
"Drawing": "Dessin", "Drawing": "Dessin",
"Drawing logs": "Journaux de dessin", "Drawing logs": "Journaux de dessin",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "Nous n'avons pas pu charger l'état de la configuration.", "We could not load the setup status.": "Nous n'avons pas pu charger l'état de la configuration.",
"We will prompt your device to confirm using biometrics or your hardware key.": "Nous allons demander à votre appareil de confirmer en utilisant la biométrie ou votre clé matérielle.", "We will prompt your device to confirm using biometrics or your hardware key.": "Nous allons demander à votre appareil de confirmer en utilisant la biométrie ou votre clé matérielle.",
"We'll be back online shortly.": "Nous serons de retour en ligne sous peu.", "We'll be back online shortly.": "Nous serons de retour en ligne sous peu.",
"Will be removed": "Sera supprimé",
"Web search": "Recherche web", "Web search": "Recherche web",
"Web Search": "Recherche web", "Web Search": "Recherche web",
"Webhook Configuration:": "Configuration du Webhook :", "Webhook Configuration:": "Configuration du Webhook :",
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "下記の設定を再確認してください。初期化が完了するまでシステムはロックされます。", "Double check the configuration below. Your system will be locked until initialization is complete.": "下記の設定を再確認してください。初期化が完了するまでシステムはロックされます。",
"Download": "ダウンロード", "Download": "ダウンロード",
"Draft": "下書き",
"Draw": "描画", "Draw": "描画",
"Drawing": "画像生成", "Drawing": "画像生成",
"Drawing logs": "描画ログ", "Drawing logs": "描画ログ",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "セットアップステータスを読み込めませんでした。", "We could not load the setup status.": "セットアップステータスを読み込めませんでした。",
"We will prompt your device to confirm using biometrics or your hardware key.": "生体認証またはハードウェアキーを使用して確認するよう、デバイスにプロンプトが表示されます。", "We will prompt your device to confirm using biometrics or your hardware key.": "生体認証またはハードウェアキーを使用して確認するよう、デバイスにプロンプトが表示されます。",
"We'll be back online shortly.": "まもなくオンラインに戻ります。", "We'll be back online shortly.": "まもなくオンラインに戻ります。",
"Will be removed": "削除予定",
"Web search": "ウェブ検索", "Web search": "ウェブ検索",
"Web Search": "Web 検索", "Web Search": "Web 検索",
"Webhook Configuration:": "Webhook設定:", "Webhook Configuration:": "Webhook設定:",
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "Дважды проверьте конфигурацию ниже. Ваша система будет заблокирована до завершения инициализации.", "Double check the configuration below. Your system will be locked until initialization is complete.": "Дважды проверьте конфигурацию ниже. Ваша система будет заблокирована до завершения инициализации.",
"Download": "Скачать", "Download": "Скачать",
"Draft": "Черновик",
"Draw": "Рисование", "Draw": "Рисование",
"Drawing": "Рисование", "Drawing": "Рисование",
"Drawing logs": "Журналы рисования", "Drawing logs": "Журналы рисования",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "Не удалось загрузить статус настройки.", "We could not load the setup status.": "Не удалось загрузить статус настройки.",
"We will prompt your device to confirm using biometrics or your hardware key.": "Мы предложим вашему устройству подтвердить действие с помощью биометрии или аппаратного ключа.", "We will prompt your device to confirm using biometrics or your hardware key.": "Мы предложим вашему устройству подтвердить действие с помощью биометрии или аппаратного ключа.",
"We'll be back online shortly.": "Мы скоро вернемся в сеть.", "We'll be back online shortly.": "Мы скоро вернемся в сеть.",
"Will be removed": "Будет удалено",
"Web search": "Веб-поиск", "Web search": "Веб-поиск",
"Web Search": "Веб-поиск", "Web Search": "Веб-поиск",
"Webhook Configuration:": "Конфигурация веб-хука:", "Webhook Configuration:": "Конфигурация веб-хука:",
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "Kiểm tra kỹ lại cấu hình bên dưới. Hệ thống của bạn sẽ bị khóa cho đến khi quá trình khởi tạo hoàn tất.", "Double check the configuration below. Your system will be locked until initialization is complete.": "Kiểm tra kỹ lại cấu hình bên dưới. Hệ thống của bạn sẽ bị khóa cho đến khi quá trình khởi tạo hoàn tất.",
"Download": "Tải xuống", "Download": "Tải xuống",
"Draft": "Bản nháp",
"Draw": "Vẽ", "Draw": "Vẽ",
"Drawing": "Vẽ", "Drawing": "Vẽ",
"Drawing logs": "Nhật ký vẽ", "Drawing logs": "Nhật ký vẽ",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "Chúng tôi không thể tải trạng thái thiết lập.", "We could not load the setup status.": "Chúng tôi không thể tải trạng thái thiết lập.",
"We will prompt your device to confirm using biometrics or your hardware key.": "Chúng tôi sẽ yêu cầu thiết bị của bạn xác nhận bằng cách sử dụng sinh trắc học hoặc khóa bảo mật phần cứng của bạn.", "We will prompt your device to confirm using biometrics or your hardware key.": "Chúng tôi sẽ yêu cầu thiết bị của bạn xác nhận bằng cách sử dụng sinh trắc học hoặc khóa bảo mật phần cứng của bạn.",
"We'll be back online shortly.": "Chúng tôi sẽ sớm trực tuyến trở lại.", "We'll be back online shortly.": "Chúng tôi sẽ sớm trực tuyến trở lại.",
"Will be removed": "Sẽ bị xóa",
"Web search": "Tìm kiếm web", "Web search": "Tìm kiếm web",
"Web Search": "Tìm kiếm web", "Web Search": "Tìm kiếm web",
"Webhook Configuration:": "Cấu hình Webhook:", "Webhook Configuration:": "Cấu hình Webhook:",
......
...@@ -1253,6 +1253,7 @@ ...@@ -1253,6 +1253,7 @@
"DoubaoVideo": "DoubaoVideo", "DoubaoVideo": "DoubaoVideo",
"Double check the configuration below. Your system will be locked until initialization is complete.": "仔细检查以下配置。您的系统将在初始化完成前保持锁定状态。", "Double check the configuration below. Your system will be locked until initialization is complete.": "仔细检查以下配置。您的系统将在初始化完成前保持锁定状态。",
"Download": "下载", "Download": "下载",
"Draft": "草稿",
"Draw": "绘图", "Draw": "绘图",
"Drawing": "绘图", "Drawing": "绘图",
"Drawing logs": "绘制日志", "Drawing logs": "绘制日志",
...@@ -4414,6 +4415,7 @@ ...@@ -4414,6 +4415,7 @@
"We could not load the setup status.": "我们无法加载设置状态。", "We could not load the setup status.": "我们无法加载设置状态。",
"We will prompt your device to confirm using biometrics or your hardware key.": "我们将提示您的设备使用生物识别或硬件密钥进行确认。", "We will prompt your device to confirm using biometrics or your hardware key.": "我们将提示您的设备使用生物识别或硬件密钥进行确认。",
"We'll be back online shortly.": "我们将很快恢复在线。", "We'll be back online shortly.": "我们将很快恢复在线。",
"Will be removed": "将被移除",
"Web search": "网络搜索", "Web search": "网络搜索",
"Web Search": "网页搜索", "Web Search": "网页搜索",
"Webhook Configuration:": "Webhook 配置:", "Webhook Configuration:": "Webhook 配置:",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment