new style of tables

This commit is contained in:
JzoNg 2024-08-21 17:06:55 +08:00
parent 85b25ebe1b
commit bc245a25bf
12 changed files with 136 additions and 145 deletions

View File

@ -14,9 +14,9 @@ const EmptyElement: FC = () => {
return ( return (
<div className='flex items-center justify-center h-full'> <div className='flex items-center justify-center h-full'>
<div className='bg-gray-50 w-[560px] h-fit box-border px-5 py-4 rounded-2xl'> <div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
<span className='text-gray-700 font-semibold'>{t('appAnnotation.noData.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span> <span className='text-text-secondary system-md-semibold'>{t('appAnnotation.noData.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
<div className='mt-2 text-gray-500 text-sm font-normal'> <div className='mt-2 text-text-tertiary system-sm-regular'>
{t('appAnnotation.noData.description')} {t('appAnnotation.noData.description')}
</div> </div>
</div> </div>

View File

@ -2,10 +2,8 @@
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import {
MagnifyingGlassIcon,
} from '@heroicons/react/24/solid'
import useSWR from 'swr' import useSWR from 'swr'
import Input from '@/app/components/base/input'
import { fetchAnnotationsCount } from '@/service/log' import { fetchAnnotationsCount } from '@/service/log'
export type QueryParam = { export type QueryParam = {
@ -31,22 +29,18 @@ const Filter: FC<IFilterProps> = ({
if (!data) if (!data)
return null return null
return ( return (
<div className='flex justify-between flex-row flex-wrap gap-y-2 gap-x-4 items-center mb-4 text-gray-900 text-base'> <div className='flex justify-between flex-row flex-wrap gap-2 items-center mb-2'>
<div className="relative"> <Input
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> wrapperClassName='w-[200px]'
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> showLeftIcon
</div> showClearIcon
<input value={queryParams.keyword}
type="text" placeholder={t('common.operation.search')!}
name="query" onChange={(e) => {
className="block w-[240px] bg-gray-100 shadow-sm rounded-md border-0 py-1.5 pl-10 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none sm:text-sm sm:leading-6" setQueryParams({ ...queryParams, keyword: e.target.value })
placeholder={t('common.operation.search') as string} }}
value={queryParams.keyword} onClear={() => setQueryParams({ ...queryParams, keyword: '' })}
onChange={(e) => { />
setQueryParams({ ...queryParams, keyword: e.target.value })
}}
/>
</div>
{children} {children}
</div> </div>
) )

View File

@ -150,8 +150,8 @@ const Annotation: FC<Props> = ({
return ( return (
<div className='flex flex-col h-full'> <div className='flex flex-col h-full'>
<p className='flex text-sm font-normal text-gray-500'>{t('appLog.description')}</p> <p className='text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
<div className='grow flex flex-col py-4 '> <div className='flex flex-col py-4 flex-1'>
<Filter appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams}> <Filter appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams}>
<div className='flex items-center space-x-2'> <div className='flex items-center space-x-2'>
{isChatApp && ( {isChatApp && (

View File

@ -4,7 +4,6 @@ import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiDeleteBinLine } from '@remixicon/react' import { RiDeleteBinLine } from '@remixicon/react'
import { Edit02 } from '../../base/icons/src/vender/line/general' import { Edit02 } from '../../base/icons/src/vender/line/general'
import s from './style.module.css'
import type { AnnotationItem } from './type' import type { AnnotationItem } from './type'
import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal' import RemoveAnnotationConfirmModal from './remove-annotation-confirm-modal'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
@ -27,21 +26,21 @@ const List: FC<Props> = ({
const [showConfirmDelete, setShowConfirmDelete] = React.useState(false) const [showConfirmDelete, setShowConfirmDelete] = React.useState(false)
return ( return (
<div className='overflow-x-auto'> <div className='overflow-x-auto'>
<table className={cn(s.logTable, 'w-full min-w-[440px] border-collapse border-0 text-sm')} > <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold"> <thead className='system-xs-medium-uppercase text-text-tertiary'>
<tr className='uppercase'> <tr>
<td className='whitespace-nowrap'>{t('appAnnotation.table.header.question')}</td> <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.question')}</td>
<td className='whitespace-nowrap'>{t('appAnnotation.table.header.answer')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.answer')}</td>
<td className='whitespace-nowrap'>{t('appAnnotation.table.header.createdAt')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.createdAt')}</td>
<td className='whitespace-nowrap'>{t('appAnnotation.table.header.hits')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.hits')}</td>
<td className='whitespace-nowrap w-[96px]'>{t('appAnnotation.table.header.actions')}</td> <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap w-[96px]'>{t('appAnnotation.table.header.actions')}</td>
</tr> </tr>
</thead> </thead>
<tbody className="text-gray-500"> <tbody className="text-text-secondary system-sm-regular">
{list.map(item => ( {list.map(item => (
<tr <tr
key={item.id} key={item.id}
className={'border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer'} className='border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer'
onClick={ onClick={
() => { () => {
onView(item) onView(item)
@ -49,16 +48,16 @@ const List: FC<Props> = ({
} }
> >
<td <td
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]' className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
title={item.question} title={item.question}
>{item.question}</td> >{item.question}</td>
<td <td
className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]' className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
title={item.answer} title={item.answer}
>{item.answer}</td> >{item.answer}</td>
<td>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td> <td className='p-3 pr-2'>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
<td>{item.hit_count}</td> <td className='p-3 pr-2'>{item.hit_count}</td>
<td className='w-[96px]' onClick={e => e.stopPropagation()}> <td className='w-[96px] p-3 pr-2' onClick={e => e.stopPropagation()}>
{/* Actions */} {/* Actions */}
<div className='flex space-x-2 text-gray-500'> <div className='flex space-x-2 text-gray-500'>
<div <div

View File

@ -1,9 +1,3 @@
.logTable td {
padding: 7px 8px;
box-sizing: border-box;
max-width: 200px;
}
.pagination li { .pagination li {
list-style: none; list-style: none;
} }

View File

@ -2,14 +2,13 @@
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import {
MagnifyingGlassIcon,
} from '@heroicons/react/24/solid'
import useSWR from 'swr' import useSWR from 'swr'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { RiCalendarLine } from '@remixicon/react'
import quarterOfYear from 'dayjs/plugin/quarterOfYear' import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import type { QueryParam } from './index' import type { QueryParam } from './index'
import { SimpleSelect } from '@/app/components/base/select' import Chip from '@/app/components/base/chip'
import Input from '@/app/components/base/input'
import Sort from '@/app/components/base/sort' import Sort from '@/app/components/base/sort'
import { fetchAnnotationsCount } from '@/service/log' import { fetchAnnotationsCount } from '@/service/log'
dayjs.extend(quarterOfYear) dayjs.extend(quarterOfYear)
@ -41,43 +40,44 @@ const Filter: FC<IFilterProps> = ({ isChatMode, appId, queryParams, setQueryPara
if (!data) if (!data)
return null return null
return ( return (
<div className='flex flex-row flex-wrap gap-2 items-center mb-4 text-gray-900 text-base'> <div className='flex flex-row flex-wrap gap-2 items-center mb-2'>
<SimpleSelect <Chip
items={TIME_PERIOD_LIST.map(item => ({ value: item.value, name: t(`appLog.filter.period.${item.name}`) }))} className='min-w-[150px]'
className='mt-0 !w-40' panelClassName='w-[270px]'
leftIcon={<RiCalendarLine className='h-4 w-4 text-text-secondary' />}
value={queryParams.period || 7}
onSelect={(item) => { onSelect={(item) => {
setQueryParams({ ...queryParams, period: item.value }) setQueryParams({ ...queryParams, period: item.value as string })
}} }}
defaultValue={queryParams.period} /> onClear={() => setQueryParams({ ...queryParams, period: 7 })}
<div className="relative rounded-md"> items={TIME_PERIOD_LIST.map(item => ({ value: item.value, name: t(`appLog.filter.period.${item.name}`) }))}
<SimpleSelect />
defaultValue={'all'} <Chip
className='!w-[300px]' className='min-w-[150px]'
onSelect={ panelClassName='w-[270px]'
(item) => { showLeftIcon={false}
setQueryParams({ ...queryParams, annotation_status: item.value as string }) value={queryParams.annotation_status || 'all'}
} onSelect={(item) => {
} setQueryParams({ ...queryParams, annotation_status: item.value as string })
items={[{ value: 'all', name: t('appLog.filter.annotation.all') }, }}
{ value: 'annotated', name: t('appLog.filter.annotation.annotated', { count: data?.count }) }, onClear={() => setQueryParams({ ...queryParams, annotation_status: 'all' })}
{ value: 'not_annotated', name: t('appLog.filter.annotation.not_annotated') }]} items={[
/> { value: 'all', name: t('appLog.filter.annotation.all') },
</div> { value: 'annotated', name: t('appLog.filter.annotation.annotated', { count: data?.count }) },
<div className="relative"> { value: 'not_annotated', name: t('appLog.filter.annotation.not_annotated') },
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> ]}
<MagnifyingGlassIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> />
</div> <Input
<input wrapperClassName='w-[200px]'
type="text" showLeftIcon
name="query" showClearIcon
className="block w-[180px] bg-gray-100 shadow-sm rounded-md border-0 py-1.5 pl-10 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none sm:text-sm sm:leading-6" value={queryParams.keyword}
placeholder={t('common.operation.search')!} placeholder={t('common.operation.search')!}
value={queryParams.keyword} onChange={(e) => {
onChange={(e) => { setQueryParams({ ...queryParams, keyword: e.target.value })
setQueryParams({ ...queryParams, keyword: e.target.value }) }}
}} onClear={() => setQueryParams({ ...queryParams, keyword: '' })}
/> />
</div>
{isChatMode && ( {isChatMode && (
<> <>
<div className='w-px h-3.5 bg-divider-regular'></div> <div className='w-px h-3.5 bg-divider-regular'></div>

View File

@ -39,12 +39,12 @@ const EmptyElement: FC<{ appUrl: string }> = ({ appUrl }) => {
const pathSegments = pathname.split('/') const pathSegments = pathname.split('/')
pathSegments.pop() pathSegments.pop()
return <div className='flex items-center justify-center h-full'> return <div className='flex items-center justify-center h-full'>
<div className='bg-gray-50 w-[560px] h-fit box-border px-5 py-4 rounded-2xl'> <div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
<span className='text-gray-700 font-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span> <span className='text-text-secondary system-md-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
<div className='mt-2 text-gray-500 text-sm font-normal'> <div className='mt-2 text-text-tertiary system-sm-regular'>
<Trans <Trans
i18nKey="appLog.table.empty.element.content" i18nKey="appLog.table.empty.element.content"
components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-primary-600' />, testLink: <Link href={appUrl} className='text-primary-600' target='_blank' rel='noopener noreferrer' /> }} components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-util-colors-blue-blue-600' />, testLink: <Link href={appUrl} className='text-util-colors-blue-blue-600' target='_blank' rel='noopener noreferrer' /> }}
/> />
</div> </div>
</div> </div>
@ -101,7 +101,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
return ( return (
<div className='flex flex-col h-full'> <div className='flex flex-col h-full'>
<p className='flex text-sm font-normal text-gray-500'>{t('appLog.description')}</p> <p className='text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
<div className='flex flex-col py-4 flex-1'> <div className='flex flex-col py-4 flex-1'>
<Filter isChatMode={isChatMode} appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams} /> <Filter isChatMode={isChatMode} appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams} />
{total === undefined {total === undefined

View File

@ -17,7 +17,6 @@ import timezone from 'dayjs/plugin/timezone'
import { createContext, useContext } from 'use-context-selector' import { createContext, useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow' import { useShallow } from 'zustand/react/shallow'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import s from './style.module.css'
import VarPanel from './var-panel' import VarPanel from './var-panel'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { randomString } from '@/utils' import { randomString } from '@/utils'
@ -642,14 +641,14 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
return ( return (
<Tooltip <Tooltip
htmlContent={ htmlContent={
<span className='text-xs text-gray-500 inline-flex items-center'> <span className='text-xs text-text-tertiary inline-flex items-center'>
<RiEditFill className='w-3 h-3 mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`} <RiEditFill className='w-3 h-3 mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`}
</span> </span>
} }
className={(isHighlight && !isChatMode) ? '' : '!hidden'} className={(isHighlight && !isChatMode) ? '' : '!hidden'}
selector={`highlight-${randomString(16)}`} selector={`highlight-${randomString(16)}`}
> >
<div className={cn(isEmptyStyle ? 'text-gray-400' : 'text-gray-700', !isHighlight ? '' : 'bg-orange-100', 'text-sm overflow-hidden text-ellipsis whitespace-nowrap')}> <div className={cn(isEmptyStyle ? 'text-text-quaternary' : 'text-text-secondary', !isHighlight ? '' : 'bg-orange-100', 'system-sm-regular overflow-hidden text-ellipsis whitespace-nowrap')}>
{value || '-'} {value || '-'}
</div> </div>
</Tooltip> </Tooltip>
@ -667,40 +666,46 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
return ( return (
<div className='overflow-x-auto'> <div className='overflow-x-auto'>
<table className={`w-full min-w-[440px] border-collapse border-0 text-sm mt-3 ${s.logTable}`}> <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
<thead className="h-8 leading-8 border-b border-gray-200 text-gray-500 font-bold"> <thead className='system-xs-medium-uppercase text-text-tertiary'>
<tr> <tr>
<td className='w-[1.375rem] whitespace-nowrap'></td> <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
<td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
<td className='whitespace-nowrap'>{t('appLog.table.header.endUser')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.endUser')}</td>
<td className='whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
<td className='whitespace-nowrap'>{t('appLog.table.header.userRate')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.userRate')}</td>
<td className='whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td>
<td className='whitespace-nowrap'>{t('appLog.table.header.updatedTime')}</td> <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.updatedTime')}</td>
<td className='whitespace-nowrap'>{t('appLog.table.header.time')}</td> <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.time')}</td>
</tr> </tr>
</thead> </thead>
<tbody className="text-gray-500"> <tbody className="text-text-secondary system-sm-regular">
{logs.data.map((log: any) => { {logs.data.map((log: any) => {
const endUser = log.from_end_user_session_id const endUser = log.from_end_user_session_id
const leftValue = get(log, isChatMode ? 'name' : 'message.inputs.query') || (!isChatMode ? (get(log, 'message.query') || get(log, 'message.inputs.default_input')) : '') || '' const leftValue = get(log, isChatMode ? 'name' : 'message.inputs.query') || (!isChatMode ? (get(log, 'message.query') || get(log, 'message.inputs.default_input')) : '') || ''
const rightValue = get(log, isChatMode ? 'message_count' : 'message.answer') const rightValue = get(log, isChatMode ? 'message_count' : 'message.answer')
return <tr return <tr
key={log.id} key={log.id}
className={`border-b border-gray-200 h-8 hover:bg-gray-50 cursor-pointer ${currentConversation?.id !== log.id ? '' : 'bg-gray-50'}`} className={cn('border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer', currentConversation?.id !== log.id ? '' : 'bg-background-default-hover')}
onClick={() => { onClick={() => {
setShowDrawer(true) setShowDrawer(true)
setCurrentConversation(log) setCurrentConversation(log)
}}> }}>
<td className='text-center align-middle'>{!log.read_at && <span className='inline-block bg-[#3F83F8] h-1.5 w-1.5 rounded'></span>}</td> <td className='h-4'>
<td style={{ maxWidth: isChatMode ? 300 : 200 }}> {!log.read_at && (
<div className='p-3 pr-0.5 flex items-center'>
<span className='inline-block bg-util-colors-blue-blue-500 h-1.5 w-1.5 rounded'></span>
</div>
)}
</td>
<td className='p-3 pr-2 w-[160px]' style={{ maxWidth: isChatMode ? 300 : 200 }}>
{renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)} {renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)}
</td> </td>
<td>{renderTdValue(endUser || defaultValue, !endUser)}</td> <td className='p-3 pr-2'>{renderTdValue(endUser || defaultValue, !endUser)}</td>
<td style={{ maxWidth: isChatMode ? 100 : 200 }}> <td className='p-3 pr-2' style={{ maxWidth: isChatMode ? 100 : 200 }}>
{renderTdValue(rightValue === 0 ? 0 : (rightValue || t('appLog.table.empty.noOutput')), !rightValue, !isChatMode && !!log.annotation?.content, log.annotation)} {renderTdValue(rightValue === 0 ? 0 : (rightValue || t('appLog.table.empty.noOutput')), !rightValue, !isChatMode && !!log.annotation?.content, log.annotation)}
</td> </td>
<td> <td className='p-3 pr-2'>
{(!log.user_feedback_stats.like && !log.user_feedback_stats.dislike) {(!log.user_feedback_stats.like && !log.user_feedback_stats.dislike)
? renderTdValue(defaultValue, true) ? renderTdValue(defaultValue, true)
: <> : <>
@ -709,7 +714,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
</> </>
} }
</td> </td>
<td> <td className='p-3 pr-2'>
{(!log.admin_feedback_stats.like && !log.admin_feedback_stats.dislike) {(!log.admin_feedback_stats.like && !log.admin_feedback_stats.dislike)
? renderTdValue(defaultValue, true) ? renderTdValue(defaultValue, true)
: <> : <>
@ -718,8 +723,8 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
</> </>
} }
</td> </td>
<td className='w-[160px]'>{formatTime(log.updated_at, t('appLog.dateTimeFormat') as string)}</td> <td className='w-[160px] p-3 pr-2'>{formatTime(log.updated_at, t('appLog.dateTimeFormat') as string)}</td>
<td className='w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td> <td className='w-[160px] p-3 pr-2'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
</tr> </tr>
})} })}
</tbody> </tbody>

View File

@ -1,9 +1,3 @@
.logTable td {
padding: 7px 8px;
box-sizing: border-box;
max-width: 200px;
}
.pagination li { .pagination li {
list-style: none; list-style: none;
} }

View File

@ -14,21 +14,19 @@ type IFilterProps = {
const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps) => { const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<div className='flex items-center gap-2 mb-2'> <div className='flex flex-row flex-wrap gap-2 mb-2'>
<div className="relative rounded-md"> <Chip
<Chip value={queryParams.status || 'all'}
value={queryParams.status || 'all'} onSelect={(item) => {
onSelect={(item) => { setQueryParams({ ...queryParams, status: item.value as string })
setQueryParams({ ...queryParams, status: item.value as string }) }}
}} onClear={() => setQueryParams({ ...queryParams, status: 'all' })}
onClear={() => setQueryParams({ ...queryParams, status: 'all' })} items={[{ value: 'all', name: 'All' },
items={[{ value: 'all', name: 'All' }, { value: 'succeeded', name: 'Success' },
{ value: 'succeeded', name: 'Success' }, { value: 'failed', name: 'Fail' },
{ value: 'failed', name: 'Fail' }, { value: 'stopped', name: 'Stop' },
{ value: 'stopped', name: 'Stop' }, ]}
]} />
/>
</div>
<Input <Input
wrapperClassName='w-[200px]' wrapperClassName='w-[200px]'
showLeftIcon showLeftIcon

View File

@ -15,6 +15,9 @@ export type Item = {
type Props = { type Props = {
className?: string className?: string
panelClassName?: string
showLeftIcon?: boolean
leftIcon?: any
value: number | string value: number | string
items: Item[] items: Item[]
onSelect: (item: any) => void onSelect: (item: any) => void
@ -22,6 +25,9 @@ type Props = {
} }
const Chip: FC<Props> = ({ const Chip: FC<Props> = ({
className, className,
panelClassName,
showLeftIcon = true,
leftIcon,
value, value,
items, items,
onSelect, onSelect,
@ -46,22 +52,23 @@ const Chip: FC<Props> = ({
className='block' className='block'
> >
<div className={cn( <div className={cn(
'flex items-center px-2 py-1 rounded-lg border-[0.5px] border-transparent bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt', 'flex items-center min-h-8 px-2 py-1 rounded-lg border-[0.5px] border-transparent bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt',
open && !value && '!bg-state-base-hover-alt hover:bg-state-base-hover-alt', open && !value && '!bg-state-base-hover-alt hover:bg-state-base-hover-alt',
!open && !!value && '!bg-components-button-secondary-bg shadow-xs !border-components-button-secondary-border hover:!bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover', !open && !!value && '!bg-components-button-secondary-bg shadow-xs !border-components-button-secondary-border hover:!bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover',
open && !!value && '!bg-components-button-secondary-bg-hover !border-components-button-secondary-border-hover shadow-xs hover:!bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover', open && !!value && '!bg-components-button-secondary-bg-hover !border-components-button-secondary-border-hover shadow-xs hover:!bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover',
className, className,
)}> )}>
<div className='p-0.5'> {showLeftIcon && (
<RiFilter3Line className={cn('h-4 w-4 text-text-tertiary', !!value && 'text-text-secondary')} /> <div className='p-0.5'>
</div> {leftIcon || (
<div className='p-1 flex items-center gap-0.5'> <RiFilter3Line className={cn('h-4 w-4 text-text-tertiary', !!value && 'text-text-secondary')} />
)}
</div>
)}
<div className='grow first-line:p-1 flex items-center gap-0.5'>
<div className={cn('system-sm-regular text-text-tertiary', !!value && 'text-text-secondary')}> <div className={cn('system-sm-regular text-text-tertiary', !!value && 'text-text-secondary')}>
{triggerContent} {triggerContent}
</div> </div>
{/* {value.length > 1 && (
<div className='system-xs-medium text-text-tertiary'>{`+${value.length - 1}`}</div>
)} */}
</div> </div>
{!value && <RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />} {!value && <RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />}
{!!value && ( {!!value && (
@ -75,7 +82,7 @@ const Chip: FC<Props> = ({
</div> </div>
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[1002]'> <PortalToFollowElemContent className='z-[1002]'>
<div className='relative w-[240px] bg-components-panel-bg-blur rounded-xl border-[0.5px] border-components-panel-border shadow-lg'> <div className={cn('relative w-[240px] bg-components-panel-bg-blur rounded-xl border-[0.5px] border-components-panel-border shadow-lg', panelClassName)}>
<div className='p-1 max-h-72 overflow-auto'> <div className='p-1 max-h-72 overflow-auto'>
{items.map(item => ( {items.map(item => (
<div <div

View File

@ -47,7 +47,7 @@ const Sort: FC<Props> = ({
className='block' className='block'
> >
<div className={cn( <div className={cn(
'flex items-center px-2 py-1.5 rounded-l-lg bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt', 'flex items-center px-2 py-1 rounded-l-lg bg-components-input-bg-normal cursor-pointer hover:bg-state-base-hover-alt',
open && '!bg-state-base-hover-alt hover:bg-state-base-hover-alt', open && '!bg-state-base-hover-alt hover:bg-state-base-hover-alt',
)}> )}>
<div className='p-1 flex items-center gap-0.5'> <div className='p-1 flex items-center gap-0.5'>
@ -80,7 +80,7 @@ const Sort: FC<Props> = ({
</PortalToFollowElemContent> </PortalToFollowElemContent>
</div> </div>
</PortalToFollowElem> </PortalToFollowElem>
<div className='ml-px p-2.5 rounded-r-lg bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer' onClick={() => onSelect(`${order ? '' : '-'}${value}`)}> <div className='ml-px p-2 rounded-r-lg bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer' onClick={() => onSelect(`${order ? '' : '-'}${value}`)}>
{!order && <RiSortAsc className='w-4 h-4 text-components-button-tertiary-text' />} {!order && <RiSortAsc className='w-4 h-4 text-components-button-tertiary-text' />}
{order && <RiSortDesc className='w-4 h-4 text-components-button-tertiary-text' />} {order && <RiSortDesc className='w-4 h-4 text-components-button-tertiary-text' />}
</div> </div>