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

View File

@ -18,6 +18,7 @@ import {
import { twMerge } from 'tailwind-merge'
import BlankState from '@/containers/BlankState'
import ModelSearch from '@/containers/ModelSearch'
import SetupRemoteModel from '@/containers/SetupRemoteModel'
@ -183,72 +184,80 @@ const MyModels = () => {
</div>
<div className="relative w-full">
{groupByEngine.map((engine, i) => {
const engineLogo = getLogoEngine(engine as InferenceEngine)
const showModel = showEngineListModel.includes(engine)
const onClickChevron = () => {
if (showModel) {
setShowEngineListModel((prev) =>
prev.filter((item) => item !== engine)
)
} else {
setShowEngineListModel((prev) => [...prev, engine])
{!groupByEngine.length ? (
<div className="mt-8">
<BlankState title="No search results found" />
</div>
) : (
groupByEngine.map((engine, i) => {
const engineLogo = getLogoEngine(engine as InferenceEngine)
const showModel = showEngineListModel.includes(engine)
const onClickChevron = () => {
if (showModel) {
setShowEngineListModel((prev) =>
prev.filter((item) => item !== engine)
)
} else {
setShowEngineListModel((prev) => [...prev, engine])
}
}
}
return (
<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="mb-1 mt-3 flex cursor-pointer items-center gap-2"
onClick={onClickChevron}
>
{engineLogo && (
<Image
className="h-6 w-6 flex-shrink-0"
width={48}
height={48}
src={engineLogo}
alt="logo"
/>
)}
<h6 className="font-medium text-[hsla(var(--text-secondary))]">
{getTitleByEngine(engine)}
</h6>
return (
<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="mb-1 mt-3 flex cursor-pointer items-center gap-2"
onClick={onClickChevron}
>
{engineLogo && (
<Image
className="h-6 w-6 flex-shrink-0"
width={48}
height={48}
src={engineLogo}
alt="logo"
/>
)}
<h6 className="font-medium text-[hsla(var(--text-secondary))]">
{getTitleByEngine(engine)}
</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 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 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 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>
</ScrollArea>