frontend: chat/meetings/messages/settings UX fixes

- settings: keep tab nav on its own row so "Developers" no longer wraps
- chat: show the "Veil active" chip once per conversation, not every turn
- chat: record cards only offer "Click for more" when they have detail
- chat: chat-history panel (pill + sheet) top-left of the AI chat with
  "Start new chat"; rename sidebar "New chat" -> "Ask temetro" (Sparkles)
- meetings: disable past dates, add an Upcoming Meetings list, and use the
  Empty component for empty days; scheduler can be pre-targeted via ?with
- messages: add a call button left of each inbox row -> Meetings (?with)
- toast: add a dismiss (x) button; call invites now ring 30s with "Accept"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-20 18:34:48 +03:00
parent 913a217e1d
commit 1d9b1b1d22
11 changed files with 360 additions and 30 deletions
+30 -6
View File
@@ -9,9 +9,10 @@ import {
Plus,
Search,
SendHorizonal,
Video,
X,
} from "lucide-react";
import { useSearchParams } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import {
type ChangeEvent,
type FormEvent,
@@ -146,6 +147,8 @@ export function MessagesView() {
const { data: session } = authClient.useSession();
const myId = session?.user?.id ?? "";
const router = useRouter();
// Deep link from a notification: /messages?conversation=<id>.
const searchParams = useSearchParams();
const deepLinkConversation = searchParams.get("conversation");
@@ -455,16 +458,36 @@ export function MessagesView() {
) : (
visible.map((c) => {
const last = c.lastMessage;
const otherId = c.isGroup
? ""
: (c.participants.find((p) => p.id !== myId)?.id ?? "");
return (
<button
<div
className={cn(
"flex w-full items-center gap-3 rounded-lg px-2 py-2 text-left transition-colors hover:bg-accent/50",
"flex w-full items-center gap-1 rounded-lg pr-2 transition-colors hover:bg-accent/50",
selected?.id === c.id && "bg-accent hover:bg-accent",
)}
key={c.id}
onClick={() => open(c.id)}
type="button"
>
<button
aria-label={t("messages.startCall", { name: c.name })}
className="flex size-9 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-primary/10 hover:text-primary"
onClick={() =>
router.push(
otherId
? `/messages/meetings?with=${encodeURIComponent(otherId)}`
: "/messages/meetings",
)
}
type="button"
>
<Video className="size-4" />
</button>
<button
className="flex min-w-0 flex-1 items-center gap-3 rounded-lg py-2 pr-1 text-left"
onClick={() => open(c.id)}
type="button"
>
<Avatar className="size-9 shrink-0">
<AvatarFallback>{initials(c.name)}</AvatarFallback>
</Avatar>
@@ -510,7 +533,8 @@ export function MessagesView() {
)}
</div>
</div>
</button>
</button>
</div>
);
})
)}