From b7a6798cad75f2a5d4675b5d3743f708fcfeff21 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Tue, 14 Apr 2026 22:10:12 +0200 Subject: [PATCH] fix(chat): normalize REST messages, full-width layout, and UI cleanup - Normalize snake_case REST API responses to camelCase in useChat so messages are correctly attributed after page reload - Remove max-w-3xl from chat-ui messages and composer for full-width - Remove discussion header/border so chat fills the tablo section - Remove plan badge pills from settings page header - Use org logo in NavigationBar avatar instead of first-letter fallback - Persist plan announcement in localStorage instead of sessionStorage Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/main/src/components/ChatMessages.tsx | 11 +++----- apps/main/src/components/NavigationBar.tsx | 6 +++++ apps/main/src/components/PlanAnnouncement.tsx | 4 +-- .../src/components/TabloDiscussionSection.tsx | 15 ++--------- apps/main/src/hooks/useChat.ts | 26 ++++++++++++++++--- apps/main/src/pages/settings.tsx | 12 --------- apps/main/src/pages/tablo-details.tsx | 2 +- packages/chat-ui/src/components/chat.tsx | 4 +-- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/apps/main/src/components/ChatMessages.tsx b/apps/main/src/components/ChatMessages.tsx index b97d4e6..896e34c 100644 --- a/apps/main/src/components/ChatMessages.tsx +++ b/apps/main/src/components/ChatMessages.tsx @@ -14,9 +14,6 @@ interface ChatMessage { createdAt: string; clientId: string; optimistic?: boolean; - // REST API returns snake_case fields from DB, WS messages use camelCase - user_id?: string; - created_at?: string; } interface Member { @@ -69,16 +66,14 @@ export function ChatMessages({ const chatMessages = useMemo( () => messages.map((msg) => { - const userId = msg.userId || msg.user_id || ""; - const createdAt = msg.createdAt || msg.created_at || ""; - const member = membersById.get(userId); + const member = membersById.get(msg.userId); return { id: msg.id, - senderId: userId, + senderId: msg.userId, senderName: member?.name ?? t("defaultUserName"), senderAvatar: member?.avatar_url ?? undefined, text: msg.text, - timestamp: new Date(createdAt), + timestamp: new Date(msg.createdAt), status: msg.optimistic ? "sending" : undefined, }; }), diff --git a/apps/main/src/components/NavigationBar.tsx b/apps/main/src/components/NavigationBar.tsx index 1195355..adf6cc6 100644 --- a/apps/main/src/components/NavigationBar.tsx +++ b/apps/main/src/components/NavigationBar.tsx @@ -163,6 +163,12 @@ export function UserMenuPopover({ isCollapsed }: { isCollapsed: boolean }) { )} > + {organizationData?.organization?.logo_url && ( + + )} {organizationData?.organization?.name?.charAt(0).toUpperCase() ?? "O"} diff --git a/apps/main/src/components/PlanAnnouncement.tsx b/apps/main/src/components/PlanAnnouncement.tsx index 9e2ed27..b68d05b 100644 --- a/apps/main/src/components/PlanAnnouncement.tsx +++ b/apps/main/src/components/PlanAnnouncement.tsx @@ -32,11 +32,11 @@ export function PlanAnnouncement() { const { active_subscription_plan } = organizationData; if (!active_subscription_plan) return; - const lastAnnouncedPlan = sessionStorage.getItem(PLAN_ANNOUNCED_KEY); + const lastAnnouncedPlan = localStorage.getItem(PLAN_ANNOUNCED_KEY); if (lastAnnouncedPlan === active_subscription_plan) return; hasAnnounced.current = true; - sessionStorage.setItem(PLAN_ANNOUNCED_KEY, active_subscription_plan); + localStorage.setItem(PLAN_ANNOUNCED_KEY, active_subscription_plan); const label = PLAN_LABELS[active_subscription_plan]; if (!label) return; diff --git a/apps/main/src/components/TabloDiscussionSection.tsx b/apps/main/src/components/TabloDiscussionSection.tsx index e223e99..4f9256b 100644 --- a/apps/main/src/components/TabloDiscussionSection.tsx +++ b/apps/main/src/components/TabloDiscussionSection.tsx @@ -1,19 +1,16 @@ import { UserTablo } from "@xtablo/shared/types/tablos.types"; import { useEffect } from "react"; -import { useTranslation } from "react-i18next"; import { useChat } from "../hooks/useChat"; import { useTabloMembers } from "../hooks/tablos"; import { useUser } from "../providers/UserStoreProvider"; import { ChatMessages } from "./ChatMessages"; -import { TabloHeaderActions } from "./TabloHeaderActions"; interface TabloDiscussionSectionProps { tablo: UserTablo; isAdmin: boolean; } -export const TabloDiscussionSection = ({ tablo, isAdmin }: TabloDiscussionSectionProps) => { - const { t } = useTranslation("chat"); +export const TabloDiscussionSection = ({ tablo }: TabloDiscussionSectionProps) => { const user = useUser(); const { messages, @@ -36,15 +33,7 @@ export const TabloDiscussionSection = ({ tablo, isAdmin }: TabloDiscussionSectio return (
-
-
-

{t("discussionTitle")}

-

{t("discussionSubtitle")}

-
- -
- -
+
[...data.messages, ...prev]); + setMessages((prev) => [...normalized, ...prev]); } else { // Initial load - setMessages(data.messages); + setMessages(normalized); } }, [channelId, token]); diff --git a/apps/main/src/pages/settings.tsx b/apps/main/src/pages/settings.tsx index dd23d2a..a21501e 100644 --- a/apps/main/src/pages/settings.tsx +++ b/apps/main/src/pages/settings.tsx @@ -25,7 +25,6 @@ import { TypographyH3, TypographyMuted, TypographySmall } from "@xtablo/ui/compo import { CameraIcon, CookieIcon, Loader2Icon, Trash2Icon, UploadIcon } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Badge } from "@xtablo/ui/components/badge"; import { LanguageSelector } from "../components/LanguageSelector"; import { SubscriptionCard } from "../components/SubscriptionCard"; import { useIntroduction } from "../hooks/intros"; @@ -243,17 +242,6 @@ export default function SettingsPage() { {t("settings:subtitle")}
- {organizationData?.active_subscription_plan === "annual" && ( - - Founder - - )} - {organizationData?.active_subscription_plan === "team" && ( - Teams - )} - {organizationData?.active_subscription_plan === "solo" && ( - Solo - )}
diff --git a/apps/main/src/pages/tablo-details.tsx b/apps/main/src/pages/tablo-details.tsx index eb1121f..a97345a 100644 --- a/apps/main/src/pages/tablo-details.tsx +++ b/apps/main/src/pages/tablo-details.tsx @@ -500,7 +500,7 @@ export const TabloDetailsPage = () => {
{/* ── Tab content ─────────────────────────────────────────────────── */} -
+
{activeSection === "overview" && (() => { const overviewBlocks: Record = { diff --git a/packages/chat-ui/src/components/chat.tsx b/packages/chat-ui/src/components/chat.tsx index 6cd9279..c505f9f 100644 --- a/packages/chat-ui/src/components/chat.tsx +++ b/packages/chat-ui/src/components/chat.tsx @@ -1017,7 +1017,7 @@ function ChatMessages({ role="log" aria-live="polite" > -
+
{items.map((item, i) => { switch (item.type) { case "date": @@ -1286,7 +1286,7 @@ function ChatComposer({ {/* Composer body — frosted glass */}
-
+
{/* Input row */}
{/* + button with attachment popout */}