From b948c19bf181b69009d4721613593fe494f518b9 Mon Sep 17 00:00:00 2001 From: cj-vana Date: Mon, 29 Sep 2025 15:46:55 -0400 Subject: [PATCH 1/2] feat: Add trusted by section with live user count to landing page Add trust-building section displaying live user statistics from Supabase auth. - Create TrustedBy component with animated user counter and use case cards - Add fetchUserCount() function to query Supabase for total registered users - Create get_total_user_count() RPC migration to access auth.users table - Position TrustedBy section between Features and GetStarted on landing page - Update README.md with "Trusted Worldwide" section - Display four use case categories: Production Companies, Corporate Events, Broadcast & Theater, and Freelancers - Implement smooth counter animation for visual engagement - Handle errors gracefully with fallback display --- README.md | 4 + apps/web/src/components/TrustedBy.tsx | 130 ++++++++++++++++++ apps/web/src/lib/supabase.ts | 21 +++ apps/web/src/pages/Landing.tsx | 2 + ...000000_create_get_total_user_count_rpc.sql | 15 ++ 5 files changed, 172 insertions(+) create mode 100644 apps/web/src/components/TrustedBy.tsx create mode 100644 supabase/migrations/20250929000000_create_get_total_user_count_rpc.sql diff --git a/README.md b/README.md index c4adf5e..1902f08 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ SoundDocs is provided free of charge and is open source under the AGPL v3 licens ![SoundDocs Hero Preview](https://i.postimg.cc/bJdc5Hmz/Screenshot-2025-06-05-at-19-21-01.png) +## 🌍 Trusted Worldwide + +SoundDocs is trusted by production companies, freelancers, and event professionals around the globe. From national tours and corporate events to theatrical productions and broadcast operations, professionals rely on SoundDocs to streamline their documentation workflow and deliver exceptional results. + ## ✨ Core Features SoundDocs empowers you to manage event audio documentation efficiently: diff --git a/apps/web/src/components/TrustedBy.tsx b/apps/web/src/components/TrustedBy.tsx new file mode 100644 index 0000000..df53e3c --- /dev/null +++ b/apps/web/src/components/TrustedBy.tsx @@ -0,0 +1,130 @@ +import React, { useEffect, useState } from "react"; +import { Users, Building2, Briefcase, Radio, Globe } from "lucide-react"; +import { fetchUserCount } from "../lib/supabase"; + +const TrustedBy: React.FC = () => { + const [userCount, setUserCount] = useState(null); + const [displayCount, setDisplayCount] = useState(0); + + useEffect(() => { + const loadUserCount = async () => { + const count = await fetchUserCount(); + if (count !== null) { + setUserCount(count); + } + }; + + loadUserCount(); + }, []); + + // Animated counter effect + useEffect(() => { + if (userCount === null) return; + + const duration = 2000; // 2 seconds + const steps = 60; + const increment = userCount / steps; + let current = 0; + + const timer = setInterval(() => { + current += increment; + if (current >= userCount) { + setDisplayCount(userCount); + clearInterval(timer); + } else { + setDisplayCount(Math.floor(current)); + } + }, duration / steps); + + return () => clearInterval(timer); + }, [userCount]); + + const formatNumber = (num: number) => { + return num.toLocaleString(); + }; + + return ( +
+
+
+

+ Trusted by Production Companies & Event Professionals{" "} + Worldwide +

+

+ Join thousands of professionals who rely on SoundDocs for their event documentation + needs +

+
+ + {/* User Count Stat */} +
+
+
+ +
+
+ {userCount !== null ? `${formatNumber(displayCount)}+` : "Loading..."} +
+

+ Event Professionals and Production Companies Worldwide +

+
+
+ + {/* Use Cases Grid */} +
+
+
+
+ +
+
+

Production Companies

+

+ National tours, regional events, and multi-venue productions +

+
+ +
+
+
+ +
+
+

Corporate Events

+

+ Conferences, trade shows, and corporate presentations +

+
+ +
+
+
+ +
+
+

Broadcast & Theater

+

+ Live broadcasts, theatrical productions, and studio sessions +

+
+ +
+
+
+ +
+
+

Freelancers

+

+ Independent engineers, designers, and production professionals +

+
+
+
+
+ ); +}; + +export default TrustedBy; diff --git a/apps/web/src/lib/supabase.ts b/apps/web/src/lib/supabase.ts index 9bf8dca..4a1c2d0 100644 --- a/apps/web/src/lib/supabase.ts +++ b/apps/web/src/lib/supabase.ts @@ -137,3 +137,24 @@ export const savePixelMap = async (payload: PixelMapPayload) => { throw error; } }; + +/** + * Fetches the total count of registered users by counting unique users who created documents. + * @returns The total number of unique users or null if query fails. + */ +export const fetchUserCount = async (): Promise => { + try { + // Use RPC function to count unique users across all document types + const { data, error } = await supabase.rpc("get_total_user_count"); + + if (error) { + console.error("Error fetching user count:", error); + return null; + } + + return data; + } catch (err) { + console.error("Error fetching user count:", err); + return null; + } +}; diff --git a/apps/web/src/pages/Landing.tsx b/apps/web/src/pages/Landing.tsx index 30b349d..681ae5e 100644 --- a/apps/web/src/pages/Landing.tsx +++ b/apps/web/src/pages/Landing.tsx @@ -3,6 +3,7 @@ import { Helmet } from "react-helmet"; import Header from "../components/Header"; import Hero from "../components/Hero"; import Features from "../components/Features"; +import TrustedBy from "../components/TrustedBy"; import GetStarted from "../components/GetStarted"; import Footer from "../components/Footer"; @@ -29,6 +30,7 @@ const Landing: React.FC = () => {
+