feat(dashboard): close legacy parity gaps
This commit is contained in:
1 parent
92287c0a9a
commit
4b3f00729f
21 files changed
+397
-28
No files matched your search
@@ -3,13 +3,21 @@ import type { ActiveChat } from '../../../../../packages/api-contracts/src/index
|
||||
import { ConversationDetail } from './conversation-detail.js';
|
||||
import { useConversationsQuery } from './conversation-queries.js';
|
||||
import { StartConversationForm } from './start-conversation-form.js';
|
||||
import { ProcessedUserLookup } from './processed-user-lookup.js';
|
||||
import './conversations.css';
|
||||
|
||||
type ConversationFilter = 'all' | 'unread' | 'awaiting-reply';
|
||||
export type ConversationFilter = 'all' | 'unread' | 'awaiting-reply';
|
||||
|
||||
export interface ConversationFilters {
|
||||
search: string;
|
||||
filter: ConversationFilter;
|
||||
}
|
||||
|
||||
export interface ConversationsPageProps {
|
||||
selectedConversationId?: string | null;
|
||||
onSelectedConversationChange?(conversationId: string | null): void;
|
||||
filters?: ConversationFilters;
|
||||
onFiltersChange?(filters: ConversationFilters): void;
|
||||
}
|
||||
|
||||
function conversationId(conversation: ActiveChat): string {
|
||||
@@ -44,23 +52,28 @@ function ConversationListItem({ conversation, isSelected, onSelect }: {
|
||||
);
|
||||
}
|
||||
|
||||
export function ConversationsPage({ selectedConversationId, onSelectedConversationChange }: ConversationsPageProps) {
|
||||
export function ConversationsPage({ selectedConversationId, onSelectedConversationChange, filters, onFiltersChange }: ConversationsPageProps) {
|
||||
const conversationsQuery = useConversationsQuery();
|
||||
const [internalSelectedId, setInternalSelectedId] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState('');
|
||||
const [filter, setFilter] = useState<ConversationFilter>('all');
|
||||
const [localFilters, setLocalFilters] = useState<ConversationFilters>({ search: '', filter: 'all' });
|
||||
const [isStartingConversation, setIsStartingConversation] = useState(false);
|
||||
const activeConversationId = selectedConversationId !== undefined ? selectedConversationId : internalSelectedId;
|
||||
const activeFilters = filters ?? localFilters;
|
||||
|
||||
function updateFilters(next: ConversationFilters) {
|
||||
if (filters === undefined) setLocalFilters(next);
|
||||
onFiltersChange?.(next);
|
||||
}
|
||||
|
||||
const visibleConversations = useMemo(() => {
|
||||
const normalizedSearch = search.trim().toLowerCase();
|
||||
const normalizedSearch = activeFilters.search.trim().toLowerCase();
|
||||
return (conversationsQuery.data ?? []).filter((conversation) => {
|
||||
const matchesSearch = normalizedSearch.length === 0
|
||||
|| conversation.username.toLowerCase().includes(normalizedSearch)
|
||||
|| conversation.lastMessageSnippet.toLowerCase().includes(normalizedSearch);
|
||||
return matchesSearch && matchesFilter(conversation, filter);
|
||||
return matchesSearch && matchesFilter(conversation, activeFilters.filter);
|
||||
});
|
||||
}, [conversationsQuery.data, filter, search]);
|
||||
}, [activeFilters, conversationsQuery.data]);
|
||||
|
||||
function selectConversation(id: string | null) {
|
||||
if (selectedConversationId === undefined) setInternalSelectedId(id);
|
||||
@@ -86,6 +99,7 @@ export function ConversationsPage({ selectedConversationId, onSelectedConversati
|
||||
<StartConversationForm onStarted={(id) => selectConversation(id)} />
|
||||
</section>
|
||||
) : null}
|
||||
<ProcessedUserLookup />
|
||||
|
||||
<div className="conversation-workspace">
|
||||
<section aria-label="Conversation inbox" className="conversation-master">
|
||||
@@ -93,16 +107,16 @@ export function ConversationsPage({ selectedConversationId, onSelectedConversati
|
||||
<label className="sr-only" htmlFor="conversation-search">Search conversations</label>
|
||||
<input
|
||||
id="conversation-search"
|
||||
onChange={(event) => setSearch(event.target.value)}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, search: event.target.value })}
|
||||
placeholder="Search conversations"
|
||||
type="search"
|
||||
value={search}
|
||||
value={activeFilters.search}
|
||||
/>
|
||||
<label className="sr-only" htmlFor="conversation-filter">Filter conversations</label>
|
||||
<select
|
||||
id="conversation-filter"
|
||||
onChange={(event) => setFilter(event.target.value as ConversationFilter)}
|
||||
value={filter}
|
||||
onChange={(event) => updateFilters({ ...activeFilters, filter: event.target.value as ConversationFilter })}
|
||||
value={activeFilters.filter}
|
||||
>
|
||||
<option value="all">All</option>
|
||||
<option value="unread">Unread</option>
|
||||
|
||||
Reference in new issue
Block a user