implement chat messaging with event proposals
- Add functional chat with server communication and test responses - Add ProposedEventCard component for confirm/reject actions - Move Constants (Day, Month) from client to shared package - Add dateHelpers utility for weekday calculations - Extend Themes.tsx with button and text colors - Update CLAUDE.md with current implementation status - Add *.tsbuildinfo to .gitignore
This commit is contained in:
@@ -19,7 +19,7 @@ services:
|
||||
image: mongo-express:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "8081:8081"
|
||||
- "8083:8083"
|
||||
environment:
|
||||
ME_CONFIG_MONGODB_URL: mongodb://root:mongoose@mongo:27017/
|
||||
ME_CONFIG_BASICAUTH_ENABLED: true
|
||||
|
||||
@@ -15,6 +15,21 @@ const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/caldav';
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
|
||||
// CORS - only needed for web browser development
|
||||
// Native mobile apps don't send Origin headers and aren't affected by CORS
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
app.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.sendStatus(200);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize repositories
|
||||
const userRepo = new MongoUserRepository();
|
||||
const eventRepo = new MongoEventRepository();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Response } from 'express';
|
||||
import { SendMessageDTO } from '@caldav/shared';
|
||||
import { ChatService } from '../services';
|
||||
import { AuthenticatedRequest } from '../middleware';
|
||||
|
||||
@@ -6,15 +7,36 @@ export class ChatController {
|
||||
constructor(private chatService: ChatService) {}
|
||||
|
||||
async sendMessage(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
try {
|
||||
const userId = req.user!.userId;
|
||||
const data: SendMessageDTO = req.body;
|
||||
const response = await this.chatService.processMessage(userId, data);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to process message' });
|
||||
}
|
||||
}
|
||||
|
||||
async confirmEvent(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
try {
|
||||
const userId = req.user!.userId;
|
||||
const { conversationId, messageId } = req.params;
|
||||
const response = await this.chatService.confirmEvent(userId, conversationId, messageId);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to confirm event' });
|
||||
}
|
||||
}
|
||||
|
||||
async rejectEvent(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
try {
|
||||
const userId = req.user!.userId;
|
||||
const { conversationId, messageId } = req.params;
|
||||
const response = await this.chatService.rejectEvent(userId, conversationId, messageId);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to reject event' });
|
||||
}
|
||||
}
|
||||
|
||||
async getConversations(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
import { ChatMessage, ChatResponse, SendMessageDTO, CalendarEvent, ConversationSummary, GetMessagesOptions } from '@caldav/shared';
|
||||
import { ChatMessage, ChatResponse, SendMessageDTO, ConversationSummary, GetMessagesOptions, ProposedEventChange, getDay } from '@caldav/shared';
|
||||
import { ChatRepository, EventRepository, AIProvider } from './interfaces';
|
||||
|
||||
// Test responses array (cycles through responses)
|
||||
let responseIndex = 0;
|
||||
|
||||
const testResponses: Array<{ content: string; proposedChange?: ProposedEventChange }> = [
|
||||
// {{{
|
||||
// Response 1: Meeting mit Jens - next Friday 14:00
|
||||
{
|
||||
content: "Alles klar! Ich erstelle dir einen Termin für das Meeting mit Jens am nächsten Freitag um 14:00 Uhr:",
|
||||
proposedChange: {
|
||||
action: 'create',
|
||||
event: {
|
||||
title: "Meeting mit Jens",
|
||||
startTime: getDay('Friday', 1, 14, 0),
|
||||
endTime: getDay('Friday', 1, 15, 0),
|
||||
description: "Arbeitstreffen",
|
||||
}
|
||||
}
|
||||
},
|
||||
// Response 2: Recurring event - every Saturday 10:00
|
||||
{
|
||||
content: "Verstanden! Ich erstelle einen wiederkehrenden Termin: Jeden Samstag um 10:00 Uhr Badezimmer putzen:",
|
||||
proposedChange: {
|
||||
action: 'create',
|
||||
event: {
|
||||
title: "Badezimmer putzen",
|
||||
startTime: getDay('Saturday', 1, 10, 0),
|
||||
endTime: getDay('Saturday', 1, 11, 0),
|
||||
isRecurring: true,
|
||||
recurrenceRule: "FREQ=WEEKLY;BYDAY=SA",
|
||||
}
|
||||
}
|
||||
},
|
||||
// Response 3: Calendar overview (text only, no proposedChange)
|
||||
{
|
||||
content: "Hier sind deine Termine für die nächsten 2 Wochen:\n\n" +
|
||||
"Freitag, 10.01. - 14:00 Uhr: Meeting mit Jens\n" +
|
||||
"Samstag, 11.01. - 10:00 Uhr: Badezimmer putzen\n" +
|
||||
"Samstag, 18.01. - 10:00 Uhr: Badezimmer putzen\n\n" +
|
||||
"Insgesamt 3 Termine.",
|
||||
},
|
||||
// }}}
|
||||
];
|
||||
|
||||
export class ChatService {
|
||||
constructor(
|
||||
private chatRepo: ChatRepository,
|
||||
@@ -9,15 +52,38 @@ export class ChatService {
|
||||
) {}
|
||||
|
||||
async processMessage(userId: string, data: SendMessageDTO): Promise<ChatResponse> {
|
||||
throw new Error('Not implemented');
|
||||
const response = testResponses[responseIndex % testResponses.length];
|
||||
responseIndex++;
|
||||
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId: data.conversationId || 'temp-conv-id',
|
||||
sender: 'assistant',
|
||||
content: response.content,
|
||||
proposedChange: response.proposedChange,
|
||||
};
|
||||
|
||||
return { message, conversationId: message.conversationId };
|
||||
}
|
||||
|
||||
async confirmEvent(userId: string, conversationId: string, messageId: string): Promise<CalendarEvent> {
|
||||
throw new Error('Not implemented');
|
||||
async confirmEvent(userId: string, conversationId: string, messageId: string): Promise<ChatResponse> {
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId,
|
||||
sender: 'assistant',
|
||||
content: 'Der Vorschlag wurde angenommen.',
|
||||
};
|
||||
return { message, conversationId };
|
||||
}
|
||||
|
||||
async rejectEvent(conversationId: string, messageId: string): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
async rejectEvent(userId: string, conversationId: string, messageId: string): Promise<ChatResponse> {
|
||||
const message: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
conversationId,
|
||||
sender: 'assistant',
|
||||
content: 'Der Vorschlag wurde abgelehnt.',
|
||||
};
|
||||
return { message, conversationId };
|
||||
}
|
||||
|
||||
async getConversations(userId: string): Promise<ConversationSummary[]> {
|
||||
|
||||
Reference in New Issue
Block a user