feat: add EditEventScreen with calendar and chat mode support

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
This commit is contained in:
2026-01-31 18:46:31 +01:00
parent 617543a603
commit 6f0d172bf2
33 changed files with 1394 additions and 289 deletions

View File

@@ -2,6 +2,7 @@ import {
ChatMessage,
Conversation,
CreateMessageDTO,
CreateEventDTO,
GetMessagesOptions,
UpdateMessageDTO,
} from "@calchat/shared";
@@ -82,4 +83,17 @@ export class MongoChatRepository implements ChatRepository {
);
return doc ? (doc.toJSON() as unknown as ChatMessage) : null;
}
async updateProposalEvent(
messageId: string,
proposalId: string,
event: CreateEventDTO,
): Promise<ChatMessage | null> {
const doc = await ChatMessageModel.findOneAndUpdate(
{ _id: messageId, "proposedChanges.id": proposalId },
{ $set: { "proposedChanges.$.event": event } },
{ new: true },
);
return doc ? (doc.toJSON() as unknown as ChatMessage) : null;
}
}

View File

@@ -23,7 +23,6 @@ const EventSchema = new Schema<CreateEventDTO>(
startTime: { type: Date, required: true },
endTime: { type: Date, required: true },
note: { type: String },
isRecurring: { type: Boolean },
recurrenceRule: { type: String },
exceptionDates: { type: [String] },
},
@@ -37,7 +36,6 @@ const UpdatesSchema = new Schema<UpdateEventDTO>(
startTime: { type: Date },
endTime: { type: Date },
note: { type: String },
isRecurring: { type: Boolean },
recurrenceRule: { type: String },
},
{ _id: false },

View File

@@ -2,16 +2,22 @@ import mongoose, { Schema, Document, Model } from "mongoose";
import { CalendarEvent } from "@calchat/shared";
import { IdVirtual } from "./types";
export interface EventDocument extends Omit<CalendarEvent, "id">, Document {
interface EventVirtuals extends IdVirtual {
isRecurring: boolean;
}
export interface EventDocument
extends Omit<CalendarEvent, "id" | "isRecurring">,
Document {
toJSON(): CalendarEvent;
}
const EventSchema = new Schema<
EventDocument,
Model<EventDocument, {}, {}, IdVirtual>,
Model<EventDocument, {}, {}, EventVirtuals>,
{},
{},
IdVirtual
EventVirtuals
>(
{
userId: {
@@ -39,10 +45,6 @@ const EventSchema = new Schema<
note: {
type: String,
},
isRecurring: {
type: Boolean,
default: false,
},
recurrenceRule: {
type: String,
},
@@ -59,6 +61,11 @@ const EventSchema = new Schema<
return this._id.toString();
},
},
isRecurring: {
get() {
return !!this.recurrenceRule;
},
},
},
toJSON: {
virtuals: true,