feat(mobile): Handy-Optimierung – Tap-to-place, Bottom-Nav, kompakte Banner (v1.4.12)

- Tap-to-place: Symbol antippen → auf Karte tippen platziert es (Alternative zu Drag&Drop, mobil-tauglich); schwebender Hinweis mit Abbrechen
- Mobile Bottom-Navigation: Karte / Journal(Auswertung) / Symbole ohne Menü erreichbar
- FAB entfernt; Karten-Steuerung (Zoom/Standort/Massstab) hebt sich mobil über die Bottom-Nav
- Touch-Ziele in der Werkzeugleiste auf 44px vergrössert
- "Einsatz/Übung beenden" jetzt auch im Mobile-Overflow-Menü
- Banner (Übung/Offline/E-Mail/Live) mobil kompakt: kleinere Höhe, gekürzte Texte, truncate
- Einsatz-beenden-Bestätigung als In-App-Dialog statt Browser-confirm()

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-19 15:20:27 +02:00
parent 947a757d9b
commit 6e3663cf7f
7 changed files with 161 additions and 55 deletions

View File

@@ -31,6 +31,7 @@ interface TenantSymbolGroup {
interface RightSidebarProps {
onSymbolDrop: (iconId: string, coordinates: [number, number], imageUrl?: string) => void
onSymbolSelect?: (symbol: { id: string; imageUrl: string }) => void
canEdit: boolean
isOpen?: boolean
onToggle?: () => void
@@ -56,9 +57,10 @@ const categoryIcons: Record<string, typeof Flame> = {
'Eigene': Upload,
}
function DraggableSymbol({ symbol, canEdit }: {
function DraggableSymbol({ symbol, canEdit, onSelect }: {
symbol: DisplaySymbol
canEdit: boolean
onSelect?: (symbol: DisplaySymbol) => void
}) {
const [{ isDragging }, drag, preview] = useDrag(() => ({
type: 'SYMBOL',
@@ -77,6 +79,7 @@ function DraggableSymbol({ symbol, canEdit }: {
return (
<div
ref={drag as unknown as React.LegacyRef<HTMLDivElement>}
onClick={() => { if (canEdit && onSelect) onSelect(symbol) }}
className={`
flex flex-col items-center gap-1 p-1.5 md:p-2 lg:p-2.5 rounded-lg border-2 transition-all
border-transparent hover:border-border hover:bg-accent
@@ -84,7 +87,7 @@ function DraggableSymbol({ symbol, canEdit }: {
${isDragging ? 'opacity-0' : ''}
${!canEdit ? 'opacity-50 cursor-not-allowed' : ''}
`}
title={symbol.name}
title={canEdit ? `${symbol.name} — antippen zum Platzieren, oder auf die Karte ziehen` : symbol.name}
>
<div className="w-10 h-10 md:w-11 md:h-11 lg:w-14 lg:h-14 flex items-center justify-center rounded-lg bg-white border border-gray-200 dark:border-gray-600">
<img
@@ -101,7 +104,7 @@ function DraggableSymbol({ symbol, canEdit }: {
)
}
export function RightSidebar({ onSymbolDrop, canEdit, isOpen, onToggle, activeTab, onTabChange, isCollapsed, onToggleCollapse, tenantId, mode }: RightSidebarProps) {
export function RightSidebar({ onSymbolDrop, onSymbolSelect, canEdit, isOpen, onToggle, activeTab, onTabChange, isCollapsed, onToggleCollapse, tenantId, mode }: RightSidebarProps) {
const journalLabel = mode === 'UEBUNG' ? 'Auswertung' : 'Journal'
const [searchQuery, setSearchQuery] = useState('')
const [activeCategory, setActiveCategory] = useState<string>('')
@@ -206,17 +209,6 @@ export function RightSidebar({ onSymbolDrop, canEdit, isOpen, onToggle, activeTa
return (
<>
{/* Mobile toggle button */}
{!isOpen && onToggle && (
<button
onClick={onToggle}
className="md:hidden fixed bottom-4 right-4 z-50 w-14 h-14 bg-primary text-primary-foreground rounded-full shadow-lg flex items-center justify-center active:scale-95 transition-transform"
title="Symbole"
>
<LayoutGrid className="w-6 h-6" />
</button>
)}
{/* Backdrop on mobile */}
{isOpen && onToggle && (
<div className="md:hidden fixed inset-0 bg-black/40 z-40" onClick={onToggle} />
@@ -375,7 +367,7 @@ export function RightSidebar({ onSymbolDrop, canEdit, isOpen, onToggle, activeTa
<div className="px-1.5 pb-1.5">
<div className="grid grid-cols-3 md:grid-cols-2 lg:grid-cols-3 gap-1">
{g.symbols.map(symbol => (
<DraggableSymbol key={symbol.id} symbol={symbol} canEdit={canEdit} />
<DraggableSymbol key={symbol.id} symbol={symbol} canEdit={canEdit} onSelect={onSymbolSelect} />
))}
</div>
</div>
@@ -440,7 +432,7 @@ export function RightSidebar({ onSymbolDrop, canEdit, isOpen, onToggle, activeTa
) : (
<div className="grid grid-cols-3 md:grid-cols-2 lg:grid-cols-3 gap-1">
{currentCategory.symbols.map((symbol) => (
<DraggableSymbol key={symbol.id} symbol={symbol} canEdit={canEdit} />
<DraggableSymbol key={symbol.id} symbol={symbol} canEdit={canEdit} onSelect={onSymbolSelect} />
))}
</div>
)}