chore: stabilize Zhinian pilot delivery
This commit is contained in:
@@ -30,7 +30,9 @@ export function AppCenter() {
|
||||
const init = useAppCenterStore((state) => state.init);
|
||||
const items = useAppCenterStore((state) => state.items);
|
||||
const selectedTagKey = useAppCenterStore((state) => state.selectedTagKey);
|
||||
const selectedItemId = useAppCenterStore((state) => state.selectedItemId);
|
||||
const selectTag = useAppCenterStore((state) => state.selectTag);
|
||||
const selectItem = useAppCenterStore((state) => state.selectItem);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
@@ -70,7 +72,7 @@ export function AppCenter() {
|
||||
<h1 className="text-2xl font-semibold tracking-normal text-slate-950 dark:text-slate-50 md:text-3xl">
|
||||
{t('title')}
|
||||
</h1>
|
||||
<p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
|
||||
<p className="mt-2 max-w-xl text-sm leading-6 text-muted-foreground">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -114,44 +116,56 @@ export function AppCenter() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 overflow-y-auto pr-1">
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(156px,1fr))] gap-3 pb-1">
|
||||
{filteredItems.map((item) => {
|
||||
const Icon = getAppIcon(item.icon);
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
openItem(item);
|
||||
}}
|
||||
className={cn(
|
||||
'group relative flex min-h-[164px] flex-col items-center overflow-hidden rounded-lg border bg-white px-3 pb-10 pt-4 text-center shadow-none transition-all duration-200',
|
||||
'hover:-translate-y-0.5 hover:border-[#7DBADB] hover:bg-white hover:shadow-[0_16px_36px_rgba(15,23,42,0.08)]',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1E3A8A]/30',
|
||||
'border-slate-200/80 dark:border-white/10 dark:bg-slate-950/75 dark:hover:shadow-[0_10px_24px_rgba(0,0,0,0.24)]',
|
||||
)}
|
||||
>
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-[#0369A1] text-white shadow-[0_8px_18px_rgba(3,105,161,0.18)]">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="mt-3 min-w-0">
|
||||
<div className="truncate text-sm font-semibold text-slate-950 dark:text-slate-50">
|
||||
{t(item.nameKey)}
|
||||
<div className="min-h-0 overflow-y-auto px-2 py-2">
|
||||
{filteredItems.length === 0 ? (
|
||||
<div className="flex min-h-[320px] items-center justify-center rounded-lg border border-dashed border-slate-200 bg-white/60 text-sm text-muted-foreground dark:border-white/10 dark:bg-white/5">
|
||||
{t('empty')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(220px,260px))] justify-center gap-4 pb-2 sm:justify-start">
|
||||
{filteredItems.map((item) => {
|
||||
const Icon = getAppIcon(item.icon);
|
||||
const isSelected = selectedItemId === item.id;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
selectItem(item.id);
|
||||
openItem(item);
|
||||
}}
|
||||
data-testid={`app-center-item-${item.id}`}
|
||||
className={cn(
|
||||
'group relative flex min-h-[216px] w-full flex-col items-center overflow-hidden rounded-lg border bg-white px-4 pb-4 pt-5 text-center shadow-none transition-all duration-200',
|
||||
'hover:border-[#7DBADB] hover:bg-white hover:shadow-[0_18px_42px_rgba(15,23,42,0.09)]',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1E3A8A]/30',
|
||||
'dark:border-white/10 dark:bg-slate-950/75 dark:hover:shadow-[0_10px_24px_rgba(0,0,0,0.24)]',
|
||||
isSelected
|
||||
? 'border-[#0369A1] bg-[#F6FBFE] shadow-[0_14px_30px_rgba(3,105,161,0.10)]'
|
||||
: 'border-slate-200/80',
|
||||
)}
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-20 bg-[linear-gradient(180deg,rgba(229,244,250,0.85),rgba(255,255,255,0))] dark:bg-[linear-gradient(180deg,rgba(30,58,138,0.18),rgba(15,23,42,0))]" />
|
||||
<div className="relative flex h-16 w-16 shrink-0 items-center justify-center rounded-lg bg-[#0369A1] text-white shadow-[0_12px_24px_rgba(3,105,161,0.18)]">
|
||||
<Icon className="h-7 w-7" />
|
||||
</div>
|
||||
<p className="mx-auto mt-1 line-clamp-2 max-w-[13rem] text-xs leading-4 text-muted-foreground">
|
||||
{t(item.descriptionKey)}
|
||||
</p>
|
||||
</div>
|
||||
<span className="absolute bottom-3 left-1/2 inline-flex h-7 -translate-x-1/2 items-center gap-1 rounded-lg bg-[#0369A1] px-2.5 text-[11px] font-medium text-white opacity-0 shadow-[0_8px_16px_rgba(3,105,161,0.16)] transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100">
|
||||
{t('actions.open')}
|
||||
<ArrowUpRight className="h-3 w-3" />
|
||||
</span>
|
||||
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t from-[#0369A1]/[0.06] to-transparent opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100 dark:from-blue-300/[0.08]" />
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="relative mt-4 min-w-0">
|
||||
<div className="truncate text-base font-semibold tracking-normal text-slate-950 dark:text-slate-50">
|
||||
{t(item.nameKey)}
|
||||
</div>
|
||||
<p className="mx-auto mt-2 line-clamp-3 max-w-[13rem] text-xs leading-5 text-muted-foreground">
|
||||
{t(item.descriptionKey)}
|
||||
</p>
|
||||
</div>
|
||||
<span className="relative mt-auto inline-flex h-8 items-center gap-1.5 rounded-lg border border-[#D5E8F3] bg-[#F4FAFD] px-3 text-xs font-medium text-[#075985] opacity-0 transition-all duration-200 group-hover:opacity-100 group-focus-visible:opacity-100 dark:border-white/10 dark:bg-white/5 dark:text-blue-200">
|
||||
{t('actions.open')}
|
||||
<ArrowUpRight className="h-3.5 w-3.5" />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</YinianPanel>
|
||||
|
||||
Reference in New Issue
Block a user