feat: add blank state component (#3413)

This commit is contained in:
Faisal Amir 2024-08-20 18:59:47 +07:00 committed by GitHub
parent d14ce6fb86
commit e9c30450b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 109 additions and 71 deletions

View File

@ -0,0 +1,24 @@
import { ReactNode } from 'react'
import LogoMark from '@/containers/Brand/Logo/Mark'
type Props = {
title: string
description?: string
action?: ReactNode
}
const BlankState = ({ title, description, action }: Props) => {
return (
<div className="mx-auto mt-10 flex h-full w-3/4 flex-col items-center justify-center text-center">
<LogoMark className="mx-auto mb-4 animate-wave" width={32} height={32} />
<h1 className="text-base font-semibold">{title}</h1>
{description && (
<p className="mt-1 text-[hsla(var(--text-secondary))]">{description}</p>
)}
{action && action}
</div>
)
}
export default BlankState

View File

@ -7,6 +7,7 @@ import { ScrollArea, Button, Select } from '@janhq/joi'
import { useAtomValue, useSetAtom } from 'jotai' import { useAtomValue, useSetAtom } from 'jotai'
import { UploadIcon } from 'lucide-react' import { UploadIcon } from 'lucide-react'
import BlankState from '@/containers/BlankState'
import CenterPanelContainer from '@/containers/CenterPanelContainer' import CenterPanelContainer from '@/containers/CenterPanelContainer'
import ModelSearch from '@/containers/ModelSearch' import ModelSearch from '@/containers/ModelSearch'
@ -92,15 +93,19 @@ const HubScreen = () => {
</div> </div>
</div> </div>
<div className="p-4 py-0 sm:px-16"> <div className="p-4 py-0 sm:px-16">
<div className="mb-4 flex w-full justify-end"> {!filteredModels.length ? (
<Select <BlankState title="No search results found" />
value={sortSelected} ) : (
onValueChange={(value) => { <div className="mb-4 flex w-full justify-end">
setSortSelected(value) <Select
}} value={sortSelected}
options={sortMenus} onValueChange={(value) => {
/> setSortSelected(value)
</div> }}
options={sortMenus}
/>
</div>
)}
<ModelList models={filteredModels} /> <ModelList models={filteredModels} />
</div> </div>
</ScrollArea> </ScrollArea>

View File

@ -18,6 +18,7 @@ import {
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import BlankState from '@/containers/BlankState'
import ModelSearch from '@/containers/ModelSearch' import ModelSearch from '@/containers/ModelSearch'
import SetupRemoteModel from '@/containers/SetupRemoteModel' import SetupRemoteModel from '@/containers/SetupRemoteModel'
@ -183,72 +184,80 @@ const MyModels = () => {
</div> </div>
<div className="relative w-full"> <div className="relative w-full">
{groupByEngine.map((engine, i) => { {!groupByEngine.length ? (
const engineLogo = getLogoEngine(engine as InferenceEngine) <div className="mt-8">
const showModel = showEngineListModel.includes(engine) <BlankState title="No search results found" />
const onClickChevron = () => { </div>
if (showModel) { ) : (
setShowEngineListModel((prev) => groupByEngine.map((engine, i) => {
prev.filter((item) => item !== engine) const engineLogo = getLogoEngine(engine as InferenceEngine)
) const showModel = showEngineListModel.includes(engine)
} else { const onClickChevron = () => {
setShowEngineListModel((prev) => [...prev, engine]) if (showModel) {
setShowEngineListModel((prev) =>
prev.filter((item) => item !== engine)
)
} else {
setShowEngineListModel((prev) => [...prev, engine])
}
} }
} return (
return ( <div className="my-6" key={i}>
<div className="my-6" key={i}> <div className="flex flex-col items-start justify-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<div className="flex flex-col items-start justify-start gap-2 sm:flex-row sm:items-center sm:justify-between"> <div
<div className="mb-1 mt-3 flex cursor-pointer items-center gap-2"
className="mb-1 mt-3 flex cursor-pointer items-center gap-2" onClick={onClickChevron}
onClick={onClickChevron} >
> {engineLogo && (
{engineLogo && ( <Image
<Image className="h-6 w-6 flex-shrink-0"
className="h-6 w-6 flex-shrink-0" width={48}
width={48} height={48}
height={48} src={engineLogo}
src={engineLogo} alt="logo"
alt="logo" />
/> )}
)} <h6 className="font-medium text-[hsla(var(--text-secondary))]">
<h6 className="font-medium text-[hsla(var(--text-secondary))]"> {getTitleByEngine(engine)}
{getTitleByEngine(engine)} </h6>
</h6> </div>
<div className="flex gap-1">
{!localEngines.includes(engine) && (
<SetupRemoteModel engine={engine} />
)}
{!showModel ? (
<Button theme="icon" onClick={onClickChevron}>
<ChevronDownIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
) : (
<Button theme="icon" onClick={onClickChevron}>
<ChevronUpIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
)}
</div>
</div> </div>
<div className="flex gap-1"> <div className="mt-2">
{!localEngines.includes(engine) && ( {filteredDownloadedModels
<SetupRemoteModel engine={engine} /> ? filteredDownloadedModels
)} .filter((x) => x.engine === engine)
{!showModel ? ( .map((model) => {
<Button theme="icon" onClick={onClickChevron}> if (!showModel) return null
<ChevronDownIcon return (
size={14} <MyModelList key={model.id} model={model} />
className="text-[hsla(var(--text-secondary))]" )
/> })
</Button> : null}
) : (
<Button theme="icon" onClick={onClickChevron}>
<ChevronUpIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
)}
</div> </div>
</div> </div>
<div className="mt-2"> )
{filteredDownloadedModels })
? filteredDownloadedModels )}
.filter((x) => x.engine === engine)
.map((model) => {
if (!showModel) return null
return <MyModelList key={model.id} model={model} />
})
: null}
</div>
</div>
)
})}
</div> </div>
</div> </div>
</ScrollArea> </ScrollArea>