mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 00:01:03 +00:00
67 lines
2.9 KiB
TypeScript
67 lines
2.9 KiB
TypeScript
import React, { useEffect, useState } from 'react'
|
|
import { Bookmark, ArrowRight, MapPin } from 'lucide-react'
|
|
import { useNavigate } from 'react-router'
|
|
import { useTranslation } from '../../i18n'
|
|
import { entityGradient } from '../../utils/gradients'
|
|
import { useCollectionLists } from './useCollectionLists'
|
|
import { readShownCollections, rememberShownCollections } from './collectionsWidgetMemory'
|
|
|
|
/**
|
|
* Dashboard sidebar widget — a glassy `.tool` card that surfaces the user's
|
|
* saved-place LISTS as compact colour-washed badges (a mini version of the
|
|
* collections hero): each badge shows the list's cover image (tinted with its
|
|
* colour) or a colour gradient, its name and place count, and jumps to that
|
|
* list. Fetches only list() (per-list place_count), so no N+1.
|
|
*/
|
|
export default function CollectionsWidget({ onOpen }: { onOpen: () => void }): React.ReactElement {
|
|
const { t } = useTranslation()
|
|
const navigate = useNavigate()
|
|
const { lists, loading } = useCollectionLists()
|
|
const [expected] = useState(readShownCollections)
|
|
useEffect(() => {
|
|
if (!loading) rememberShownCollections(Math.min(lists.length, 6))
|
|
}, [loading, lists.length])
|
|
|
|
return (
|
|
<div className="tool">
|
|
<div className="tool-head">
|
|
<div className="tool-title"><Bookmark size={14} /> {t('collections.widget.title')}</div>
|
|
<button type="button" className="tool-action" aria-label={t('collections.widget.title')} onClick={onOpen}>
|
|
<ArrowRight size={14} />
|
|
</button>
|
|
</div>
|
|
{loading ? (
|
|
expected ? (
|
|
<div className="col-badges" aria-hidden>
|
|
{Array.from({ length: expected }, (_, i) => <div key={i} className="col-badge col-badge-pending trek-skeleton" />)}
|
|
</div>
|
|
) : (
|
|
<div className="col-empty trek-skeleton" aria-hidden>{t('collections.widget.empty')}</div>
|
|
)
|
|
) : lists.length === 0 ? (
|
|
<div className="col-empty">{t('collections.widget.empty')}</div>
|
|
) : (
|
|
<div className="col-badges">
|
|
{lists.slice(0, 6).map(list => (
|
|
<button type="button"
|
|
key={list.id}
|
|
className="col-badge"
|
|
style={{ ['--badge-color' as string]: list.color || '#6366f1' }}
|
|
onClick={() => navigate(`/collections/${list.id}`)}
|
|
>
|
|
{list.cover_image
|
|
? <img className="col-badge-media" src={list.cover_image} alt="" />
|
|
: <div className="col-badge-media" style={{ backgroundImage: entityGradient(list.id) }} />}
|
|
<div className="col-badge-tint" />
|
|
<div className="col-badge-body">
|
|
<span className="col-badge-name">{list.name}</span>
|
|
<span className="col-badge-count"><MapPin size={11} /> {list.place_count ?? 0}</span>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|