mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-06 17:46:23 +00:00
refactor: refine toolbar controls and model drawer
This commit is contained in:
@@ -76,11 +76,6 @@ export type DataTableToolbarProps<TData> = {
|
|||||||
* search input and filter chips.
|
* search input and filter chips.
|
||||||
*/
|
*/
|
||||||
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
|
||||||
@@ -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>) {
|
|||||||
{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() {
|
|||||||
singleSelect: true,
|
singleSelect: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
afterFilters: (
|
preActions: (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger
|
<TooltipTrigger
|
||||||
render={
|
render={
|
||||||
|
|||||||
+53
-50
@@ -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({
|
|||||||
|
|
||||||
// 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({
|
|||||||
|
|
||||||
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({
|
|||||||
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({
|
|||||||
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,9 +422,10 @@ export function ModelMutateDrawer({
|
|||||||
...modelData
|
...modelData
|
||||||
} = submitData
|
} = submitData
|
||||||
|
|
||||||
const response = isEditing
|
const response =
|
||||||
? await updateModel({ ...modelData, id: currentRow!.id })
|
isEditing && currentModelId
|
||||||
: await createModel(modelData)
|
? await updateModel({ ...modelData, id: currentModelId })
|
||||||
|
: await createModel(modelData)
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
// Handle ratio configuration updates in system settings
|
// Handle ratio configuration updates in system settings
|
||||||
@@ -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({
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
isEditing,
|
isEditing,
|
||||||
currentRow,
|
currentModelId,
|
||||||
queryClient,
|
queryClient,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
pricingMode,
|
pricingMode,
|
||||||
@@ -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({
|
|||||||
<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({
|
|||||||
<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({
|
|||||||
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({
|
|||||||
/>
|
/>
|
||||||
</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({
|
|||||||
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({
|
|||||||
</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,8 +1058,7 @@ 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>
|
||||||
<Input
|
<Input
|
||||||
@@ -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({
|
|||||||
/>
|
/>
|
||||||
<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
|
||||||
|
|||||||
+22
-19
@@ -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,26 +265,28 @@ 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 />
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger
|
|
||||||
render={
|
|
||||||
<Button
|
|
||||||
variant='ghost'
|
|
||||||
size='icon'
|
|
||||||
onClick={() => setSensitiveVisible(!sensitiveVisible)}
|
|
||||||
aria-label={sensitiveVisible ? t('Hide') : t('Show')}
|
|
||||||
className='text-muted-foreground hover:text-foreground size-7'
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{sensitiveVisible ? <Eye /> : <EyeOff />}
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
{sensitiveVisible ? t('Hide') : t('Show')}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
const sensitiveToggle = (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger
|
||||||
|
render={
|
||||||
|
<Button
|
||||||
|
variant='ghost'
|
||||||
|
size='icon'
|
||||||
|
onClick={() => setSensitiveVisible(!sensitiveVisible)}
|
||||||
|
aria-label={sensitiveVisible ? t('Hide') : t('Show')}
|
||||||
|
className='text-muted-foreground hover:text-foreground size-7'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{sensitiveVisible ? <Eye /> : <EyeOff />}
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{sensitiveVisible ? t('Hide') : t('Show')}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
|
||||||
const dateRangeFilter = (
|
const dateRangeFilter = (
|
||||||
<LogsFilterField wide>
|
<LogsFilterField wide>
|
||||||
@@ -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/>.
|
|||||||
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> {
|
|||||||
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>) {
|
|||||||
<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>) {
|
|||||||
<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'
|
||||||
|
|||||||
Reference in New Issue
Block a user