import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { getErrorMessage, useForcePricingSyncMutation, useGetCoreConfigQuery, useUpdateCoreConfigMutation } from "@/lib/store"; import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { useEffect, useMemo } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; interface PricingFormData { pricing_datasheet_url: string; pricing_sync_interval_hours: number; } export default function PricingConfigView() { const hasSettingsUpdateAccess = useRbac(RbacResource.Settings, RbacOperation.Update); const { data: bifrostConfig } = useGetCoreConfigQuery({ fromDB: true }); const config = bifrostConfig?.framework_config; const [updateCoreConfig, { isLoading }] = useUpdateCoreConfigMutation(); const [forcePricingSync, { isLoading: isForceSyncing }] = useForcePricingSyncMutation(); const { register, handleSubmit, formState: { errors, isDirty }, reset, watch, } = useForm({ defaultValues: { pricing_datasheet_url: "", pricing_sync_interval_hours: 24, }, }); const formValues = watch(); useEffect(() => { if (bifrostConfig && config) { reset({ pricing_datasheet_url: config.pricing_url || "", pricing_sync_interval_hours: Math.round(config.pricing_sync_interval / 3600) || 24, }); } }, [config, bifrostConfig, reset]); const hasChanges = useMemo(() => { if (!config || !isDirty) return false; const serverUrl = config.pricing_url || ""; const serverInterval = Math.round(config.pricing_sync_interval / 3600); return formValues.pricing_datasheet_url !== serverUrl || formValues.pricing_sync_interval_hours !== serverInterval; }, [config, formValues, isDirty]); const onSubmit = async (data: PricingFormData) => { try { await updateCoreConfig({ ...bifrostConfig!, framework_config: { ...config, id: bifrostConfig?.framework_config.id || 0, pricing_url: data.pricing_datasheet_url, pricing_sync_interval: data.pricing_sync_interval_hours * 3600, }, }).unwrap(); toast.success("Pricing configuration updated successfully."); reset(data); } catch (error) { toast.error(getErrorMessage(error)); } }; const handleForceSync = async () => { try { await forcePricingSync().unwrap(); toast.success("Pricing sync triggered successfully."); } catch (error) { toast.error(getErrorMessage(error)); } }; return (

Pricing Configuration

Configure custom pricing datasheet and sync intervals.

{/* Pricing Datasheet URL */}

URL to a custom pricing datasheet. Leave empty to use default pricing.

{ if (!value) return true; // Allow empty return value.startsWith("http://") || value.startsWith("https://") || "URL must start with http:// or https://"; }, }, })} className={errors.pricing_datasheet_url ? "border-destructive" : ""} /> {errors.pricing_datasheet_url &&

{errors.pricing_datasheet_url.message}

}
{/* Pricing Sync Interval */}

How often to sync pricing data from the datasheet URL.

{errors.pricing_sync_interval_hours && (

{errors.pricing_sync_interval_hours.message}

)}
); }