feat: replace mobile agent card slider with horizontal swipe carousel and improve messaging header responsiveness

This commit is contained in:
pradeepkumar
2026-04-18 11:32:45 +05:30
parent ff17bab0db
commit 396d68649f
6 changed files with 60 additions and 52 deletions

View File

@@ -33,14 +33,14 @@ export function ConnectionCard({
const isPlaceholder = !avatar || avatar === placeholder;
return (
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 py-4">
<div className="flex flex-row items-center gap-3 sm:gap-4 py-4">
{/* Avatar */}
<div className="flex-shrink-0">
<div className="w-[80px] h-[80px] rounded-full overflow-hidden relative bg-[#e8e8e8]">
<div className="w-[56px] h-[56px] sm:w-[80px] sm:h-[80px] rounded-full overflow-hidden relative bg-[#e8e8e8]">
{/* Shimmer while loading, initials only on error */}
{avatarError || isPlaceholder ? (
<div className="absolute inset-0 rounded-full bg-[#c4d9d4] flex items-center justify-center">
<span className="font-bold text-[#00293d] text-[32px]">{name?.[0]?.toUpperCase() || '?'}</span>
<span className="font-bold text-[#00293d] text-[22px] sm:text-[32px]">{name?.[0]?.toUpperCase() || '?'}</span>
</div>
) : !avatarLoaded ? (
<div className="absolute inset-0 rounded-full shimmer-loading" />
@@ -58,19 +58,15 @@ export function ConnectionCard({
</div>
</div>
{/* Content */}
{/* Content + Message button (stacks on mobile, inline on desktop) */}
<div className="flex-1 min-w-0 flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
<div className="flex-1 min-w-0">
{/* Name */}
<h3 className="font-serif font-bold text-[14px] leading-[19px] text-[#00293D] mb-1">
<h3 className="font-serif font-bold text-[14px] leading-[19px] text-[#00293D] mb-1 truncate">
{name}
</h3>
{/* Email */}
<p className="font-serif font-normal text-[13px] leading-[18px] text-[#00293D]/70 mb-1">
<p className="font-serif font-normal text-[13px] leading-[18px] text-[#00293D]/70 mb-1 truncate">
{email}
</p>
{/* Connected date */}
{connectedAt && (
<p className="font-serif font-normal text-[12px] text-[#00293D]/50">
Connected on {formatDate(connectedAt)}
@@ -78,7 +74,7 @@ export function ConnectionCard({
)}
</div>
{/* Action Buttons */}
{/* Action Button */}
<div className="flex items-center gap-3 flex-shrink-0">
<button
onClick={() => onMessage?.(id)}
@@ -94,5 +90,6 @@ export function ConnectionCard({
</button>
</div>
</div>
</div>
);
}

View File

@@ -106,18 +106,30 @@ export function FeaturesSection({ content }: { content?: FeaturesContent }) {
</div>
)}
{/* Mobile Agent Cards - Tab style with dots */}
{/* Mobile Agent Cards - Horizontal swipe carousel */}
{agents.length > 0 && (
<div className="lg:hidden">
<div className="flex justify-center">
<AgentCard agent={agents[activeCard]} className="shadow-lg" />
<div
className="flex gap-4 overflow-x-auto snap-x snap-mandatory scroll-smooth pb-2 -mx-4 px-4"
onScroll={(e) => {
const el = e.currentTarget;
const cardWidth = el.children[0]?.clientWidth || 254;
const gap = 16;
const idx = Math.round(el.scrollLeft / (cardWidth + gap));
if (idx !== activeCard) setActiveCard(idx);
}}
>
{agents.map((agent, index) => (
<div key={index} className="snap-center flex-shrink-0">
<AgentCard agent={agent} className="shadow-lg" />
</div>
{/* Dot indicators */}
))}
</div>
{/* Dot indicators (visual only — swipe to change) */}
<div className="flex justify-center gap-2 mt-4">
{agents.map((_, index) => (
<button
<span
key={index}
onClick={() => setActiveCard(index)}
className={`rounded-full transition-all ${
index === activeCard
? 'w-[10px] h-[10px] bg-[#00293d]'

View File

@@ -351,8 +351,7 @@ export function MessageInput({ onSend, onSendGif, onSendFile, onTypingStart, onT
{showGifPicker && (
<div
ref={gifPickerRef}
className="absolute bottom-full left-0 mb-2 z-50 bg-white border border-[#00293d]/10 rounded-[15px] shadow-lg overflow-hidden"
style={{ width: '360px', height: '420px' }}
className="absolute bottom-full left-0 mb-2 z-50 bg-white border border-[#00293d]/10 rounded-[15px] shadow-lg overflow-hidden w-[min(360px,calc(100vw-32px))] h-[420px]"
>
<div className="p-3 border-b border-[#00293d]/10">
<input
@@ -368,7 +367,7 @@ export function MessageInput({ onSend, onSendGif, onSendFile, onTypingStart, onT
<Grid
key={gifSearchQuery}
fetchGifs={fetchGifs}
width={356}
width={typeof window !== 'undefined' ? Math.min(356, window.innerWidth - 40) : 356}
columns={2}
gutter={6}
noLink

View File

@@ -665,7 +665,7 @@ export function MessagingPage({ initialConversationId, initialAgentProfileId }:
<div className="border border-[#00293d]/10 rounded-[20px] bg-white overflow-hidden">
{/* Header */}
<div className="flex items-center gap-4 p-4 border-b border-[#00293d]/10">
<div className="flex flex-wrap md:flex-nowrap items-center gap-3 md:gap-4 p-4 border-b border-[#00293d]/10">
<h1 className="font-fractul font-bold text-[18px] leading-[22px] text-[#00293D]">
Messaging
</h1>
@@ -678,8 +678,8 @@ export function MessagingPage({ initialConversationId, initialAgentProfileId }:
/>
</div>
{/* Search bar */}
<div className="flex-1 max-w-[600px]">
{/* Search bar — full width below on mobile, inline on desktop */}
<div className="order-3 md:order-none w-full md:w-auto md:flex-1 md:max-w-[600px]">
<div className="flex items-center gap-2 border border-[#00293d]/10 rounded-[15px] px-4 py-2">
<Image
src="/assets/icons/search-icon.svg"
@@ -692,7 +692,7 @@ export function MessagingPage({ initialConversationId, initialAgentProfileId }:
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search Message"
className="flex-1 font-serif font-normal text-[18px] leading-[24px] text-[#00293D] placeholder-[#00293D]/50 focus:outline-none"
className="flex-1 min-w-0 font-serif font-normal text-[14px] md:text-[18px] leading-[24px] text-[#00293D] placeholder-[#00293D]/50 focus:outline-none"
/>
</div>
</div>

View File

@@ -76,7 +76,7 @@ export function FilterModal({ isOpen, onClose, filters, filterFields, onToggleFi
</svg>
</button>
{expandedSections[field.slug] && (
<div className={`grid ${field.options.length > 6 ? 'grid-cols-3' : 'grid-cols-3'} gap-x-6 gap-y-3`}>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-x-4 md:gap-x-6 gap-y-3">
{field.options.map((option) => (
<label key={option.value} className="flex items-start gap-2 cursor-pointer">
<input

View File

@@ -278,13 +278,13 @@ export function NotificationsForm({ onSave }: NotificationsFormProps) {
</p>
{/* Frequency Tabs */}
<div className="inline-flex items-center border border-[#00293D]/10 rounded-[7px] h-[49px] px-2">
<div className="flex items-center border border-[#00293D]/10 rounded-[7px] h-[49px] px-2 w-full md:w-fit">
{frequencyOptions.map((option, index) => (
<div key={option.value} className="flex items-center h-full">
<div key={option.value} className="flex items-center h-full flex-1 md:flex-initial">
<button
type="button"
onClick={() => setDigestFrequency(option.value)}
className={`px-5 py-1.5 text-[14px] font-fractul transition-colors rounded-[15px] ${
className={`w-full md:w-auto px-3 md:px-5 py-1.5 text-[14px] font-fractul transition-colors rounded-[15px] ${
digestFrequency === option.value
? 'bg-[#e58625] text-[#00293D]'
: 'text-[#00293D] hover:bg-[#00293D]/5'