Commit 0229dc20 by CaIon

refactor: refine toolbar controls and model drawer

parent 9100e15e
...@@ -77,11 +77,6 @@ export type DataTableToolbarProps<TData> = { ...@@ -77,11 +77,6 @@ export type DataTableToolbarProps<TData> = {
*/ */
additionalSearch?: ReactNode additionalSearch?: ReactNode
/** /**
* Extra controls displayed immediately after the filter chips, before the
* right-aligned action cluster.
*/
afterFilters?: ReactNode
/**
* Whether non-table filters (e.g. `additionalSearch` or `expandable` * Whether non-table filters (e.g. `additionalSearch` or `expandable`
* inputs) are currently active. Controls Reset button visibility * inputs) are currently active. Controls Reset button visibility
* when no column filters are set. * when no column filters are set.
...@@ -351,7 +346,6 @@ export function DataTableToolbar<TData>(props: DataTableToolbarProps<TData>) { ...@@ -351,7 +346,6 @@ export function DataTableToolbar<TData>(props: DataTableToolbarProps<TData>) {
{props.customSearch !== undefined ? props.customSearch : searchInput} {props.customSearch !== undefined ? props.customSearch : searchInput}
{props.additionalSearch} {props.additionalSearch}
{filterChips} {filterChips}
{props.afterFilters}
<div className='ms-auto flex shrink-0 items-center gap-1.5 sm:gap-2'> <div className='ms-auto flex shrink-0 items-center gap-1.5 sm:gap-2'>
{expandToggle} {expandToggle}
</div> </div>
...@@ -387,7 +381,6 @@ export function DataTableToolbar<TData>(props: DataTableToolbarProps<TData>) { ...@@ -387,7 +381,6 @@ export function DataTableToolbar<TData>(props: DataTableToolbarProps<TData>) {
{props.customSearch !== undefined ? props.customSearch : searchInput} {props.customSearch !== undefined ? props.customSearch : searchInput}
{props.additionalSearch} {props.additionalSearch}
{filterChips} {filterChips}
{props.afterFilters}
{expanded && hasExpandable && props.expandable} {expanded && hasExpandable && props.expandable}
<div className='ms-auto flex shrink-0 items-center gap-1.5 sm:gap-2'> <div className='ms-auto flex shrink-0 items-center gap-1.5 sm:gap-2'>
......
...@@ -409,7 +409,7 @@ export function ChannelsTable() { ...@@ -409,7 +409,7 @@ export function ChannelsTable() {
singleSelect: true, singleSelect: true,
}, },
], ],
afterFilters: ( preActions: (
<Tooltip> <Tooltip>
<TooltipTrigger <TooltipTrigger
render={ render={
......
...@@ -123,7 +123,8 @@ export function ModelMutateDrawer({ ...@@ -123,7 +123,8 @@ export function ModelMutateDrawer({
}: ModelMutateDrawerProps) { }: ModelMutateDrawerProps) {
const { t } = useTranslation() const { t } = useTranslation()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const isEditing = Boolean(currentRow?.id) const currentModelId = currentRow?.id
const isEditing = Boolean(currentModelId)
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
const [pricingMode, setPricingMode] = useState<PricingMode>('per-token') const [pricingMode, setPricingMode] = useState<PricingMode>('per-token')
const [pricingSubMode, setPricingSubMode] = useState<PricingSubMode>('ratio') const [pricingSubMode, setPricingSubMode] = useState<PricingSubMode>('ratio')
...@@ -143,8 +144,13 @@ export function ModelMutateDrawer({ ...@@ -143,8 +144,13 @@ export function ModelMutateDrawer({
// Fetch model detail if editing // Fetch model detail if editing
const { data: modelData } = useQuery({ const { data: modelData } = useQuery({
queryKey: modelsQueryKeys.detail(currentRow?.id || 0), queryKey: modelsQueryKeys.detail(currentModelId || 0),
queryFn: () => getModel(currentRow!.id), queryFn: () => {
if (!currentModelId) {
throw new Error('Model ID is required')
}
return getModel(currentModelId)
},
enabled: open && isEditing, enabled: open && isEditing,
}) })
...@@ -230,13 +236,13 @@ export function ModelMutateDrawer({ ...@@ -230,13 +236,13 @@ export function ModelMutateDrawer({
const validateNumber = (value: string) => { const validateNumber = (value: string) => {
if (value === '') return true if (value === '') return true
return !isNaN(parseFloat(value)) return !Number.isNaN(Number.parseFloat(value))
} }
const handlePromptPriceChange = (value: string) => { const handlePromptPriceChange = (value: string) => {
setPromptPrice(value) setPromptPrice(value)
if (value && !isNaN(parseFloat(value))) { if (value && !Number.isNaN(Number.parseFloat(value))) {
const ratio = parseFloat(value) / 2 const ratio = Number.parseFloat(value) / 2
form.setValue('ratio', ratio.toString()) form.setValue('ratio', ratio.toString())
} else { } else {
form.setValue('ratio', '') form.setValue('ratio', '')
...@@ -247,12 +253,12 @@ export function ModelMutateDrawer({ ...@@ -247,12 +253,12 @@ export function ModelMutateDrawer({
setCompletionPrice(value) setCompletionPrice(value)
if ( if (
value && value &&
!isNaN(parseFloat(value)) && !Number.isNaN(Number.parseFloat(value)) &&
promptPrice && promptPrice &&
!isNaN(parseFloat(promptPrice)) && !Number.isNaN(Number.parseFloat(promptPrice)) &&
parseFloat(promptPrice) > 0 Number.parseFloat(promptPrice) > 0
) { ) {
const completionRatio = parseFloat(value) / parseFloat(promptPrice) const completionRatio = Number.parseFloat(value) / Number.parseFloat(promptPrice)
form.setValue('completionRatio', completionRatio.toString()) form.setValue('completionRatio', completionRatio.toString())
} else { } else {
form.setValue('completionRatio', '') form.setValue('completionRatio', '')
...@@ -398,7 +404,7 @@ export function ModelMutateDrawer({ ...@@ -398,7 +404,7 @@ export function ModelMutateDrawer({
try { try {
const submitData = { const submitData = {
...values, ...values,
id: isEditing ? currentRow!.id : undefined, id: isEditing ? currentModelId : undefined,
tags: Array.isArray(values.tags) ? values.tags.join(',') : '', tags: Array.isArray(values.tags) ? values.tags.join(',') : '',
status: values.status ? 1 : 0, status: values.status ? 1 : 0,
sync_official: values.sync_official ? 1 : 0, sync_official: values.sync_official ? 1 : 0,
...@@ -416,8 +422,9 @@ export function ModelMutateDrawer({ ...@@ -416,8 +422,9 @@ export function ModelMutateDrawer({
...modelData ...modelData
} = submitData } = submitData
const response = isEditing const response =
? await updateModel({ ...modelData, id: currentRow!.id }) isEditing && currentModelId
? await updateModel({ ...modelData, id: currentModelId })
: await createModel(modelData) : await createModel(modelData)
if (response.success) { if (response.success) {
...@@ -496,30 +503,30 @@ export function ModelMutateDrawer({ ...@@ -496,30 +503,30 @@ export function ModelMutateDrawer({
values.price && values.price &&
values.price !== '' values.price !== ''
) { ) {
priceMap[finalModelName] = parseFloat(values.price) priceMap[finalModelName] = Number.parseFloat(values.price)
} else if (pricingMode === 'per-token') { } else if (pricingMode === 'per-token') {
if (values.ratio && values.ratio !== '') { if (values.ratio && values.ratio !== '') {
ratioMap[finalModelName] = parseFloat(values.ratio) ratioMap[finalModelName] = Number.parseFloat(values.ratio)
} }
if (values.cacheRatio && values.cacheRatio !== '') { if (values.cacheRatio && values.cacheRatio !== '') {
cacheMap[finalModelName] = parseFloat(values.cacheRatio) cacheMap[finalModelName] = Number.parseFloat(values.cacheRatio)
} }
if (values.completionRatio && values.completionRatio !== '') { if (values.completionRatio && values.completionRatio !== '') {
completionMap[finalModelName] = parseFloat( completionMap[finalModelName] = Number.parseFloat(
values.completionRatio values.completionRatio
) )
} }
if (values.imageRatio && values.imageRatio !== '') { if (values.imageRatio && values.imageRatio !== '') {
imageMap[finalModelName] = parseFloat(values.imageRatio) imageMap[finalModelName] = Number.parseFloat(values.imageRatio)
} }
if (values.audioRatio && values.audioRatio !== '') { if (values.audioRatio && values.audioRatio !== '') {
audioMap[finalModelName] = parseFloat(values.audioRatio) audioMap[finalModelName] = Number.parseFloat(values.audioRatio)
} }
if ( if (
values.audioCompletionRatio && values.audioCompletionRatio &&
values.audioCompletionRatio !== '' values.audioCompletionRatio !== ''
) { ) {
audioCompletionMap[finalModelName] = parseFloat( audioCompletionMap[finalModelName] = Number.parseFloat(
values.audioCompletionRatio values.audioCompletionRatio
) )
} }
...@@ -615,7 +622,7 @@ export function ModelMutateDrawer({ ...@@ -615,7 +622,7 @@ export function ModelMutateDrawer({
}, },
[ [
isEditing, isEditing,
currentRow, currentModelId,
queryClient, queryClient,
onOpenChange, onOpenChange,
pricingMode, pricingMode,
...@@ -728,14 +735,14 @@ export function ModelMutateDrawer({ ...@@ -728,14 +735,14 @@ export function ModelMutateDrawer({
<FormItem> <FormItem>
<FormLabel>{t('Vendor')}</FormLabel> <FormLabel>{t('Vendor')}</FormLabel>
<Select <Select
items={[ items={vendors.map((vendor) => ({
...vendors.map((vendor) => ({
value: String(vendor.id), value: String(vendor.id),
label: vendor.name, label: vendor.name,
})), }))}
]}
onValueChange={(value) => onValueChange={(value) =>
field.onChange(value ? parseInt(value) : undefined) field.onChange(
value ? Number.parseInt(value) : undefined
)
} }
value={field.value ? String(field.value) : undefined} value={field.value ? String(field.value) : undefined}
> >
...@@ -797,7 +804,7 @@ export function ModelMutateDrawer({ ...@@ -797,7 +804,7 @@ export function ModelMutateDrawer({
<FormControl> <FormControl>
<RadioGroup <RadioGroup
onValueChange={(value) => onValueChange={(value) =>
field.onChange(parseInt(value)) field.onChange(Number.parseInt(value))
} }
value={String(field.value)} value={String(field.value)}
className='grid grid-cols-2 gap-4' className='grid grid-cols-2 gap-4'
...@@ -835,12 +842,10 @@ export function ModelMutateDrawer({ ...@@ -835,12 +842,10 @@ export function ModelMutateDrawer({
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
<h3 className='text-sm font-semibold'>{t('Endpoints')}</h3> <h3 className='text-sm font-semibold'>{t('Endpoints')}</h3>
<Select<string> <Select<string>
items={[ items={Object.keys(ENDPOINT_TEMPLATES).map((key) => ({
...Object.keys(ENDPOINT_TEMPLATES).map((key) => ({
value: key, value: key,
label: key, label: key,
})), }))}
]}
onValueChange={(v) => onValueChange={(v) =>
v !== null && handleFillEndpointTemplate(v) v !== null && handleFillEndpointTemplate(v)
} }
...@@ -991,7 +996,7 @@ export function ModelMutateDrawer({ ...@@ -991,7 +996,7 @@ export function ModelMutateDrawer({
field.onChange(value) field.onChange(value)
if (value) { if (value) {
setPromptPrice( setPromptPrice(
(parseFloat(value) * 2).toString() (Number.parseFloat(value) * 2).toString()
) )
} else { } else {
setPromptPrice('') setPromptPrice('')
...@@ -1001,8 +1006,8 @@ export function ModelMutateDrawer({ ...@@ -1001,8 +1006,8 @@ export function ModelMutateDrawer({
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{field.value && !isNaN(parseFloat(field.value)) {field.value && !Number.isNaN(Number.parseFloat(field.value))
? `Calculated price: $${(parseFloat(field.value) * 2).toFixed(4)} per 1M tokens` ? `Calculated price: $${(Number.parseFloat(field.value) * 2).toFixed(4)} per 1M tokens`
: t('Multiplier for prompt tokens.')} : t('Multiplier for prompt tokens.')}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
...@@ -1028,9 +1033,9 @@ export function ModelMutateDrawer({ ...@@ -1028,9 +1033,9 @@ export function ModelMutateDrawer({
const ratio = form.getValues('ratio') const ratio = form.getValues('ratio')
if (value && ratio) { if (value && ratio) {
const compPrice = const compPrice =
parseFloat(ratio) * Number.parseFloat(ratio) *
2 * 2 *
parseFloat(value) Number.parseFloat(value)
setCompletionPrice(compPrice.toString()) setCompletionPrice(compPrice.toString())
} else { } else {
setCompletionPrice('') setCompletionPrice('')
...@@ -1041,10 +1046,10 @@ export function ModelMutateDrawer({ ...@@ -1041,10 +1046,10 @@ export function ModelMutateDrawer({
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{field.value && {field.value &&
!isNaN(parseFloat(field.value)) && !Number.isNaN(Number.parseFloat(field.value)) &&
promptPrice && promptPrice &&
!isNaN(parseFloat(promptPrice)) !Number.isNaN(Number.parseFloat(promptPrice))
? `Calculated price: $${(parseFloat(promptPrice) * parseFloat(field.value)).toFixed(4)} per 1M tokens` ? `Calculated price: $${(Number.parseFloat(promptPrice) * Number.parseFloat(field.value)).toFixed(4)} per 1M tokens`
: t('Multiplier for completion tokens.')} : t('Multiplier for completion tokens.')}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
...@@ -1053,7 +1058,6 @@ export function ModelMutateDrawer({ ...@@ -1053,7 +1058,6 @@ export function ModelMutateDrawer({
/> />
</> </>
) : ( ) : (
<>
<div className='space-y-4'> <div className='space-y-4'>
<div className='space-y-2'> <div className='space-y-2'>
<Label>{t('Prompt price ($/1M tokens)')}</Label> <Label>{t('Prompt price ($/1M tokens)')}</Label>
...@@ -1066,8 +1070,8 @@ export function ModelMutateDrawer({ ...@@ -1066,8 +1070,8 @@ export function ModelMutateDrawer({
} }
/> />
<p className='text-muted-foreground text-sm'> <p className='text-muted-foreground text-sm'>
{promptPrice && !isNaN(parseFloat(promptPrice)) {promptPrice && !Number.isNaN(Number.parseFloat(promptPrice))
? `Calculated ratio: ${(parseFloat(promptPrice) / 2).toFixed(4)}` ? `Calculated ratio: ${(Number.parseFloat(promptPrice) / 2).toFixed(4)}`
: t('Enter Input price to calculate ratio')} : t('Enter Input price to calculate ratio')}
</p> </p>
</div> </div>
...@@ -1084,16 +1088,15 @@ export function ModelMutateDrawer({ ...@@ -1084,16 +1088,15 @@ export function ModelMutateDrawer({
/> />
<p className='text-muted-foreground text-sm'> <p className='text-muted-foreground text-sm'>
{completionPrice && {completionPrice &&
!isNaN(parseFloat(completionPrice)) && !Number.isNaN(Number.parseFloat(completionPrice)) &&
promptPrice && promptPrice &&
!isNaN(parseFloat(promptPrice)) && !Number.isNaN(Number.parseFloat(promptPrice)) &&
parseFloat(promptPrice) > 0 Number.parseFloat(promptPrice) > 0
? `Calculated ratio: ${(parseFloat(completionPrice) / parseFloat(promptPrice)).toFixed(4)}` ? `Calculated ratio: ${(Number.parseFloat(completionPrice) / Number.parseFloat(promptPrice)).toFixed(4)}`
: t('Enter Completion price to calculate ratio')} : t('Enter Completion price to calculate ratio')}
</p> </p>
</div> </div>
</div> </div>
</>
)} )}
<Collapsible <Collapsible
......
...@@ -19,7 +19,7 @@ For commercial licensing, please contact support@quantumnous.com ...@@ -19,7 +19,7 @@ For commercial licensing, please contact support@quantumnous.com
import { useState, useCallback, useMemo } from 'react' import { useState, useCallback, useMemo } from 'react'
import { useQueryClient, useIsFetching } from '@tanstack/react-query' import { useQueryClient, useIsFetching } from '@tanstack/react-query'
import { useNavigate, getRouteApi } from '@tanstack/react-router' import { useNavigate, getRouteApi } from '@tanstack/react-router'
import { type Table } from '@tanstack/react-table' import type { Table } from '@tanstack/react-table'
import { Eye, EyeOff } from 'lucide-react' import { Eye, EyeOff } from 'lucide-react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useIsAdmin } from '@/hooks/use-admin' import { useIsAdmin } from '@/hooks/use-admin'
...@@ -265,6 +265,9 @@ export function CommonLogsFilterBar<TData>( ...@@ -265,6 +265,9 @@ export function CommonLogsFilterBar<TData>(
const statsBar = ( const statsBar = (
<div className='flex flex-wrap items-center gap-2'> <div className='flex flex-wrap items-center gap-2'>
<CommonLogsStats /> <CommonLogsStats />
</div>
)
const sensitiveToggle = (
<Tooltip> <Tooltip>
<TooltipTrigger <TooltipTrigger
render={ render={
...@@ -283,7 +286,6 @@ export function CommonLogsFilterBar<TData>( ...@@ -283,7 +286,6 @@ export function CommonLogsFilterBar<TData>(
{sensitiveVisible ? t('Hide') : t('Show')} {sensitiveVisible ? t('Hide') : t('Show')}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</div>
) )
const dateRangeFilter = ( const dateRangeFilter = (
...@@ -410,6 +412,7 @@ export function CommonLogsFilterBar<TData>( ...@@ -410,6 +412,7 @@ export function CommonLogsFilterBar<TData>(
<LogsFilterToolbar <LogsFilterToolbar
table={props.table} table={props.table}
stats={statsBar} stats={statsBar}
actionStart={sensitiveToggle}
primaryFilters={ primaryFilters={
<> <>
{dateRangeFilter} {dateRangeFilter}
......
...@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. ...@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import { useState, type ComponentProps, type ReactNode } from 'react' import { useState, type ComponentProps, type ReactNode } from 'react'
import { type Table } from '@tanstack/react-table' import type { Table } from '@tanstack/react-table'
import { useMediaQuery } from '@/hooks' import { useMediaQuery } from '@/hooks'
import { ChevronDown, Loader2 } from 'lucide-react' import { ChevronDown, Loader2 } from 'lucide-react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
...@@ -44,6 +44,7 @@ interface LogsFilterToolbarProps<TData> { ...@@ -44,6 +44,7 @@ interface LogsFilterToolbarProps<TData> {
mobileFilters?: ReactNode mobileFilters?: ReactNode
mobileFilterCount?: number mobileFilterCount?: number
stats?: ReactNode stats?: ReactNode
actionStart?: ReactNode
hasActiveFilters: boolean hasActiveFilters: boolean
hasAdvancedActiveFilters?: boolean hasAdvancedActiveFilters?: boolean
advancedFilterCount?: number advancedFilterCount?: number
...@@ -142,6 +143,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) { ...@@ -142,6 +143,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) {
<div className='mt-2 flex flex-col gap-2'> <div className='mt-2 flex flex-col gap-2'>
{props.stats} {props.stats}
<div className='flex items-center justify-end gap-1.5'> <div className='flex items-center justify-end gap-1.5'>
{props.actionStart}
<DrawerTrigger asChild> <DrawerTrigger asChild>
<Button <Button
type='button' type='button'
...@@ -240,6 +242,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) { ...@@ -240,6 +242,7 @@ export function LogsFilterToolbar<TData>(props: LogsFilterToolbarProps<TData>) {
<div className='mt-2 flex flex-wrap items-center gap-2'> <div className='mt-2 flex flex-wrap items-center gap-2'>
{props.stats} {props.stats}
<div className='ms-auto flex flex-wrap items-center justify-end gap-1.5 sm:gap-2'> <div className='ms-auto flex flex-wrap items-center justify-end gap-1.5 sm:gap-2'>
{props.actionStart}
<Button <Button
type='button' type='button'
variant='outline' variant='outline'
......
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