diff --git a/website/components/founder-portal/founder-chat.tsx b/website/components/founder-portal/founder-chat.tsx
index 1e54c20..1707e08 100644
--- a/website/components/founder-portal/founder-chat.tsx
+++ b/website/components/founder-portal/founder-chat.tsx
@@ -60,6 +60,7 @@ export function FounderChat({ context }: FounderChatProps) {
const activeChannel = CHANNELS.find((c) => c.id === activeChannelId) || CHANNELS[0];
const isRestrictedChannel = activeChannel.requiresOfficer && !isOfficer;
+ const hasExampleMessages = messages.length > 0 && messages.every((m) => m.isExample);
useEffect(() => {
setError(null);
@@ -67,7 +68,7 @@ export function FounderChat({ context }: FounderChatProps) {
if (isDemoFixture) {
const channelMsgs = FIXTURE_CHAT_MESSAGES[activeChannelId] || [];
- setMessages(channelMsgs.map((m) => ({ ...m, clubId: club.id, channelId: activeChannelId })));
+ setMessages(channelMsgs.map((m) => ({ ...m, clubId: club.id, channelId: activeChannelId, isExample: true })));
} else {
unsubscribe = subscribeClubChat(
club.id,
@@ -143,10 +144,17 @@ export function FounderChat({ context }: FounderChatProps) {
Squad Live Chat
-
-
- Live Connected
-
+ {hasExampleMessages ? (
+
+ visibility
+ Example thread
+
+ ) : (
+
+
+ Live Connected
+
+ )}
Real-time squad channels for ride organization, road condition warnings, and administrative officer discussions.
@@ -179,6 +187,16 @@ export function FounderChat({ context }: FounderChatProps) {
+ {hasExampleMessages && (
+
+ info
+
+ These are example messages — this
+ channel has no posts yet. Send a message to start the real conversation.
+
+
+ )}
+
{error && (
diff --git a/website/components/founder-portal/founder-routes.tsx b/website/components/founder-portal/founder-routes.tsx
index 9c5c2b5..d4c0a13 100644
--- a/website/components/founder-portal/founder-routes.tsx
+++ b/website/components/founder-portal/founder-routes.tsx
@@ -178,6 +178,8 @@ export function FounderRoutes({ context }: FounderRoutesProps) {
const canCurate = permissions.canPlanRides || permissions.canManageSettings;
+ const hasExampleRoutes = routes.length > 0 && routes.every((r) => r.isExample);
+
return (
{/* Top Header Card */}
@@ -187,8 +189,14 @@ export function FounderRoutes({ context }: FounderRoutesProps) {
Curated Club Routes
-
- {routes.length} Available
+
+ {hasExampleRoutes ? `${routes.length} Examples` : `${routes.length} Available`}
@@ -207,6 +215,16 @@ export function FounderRoutes({ context }: FounderRoutesProps) {
)}
+ {hasExampleRoutes && (
+
+ info
+
+ These are example routes — your club
+ hasn’t curated any yet. Curate a route to replace them with your own.
+
+
+ )}
+
{error && (
@@ -314,9 +332,16 @@ export function FounderRoutes({ context }: FounderRoutesProps) {
{/* Card Header & Badge */}
-
- {route.title}
-
+
+
+ {route.title}
+
+ {route.isExample && (
+
+ Example
+
+ )}
+
{
if (options?.allowDemoPreview) {
- return FIXTURE_CLUB_ROUTES.map((r) => ({ ...r, clubId }));
+ return FIXTURE_CLUB_ROUTES.map((r) => ({ ...r, clubId, isExample: true }));
}
callerUid();
const routesRef = collection(firestore, 'clubs', clubId, 'routes');
const q = query(routesRef, orderBy('createdAt', 'desc'), limit(50));
const snap = await getDocs(q);
if (snap.empty) {
- // Return sample starter routes if no custom routes have been uploaded
- return FIXTURE_CLUB_ROUTES.map((r) => ({ ...r, clubId }));
+ // No custom routes uploaded yet: surface sample starter routes, explicitly flagged as
+ // examples so the UI can label them and they are never mistaken for real club routes.
+ return FIXTURE_CLUB_ROUTES.map((r) => ({ ...r, clubId, isExample: true }));
}
return snap.docs.map((d) => {
const data = d.data();
@@ -358,6 +361,8 @@ export interface ChatMessage {
authorRole: ClubRole;
content: string;
createdAt: string;
+ /** True when this is MotorSignal sample/example content, not a real posted message. */
+ isExample?: boolean;
}
export const FIXTURE_CHAT_MESSAGES: Record = {
@@ -441,9 +446,10 @@ export function subscribeClubChat(
q,
(snap) => {
if (snap.empty) {
- // Fallback to fixture messages if channel has no messages yet
+ // No messages in this channel yet: surface sample messages, explicitly flagged as
+ // examples so the UI can label them and they are never mistaken for real posts.
const initial = FIXTURE_CHAT_MESSAGES[channelId] || [];
- onMessages(initial.map((m) => ({ ...m, clubId, channelId })));
+ onMessages(initial.map((m) => ({ ...m, clubId, channelId, isExample: true })));
return;
}
const msgs: ChatMessage[] = snap.docs.map((d) => {
diff --git a/website/tests/founder-routes-chat.test.tsx b/website/tests/founder-routes-chat.test.tsx
index 625c276..fbb481b 100644
--- a/website/tests/founder-routes-chat.test.tsx
+++ b/website/tests/founder-routes-chat.test.tsx
@@ -250,6 +250,18 @@ describe('Founder Routes Management', () => {
expect(mocks.deleteRoute).toHaveBeenCalledWith('ClubID', 'r1');
});
});
+
+ // WEB-W-routes regression: fixture fallback routes must be labelled as examples so a
+ // rider can never mistake sample content for the club's real curated routes.
+ it('labels example routes when the club has none of its own', async () => {
+ mocks.fetchRoutes.mockResolvedValue(sampleRoutes.map((r) => ({ ...r, isExample: true })));
+ render();
+ await screen.findByText('Blood Mountain Gap Loop');
+
+ expect(screen.getByText(/example routes/i)).toBeDefined();
+ expect(screen.getByText(/Examples$/)).toBeDefined();
+ expect(screen.getAllByText('Example').length).toBeGreaterThanOrEqual(1);
+ });
});
describe('Founder Chat Management', () => {
@@ -296,6 +308,36 @@ describe('Founder Chat Management', () => {
});
});
+ // WEB-W-chat regression: fixture fallback messages must be labelled as examples and the
+ // header must not claim a live connection when only sample content is shown.
+ it('labels example messages when the channel has no real posts', async () => {
+ mocks.subscribeChat.mockImplementation((_clubId, _channelId, onMessages) => {
+ onMessages([
+ {
+ id: 'example-msg-1',
+ clubId: 'ClubID',
+ channelId: 'general',
+ authorUid: 'owner',
+ authorDisplayName: 'Morgan Vance',
+ authorHandle: '@morgan_v',
+ authorDisplayRank: 'President',
+ authorRole: 'owner',
+ content: 'Welcome to squad chat!',
+ createdAt: '2026-06-01T12:00:00Z',
+ isExample: true,
+ },
+ ]);
+ return vi.fn();
+ });
+
+ render();
+ await screen.findByText('Welcome to squad chat!');
+
+ expect(screen.getByText('Example thread')).toBeDefined();
+ expect(screen.getByText(/example messages/i)).toBeDefined();
+ expect(screen.queryByText('Live Connected')).toBeNull();
+ });
+
it('restricts Officers Lounge channel for non-officers', async () => {
const memberContext: FounderClubContext = {
...mockContext,