fix: filter panel overlap, Wallet & Stash rename, Sasquatch on all pages
Filter panel (MapPage): - Sheet now h-[82vh] max-h-[680px] with flex-col layout - Scrollable content area uses flex-1 min-h-0 (proper flex children) - "Show Results" button is now flex-shrink-0 at bottom of column - No more overlap between Show Results and Stash/Tour quick action buttons Wallet & Stash rename: - Settings tab renamed from "Wallet" to "Wallet & Stash" - "Me" dropdown in mobile header shows "Wallet & Stash" for the wallet link Sasquatch persistent button (all pages): - Minimized bubble is now ALWAYS visible on every page (not just pages with a guide message) - Tapping the bubble on any page opens Sasquatch with a generic helpful greeting - Pages with specific context (/, /map, /lab, /onboarding) still show their custom message - Pages without context (/wallet, /settings, /stash, /events, etc.) now show a generic "Ask me anything" greeting with common questions - Removed the separate X close button (minimize was already the same) — bubble stays visible as entry point at all times - onPointerUp now also sets isVisible=true so panel opens on pages without a message Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
340f6fd89f
commit
0dc0d272e1
4 changed files with 49 additions and 35 deletions
|
|
@ -204,7 +204,7 @@ function MobileHeaderUserMenu({
|
|||
<DropdownMenuItem asChild>
|
||||
<Link to="/wallet" className="flex items-center gap-2 cursor-pointer">
|
||||
<Wallet className="h-4 w-4 text-orange-400" />
|
||||
<span className="text-orange-400">Wallet</span>
|
||||
<span className="text-orange-400">Wallet & Stash</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
|
|
|
|||
|
|
@ -358,7 +358,9 @@ export function SasquatchGuide({ message, onDismiss }: SasquatchGuideProps) {
|
|||
dragMoved.current = false;
|
||||
setTimeout(() => setIsDragging(false), 50);
|
||||
if (!wasDrag) {
|
||||
// Always open the panel — even on pages without a context message
|
||||
setIsMinimized(false);
|
||||
setIsVisible(true);
|
||||
}
|
||||
}, [setIsMinimized]);
|
||||
|
||||
|
|
@ -576,16 +578,33 @@ Instructions:
|
|||
};
|
||||
}, [handleSendMessage, setIsMinimized]);
|
||||
|
||||
// Show if we have a message prop OR if we've been opened externally
|
||||
const shouldShow = !!message || isVisible;
|
||||
if (!shouldShow) return null;
|
||||
// The minimized bubble is ALWAYS visible so the user can reopen Sasquatch on any page.
|
||||
// The full panel shows only when there's a message or when opened externally/by user tap.
|
||||
const shouldShowPanel = !!message || isVisible;
|
||||
|
||||
const inputDisabled = isThinking;
|
||||
|
||||
// Build a fallback generic message for pages with no specific context
|
||||
const activeMessage = message ?? {
|
||||
id: 'generic',
|
||||
title: 'SkateSpots Assistant 🦧',
|
||||
message: "Yo! I'm The Sasquatch. Ask me anything about spots, events, coaches, gear, or how the app works. I've got live data from the SkateSpots relay!",
|
||||
context: undefined,
|
||||
suggestedQuestions: [
|
||||
'Find spots near me',
|
||||
'Any upcoming events?',
|
||||
'What is Nostr?',
|
||||
'How do zaps work?',
|
||||
'What is My Stash?',
|
||||
'How to add a spot?',
|
||||
],
|
||||
enableChat: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ── Minimized bubble — draggable ── */}
|
||||
{isMinimized && (
|
||||
{/* ── Minimized bubble — always visible, draggable ── */}
|
||||
{(!shouldShowPanel || isMinimized) && (
|
||||
<div
|
||||
ref={dragRef}
|
||||
className={cn(
|
||||
|
|
@ -621,7 +640,7 @@ Instructions:
|
|||
)}
|
||||
|
||||
{/* ── Full panel ── */}
|
||||
{!isMinimized && (
|
||||
{shouldShowPanel && !isMinimized && (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed z-50 transition-all duration-300 ease-out',
|
||||
|
|
@ -665,24 +684,19 @@ Instructions:
|
|||
<CVMStatus connected={cvmConnected} />
|
||||
</div>
|
||||
<div className="text-[10px] text-[#ccff00]/50 leading-none mt-0.5">
|
||||
{message.title}
|
||||
{activeMessage.title}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1 flex-shrink-0">
|
||||
{/* Minimize — keeps the bubble visible */}
|
||||
<button
|
||||
onClick={() => setIsMinimized(true)}
|
||||
className="w-6 h-6 flex items-center justify-center rounded text-[#ccff00]/40 hover:text-[#ccff00] hover:bg-[#ccff00]/10 transition-colors"
|
||||
aria-label="Minimize"
|
||||
aria-label="Minimize to bubble"
|
||||
title="Minimize — bubble stays visible so you can reopen anytime"
|
||||
>
|
||||
<Minimize2 className="h-3 w-3" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsMinimized(true)}
|
||||
className="w-6 h-6 flex items-center justify-center rounded text-[#ccff00]/40 hover:text-[#ff0040] hover:bg-[#ff0040]/10 transition-colors"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -690,21 +704,21 @@ Instructions:
|
|||
<div className="p-3">
|
||||
{/* Greeting */}
|
||||
<p className="text-xs text-gray-300 leading-relaxed mb-3">
|
||||
{message.message}
|
||||
{activeMessage.message}
|
||||
</p>
|
||||
|
||||
{/* Action button */}
|
||||
{message.action && (
|
||||
{activeMessage.action && (
|
||||
<button
|
||||
onClick={message.action.onClick}
|
||||
onClick={activeMessage.action.onClick}
|
||||
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-black bg-[#ccff00] hover:bg-[#d4ff1a] transition-colors mb-3"
|
||||
>
|
||||
{message.action.label}
|
||||
{activeMessage.action.label}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Chat section */}
|
||||
{message.enableChat && (
|
||||
{activeMessage.enableChat && (
|
||||
<div className="border-t border-[#ccff00]/10 pt-3">
|
||||
{!showChat ? (
|
||||
<button
|
||||
|
|
@ -712,21 +726,21 @@ Instructions:
|
|||
className="w-full py-2 px-3 rounded-lg text-xs font-bold text-[#ccff00] border border-[#ccff00]/30 hover:border-[#ccff00]/60 hover:bg-[#ccff00]/5 transition-all flex items-center justify-center gap-1.5"
|
||||
>
|
||||
<HelpCircle className="h-3 w-3" />
|
||||
{message.context === 'lab' && 'Ask about Coaching & Filming'}
|
||||
{message.context === 'map' && 'Ask about Spots, Shops & Gear'}
|
||||
{(message.context === 'home' || message.context === 'onboarding') && 'Ask me anything 🛹'}
|
||||
{!message.context && 'Ask me anything 🛹'}
|
||||
{activeMessage.context === 'lab' && 'Ask about Coaching & Filming'}
|
||||
{activeMessage.context === 'map' && 'Ask about Spots, Shops & Gear'}
|
||||
{(activeMessage.context === 'home' || activeMessage.context === 'onboarding') && 'Ask me anything 🛹'}
|
||||
{(!activeMessage.context || (activeMessage.context !== 'lab' && activeMessage.context !== 'map' && activeMessage.context !== 'home' && activeMessage.context !== 'onboarding')) && 'Ask me anything 🛹'}
|
||||
</button>
|
||||
) : (
|
||||
<div className="space-y-2.5">
|
||||
|
||||
{/* Suggested questions */}
|
||||
{chatMessages.length === 0 && message.suggestedQuestions && (
|
||||
{chatMessages.length === 0 && activeMessage.suggestedQuestions && (
|
||||
<div className="space-y-1">
|
||||
<div className="text-[9px] text-[#ccff00]/50 font-bold uppercase tracking-wider mb-1.5">
|
||||
Quick questions:
|
||||
</div>
|
||||
{message.suggestedQuestions.map((q) => (
|
||||
{activeMessage.suggestedQuestions.map((q) => (
|
||||
<button
|
||||
key={q}
|
||||
onClick={() => handleSendMessage(q)}
|
||||
|
|
|
|||
|
|
@ -682,8 +682,8 @@ export default function MapPage() {
|
|||
)}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="bottom" className="h-[70vh] max-h-[600px] rounded-t-2xl px-4">
|
||||
<SheetHeader className="pb-2">
|
||||
<SheetContent side="bottom" className="h-[82vh] max-h-[680px] rounded-t-2xl px-4 flex flex-col">
|
||||
<SheetHeader className="pb-2 flex-shrink-0">
|
||||
<SheetTitle className="flex items-center justify-between text-base">
|
||||
<span>Filters</span>
|
||||
{hasActiveFilters && (
|
||||
|
|
@ -702,7 +702,7 @@ export default function MapPage() {
|
|||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="space-y-4 overflow-y-auto pb-20 -mx-4 px-4" style={{ maxHeight: 'calc(70vh - 120px)' }}>
|
||||
<div className="space-y-4 overflow-y-auto flex-1 min-h-0 -mx-4 px-4 pb-2 scroll-container">
|
||||
{/* Layer toggles */}
|
||||
<div>
|
||||
<h3 className="text-xs font-bold text-muted-foreground mb-2 uppercase tracking-wide">Show on Map</h3>
|
||||
|
|
@ -858,10 +858,10 @@ export default function MapPage() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Apply button fixed at bottom */}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3 bg-background border-t safe-area-bottom">
|
||||
{/* Show Results button — sticks to bottom, never overlaps content */}
|
||||
<div className="flex-shrink-0 pt-3 pb-2 border-t safe-area-bottom bg-background">
|
||||
<Button
|
||||
className="w-full h-11 font-bold"
|
||||
className="w-full h-12 font-bold text-base"
|
||||
onClick={() => setMobileFiltersOpen(false)}
|
||||
>
|
||||
Show Results
|
||||
|
|
|
|||
|
|
@ -659,9 +659,9 @@ export default function SettingsPage() {
|
|||
<Radio className="h-4 w-4 flex-shrink-0" />
|
||||
<span className="text-[10px] font-semibold whitespace-nowrap">Relays</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="wallet" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[64px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
||||
<TabsTrigger value="wallet" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[72px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
||||
<Wallet className="h-4 w-4 flex-shrink-0" />
|
||||
<span className="text-[10px] font-semibold whitespace-nowrap">Wallet</span>
|
||||
<span className="text-[10px] font-semibold whitespace-nowrap">Wallet & Stash</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="advanced" className="flex-col gap-1 px-3 py-2.5 h-auto min-w-[64px] sm:min-w-0 rounded-lg data-[state=active]:bg-background">
|
||||
<Wrench className="h-4 w-4 flex-shrink-0" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue