mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 22:01:45 +00:00
feat(ui): unified toolbar design + redesigned budget widgets + polish
Trip planner now has a consistent rounded toolbar across bookings, lists, budget and files. Each panel shows title, inline filter pills (with counts where useful) and an accent action button on the right. Moved per-tab controls into the toolbar — lists import, todo add, budget currency/add-category, files trash/filters — and dropped the redundant in-panel headers. Budget sidebar redesigned: total-budget card with indigo-ringed avatars and coloured split bar; settlement flows as paired avatar cards; by-category donut rebuilt in SVG with per-category gradients. Both cards now follow dark/light mode via a widgetTheme helper. Todo: add-new-task is a portalled modal on desktop, the add-task input bar is gone; new SORT BY section in the sidebar; inline category creation in the task editor. Reservations: pending / confirmed sections remember their collapsed state per trip (localStorage). Misc: per-trip connections toggle moved into the day-plan sidebar, booking endpoints fixed to show on map for trains/cruises/cars as well, label localStorage persistence, RESMODAL test updated to the new airport-select flow. i18n: the new booking / map / todo / budget strings are translated into all 15 supported languages.
This commit is contained in:
@@ -729,9 +729,10 @@ function MenuItem({ icon, label, onClick, danger }: MenuItemProps) {
|
||||
interface PackingListPanelProps {
|
||||
tripId: number
|
||||
items: PackingItem[]
|
||||
openImportSignal?: number
|
||||
}
|
||||
|
||||
export default function PackingListPanel({ tripId, items }: PackingListPanelProps) {
|
||||
export default function PackingListPanel({ tripId, items, openImportSignal = 0 }: PackingListPanelProps) {
|
||||
const [filter, setFilter] = useState('alle') // 'alle' | 'offen' | 'erledigt'
|
||||
const [addingCategory, setAddingCategory] = useState(false)
|
||||
const [newCatName, setNewCatName] = useState('')
|
||||
@@ -896,6 +897,14 @@ export default function PackingListPanel({ tripId, items }: PackingListPanelProp
|
||||
const [saveTemplateName, setSaveTemplateName] = useState('')
|
||||
const [showImportModal, setShowImportModal] = useState(false)
|
||||
const [importText, setImportText] = useState('')
|
||||
const lastHandledImportSignal = useRef(openImportSignal)
|
||||
|
||||
useEffect(() => {
|
||||
if (openImportSignal !== lastHandledImportSignal.current && openImportSignal > 0) {
|
||||
setShowImportModal(true)
|
||||
}
|
||||
lastHandledImportSignal.current = openImportSignal
|
||||
}, [openImportSignal])
|
||||
const csvInputRef = useRef<HTMLInputElement>(null)
|
||||
const templateDropdownRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -999,14 +1008,13 @@ export default function PackingListPanel({ tripId, items }: PackingListPanelProp
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', ...font }}>
|
||||
|
||||
{/* ── Header ── */}
|
||||
<div style={{ padding: '20px 24px 16px', borderBottom: '1px solid rgba(0,0,0,0.06)', flexShrink: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 14 }}>
|
||||
<div>
|
||||
<h2 style={{ margin: 0, fontSize: 18, fontWeight: 700, color: 'var(--text-primary)' }}>{t('packing.title')}</h2>
|
||||
<p style={{ margin: '2px 0 0', fontSize: 12.5, color: 'var(--text-faint)' }}>
|
||||
{items.length === 0 ? t('packing.empty') : t('packing.progress', { packed: abgehakt, total: items.length, percent: fortschritt })}
|
||||
<div style={{ padding: '0 0 16px', flexShrink: 0 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 14 }}>
|
||||
{items.length > 0 ? (
|
||||
<p style={{ margin: 0, fontSize: 12.5, color: 'var(--text-faint)' }}>
|
||||
{t('packing.progress', { packed: abgehakt, total: items.length, percent: fortschritt })}
|
||||
</p>
|
||||
</div>
|
||||
) : <span />}
|
||||
<div style={{ display: 'flex', gap: 6 }}>
|
||||
{canEdit && abgehakt > 0 && (
|
||||
<button onClick={handleClearChecked} style={{
|
||||
@@ -1017,15 +1025,6 @@ export default function PackingListPanel({ tripId, items }: PackingListPanelProp
|
||||
<span className="sm:hidden">{t('packing.clearCheckedShort', { count: abgehakt })}</span>
|
||||
</button>
|
||||
)}
|
||||
{canEdit && (
|
||||
<button onClick={() => setShowImportModal(true)} style={{
|
||||
display: 'flex', alignItems: 'center', gap: 5, padding: '5px 11px', borderRadius: 99,
|
||||
border: '1px solid var(--border-primary)', fontSize: 12, fontWeight: 500, cursor: 'pointer',
|
||||
fontFamily: 'inherit', background: 'var(--bg-card)', color: 'var(--text-muted)',
|
||||
}}>
|
||||
<Upload size={12} /> <span className="hidden sm:inline">{t('packing.import')}</span>
|
||||
</button>
|
||||
)}
|
||||
{canEdit && availableTemplates.length > 0 && (
|
||||
<div ref={templateDropdownRef} style={{ position: 'relative' }}>
|
||||
<button onClick={() => setShowTemplateDropdown(v => !v)} disabled={applyingTemplate} style={{
|
||||
@@ -1151,7 +1150,7 @@ export default function PackingListPanel({ tripId, items }: PackingListPanelProp
|
||||
|
||||
{/* ── Filter-Tabs ── */}
|
||||
{items.length > 0 && (
|
||||
<div style={{ display: 'flex', gap: 4, padding: '10px 16px 0', flexShrink: 0 }}>
|
||||
<div style={{ display: 'flex', gap: 4, padding: '10px 0 0', flexShrink: 0 }}>
|
||||
{[['alle', t('packing.filterAll')], ['offen', t('packing.filterOpen')], ['erledigt', t('packing.filterDone')]].map(([id, label]) => (
|
||||
<button key={id} onClick={() => setFilter(id)} style={{
|
||||
padding: '4px 12px', borderRadius: 99, border: 'none', cursor: 'pointer',
|
||||
@@ -1165,7 +1164,7 @@ export default function PackingListPanel({ tripId, items }: PackingListPanelProp
|
||||
|
||||
{/* ── Liste + Bags Sidebar ── */}
|
||||
<div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '10px 12px 16px' }}>
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '10px 0 16px' }}>
|
||||
{items.length === 0 ? (
|
||||
<div style={{ textAlign: 'center', padding: '60px 20px' }}>
|
||||
<Luggage size={40} style={{ color: 'var(--text-faint)', display: 'block', margin: '0 auto 10px' }} />
|
||||
|
||||
Reference in New Issue
Block a user