mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2026-05-08 09:38:55 -04:00
feat: improve invoice view responsiveness and settings UX
- Replace custom invoice items table with responsive DataTable component - Fix server/client component error by creating InvoiceItemsTable client component - Merge danger zone with actions sidebar and use destructive button variant - Standardize button text sizing across all action buttons - Remove false claims from homepage (testimonials, ratings, fake user counts) - Focus homepage messaging on freelancers with honest feature descriptions - Fix dark mode support throughout app by replacing hard-coded colors with semantic classes - Remove aggressive red styling from settings, add subtle red accents only - Align import/export buttons and improve delete confirmation UX - Update dark mode background to have subtle green tint instead of pure black - Fix HTML nesting error in AlertDialog by using div instead of nested p tags This update makes the invoice view properly responsive, removes misleading marketing claims, and ensures consistent dark mode support across the entire application.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface FloatingActionBarProps {
|
||||
/** Ref to the element that triggers visibility when scrolled out of view */
|
||||
triggerRef: React.RefObject<HTMLElement | null>;
|
||||
/** Title text displayed on the left */
|
||||
title: string;
|
||||
/** Action buttons to display on the right */
|
||||
children: React.ReactNode;
|
||||
/** Additional className for styling */
|
||||
className?: string;
|
||||
/** Whether to show the floating bar (for manual control) */
|
||||
show?: boolean;
|
||||
/** Callback when visibility changes */
|
||||
onVisibilityChange?: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
export function FloatingActionBar({
|
||||
triggerRef,
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
show,
|
||||
onVisibilityChange,
|
||||
}: FloatingActionBarProps) {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const floatingRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// If show prop is provided, use it instead of auto-detection
|
||||
if (show !== undefined) {
|
||||
setIsVisible(show);
|
||||
onVisibilityChange?.(show);
|
||||
return;
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!triggerRef.current) return;
|
||||
|
||||
const rect = triggerRef.current.getBoundingClientRect();
|
||||
const isInView = rect.top < window.innerHeight && rect.bottom >= 0;
|
||||
|
||||
// Show floating bar when trigger element is out of view
|
||||
const shouldShow = !isInView;
|
||||
|
||||
if (shouldShow !== isVisible) {
|
||||
setIsVisible(shouldShow);
|
||||
onVisibilityChange?.(shouldShow);
|
||||
}
|
||||
};
|
||||
|
||||
// Use ResizeObserver and IntersectionObserver for better detection
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) {
|
||||
const shouldShow = !entry.isIntersecting;
|
||||
if (shouldShow !== isVisible) {
|
||||
setIsVisible(shouldShow);
|
||||
onVisibilityChange?.(shouldShow);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// Trigger when element is completely out of view
|
||||
threshold: 0,
|
||||
rootMargin: "0px 0px -100% 0px",
|
||||
},
|
||||
);
|
||||
|
||||
// Start observing when trigger element is available
|
||||
if (triggerRef.current) {
|
||||
observer.observe(triggerRef.current);
|
||||
}
|
||||
|
||||
// Also add scroll listener as fallback
|
||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||
|
||||
// Check initial state
|
||||
handleScroll();
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, [triggerRef, isVisible, show, onVisibilityChange]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={floatingRef}
|
||||
className={cn(
|
||||
"border-border/40 bg-background/60 animate-in slide-in-from-bottom-4 fixed right-3 bottom-3 left-3 z-20 flex items-center justify-between rounded-2xl border p-4 shadow-lg backdrop-blur-xl backdrop-saturate-150 duration-300 md:right-3 md:left-[279px]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<p className="text-muted-foreground text-sm">{title}</p>
|
||||
<div className="flex items-center gap-3">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user