@agent-relay/dashboard 2.0.80 → 2.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/404.html +1 -1
- package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
- package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
- package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
- package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
- package/out/about.html +2 -2
- package/out/about.txt +1 -1
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +1 -1
- package/out/app.html +1 -1
- package/out/app.txt +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
- package/out/blog.html +2 -2
- package/out/blog.txt +1 -1
- package/out/careers.html +2 -2
- package/out/careers.txt +1 -1
- package/out/changelog.html +2 -2
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +2 -2
- package/out/complete-profile.html +2 -2
- package/out/complete-profile.txt +1 -1
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +1 -1
- package/out/contact.html +2 -2
- package/out/contact.txt +1 -1
- package/out/docs.html +2 -2
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +2 -2
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +2 -2
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +2 -2
- package/out/pricing.txt +1 -1
- package/out/privacy.html +2 -2
- package/out/privacy.txt +1 -1
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +1 -1
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +1 -1
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +1 -1
- package/out/providers.html +1 -1
- package/out/providers.txt +1 -1
- package/out/security.html +2 -2
- package/out/security.txt +1 -1
- package/out/signup.html +2 -2
- package/out/signup.txt +1 -1
- package/out/terms.html +2 -2
- package/out/terms.txt +1 -1
- package/package.json +7 -1
- package/src/app/about/page.tsx +7 -0
- package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
- package/src/app/app/[[...slug]]/page.tsx +23 -0
- package/src/app/app/onboarding/page.tsx +394 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
- package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
- package/src/app/blog/page.tsx +15 -0
- package/src/app/careers/page.tsx +7 -0
- package/src/app/changelog/page.tsx +7 -0
- package/src/app/cloud/link/page.tsx +464 -0
- package/src/app/complete-profile/page.tsx +204 -0
- package/src/app/connect-repos/page.tsx +410 -0
- package/src/app/contact/page.tsx +7 -0
- package/src/app/docs/page.tsx +7 -0
- package/src/app/favicon.png +0 -0
- package/src/app/globals.css +200 -0
- package/src/app/history/page.tsx +658 -0
- package/src/app/layout.tsx +25 -0
- package/src/app/login/page.tsx +424 -0
- package/src/app/metrics/page.tsx +781 -0
- package/src/app/page.tsx +59 -0
- package/src/app/pricing/page.tsx +7 -0
- package/src/app/privacy/page.tsx +7 -0
- package/src/app/providers/page.tsx +193 -0
- package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
- package/src/app/providers/setup/[provider]/constants.ts +35 -0
- package/src/app/providers/setup/[provider]/page.tsx +42 -0
- package/src/app/security/page.tsx +7 -0
- package/src/app/signup/page.tsx +533 -0
- package/src/app/terms/page.tsx +7 -0
- package/src/components/ActivityFeed.tsx +216 -0
- package/src/components/AddWorkspaceModal.tsx +170 -0
- package/src/components/AgentCard.test.tsx +134 -0
- package/src/components/AgentCard.tsx +585 -0
- package/src/components/AgentList.test.tsx +147 -0
- package/src/components/AgentList.tsx +419 -0
- package/src/components/AgentLogPreview.tsx +173 -0
- package/src/components/AgentProfilePanel.tsx +569 -0
- package/src/components/App.tsx +3424 -0
- package/src/components/BillingPanel.tsx +922 -0
- package/src/components/BillingResult.tsx +447 -0
- package/src/components/BroadcastComposer.tsx +690 -0
- package/src/components/ChannelAdminPanel.tsx +773 -0
- package/src/components/ChannelBrowser.tsx +385 -0
- package/src/components/ChannelChat.tsx +261 -0
- package/src/components/ChannelSidebar.tsx +399 -0
- package/src/components/CloudSessionProvider.tsx +130 -0
- package/src/components/CommandPalette.tsx +815 -0
- package/src/components/ConfirmationDialog.tsx +133 -0
- package/src/components/ConversationHistory.tsx +518 -0
- package/src/components/CoordinatorPanel.tsx +956 -0
- package/src/components/DecisionQueue.tsx +717 -0
- package/src/components/DirectMessageView.tsx +164 -0
- package/src/components/FileAutocomplete.tsx +368 -0
- package/src/components/FleetOverview.tsx +278 -0
- package/src/components/LogViewer.tsx +310 -0
- package/src/components/LogViewerPanel.tsx +482 -0
- package/src/components/Logo.tsx +284 -0
- package/src/components/MentionAutocomplete.tsx +384 -0
- package/src/components/MessageComposer.tsx +473 -0
- package/src/components/MessageList.tsx +725 -0
- package/src/components/MessageSenderName.tsx +91 -0
- package/src/components/MessageStatusIndicator.tsx +142 -0
- package/src/components/NewConversationModal.tsx +400 -0
- package/src/components/NotificationToast.tsx +488 -0
- package/src/components/OnlineUsersIndicator.tsx +164 -0
- package/src/components/Pagination.tsx +124 -0
- package/src/components/PricingPlans.tsx +386 -0
- package/src/components/ProjectList.tsx +711 -0
- package/src/components/ProviderAuthFlow.tsx +343 -0
- package/src/components/ProviderConnectionList.tsx +375 -0
- package/src/components/ProvisioningProgress.tsx +730 -0
- package/src/components/ReactionChips.tsx +70 -0
- package/src/components/ReactionPicker.tsx +121 -0
- package/src/components/RepoAccessPanel.tsx +787 -0
- package/src/components/RepositoriesPanel.tsx +901 -0
- package/src/components/ServerCard.tsx +202 -0
- package/src/components/SessionExpiredModal.tsx +128 -0
- package/src/components/SpawnModal.test.tsx +190 -0
- package/src/components/SpawnModal.tsx +1001 -0
- package/src/components/TaskAssignmentUI.tsx +375 -0
- package/src/components/TerminalProviderSetup.tsx +517 -0
- package/src/components/ThemeProvider.tsx +159 -0
- package/src/components/ThinkingIndicator.tsx +231 -0
- package/src/components/ThreadList.tsx +198 -0
- package/src/components/ThreadPanel.tsx +405 -0
- package/src/components/TrajectoryViewer.tsx +698 -0
- package/src/components/TypingIndicator.tsx +69 -0
- package/src/components/UsageBanner.tsx +231 -0
- package/src/components/UserProfilePanel.tsx +233 -0
- package/src/components/WorkspaceContext.tsx +95 -0
- package/src/components/WorkspaceSelector.tsx +234 -0
- package/src/components/WorkspaceStatusIndicator.tsx +396 -0
- package/src/components/XTermInteractive.tsx +516 -0
- package/src/components/XTermLogViewer.tsx +719 -0
- package/src/components/channels/ChannelDialogs.tsx +1411 -0
- package/src/components/channels/ChannelHeader.tsx +317 -0
- package/src/components/channels/ChannelMessageList.tsx +463 -0
- package/src/components/channels/ChannelViewV1.tsx +146 -0
- package/src/components/channels/MessageInput.tsx +302 -0
- package/src/components/channels/SearchInput.tsx +172 -0
- package/src/components/channels/SearchResults.tsx +336 -0
- package/src/components/channels/api.test.ts +1527 -0
- package/src/components/channels/api.ts +703 -0
- package/src/components/channels/index.ts +76 -0
- package/src/components/channels/mockApi.ts +344 -0
- package/src/components/channels/types.ts +566 -0
- package/src/components/hooks/index.ts +58 -0
- package/src/components/hooks/useAgentLogs.ts +504 -0
- package/src/components/hooks/useAgents.ts +127 -0
- package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
- package/src/components/hooks/useBroadcastDedup.ts +86 -0
- package/src/components/hooks/useChannelAdmin.ts +329 -0
- package/src/components/hooks/useChannelBrowser.ts +239 -0
- package/src/components/hooks/useChannelCommands.ts +138 -0
- package/src/components/hooks/useChannels.ts +367 -0
- package/src/components/hooks/useDebounce.ts +29 -0
- package/src/components/hooks/useDirectMessage.test.ts +952 -0
- package/src/components/hooks/useDirectMessage.ts +141 -0
- package/src/components/hooks/useMessages.ts +310 -0
- package/src/components/hooks/useOrchestrator.test.ts +165 -0
- package/src/components/hooks/useOrchestrator.ts +424 -0
- package/src/components/hooks/usePinnedAgents.test.ts +356 -0
- package/src/components/hooks/usePinnedAgents.ts +140 -0
- package/src/components/hooks/usePresence.test.ts +245 -0
- package/src/components/hooks/usePresence.ts +377 -0
- package/src/components/hooks/useRecentRepos.ts +130 -0
- package/src/components/hooks/useSession.ts +209 -0
- package/src/components/hooks/useThread.ts +138 -0
- package/src/components/hooks/useTrajectory.ts +265 -0
- package/src/components/hooks/useWebSocket.ts +290 -0
- package/src/components/hooks/useWorkspaceMembers.ts +132 -0
- package/src/components/hooks/useWorkspaceRepos.ts +73 -0
- package/src/components/hooks/useWorkspaceStatus.ts +237 -0
- package/src/components/index.ts +81 -0
- package/src/components/layout/Header.tsx +311 -0
- package/src/components/layout/RepoContextHeader.tsx +361 -0
- package/src/components/layout/Sidebar.archive.test.tsx +126 -0
- package/src/components/layout/Sidebar.test.tsx +691 -0
- package/src/components/layout/Sidebar.tsx +900 -0
- package/src/components/layout/index.ts +7 -0
- package/src/components/settings/BillingSettingsPanel.tsx +564 -0
- package/src/components/settings/SettingsPage.tsx +683 -0
- package/src/components/settings/TeamSettingsPanel.tsx +560 -0
- package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
- package/src/components/settings/index.ts +11 -0
- package/src/components/settings/types.ts +79 -0
- package/src/components/utils/messageFormatting.test.tsx +331 -0
- package/src/components/utils/messageFormatting.tsx +597 -0
- package/src/index.ts +63 -0
- package/src/landing/AboutPage.tsx +77 -0
- package/src/landing/BlogContent.tsx +187 -0
- package/src/landing/BlogPage.tsx +47 -0
- package/src/landing/CareersPage.tsx +53 -0
- package/src/landing/ChangelogPage.tsx +33 -0
- package/src/landing/ContactPage.tsx +41 -0
- package/src/landing/DocsPage.tsx +43 -0
- package/src/landing/LandingPage.tsx +702 -0
- package/src/landing/PricingPage.tsx +549 -0
- package/src/landing/PrivacyPage.tsx +117 -0
- package/src/landing/SecurityPage.tsx +42 -0
- package/src/landing/StaticPage.tsx +165 -0
- package/src/landing/TermsPage.tsx +125 -0
- package/src/landing/blogData.ts +312 -0
- package/src/landing/index.ts +18 -0
- package/src/landing/styles.css +3673 -0
- package/src/lib/agent-merge.test.ts +43 -0
- package/src/lib/agent-merge.ts +35 -0
- package/src/lib/api.ts +1294 -0
- package/src/lib/cloudApi.ts +893 -0
- package/src/lib/colors.test.ts +175 -0
- package/src/lib/colors.ts +218 -0
- package/src/lib/config.ts +109 -0
- package/src/lib/hierarchy.ts +242 -0
- package/src/lib/stuckDetection.ts +142 -0
- package/src/lib/useUrlRouting.ts +190 -0
- package/src/types/index.ts +317 -0
- package/src/types/threading.ts +7 -0
- package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
- package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
- /package/out/_next/static/{AqelRhy1vr2nBUcU0Iqcp → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
- /package/out/_next/static/{AqelRhy1vr2nBUcU0Iqcp → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
- /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
- /package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/{page-a58308f43557b908.js → page-b194f207fbd91862.js} +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channels V1 Component Library
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive channel-based messaging UI components.
|
|
5
|
+
* Built for the Agent Relay dashboard.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Types
|
|
9
|
+
export * from './types';
|
|
10
|
+
|
|
11
|
+
// API (real + mock fallback)
|
|
12
|
+
export {
|
|
13
|
+
listChannels,
|
|
14
|
+
getChannel,
|
|
15
|
+
getMessages,
|
|
16
|
+
createChannel,
|
|
17
|
+
sendMessage,
|
|
18
|
+
joinChannel,
|
|
19
|
+
leaveChannel,
|
|
20
|
+
archiveChannel,
|
|
21
|
+
unarchiveChannel,
|
|
22
|
+
deleteChannel,
|
|
23
|
+
markRead,
|
|
24
|
+
pinMessage,
|
|
25
|
+
unpinMessage,
|
|
26
|
+
getMentionSuggestions,
|
|
27
|
+
searchMessages,
|
|
28
|
+
searchChannel,
|
|
29
|
+
// Admin API (Task 10)
|
|
30
|
+
updateChannel,
|
|
31
|
+
addMember,
|
|
32
|
+
removeMember,
|
|
33
|
+
updateMemberRole,
|
|
34
|
+
getChannelMembers,
|
|
35
|
+
isRealApiEnabled,
|
|
36
|
+
setApiMode,
|
|
37
|
+
getApiMode,
|
|
38
|
+
ApiError,
|
|
39
|
+
} from './api';
|
|
40
|
+
|
|
41
|
+
// Components
|
|
42
|
+
export { ChannelHeader } from './ChannelHeader';
|
|
43
|
+
export { ChannelMessageList } from './ChannelMessageList';
|
|
44
|
+
export { MessageInput } from './MessageInput';
|
|
45
|
+
export { ChannelViewV1 } from './ChannelViewV1';
|
|
46
|
+
export { SearchInput } from './SearchInput';
|
|
47
|
+
export { SearchResults } from './SearchResults';
|
|
48
|
+
|
|
49
|
+
// Dialogs
|
|
50
|
+
export {
|
|
51
|
+
ArchiveChannelDialog,
|
|
52
|
+
DeleteChannelDialog,
|
|
53
|
+
LeaveChannelDialog,
|
|
54
|
+
CreateChannelModal,
|
|
55
|
+
InviteToChannelModal,
|
|
56
|
+
// Admin Tools (Task 10)
|
|
57
|
+
ChannelSettingsModal,
|
|
58
|
+
MemberManagementPanel,
|
|
59
|
+
} from './ChannelDialogs';
|
|
60
|
+
|
|
61
|
+
// Re-export prop types for convenience
|
|
62
|
+
export type { ChannelHeaderProps } from './types';
|
|
63
|
+
export type { ChannelMessageListProps } from './types';
|
|
64
|
+
export type { MessageInputProps } from './types';
|
|
65
|
+
export type { ChannelViewV1Props } from './ChannelViewV1';
|
|
66
|
+
export type { SearchInputProps, SearchResultsProps, SearchResult, SearchResponse } from './types';
|
|
67
|
+
export type {
|
|
68
|
+
ArchiveChannelDialogProps,
|
|
69
|
+
DeleteChannelDialogProps,
|
|
70
|
+
LeaveChannelDialogProps,
|
|
71
|
+
CreateChannelModalProps,
|
|
72
|
+
InviteToChannelModalProps,
|
|
73
|
+
// Admin Tools (Task 10)
|
|
74
|
+
ChannelSettingsModalProps,
|
|
75
|
+
MemberManagementPanelProps,
|
|
76
|
+
} from './ChannelDialogs';
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock API Service for Channels V1
|
|
3
|
+
*
|
|
4
|
+
* Provides mock implementations of the channel APIs for development.
|
|
5
|
+
* Replace with real API calls when backend is ready.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Channel,
|
|
10
|
+
ChannelMember,
|
|
11
|
+
ChannelMessage,
|
|
12
|
+
ListChannelsResponse,
|
|
13
|
+
GetChannelResponse,
|
|
14
|
+
GetMessagesResponse,
|
|
15
|
+
CreateChannelRequest,
|
|
16
|
+
CreateChannelResponse,
|
|
17
|
+
SendMessageRequest,
|
|
18
|
+
SendMessageResponse,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
MOCK_CHANNELS,
|
|
23
|
+
MOCK_ARCHIVED_CHANNELS,
|
|
24
|
+
MOCK_MESSAGES,
|
|
25
|
+
} from './types';
|
|
26
|
+
|
|
27
|
+
// Only seed demo data when explicitly enabled (prevents example channels in cloud)
|
|
28
|
+
const ENABLE_DEMO_DATA = process.env.NEXT_PUBLIC_ENABLE_CHANNEL_DEMO === 'true';
|
|
29
|
+
|
|
30
|
+
function getInitialChannels(): Channel[] {
|
|
31
|
+
return ENABLE_DEMO_DATA ? [...MOCK_CHANNELS] : [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getInitialArchivedChannels(): Channel[] {
|
|
35
|
+
return ENABLE_DEMO_DATA ? [...MOCK_ARCHIVED_CHANNELS] : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getInitialMessages(): ChannelMessage[] {
|
|
39
|
+
return ENABLE_DEMO_DATA ? [...MOCK_MESSAGES] : [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Simulated latency for realistic UX
|
|
43
|
+
const MOCK_LATENCY_MS = 300;
|
|
44
|
+
|
|
45
|
+
// In-memory state
|
|
46
|
+
let channels = getInitialChannels();
|
|
47
|
+
let archivedChannels = getInitialArchivedChannels();
|
|
48
|
+
let messages = getInitialMessages();
|
|
49
|
+
let messageIdCounter = ENABLE_DEMO_DATA ? 100 : 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Simulate network latency
|
|
53
|
+
*/
|
|
54
|
+
async function delay(ms: number = MOCK_LATENCY_MS): Promise<void> {
|
|
55
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate a unique message ID
|
|
60
|
+
*/
|
|
61
|
+
function generateMessageId(): string {
|
|
62
|
+
return `msg-${++messageIdCounter}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Mock: List channels for current user
|
|
67
|
+
*/
|
|
68
|
+
export async function listChannels(): Promise<ListChannelsResponse> {
|
|
69
|
+
await delay();
|
|
70
|
+
return {
|
|
71
|
+
channels: [...channels],
|
|
72
|
+
archivedChannels: [...archivedChannels],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Mock: Get channel details
|
|
78
|
+
*/
|
|
79
|
+
export async function getChannel(channelId: string): Promise<GetChannelResponse> {
|
|
80
|
+
await delay();
|
|
81
|
+
|
|
82
|
+
const channel = [...channels, ...archivedChannels].find(c => c.id === channelId);
|
|
83
|
+
if (!channel) {
|
|
84
|
+
throw new Error(`Channel not found: ${channelId}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Generate mock members
|
|
88
|
+
const mockMembers: ChannelMember[] = [
|
|
89
|
+
{
|
|
90
|
+
id: 'Lead',
|
|
91
|
+
displayName: 'Lead Agent',
|
|
92
|
+
entityType: 'agent',
|
|
93
|
+
role: 'owner',
|
|
94
|
+
status: 'online',
|
|
95
|
+
joinedAt: channel.createdAt,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 'Frontend',
|
|
99
|
+
displayName: 'Frontend Engineer',
|
|
100
|
+
entityType: 'agent',
|
|
101
|
+
role: 'member',
|
|
102
|
+
status: 'online',
|
|
103
|
+
joinedAt: channel.createdAt,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'CodeReviewer',
|
|
107
|
+
displayName: 'Code Reviewer',
|
|
108
|
+
entityType: 'agent',
|
|
109
|
+
role: 'member',
|
|
110
|
+
status: 'away',
|
|
111
|
+
joinedAt: channel.createdAt,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
channel,
|
|
117
|
+
members: mockMembers,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Mock: Get messages in a channel
|
|
123
|
+
*/
|
|
124
|
+
export async function getMessages(
|
|
125
|
+
channelId: string,
|
|
126
|
+
options?: { before?: string; limit?: number; threadId?: string }
|
|
127
|
+
): Promise<GetMessagesResponse> {
|
|
128
|
+
await delay();
|
|
129
|
+
|
|
130
|
+
const limit = options?.limit || 50;
|
|
131
|
+
let channelMessages = messages.filter(m => m.channelId === channelId);
|
|
132
|
+
|
|
133
|
+
if (options?.threadId) {
|
|
134
|
+
channelMessages = channelMessages.filter(m =>
|
|
135
|
+
m.threadId === options.threadId || m.id === options.threadId
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (options?.before) {
|
|
140
|
+
const beforeIndex = channelMessages.findIndex(m => m.id === options.before);
|
|
141
|
+
if (beforeIndex > 0) {
|
|
142
|
+
channelMessages = channelMessages.slice(0, beforeIndex);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const result = channelMessages.slice(-limit);
|
|
147
|
+
|
|
148
|
+
// Calculate unread state (mock: last 2 messages are unread)
|
|
149
|
+
const unreadCount = Math.min(2, result.filter(m => !m.isRead).length);
|
|
150
|
+
const firstUnread = result.find(m => !m.isRead);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
messages: result,
|
|
154
|
+
hasMore: channelMessages.length > limit,
|
|
155
|
+
unread: {
|
|
156
|
+
count: unreadCount,
|
|
157
|
+
firstUnreadMessageId: firstUnread?.id,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Mock: Create a channel
|
|
164
|
+
*/
|
|
165
|
+
export async function createChannel(
|
|
166
|
+
request: CreateChannelRequest
|
|
167
|
+
): Promise<CreateChannelResponse> {
|
|
168
|
+
await delay();
|
|
169
|
+
|
|
170
|
+
const newChannel: Channel = {
|
|
171
|
+
id: `#${request.name}`,
|
|
172
|
+
name: request.name,
|
|
173
|
+
description: request.description,
|
|
174
|
+
visibility: request.visibility,
|
|
175
|
+
status: 'active',
|
|
176
|
+
createdAt: new Date().toISOString(),
|
|
177
|
+
createdBy: 'CurrentUser',
|
|
178
|
+
memberCount: 1,
|
|
179
|
+
unreadCount: 0,
|
|
180
|
+
hasMentions: false,
|
|
181
|
+
isDm: false,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
channels.push(newChannel);
|
|
185
|
+
|
|
186
|
+
return { channel: newChannel };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Mock: Send a message
|
|
191
|
+
*/
|
|
192
|
+
export async function sendMessage(
|
|
193
|
+
channelId: string,
|
|
194
|
+
request: SendMessageRequest
|
|
195
|
+
): Promise<SendMessageResponse> {
|
|
196
|
+
await delay(100); // Fast for good UX
|
|
197
|
+
|
|
198
|
+
const newMessage: ChannelMessage = {
|
|
199
|
+
id: generateMessageId(),
|
|
200
|
+
channelId,
|
|
201
|
+
from: 'CurrentUser',
|
|
202
|
+
fromEntityType: 'user',
|
|
203
|
+
content: request.content,
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
threadId: request.threadId,
|
|
206
|
+
isRead: true,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
messages.push(newMessage);
|
|
210
|
+
|
|
211
|
+
// Update channel's last activity
|
|
212
|
+
const channel = channels.find(c => c.id === channelId);
|
|
213
|
+
if (channel) {
|
|
214
|
+
channel.lastActivityAt = newMessage.timestamp;
|
|
215
|
+
channel.lastMessage = {
|
|
216
|
+
content: request.content.slice(0, 100),
|
|
217
|
+
from: 'CurrentUser',
|
|
218
|
+
timestamp: newMessage.timestamp,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { message: newMessage };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Mock: Join a channel
|
|
227
|
+
*/
|
|
228
|
+
export async function joinChannel(channelId: string): Promise<Channel> {
|
|
229
|
+
await delay();
|
|
230
|
+
|
|
231
|
+
const channel = channels.find(c => c.id === channelId);
|
|
232
|
+
if (!channel) {
|
|
233
|
+
throw new Error(`Channel not found: ${channelId}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
channel.memberCount++;
|
|
237
|
+
return channel;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Mock: Leave a channel
|
|
242
|
+
*/
|
|
243
|
+
export async function leaveChannel(channelId: string): Promise<void> {
|
|
244
|
+
await delay();
|
|
245
|
+
|
|
246
|
+
const channelIndex = channels.findIndex(c => c.id === channelId);
|
|
247
|
+
if (channelIndex >= 0) {
|
|
248
|
+
channels[channelIndex].memberCount--;
|
|
249
|
+
// Optionally remove from list if member count is personal preference
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Mock: Archive a channel
|
|
255
|
+
*/
|
|
256
|
+
export async function archiveChannel(channelId: string): Promise<Channel> {
|
|
257
|
+
await delay();
|
|
258
|
+
|
|
259
|
+
const channelIndex = channels.findIndex(c => c.id === channelId);
|
|
260
|
+
if (channelIndex < 0) {
|
|
261
|
+
throw new Error(`Channel not found: ${channelId}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const channel = channels[channelIndex];
|
|
265
|
+
channel.status = 'archived';
|
|
266
|
+
channels.splice(channelIndex, 1);
|
|
267
|
+
archivedChannels.push(channel);
|
|
268
|
+
|
|
269
|
+
return channel;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Mock: Unarchive a channel
|
|
274
|
+
*/
|
|
275
|
+
export async function unarchiveChannel(channelId: string): Promise<Channel> {
|
|
276
|
+
await delay();
|
|
277
|
+
|
|
278
|
+
const channelIndex = archivedChannels.findIndex(c => c.id === channelId);
|
|
279
|
+
if (channelIndex < 0) {
|
|
280
|
+
throw new Error(`Channel not found: ${channelId}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const channel = archivedChannels[channelIndex];
|
|
284
|
+
channel.status = 'active';
|
|
285
|
+
archivedChannels.splice(channelIndex, 1);
|
|
286
|
+
channels.push(channel);
|
|
287
|
+
|
|
288
|
+
return channel;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Mock: Delete a channel
|
|
293
|
+
*/
|
|
294
|
+
export async function deleteChannel(channelId: string): Promise<void> {
|
|
295
|
+
await delay();
|
|
296
|
+
|
|
297
|
+
channels = channels.filter(c => c.id !== channelId);
|
|
298
|
+
archivedChannels = archivedChannels.filter(c => c.id !== channelId);
|
|
299
|
+
messages = messages.filter(m => m.channelId !== channelId);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Mock: Mark messages as read
|
|
304
|
+
*/
|
|
305
|
+
export async function markRead(
|
|
306
|
+
channelId: string,
|
|
307
|
+
upToTimestamp: string
|
|
308
|
+
): Promise<void> {
|
|
309
|
+
await delay(50);
|
|
310
|
+
|
|
311
|
+
const channelMessages = messages.filter(m => m.channelId === channelId);
|
|
312
|
+
const upToTime = new Date(upToTimestamp).getTime();
|
|
313
|
+
|
|
314
|
+
channelMessages.forEach(m => {
|
|
315
|
+
if (new Date(m.timestamp).getTime() <= upToTime) {
|
|
316
|
+
m.isRead = true;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Update channel unread count
|
|
321
|
+
const channel = channels.find(c => c.id === channelId);
|
|
322
|
+
if (channel) {
|
|
323
|
+
channel.unreadCount = channelMessages.filter(m => !m.isRead).length;
|
|
324
|
+
channel.hasMentions = false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Mock: Get mention suggestions
|
|
330
|
+
*/
|
|
331
|
+
export async function getMentionSuggestions(): Promise<string[]> {
|
|
332
|
+
await delay(50);
|
|
333
|
+
return ['Lead', 'Frontend', 'CodeReviewer', 'Backend', 'DevOps', 'QA'];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Reset mock state (for testing)
|
|
338
|
+
*/
|
|
339
|
+
export function resetMockState(): void {
|
|
340
|
+
channels = getInitialChannels();
|
|
341
|
+
archivedChannels = getInitialArchivedChannels();
|
|
342
|
+
messages = getInitialMessages();
|
|
343
|
+
messageIdCounter = ENABLE_DEMO_DATA ? 100 : 0;
|
|
344
|
+
}
|