mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-01 20:16:36 +08:00
refactor: split modules and harden scraping workflows
This commit is contained in:
@@ -6,6 +6,7 @@ type ShortcutItem = {
|
||||
title: string
|
||||
description: string
|
||||
badge?: string
|
||||
group?: string
|
||||
}
|
||||
|
||||
type ManagementShortcutsProps = {
|
||||
@@ -15,43 +16,80 @@ type ManagementShortcutsProps = {
|
||||
}
|
||||
|
||||
export function ManagementShortcuts({ title, description, items }: ManagementShortcutsProps) {
|
||||
const groups = groupShortcutItems(items)
|
||||
const hasNamedGroups = groups.some((group) => group.label)
|
||||
|
||||
return (
|
||||
<section className="rounded-3xl border border-gray-200 bg-white p-5 shadow-sm">
|
||||
<div className="mb-4 flex flex-wrap items-end justify-between gap-3">
|
||||
<section className="space-y-4">
|
||||
<div className="flex flex-wrap items-end justify-between gap-3 border-b border-[var(--app-border)] pb-3">
|
||||
<div>
|
||||
<h2 className="font-display text-lg font-bold text-ink-600">{title}</h2>
|
||||
{description && <p className="mt-1 text-sm text-ink-50">{description}</p>}
|
||||
<h2 className="font-display text-lg font-bold text-[var(--app-text)]">{title}</h2>
|
||||
{description && <p className="mt-1 text-sm text-[var(--app-muted)]">{description}</p>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{items.map((item) => (
|
||||
<Link
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className="group rounded-2xl border border-gray-200 bg-gray-50/70 p-4 transition hover:-translate-y-0.5 hover:border-primary-400/50 hover:bg-white hover:shadow-md"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="truncate text-sm font-bold text-ink-600">{item.title}</h3>
|
||||
{item.badge && (
|
||||
<span className="shrink-0 rounded-full bg-primary-400/10 px-2 py-0.5 text-[10px] font-bold text-brand-500">
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-2 line-clamp-2 text-xs leading-5 text-ink-50">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
<ArrowRight
|
||||
size={16}
|
||||
className="mt-0.5 shrink-0 text-sand-500 transition group-hover:translate-x-0.5 group-hover:text-brand-500"
|
||||
/>
|
||||
{hasNamedGroups ? (
|
||||
<div className="grid gap-5 lg:grid-cols-2">
|
||||
{groups.map((group) => (
|
||||
<div key={group.label || 'default'} className="space-y-3">
|
||||
{group.label && <h3 className="text-xs font-bold uppercase tracking-wider text-[var(--app-brand-text)]">{group.label}</h3>}
|
||||
<ShortcutGrid items={group.items} />
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<ShortcutGrid items={items} />
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function groupShortcutItems(items: ShortcutItem[]) {
|
||||
const groups: { label: string; items: ShortcutItem[] }[] = []
|
||||
items.forEach((item) => {
|
||||
const label = item.group ?? ''
|
||||
const group = groups.find((candidate) => candidate.label === label)
|
||||
if (group) {
|
||||
group.items.push(item)
|
||||
return
|
||||
}
|
||||
groups.push({ label, items: [item] })
|
||||
})
|
||||
return groups
|
||||
}
|
||||
|
||||
function ShortcutGrid({ items }: { items: ShortcutItem[] }) {
|
||||
return (
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
{items.map((item) => (
|
||||
<ShortcutCard key={item.to} item={item} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ShortcutCard({ item }: { item: ShortcutItem }) {
|
||||
return (
|
||||
<Link
|
||||
to={item.to}
|
||||
className="group rounded-lg border border-[var(--app-border)] bg-[var(--app-panel)] p-4 shadow-sm transition hover:-translate-y-0.5 hover:border-brand-500/50 hover:bg-[var(--app-panel-soft)] hover:shadow-md"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="truncate text-sm font-bold text-[var(--app-text)]">{item.title}</h3>
|
||||
{item.badge && (
|
||||
<span className="shrink-0 rounded-full border border-[var(--app-brand-border)] bg-[var(--app-brand-soft)] px-2 py-0.5 text-[10px] font-bold text-[var(--app-brand-text)]">
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-2 line-clamp-2 text-xs leading-5 text-[var(--app-muted)]">{item.description}</p>
|
||||
</div>
|
||||
<ArrowRight
|
||||
size={16}
|
||||
className="mt-0.5 shrink-0 text-[var(--app-brand-text)] transition group-hover:translate-x-0.5 group-hover:text-brand-500"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user