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
156 lines
3.1 KiB
TypeScript
156 lines
3.1 KiB
TypeScript
import mongoose, { Schema, Document, Model } from "mongoose";
|
|
import {
|
|
ChatMessage,
|
|
Conversation,
|
|
CreateEventDTO,
|
|
UpdateEventDTO,
|
|
ProposedEventChange,
|
|
} from "@calchat/shared";
|
|
import { IdVirtual } from "./types";
|
|
|
|
export interface ChatMessageDocument extends Omit<ChatMessage, "id">, Document {
|
|
toJSON(): ChatMessage;
|
|
}
|
|
export interface ConversationDocument
|
|
extends Omit<Conversation, "id">, Document {
|
|
toJSON(): Conversation;
|
|
}
|
|
|
|
const EventSchema = new Schema<CreateEventDTO>(
|
|
{
|
|
title: { type: String, required: true },
|
|
description: { type: String },
|
|
startTime: { type: Date, required: true },
|
|
endTime: { type: Date, required: true },
|
|
note: { type: String },
|
|
recurrenceRule: { type: String },
|
|
exceptionDates: { type: [String] },
|
|
},
|
|
{ _id: false },
|
|
);
|
|
|
|
const UpdatesSchema = new Schema<UpdateEventDTO>(
|
|
{
|
|
title: { type: String },
|
|
description: { type: String },
|
|
startTime: { type: Date },
|
|
endTime: { type: Date },
|
|
note: { type: String },
|
|
recurrenceRule: { type: String },
|
|
},
|
|
{ _id: false },
|
|
);
|
|
|
|
const ProposedChangeSchema = new Schema<ProposedEventChange>(
|
|
{
|
|
id: { type: String, required: true },
|
|
action: {
|
|
type: String,
|
|
enum: ["create", "update", "delete"],
|
|
required: true,
|
|
},
|
|
eventId: { type: String },
|
|
event: { type: EventSchema },
|
|
updates: { type: UpdatesSchema },
|
|
respondedAction: {
|
|
type: String,
|
|
enum: ["confirm", "reject"],
|
|
},
|
|
deleteMode: {
|
|
type: String,
|
|
enum: ["single", "future", "all"],
|
|
},
|
|
occurrenceDate: { type: String },
|
|
},
|
|
{ _id: false },
|
|
);
|
|
|
|
const ChatMessageSchema = new Schema<
|
|
ChatMessageDocument,
|
|
Model<ChatMessageDocument, {}, {}, IdVirtual>,
|
|
{},
|
|
{},
|
|
IdVirtual
|
|
>(
|
|
{
|
|
conversationId: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
sender: {
|
|
type: String,
|
|
enum: ["user", "assistant"],
|
|
required: true,
|
|
},
|
|
content: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
proposedChanges: {
|
|
type: [ProposedChangeSchema],
|
|
default: undefined,
|
|
},
|
|
},
|
|
{
|
|
timestamps: true,
|
|
virtuals: {
|
|
id: {
|
|
get() {
|
|
return this._id.toString();
|
|
},
|
|
},
|
|
},
|
|
toJSON: {
|
|
virtuals: true,
|
|
transform: (_, ret: Record<string, unknown>) => {
|
|
delete ret._id;
|
|
delete ret.__v;
|
|
return ret;
|
|
},
|
|
},
|
|
},
|
|
);
|
|
|
|
const ConversationSchema = new Schema<
|
|
ConversationDocument,
|
|
Model<ConversationDocument, {}, {}, IdVirtual>,
|
|
{},
|
|
{},
|
|
IdVirtual
|
|
>(
|
|
{
|
|
userId: {
|
|
type: String,
|
|
required: true,
|
|
index: true,
|
|
},
|
|
},
|
|
{
|
|
timestamps: true,
|
|
virtuals: {
|
|
id: {
|
|
get() {
|
|
return this._id.toString();
|
|
},
|
|
},
|
|
},
|
|
toJSON: {
|
|
virtuals: true,
|
|
transform: (_, ret: Record<string, unknown>) => {
|
|
delete ret._id;
|
|
delete ret.__v;
|
|
return ret;
|
|
},
|
|
},
|
|
},
|
|
);
|
|
|
|
export const ChatMessageModel = mongoose.model<ChatMessageDocument>(
|
|
"ChatMessage",
|
|
ChatMessageSchema,
|
|
);
|
|
export const ConversationModel = mongoose.model<ConversationDocument>(
|
|
"Conversation",
|
|
ConversationSchema,
|
|
);
|