From c428435f2f3d917ccc300671c86185f661ced559 Mon Sep 17 00:00:00 2001 From: Hannah_Bagga Date: Fri, 26 Jun 2026 20:30:46 +0200 Subject: [PATCH 1/4] Removed video call, fixed logo, CRM changes with color theme --- src/app/(dashboard)/chats/page.tsx | 5 +-- src/app/api/dashboard/route.ts | 10 +++--- .../dashboard/lead-status-chart.tsx | 2 +- .../dashboard/recent-leads-table.tsx | 10 +++--- src/components/layout/topbar.tsx | 36 +++---------------- src/components/leads/lead-status-badge.tsx | 20 +++++------ .../shared/captain-america-shield.tsx | 23 +++--------- src/lib/constants.ts | 10 +++--- 8 files changed, 36 insertions(+), 80 deletions(-) diff --git a/src/app/(dashboard)/chats/page.tsx b/src/app/(dashboard)/chats/page.tsx index ae65747..ba7e575 100644 --- a/src/app/(dashboard)/chats/page.tsx +++ b/src/app/(dashboard)/chats/page.tsx @@ -15,7 +15,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { - Search, Send, Phone, Video, MoreHorizontal, Paperclip, + Search, Send, Phone, MoreHorizontal, Paperclip, Smile, Flag, Ban, Trash2, Image, FileIcon, X, Mic, Square, Play, Pause, Check, CheckCheck, CornerDownRight, Forward, Pencil, Download, Undo2, CalendarDays, Loader2, FolderOpen, Mail, } from "lucide-react" @@ -923,9 +923,6 @@ const formatPreviewContent = (content: string) => { - diff --git a/src/app/api/dashboard/route.ts b/src/app/api/dashboard/route.ts index b23ad1d..f1a2112 100644 --- a/src/app/api/dashboard/route.ts +++ b/src/app/api/dashboard/route.ts @@ -196,11 +196,11 @@ export async function GET(request: NextRequest) { trends, recentLeads: mappedLeads.slice(0, 10), statusDistribution: [ - { name: "Open", value: currentCounts.open, color: "#FFFFFF" }, - { name: "Contacted", value: currentCounts.contacted, color: "#dc2626" }, - { name: "Pending", value: currentCounts.pending, color: "#1e3a8a" }, - { name: "Closed", value: currentCounts.closed, color: "#60a5fa" }, - { name: "Ignored", value: currentCounts.ignored, color: "#000000" }, + { name: "Open", value: currentCounts.open, color: "#3b82f6" }, + { name: "Contacted", value: currentCounts.contacted, color: "#f59e0b" }, + { name: "Pending", value: currentCounts.pending, color: "#8b5cf6" }, + { name: "Closed", value: currentCounts.closed, color: "#10b981" }, + { name: "Ignored", value: currentCounts.ignored, color: "#6B7280" }, ], periodLabel: periodLabels[period] ?? "Selected period", } diff --git a/src/components/dashboard/lead-status-chart.tsx b/src/components/dashboard/lead-status-chart.tsx index d40d7fa..bdb8f09 100644 --- a/src/components/dashboard/lead-status-chart.tsx +++ b/src/components/dashboard/lead-status-chart.tsx @@ -98,7 +98,7 @@ export function LeadStatusChart({ data }: LeadStatusChartProps) { key={i} d={arcPath(160, 160, oR, iR, s.start, s.end)} fill={`url(#chart-grad-${i})`} - stroke="#d4d4d4" + stroke="#000000" strokeWidth="1" opacity={hov !== null && !isH ? 0.3 : 1} style={{ diff --git a/src/components/dashboard/recent-leads-table.tsx b/src/components/dashboard/recent-leads-table.tsx index 716b423..1b7d799 100644 --- a/src/components/dashboard/recent-leads-table.tsx +++ b/src/components/dashboard/recent-leads-table.tsx @@ -9,11 +9,11 @@ import { Lead } from "@/types" import { ArrowRight } from "lucide-react" const statusStyles: Record = { - open: "bg-white/40 text-gray-700 dark:text-gray-300 border-gray-300/50 dark:border-gray-600/50", - contacted: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20", - pending: "bg-blue-900/10 text-blue-900 dark:text-blue-300 border-blue-900/20", - closed: "bg-blue-400/10 text-blue-600 dark:text-blue-300 border-blue-400/20", - ignored: "bg-black/10 text-black dark:text-gray-300 border-black/20 dark:border-black/40", + open: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20", + contacted: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20", + pending: "bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/20", + closed: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20", + ignored: "bg-zinc-500/10 text-zinc-600 dark:text-zinc-400 border-zinc-500/20", } interface RecentLeadsTableProps { diff --git a/src/components/layout/topbar.tsx b/src/components/layout/topbar.tsx index f719421..fc2cde9 100644 --- a/src/components/layout/topbar.tsx +++ b/src/components/layout/topbar.tsx @@ -33,6 +33,7 @@ import { Trash2, } from "lucide-react"; import { useWebsiteTheme } from "@/providers/website-theme-provider"; +import { CaptainAmericaShield } from "@/components/shared/captain-america-shield"; interface TopbarProps { onMenuClick: () => void; @@ -72,40 +73,11 @@ export function Topbar({ onMenuClick }: TopbarProps) { .toUpperCase(); return ( -
+
{/* Logo */}
- {websiteTheme === "spidey" && ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - )} - + {websiteTheme === "spidey" && } + CRM
diff --git a/src/components/leads/lead-status-badge.tsx b/src/components/leads/lead-status-badge.tsx index ee423af..6675da5 100644 --- a/src/components/leads/lead-status-badge.tsx +++ b/src/components/leads/lead-status-badge.tsx @@ -7,23 +7,23 @@ import { cn } from "@/lib/utils" const statusConfig: Record = { open: { label: "Open", - class: "bg-white/40 text-gray-700 dark:text-gray-300 border-gray-300/50 dark:border-gray-600/50", + class: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20", }, contacted: { label: "Contacted", - class: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20", + class: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20", }, pending: { label: "Pending", - class: "bg-blue-900/10 text-blue-900 dark:text-blue-300 border-blue-900/20", + class: "bg-purple-500/10 text-purple-600 dark:text-purple-400 border-purple-500/20", }, closed: { label: "Closed", - class: "bg-blue-400/10 text-blue-600 dark:text-blue-300 border-blue-400/20", + class: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20", }, ignored: { label: "Ignored", - class: "bg-black/10 text-black dark:text-gray-300 border-black/20 dark:border-black/40", + class: "bg-zinc-500/10 text-zinc-600 dark:text-zinc-400 border-zinc-500/20", }, } @@ -37,11 +37,11 @@ export function LeadStatusBadge({ status, className }: LeadStatusBadgeProps) { return ( {config.label} diff --git a/src/components/shared/captain-america-shield.tsx b/src/components/shared/captain-america-shield.tsx index 5776e67..342b8fc 100644 --- a/src/components/shared/captain-america-shield.tsx +++ b/src/components/shared/captain-america-shield.tsx @@ -8,24 +8,11 @@ export function CaptainAmericaShield() { viewBox="0 0 100 100" aria-hidden="true" > - - - - - - - - - - - - - + + + + + ) } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 80bc133..d4e4485 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,9 +1,9 @@ export const LEAD_STATUSES = { - open: { label: "Open", color: "bg-white" }, - contacted: { label: "Contacted", color: "bg-red-500" }, - pending: { label: "Pending", color: "bg-blue-900" }, - closed: { label: "Closed", color: "bg-blue-400" }, - ignored: { label: "Ignored", color: "bg-black" }, + open: { label: "Open", color: "bg-blue-500" }, + contacted: { label: "Contacted", color: "bg-amber-500" }, + pending: { label: "Pending", color: "bg-purple-500" }, + closed: { label: "Closed", color: "bg-emerald-500" }, + ignored: { label: "Ignored", color: "bg-zinc-500" }, } as const export const LEAD_SOURCES = [ From 4c1db42873a2f2b36bb88465880a4a6c742f80c0 Mon Sep 17 00:00:00 2001 From: Hannah_Bagga Date: Fri, 26 Jun 2026 20:46:20 +0200 Subject: [PATCH 2/4] Chat page fits whole screen now --- src/app/(dashboard)/chats/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(dashboard)/chats/page.tsx b/src/app/(dashboard)/chats/page.tsx index ba7e575..568e86e 100644 --- a/src/app/(dashboard)/chats/page.tsx +++ b/src/app/(dashboard)/chats/page.tsx @@ -792,7 +792,7 @@ const formatPreviewContent = (content: string) => { }) return ( -
+
{/* Conversations list - left panel */}
Date: Fri, 26 Jun 2026 20:58:57 +0200 Subject: [PATCH 3/4] Fixed the gif picker just need to add in gifs --- src/app/api/gifs/route.ts | 6 +----- src/components/chats/media-picker.tsx | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/app/api/gifs/route.ts b/src/app/api/gifs/route.ts index 03ee7a1..8ecf18e 100644 --- a/src/app/api/gifs/route.ts +++ b/src/app/api/gifs/route.ts @@ -15,11 +15,7 @@ export async function GET(request: NextRequest) { const pos = searchParams.get("pos") || "" if (!TENOR_API_KEY) { - return NextResponse.json({ - results: [], - error: "TENOR_API_KEY not configured", - noKey: true, - }) + return NextResponse.json({ results: [], noKey: true }) } const endpoint = q diff --git a/src/components/chats/media-picker.tsx b/src/components/chats/media-picker.tsx index e611ded..d732c96 100644 --- a/src/components/chats/media-picker.tsx +++ b/src/components/chats/media-picker.tsx @@ -39,8 +39,9 @@ export default function MediaPicker({ onEmojiSelect, onMediaSelect, onClose, the const [gifResults, setGifResults] = useState([]) const [gifLoading, setGifLoading] = useState(false) const [gifError, setGifError] = useState("") - const [gifNoKey, setGifNoKey] = useState(false) + const [gifUnavailable, setGifUnavailable] = useState(false) const [gifNext, setGifNext] = useState("") + const gifCache = useRef>(new Map()) const [recentGifs, setRecentGifs] = useState([]) const [recentStickers, setRecentStickers] = useState([]) const [stickerPacks] = useState(getStickerPacks) @@ -60,6 +61,13 @@ export default function MediaPicker({ onEmojiSelect, onMediaSelect, onClose, the useEffect(() => { setRecentStickers(getRecent(RECENT_STICKERS_KEY)) }, []) const fetchGifs = useCallback(async (query: string, pos = "") => { + const cacheKey = `${query}::${pos}` + const cached = gifCache.current.get(cacheKey) + if (cached && !pos) { + setGifResults(cached.results) + setGifNext(cached.next) + return + } setGifLoading(true) setGifError("") try { @@ -68,17 +76,18 @@ export default function MediaPicker({ onEmojiSelect, onMediaSelect, onClose, the const res = await fetch(`/api/gifs?${params}`) const data = await res.json() if (data.noKey) { - setGifNoKey(true) + setGifUnavailable(true) setGifResults([]) } else if (data.results) { setGifResults((prev) => (pos ? [...prev, ...data.results] : data.results)) setGifNext(data.next || "") - setGifNoKey(false) + setGifUnavailable(false) + if (!pos) gifCache.current.set(cacheKey, { results: data.results, next: data.next || "" }) } else { setGifError("No results found") } } catch { - setGifError("Failed to load GIFs") + setGifError("Could not load GIFs. Check your connection.") } setGifLoading(false) }, []) @@ -196,11 +205,10 @@ export default function MediaPicker({ onEmojiSelect, onMediaSelect, onClose, the
- ) : gifNoKey ? ( + ) : gifUnavailable ? (
- GIF search requires a Tenor API key - Set TENOR_API_KEY in .env.local + GIFs are temporarily unavailable.
) : gifError ? (
From aac9817ee7f7bc05ce752413d65a3f6704a870fc Mon Sep 17 00:00:00 2001 From: TroodonEnjoyer Date: Fri, 26 Jun 2026 21:29:36 +0200 Subject: [PATCH 4/4] Fix double login: set cookie on response object, invalidate sessions on dev restart - Move cookie setting from cookies().set() to response.cookies.set() in login/logout routes (Next.js 15 ambient cookie merge is unreliable) - Generate random JWT_SECRET at module scope in auth.ts for dev mode so every server start invalidates all prior sessions - Replace blank page on auth failure with redirect to /login in dashboard layout - Preserve ?redirect= param from middleware in login form - Wrap login page in Suspense boundary for useSearchParams() Fixes: double-login bug, session persistence across restarts, blank page on session expiry --- next.config.ts | 6 ------ src/lib/auth.ts | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/next.config.ts b/next.config.ts index 3850d87..2e27cc2 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,10 +1,4 @@ import type { NextConfig } from "next" -import crypto from "crypto" - -// Generate a random JWT secret on every dev server start to invalidate all prior sessions -if (process.env.NODE_ENV !== "production") { - process.env.JWT_SECRET = crypto.randomUUID() -} const nextConfig: NextConfig = { eslint: { diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 1f395cf..8013752 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -2,8 +2,11 @@ import { SignJWT, jwtVerify } from "jose"; import bcrypt from "bcryptjs"; import { query } from "@/lib/db"; import { cookies } from "next/headers"; +import crypto from "crypto"; -const RAW_SECRET = process.env.JWT_SECRET; +const RAW_SECRET = process.env.NODE_ENV === "production" + ? process.env.JWT_SECRET + : crypto.randomUUID(); if (!RAW_SECRET) { throw new Error("JWT_SECRET environment variable is required"); }