2025-12-24 11:51:40 +00:00
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
2025-12-13 02:34:34 +00:00
|
|
|
import { useChatStore } from '../store/chatStore';
|
2025-12-03 21:56:50 +00:00
|
|
|
import { ChatMessageComponent } from './ChatMessage';
|
|
|
|
|
import { useChat } from '../hooks/useChat';
|
2026-02-22 02:46:10 +00:00
|
|
|
import { useWebRTC } from '../hooks/useWebRTC';
|
2025-12-24 11:51:40 +00:00
|
|
|
import { MessageSearch } from './MessageSearch';
|
|
|
|
|
import { TypingIndicator } from './TypingIndicator';
|
2026-02-22 02:46:10 +00:00
|
|
|
import { CallButton } from './CallButton';
|
|
|
|
|
import { IncomingCallModal } from './IncomingCallModal';
|
|
|
|
|
import { ActiveCallBar } from './ActiveCallBar';
|
2026-01-26 13:12:17 +00:00
|
|
|
import {
|
2026-03-06 17:52:08 +00:00
|
|
|
Search,
|
|
|
|
|
X,
|
|
|
|
|
MessageSquare,
|
|
|
|
|
UserPlus,
|
|
|
|
|
Users,
|
2026-01-26 13:12:17 +00:00
|
|
|
} from 'lucide-react';
|
2025-12-24 11:51:40 +00:00
|
|
|
import { Button } from '@/components/ui/button';
|
2026-01-11 02:20:52 +00:00
|
|
|
import { cn } from '@/lib/utils';
|
2026-03-06 17:52:08 +00:00
|
|
|
import { InviteRoomModal } from './InviteRoomModal';
|
|
|
|
|
import { RoomMembersModal } from './RoomMembersModal';
|
2026-01-16 11:31:40 +00:00
|
|
|
import { useUser } from '@/features/auth/hooks/useUser';
|
2026-03-02 18:25:37 +00:00
|
|
|
import { adminService } from '@/services/adminService';
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
interface ChatRoomProps {
|
|
|
|
|
conversationId: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const ChatRoom: React.FC<ChatRoomProps> = ({ conversationId }) => {
|
2026-02-22 02:46:10 +00:00
|
|
|
const { messages, conversations, userId, incomingCall, activeCall } =
|
|
|
|
|
useChatStore();
|
|
|
|
|
const { fetchHistory, sendRawMessage, wsStatus } = useChat();
|
fix: stabilize frontend — 98 TS errors to 0, align API endpoints, optimize bundle
- Fix 98 TypeScript errors across 37 files:
- Service layer double-unwrapping (subscriptionService, distributionService, gearService)
- Self-referencing variables in SearchPageResults
- FeedView/ExploreView .posts→.items alignment
- useQueueSync Zustand subscribe API
- AdminAuditLogsView missing interface fields
- Toast proxy type, interceptor type narrowing
- 22 unused imports/variables removed
- 5 storybook mock data fixes
- Align frontend API calls with backend endpoints:
- Analytics: useAnalyticsView now calls /creator/analytics/dashboard (was /analytics)
- Chat: chatService uses /conversations (was mock data), WS URL from backend token
- Dashboard StatsSection: uses real /dashboard API data (was hardcoded zeros)
- Settings: suppress 2FA toast error when endpoint unavailable
- Fix marketplace products: seed uses 'active' status (was 'published')
- Enrich seed: admin follows all creators (feed has content)
- Optimize bundle: vendor catch-all 793KB→318KB gzip (-60%)
Split into vendor-charts, vendor-emoji, vendor-swagger, vendor-media, etc.
- Clean repo: remove ~100 orphaned screenshots, audit reports, logs from root
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:18:49 +00:00
|
|
|
const { data: _user } = useUser();
|
2026-02-22 02:46:10 +00:00
|
|
|
const webrtc = useWebRTC({ sendMessage: sendRawMessage });
|
2025-12-03 21:56:50 +00:00
|
|
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
2025-12-24 11:51:40 +00:00
|
|
|
const [showSearch, setShowSearch] = useState(false);
|
2026-03-06 17:52:08 +00:00
|
|
|
const [showInviteModal, setShowInviteModal] = useState(false);
|
|
|
|
|
const [showMembersModal, setShowMembersModal] = useState(false);
|
2026-03-02 18:25:37 +00:00
|
|
|
const [webrtcEnabled, setWebrtcEnabled] = useState(true);
|
2026-01-13 18:47:57 +00:00
|
|
|
const [highlightedMessageId, setHighlightedMessageId] = useState<
|
|
|
|
|
string | null
|
|
|
|
|
>(null);
|
2026-02-12 21:59:09 +00:00
|
|
|
const highlightTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
|
|
2026-03-02 18:25:37 +00:00
|
|
|
useEffect(() => {
|
feat: frontend pages and feature modules polish
Update dashboard (stats, recent tracks/activity), discover, distribution,
education, feed, subscription, support, search, settings, live, cloud,
analytics, auth, chat, social, tracks, playlists, presence, upload,
and library manager. Consistent UI patterns and error handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:46:21 +00:00
|
|
|
let cancelled = false;
|
2026-03-02 18:25:37 +00:00
|
|
|
adminService.getClientFeatureFlags().then((flags) => {
|
feat: frontend pages and feature modules polish
Update dashboard (stats, recent tracks/activity), discover, distribution,
education, feed, subscription, support, search, settings, live, cloud,
analytics, auth, chat, social, tracks, playlists, presence, upload,
and library manager. Consistent UI patterns and error handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:46:21 +00:00
|
|
|
if (cancelled) return;
|
2026-03-02 18:25:37 +00:00
|
|
|
const webrtc = flags.find((f) => f.name === 'WEBRTC_CALLS');
|
|
|
|
|
setWebrtcEnabled(webrtc?.enabled ?? true);
|
feat: frontend pages and feature modules polish
Update dashboard (stats, recent tracks/activity), discover, distribution,
education, feed, subscription, support, search, settings, live, cloud,
analytics, auth, chat, social, tracks, playlists, presence, upload,
and library manager. Consistent UI patterns and error handling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:46:21 +00:00
|
|
|
}).catch(() => { if (!cancelled) setWebrtcEnabled(true); });
|
|
|
|
|
return () => { cancelled = true; };
|
2026-03-02 18:25:37 +00:00
|
|
|
}, []);
|
|
|
|
|
|
2026-02-12 21:59:09 +00:00
|
|
|
// Cleanup highlight timeout on unmount
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return () => {
|
|
|
|
|
if (highlightTimeoutRef.current) {
|
|
|
|
|
clearTimeout(highlightTimeoutRef.current);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
const currentMessages = messages[conversationId] || [];
|
2026-01-03 17:48:45 +00:00
|
|
|
const fetchingRef = useRef<{ [key: string]: boolean }>({});
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
useEffect(() => {
|
2026-01-13 18:47:57 +00:00
|
|
|
if (
|
|
|
|
|
conversationId &&
|
|
|
|
|
!messages[conversationId] &&
|
|
|
|
|
!fetchingRef.current[conversationId]
|
|
|
|
|
) {
|
2026-01-03 17:48:45 +00:00
|
|
|
fetchingRef.current[conversationId] = true;
|
|
|
|
|
fetchHistory(conversationId).finally(() => {
|
2026-01-11 02:20:52 +00:00
|
|
|
// Fetch complete
|
2026-01-03 17:48:45 +00:00
|
|
|
});
|
2025-12-03 21:56:50 +00:00
|
|
|
}
|
2026-01-03 17:48:45 +00:00
|
|
|
}, [conversationId, messages[conversationId], fetchHistory]);
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-01-11 02:20:52 +00:00
|
|
|
if (messagesEndRef.current) {
|
|
|
|
|
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
|
|
|
}
|
|
|
|
|
}, [currentMessages.length, conversationId]); // Scroll on new messages or channel switch
|
2025-12-03 21:56:50 +00:00
|
|
|
|
2025-12-24 11:51:40 +00:00
|
|
|
const handleMessageSelect = (messageId: string) => {
|
|
|
|
|
setHighlightedMessageId(messageId);
|
|
|
|
|
const messageElement = document.getElementById(`message-${messageId}`);
|
|
|
|
|
if (messageElement) {
|
|
|
|
|
messageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
2026-02-12 21:59:09 +00:00
|
|
|
if (highlightTimeoutRef.current) {
|
|
|
|
|
clearTimeout(highlightTimeoutRef.current);
|
|
|
|
|
}
|
|
|
|
|
highlightTimeoutRef.current = setTimeout(() => setHighlightedMessageId(null), 3000);
|
2025-12-24 11:51:40 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-22 02:46:10 +00:00
|
|
|
const conversation = conversations.find((c) => c.id === conversationId);
|
|
|
|
|
const isDM =
|
|
|
|
|
conversation?.type === 'direct' && conversation.participants.length === 2;
|
2026-03-06 17:52:08 +00:00
|
|
|
const isGroupRoom = conversation && conversation.type !== 'direct';
|
2026-02-22 02:46:10 +00:00
|
|
|
const targetUserId =
|
|
|
|
|
isDM && userId
|
|
|
|
|
? conversation.participants.find((p) => p !== userId) ?? null
|
|
|
|
|
: null;
|
|
|
|
|
const remoteUserName =
|
|
|
|
|
conversation?.name && conversation.name !== 'direct'
|
|
|
|
|
? conversation.name
|
|
|
|
|
: 'Utilisateur';
|
|
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
if (!conversationId) {
|
|
|
|
|
return (
|
2026-02-09 22:23:09 +00:00
|
|
|
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground space-y-4 animate-empty-state-in">
|
|
|
|
|
<div className="w-24 h-24 rounded-full bg-muted flex items-center justify-center">
|
|
|
|
|
<MessageSquare className="w-10 h-10 text-muted-foreground" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<p className="text-sm font-medium text-foreground mb-1">
|
|
|
|
|
No conversation selected
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Pick a channel from the sidebar to start chatting.
|
|
|
|
|
</p>
|
2026-01-11 02:20:52 +00:00
|
|
|
</div>
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2026-02-22 02:46:10 +00:00
|
|
|
<div className="flex-1 flex flex-col h-full overflow-hidden relative">
|
|
|
|
|
<IncomingCallModal
|
|
|
|
|
open={!!incomingCall}
|
|
|
|
|
callerName={remoteUserName}
|
|
|
|
|
onAccept={() =>
|
|
|
|
|
incomingCall &&
|
|
|
|
|
webrtc.acceptCall(
|
|
|
|
|
incomingCall.conversationId,
|
|
|
|
|
incomingCall.callerUserId,
|
|
|
|
|
incomingCall.sdp,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
onReject={() =>
|
|
|
|
|
incomingCall &&
|
|
|
|
|
webrtc.rejectCall(
|
|
|
|
|
incomingCall.conversationId,
|
|
|
|
|
incomingCall.callerUserId,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
{activeCall && (
|
|
|
|
|
<div className="absolute bottom-0 left-0 right-0 z-30">
|
|
|
|
|
<ActiveCallBar
|
|
|
|
|
remoteUserName={remoteUserName}
|
|
|
|
|
isMuted={webrtc.isMuted}
|
|
|
|
|
onToggleMute={webrtc.toggleMute}
|
|
|
|
|
onHangup={() =>
|
|
|
|
|
webrtc.hangup(activeCall.conversationId, activeCall.remoteUserId)
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-11 02:20:52 +00:00
|
|
|
{/* Search Header Overlay */}
|
2026-01-13 18:47:57 +00:00
|
|
|
<div
|
|
|
|
|
className={cn(
|
2026-02-12 01:09:29 +00:00
|
|
|
'absolute top-0 left-0 right-0 z-20 px-4 py-2 transition-all duration-[var(--sumi-duration-normal)]',
|
2026-01-13 18:47:57 +00:00
|
|
|
showSearch
|
feat(ui): semantic tokens on library, chat, dashboard, search
PlaylistDetailView: hero border, overlay, sort buttons, table header, row hover → border-border, bg-background/50, hover:bg-muted/50
ChatMessage: action buttons hover, own/other bubbles, attachment preview, context menu, modal → muted/border/foreground
ChatRoom: header bar, channel item hover, input pill → bg-card/90 border-border, hover:bg-muted/50, bg-muted/30
TrackList: play icon and title when not current → text-foreground
SearchPageHeader: title, search container, input, clear button → text-foreground, bg-card/80 border-border, hover:bg-muted/50
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 08:53:17 +00:00
|
|
|
? 'bg-card/90 backdrop-blur-md border-b border-border'
|
2026-01-13 18:47:57 +00:00
|
|
|
: 'bg-transparent pointer-events-none',
|
|
|
|
|
)}
|
|
|
|
|
>
|
2025-12-24 11:51:40 +00:00
|
|
|
{showSearch ? (
|
2026-01-11 02:20:52 +00:00
|
|
|
<div className="flex items-center gap-2 max-w-2xl mx-auto">
|
2025-12-24 11:51:40 +00:00
|
|
|
<div className="flex-1">
|
|
|
|
|
<MessageSearch
|
|
|
|
|
conversationId={conversationId}
|
|
|
|
|
onMessageSelect={handleMessageSelect}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowSearch(false)}
|
feat(ui): semantic tokens on library, chat, dashboard, search
PlaylistDetailView: hero border, overlay, sort buttons, table header, row hover → border-border, bg-background/50, hover:bg-muted/50
ChatMessage: action buttons hover, own/other bubbles, attachment preview, context menu, modal → muted/border/foreground
ChatRoom: header bar, channel item hover, input pill → bg-card/90 border-border, hover:bg-muted/50, bg-muted/30
TrackList: play icon and title when not current → text-foreground
SearchPageHeader: title, search container, input, clear button → text-foreground, bg-card/80 border-border, hover:bg-muted/50
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 08:53:17 +00:00
|
|
|
className="hover:bg-muted/50"
|
2025-12-24 11:51:40 +00:00
|
|
|
>
|
|
|
|
|
<X className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
2026-02-22 02:46:10 +00:00
|
|
|
<div className="flex justify-end items-center gap-2 pointer-events-auto">
|
2026-03-06 17:52:08 +00:00
|
|
|
{isGroupRoom && (
|
|
|
|
|
<>
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowInviteModal(true)}
|
fix: stabilize builds, tests, and lint across all stacks
Complete stabilization pass bringing all 3 stacks to green:
Frontend (apps/web/):
- Fix TypeScript nullability in useSeason.ts, useTimeOfDay.ts hooks
- Disable no-undef in ESLint config (TypeScript handles it; JSX misidentified)
- Rename 306 story imports from @storybook/react to @storybook/react-vite
- Fix conditional hook call in useMediaQuery.ts useIsTablet
- Move useQuery to top of LoginPage.tsx component
- Remove useless try/catch in GearFormModal.tsx
- Fix stale closure in ResetPasswordPage.tsx handleChange
- Make Storybook decorators (withRouter, withQueryClient, withToast, withAudio)
no-ops since global StorybookDecorator already provides these — prevents
nested Router / duplicate provider crashes in vitest-browser
- Fix nested MemoryRouter in 3 page stories (TrackDetail, PlaylistDetail, UserProfile)
- Update i18n initialization in test setup (await init before changeLanguage)
- Update ~30 test assertions from English to French to match i18n translations
- Update test assertions to match SUMI V3 design changes (shadow vs border)
- Fix remaining story type errors (PlayerError, PlaylistBatchActions,
TrackFilters, VirtualizedChatMessages)
Backend (veza-backend-api/):
- Fix response_test.go RespondWithAppError signature (2 args, not 3)
- Fix TestErrorContractAuthEndpoints expected error codes
(ErrCodeUnauthorized vs ErrCodeInvalidCredentials)
- Fix TestTrackHandler_GetTrackLikes_Success missing auth middleware setup
- Fix TestPlaybackAnalyticsService_GetTrackStats k-anonymity threshold
(needs 5 unique users, not 1)
- Replace NOW() PostgreSQL function with time.Now() parameter in marketplace
service for SQLite test compatibility
- Add missing AutoMigrate entries in marketplace_test.go
(ProductImage, ProductPreview, ProductLicense, ProductReview)
Results:
- Frontend TypeCheck: 617 errors -> 0 errors
- Frontend ESLint: 349 errors -> 0 errors
- Frontend Vitest: 196 failing tests -> 1 skipped (3396/3397 passing)
- Backend go vet: 1 error -> 0 errors
- Backend tests: 5 failing -> all 13 packages passing
- Rust: 150/150 tests passing (unchanged)
- Storybook audit: 0 errors across 1244 stories
Triage report: docs/TRIAGE_REPORT.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:48:07 +00:00
|
|
|
className="text-muted-foreground/50 hover:text-foreground hover:bg-muted/50 bg-muted/30 backdrop-blur-sm rounded-full h-8 px-4 shadow-[0_0_8px_rgba(26,26,30,0.05)]"
|
2026-03-06 17:52:08 +00:00
|
|
|
>
|
|
|
|
|
<UserPlus className="h-3 w-3 mr-2" />
|
|
|
|
|
<span className="text-xs font-mono uppercase">Invite</span>
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowMembersModal(true)}
|
fix: stabilize builds, tests, and lint across all stacks
Complete stabilization pass bringing all 3 stacks to green:
Frontend (apps/web/):
- Fix TypeScript nullability in useSeason.ts, useTimeOfDay.ts hooks
- Disable no-undef in ESLint config (TypeScript handles it; JSX misidentified)
- Rename 306 story imports from @storybook/react to @storybook/react-vite
- Fix conditional hook call in useMediaQuery.ts useIsTablet
- Move useQuery to top of LoginPage.tsx component
- Remove useless try/catch in GearFormModal.tsx
- Fix stale closure in ResetPasswordPage.tsx handleChange
- Make Storybook decorators (withRouter, withQueryClient, withToast, withAudio)
no-ops since global StorybookDecorator already provides these — prevents
nested Router / duplicate provider crashes in vitest-browser
- Fix nested MemoryRouter in 3 page stories (TrackDetail, PlaylistDetail, UserProfile)
- Update i18n initialization in test setup (await init before changeLanguage)
- Update ~30 test assertions from English to French to match i18n translations
- Update test assertions to match SUMI V3 design changes (shadow vs border)
- Fix remaining story type errors (PlayerError, PlaylistBatchActions,
TrackFilters, VirtualizedChatMessages)
Backend (veza-backend-api/):
- Fix response_test.go RespondWithAppError signature (2 args, not 3)
- Fix TestErrorContractAuthEndpoints expected error codes
(ErrCodeUnauthorized vs ErrCodeInvalidCredentials)
- Fix TestTrackHandler_GetTrackLikes_Success missing auth middleware setup
- Fix TestPlaybackAnalyticsService_GetTrackStats k-anonymity threshold
(needs 5 unique users, not 1)
- Replace NOW() PostgreSQL function with time.Now() parameter in marketplace
service for SQLite test compatibility
- Add missing AutoMigrate entries in marketplace_test.go
(ProductImage, ProductPreview, ProductLicense, ProductReview)
Results:
- Frontend TypeCheck: 617 errors -> 0 errors
- Frontend ESLint: 349 errors -> 0 errors
- Frontend Vitest: 196 failing tests -> 1 skipped (3396/3397 passing)
- Backend go vet: 1 error -> 0 errors
- Backend tests: 5 failing -> all 13 packages passing
- Rust: 150/150 tests passing (unchanged)
- Storybook audit: 0 errors across 1244 stories
Triage report: docs/TRIAGE_REPORT.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:48:07 +00:00
|
|
|
className="text-muted-foreground/50 hover:text-foreground hover:bg-muted/50 bg-muted/30 backdrop-blur-sm rounded-full h-8 px-4 shadow-[0_0_8px_rgba(26,26,30,0.05)]"
|
2026-03-06 17:52:08 +00:00
|
|
|
>
|
|
|
|
|
<Users className="h-3 w-3 mr-2" />
|
|
|
|
|
<span className="text-xs font-mono uppercase">Members</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2026-03-02 18:25:37 +00:00
|
|
|
{isDM && targetUserId && webrtcEnabled && (
|
2026-02-22 02:46:10 +00:00
|
|
|
<CallButton
|
|
|
|
|
conversationId={conversationId}
|
|
|
|
|
targetUserId={targetUserId}
|
|
|
|
|
onCall={() =>
|
|
|
|
|
webrtc.startCall(conversationId, targetUserId, 'audio')
|
|
|
|
|
}
|
|
|
|
|
disabled={wsStatus !== 'connected'}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-12-24 11:51:40 +00:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowSearch(true)}
|
fix: stabilize builds, tests, and lint across all stacks
Complete stabilization pass bringing all 3 stacks to green:
Frontend (apps/web/):
- Fix TypeScript nullability in useSeason.ts, useTimeOfDay.ts hooks
- Disable no-undef in ESLint config (TypeScript handles it; JSX misidentified)
- Rename 306 story imports from @storybook/react to @storybook/react-vite
- Fix conditional hook call in useMediaQuery.ts useIsTablet
- Move useQuery to top of LoginPage.tsx component
- Remove useless try/catch in GearFormModal.tsx
- Fix stale closure in ResetPasswordPage.tsx handleChange
- Make Storybook decorators (withRouter, withQueryClient, withToast, withAudio)
no-ops since global StorybookDecorator already provides these — prevents
nested Router / duplicate provider crashes in vitest-browser
- Fix nested MemoryRouter in 3 page stories (TrackDetail, PlaylistDetail, UserProfile)
- Update i18n initialization in test setup (await init before changeLanguage)
- Update ~30 test assertions from English to French to match i18n translations
- Update test assertions to match SUMI V3 design changes (shadow vs border)
- Fix remaining story type errors (PlayerError, PlaylistBatchActions,
TrackFilters, VirtualizedChatMessages)
Backend (veza-backend-api/):
- Fix response_test.go RespondWithAppError signature (2 args, not 3)
- Fix TestErrorContractAuthEndpoints expected error codes
(ErrCodeUnauthorized vs ErrCodeInvalidCredentials)
- Fix TestTrackHandler_GetTrackLikes_Success missing auth middleware setup
- Fix TestPlaybackAnalyticsService_GetTrackStats k-anonymity threshold
(needs 5 unique users, not 1)
- Replace NOW() PostgreSQL function with time.Now() parameter in marketplace
service for SQLite test compatibility
- Add missing AutoMigrate entries in marketplace_test.go
(ProductImage, ProductPreview, ProductLicense, ProductReview)
Results:
- Frontend TypeCheck: 617 errors -> 0 errors
- Frontend ESLint: 349 errors -> 0 errors
- Frontend Vitest: 196 failing tests -> 1 skipped (3396/3397 passing)
- Backend go vet: 1 error -> 0 errors
- Backend tests: 5 failing -> all 13 packages passing
- Rust: 150/150 tests passing (unchanged)
- Storybook audit: 0 errors across 1244 stories
Triage report: docs/TRIAGE_REPORT.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:48:07 +00:00
|
|
|
className="text-muted-foreground/50 hover:text-foreground hover:bg-muted/50 bg-muted/30 backdrop-blur-sm rounded-full h-8 px-4 shadow-[0_0_8px_rgba(26,26,30,0.05)]"
|
2025-12-24 11:51:40 +00:00
|
|
|
>
|
2026-01-11 02:20:52 +00:00
|
|
|
<Search className="h-3 w-3 mr-2" />
|
|
|
|
|
<span className="text-xs font-mono uppercase">Search Log</span>
|
2025-12-24 11:51:40 +00:00
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-11 02:20:52 +00:00
|
|
|
<div className="flex-1 overflow-y-auto custom-scrollbar p-6 space-y-4 scroll-smooth">
|
|
|
|
|
{/* Welcome Message for Empty Room */}
|
|
|
|
|
{currentMessages.length === 0 && (
|
2026-02-10 13:06:30 +00:00
|
|
|
<div className="flex flex-col items-center justify-center h-layout-lyrics-sm text-center space-y-4 animate-empty-state-in">
|
2026-02-09 22:23:09 +00:00
|
|
|
<div className="w-14 h-14 rounded-full bg-muted flex items-center justify-center">
|
|
|
|
|
<MessageSquare className="w-7 h-7 text-muted-foreground" />
|
2026-01-11 02:20:52 +00:00
|
|
|
</div>
|
|
|
|
|
<div>
|
2026-02-09 22:23:09 +00:00
|
|
|
<p className="text-foreground font-medium">No messages yet</p>
|
2026-02-08 23:13:27 +00:00
|
|
|
<p className="text-sm text-muted-foreground mt-1">
|
2026-02-09 22:23:09 +00:00
|
|
|
Send the first message to start the conversation.
|
2026-01-13 18:47:57 +00:00
|
|
|
</p>
|
2026-01-11 02:20:52 +00:00
|
|
|
</div>
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
2026-01-11 02:20:52 +00:00
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Message Stream */}
|
2026-02-12 23:32:08 +00:00
|
|
|
{currentMessages.map((msg) => {
|
2026-01-11 02:20:52 +00:00
|
|
|
return (
|
2025-12-24 11:51:40 +00:00
|
|
|
<div
|
|
|
|
|
key={msg.id}
|
|
|
|
|
id={`message-${msg.id}`}
|
2026-01-11 02:20:52 +00:00
|
|
|
className={cn(
|
2026-02-12 01:09:29 +00:00
|
|
|
'transition-all duration-[var(--sumi-duration-slow)] animate-slideUp',
|
2026-01-13 18:47:57 +00:00
|
|
|
highlightedMessageId === msg.id &&
|
refactor: Phase 3a — Global color class migration to SUMI semantics
- Replace all kodo-* color classes across ~100 TSX files:
kodo-void → background, kodo-ink → card, kodo-graphite → muted,
kodo-steel → muted-foreground, kodo-cyan → primary, kodo-magenta → destructive,
kodo-lime → success, kodo-red → destructive, kodo-gold → warning
- Replace cyan-500, magenta-500, lime-500 default Tailwind colors with
semantic equivalents (primary, destructive, success)
- Fix WaveformVisualizer hardcoded hex colors to SUMI values
- Delete global-effects.css (conflicting, redundant with index.css)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 00:51:49 +00:00
|
|
|
'bg-muted/10 rounded-xl -mx-4 px-4 py-2 ring-1 ring-border/30',
|
2026-01-11 02:20:52 +00:00
|
|
|
)}
|
2025-12-24 11:51:40 +00:00
|
|
|
>
|
|
|
|
|
<ChatMessageComponent message={msg} />
|
|
|
|
|
</div>
|
2026-01-11 02:20:52 +00:00
|
|
|
);
|
|
|
|
|
})}
|
2026-01-13 18:47:57 +00:00
|
|
|
|
2025-12-24 11:51:40 +00:00
|
|
|
<TypingIndicator conversationId={conversationId} />
|
2026-01-11 02:20:52 +00:00
|
|
|
<div ref={messagesEndRef} className="h-4" />
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
2026-03-06 17:52:08 +00:00
|
|
|
|
|
|
|
|
{isGroupRoom && (
|
|
|
|
|
<>
|
|
|
|
|
<InviteRoomModal
|
|
|
|
|
open={showInviteModal}
|
|
|
|
|
onOpenChange={setShowInviteModal}
|
|
|
|
|
conversationId={conversationId}
|
|
|
|
|
/>
|
|
|
|
|
<RoomMembersModal
|
|
|
|
|
open={showMembersModal}
|
|
|
|
|
onOpenChange={setShowMembersModal}
|
|
|
|
|
conversationId={conversationId}
|
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2025-12-03 21:56:50 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-13 18:47:57 +00:00
|
|
|
};
|