Add a unified event editor that works in two modes: - Calendar mode: Create/edit events directly via EventService API - Chat mode: Edit AI-proposed events before confirming them The chat mode allows users to modify proposed events (title, time, recurrence) and persists changes both locally and to the server. New components: DateTimePicker, ScrollableDropdown, useDropdownPosition New API: PUT /api/chat/messages/:messageId/proposal
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import {
|
|
SendMessageDTO,
|
|
ChatResponse,
|
|
ChatMessage,
|
|
ConversationSummary,
|
|
GetMessagesOptions,
|
|
CreateEventDTO,
|
|
UpdateEventDTO,
|
|
EventAction,
|
|
RecurringDeleteMode,
|
|
} from "@calchat/shared";
|
|
import { ApiClient } from "./ApiClient";
|
|
|
|
interface ConfirmEventRequest {
|
|
proposalId: string;
|
|
action: EventAction;
|
|
event?: CreateEventDTO;
|
|
eventId?: string;
|
|
updates?: UpdateEventDTO;
|
|
deleteMode?: RecurringDeleteMode;
|
|
occurrenceDate?: string;
|
|
}
|
|
|
|
interface RejectEventRequest {
|
|
proposalId: string;
|
|
}
|
|
|
|
export const ChatService = {
|
|
sendMessage: async (data: SendMessageDTO): Promise<ChatResponse> => {
|
|
return ApiClient.post<ChatResponse>("/chat/message", data);
|
|
},
|
|
|
|
confirmEvent: async (
|
|
conversationId: string,
|
|
messageId: string,
|
|
proposalId: string,
|
|
action: EventAction,
|
|
event?: CreateEventDTO,
|
|
eventId?: string,
|
|
updates?: UpdateEventDTO,
|
|
deleteMode?: RecurringDeleteMode,
|
|
occurrenceDate?: string,
|
|
): Promise<ChatResponse> => {
|
|
const body: ConfirmEventRequest = {
|
|
proposalId,
|
|
action,
|
|
event,
|
|
eventId,
|
|
updates,
|
|
deleteMode,
|
|
occurrenceDate,
|
|
};
|
|
return ApiClient.post<ChatResponse>(
|
|
`/chat/confirm/${conversationId}/${messageId}`,
|
|
body,
|
|
);
|
|
},
|
|
|
|
rejectEvent: async (
|
|
conversationId: string,
|
|
messageId: string,
|
|
proposalId: string,
|
|
): Promise<ChatResponse> => {
|
|
const body: RejectEventRequest = { proposalId };
|
|
return ApiClient.post<ChatResponse>(
|
|
`/chat/reject/${conversationId}/${messageId}`,
|
|
body,
|
|
);
|
|
},
|
|
|
|
getConversations: async (): Promise<ConversationSummary[]> => {
|
|
return ApiClient.get<ConversationSummary[]>("/chat/conversations");
|
|
},
|
|
|
|
getConversation: async (
|
|
id: string,
|
|
options?: GetMessagesOptions,
|
|
): Promise<ChatMessage[]> => {
|
|
const params = new URLSearchParams();
|
|
if (options?.before) params.append("before", options.before);
|
|
if (options?.limit) params.append("limit", options.limit.toString());
|
|
|
|
const queryString = params.toString();
|
|
const url = `/chat/conversations/${id}${queryString ? `?${queryString}` : ""}`;
|
|
|
|
return ApiClient.get<ChatMessage[]>(url);
|
|
},
|
|
|
|
updateProposalEvent: async (
|
|
messageId: string,
|
|
proposalId: string,
|
|
event: CreateEventDTO,
|
|
): Promise<ChatMessage> => {
|
|
return ApiClient.put<ChatMessage>(`/chat/messages/${messageId}/proposal`, {
|
|
proposalId,
|
|
event,
|
|
});
|
|
},
|
|
};
|