From e3f7a778c78ac1a7c416a0c748dffa7227ba3ce6 Mon Sep 17 00:00:00 2001 From: Linus Waldowsky Date: Sun, 4 Jan 2026 16:17:36 +0100 Subject: [PATCH] format codebase with prettier --- apps/client/src/Themes.tsx | 28 +- apps/client/src/app/(tabs)/_layout.tsx | 26 +- apps/client/src/app/(tabs)/chat.tsx | 19 +- apps/client/src/app/event/[id].tsx | 8 +- apps/client/src/app/login.tsx | 6 +- apps/client/src/app/note/[id].tsx | 8 +- apps/client/src/app/register.tsx | 6 +- apps/client/src/components/BaseBackground.tsx | 10 +- apps/client/src/components/EventCard.tsx | 6 +- .../src/components/EventConfirmDialog.tsx | 6 +- .../src/components/ProposedEventCard.tsx | 15 +- apps/client/src/services/AuthService.ts | 10 +- apps/client/src/services/ChatService.ts | 19 +- apps/client/src/services/EventService.ts | 24 +- apps/client/src/services/index.ts | 8 +- apps/client/src/stores/AuthStore.ts | 10 +- apps/client/src/stores/EventsStore.ts | 12 +- apps/client/src/stores/index.ts | 4 +- apps/server/src/ai/ClaudeAdapter.ts | 13 +- apps/server/src/ai/index.ts | 2 +- apps/server/src/app.ts | 64 ++-- apps/server/src/controllers/AuthController.ts | 8 +- apps/server/src/controllers/ChatController.ts | 41 ++- .../server/src/controllers/EventController.ts | 23 +- apps/server/src/controllers/index.ts | 6 +- apps/server/src/middleware/AuthMiddleware.ts | 14 +- apps/server/src/middleware/index.ts | 2 +- apps/server/src/repositories/index.ts | 2 +- .../repositories/mongo/MongoChatRepository.ts | 29 +- .../mongo/MongoEventRepository.ts | 23 +- .../repositories/mongo/MongoUserRepository.ts | 8 +- apps/server/src/repositories/mongo/index.ts | 6 +- .../repositories/mongo/models/ChatModel.ts | 61 ++- .../repositories/mongo/models/EventModel.ts | 20 +- .../repositories/mongo/models/UserModel.ts | 20 +- .../src/repositories/mongo/models/index.ts | 6 +- apps/server/src/routes/auth.routes.ts | 12 +- apps/server/src/routes/chat.routes.ts | 24 +- apps/server/src/routes/event.routes.ts | 18 +- apps/server/src/routes/index.ts | 26 +- apps/server/src/services/AuthService.ts | 22 +- apps/server/src/services/ChatService.ts | 347 ++++++++++-------- apps/server/src/services/EventService.ts | 28 +- apps/server/src/services/index.ts | 8 +- .../src/services/interfaces/AIProvider.ts | 6 +- .../src/services/interfaces/ChatRepository.ts | 17 +- .../services/interfaces/EventRepository.ts | 8 +- .../src/services/interfaces/UserRepository.ts | 2 +- apps/server/src/services/interfaces/index.ts | 8 +- apps/server/src/utils/eventFormatters.ts | 49 ++- apps/server/src/utils/index.ts | 4 +- apps/server/src/utils/jwt.ts | 12 +- apps/server/src/utils/password.ts | 7 +- apps/server/src/utils/recurrenceExpander.ts | 51 +-- package-lock.json | 2 +- package.json | 1 + packages/shared/src/index.ts | 4 +- packages/shared/src/models/ChatMessage.ts | 16 +- packages/shared/src/models/Constants.ts | 62 ++-- packages/shared/src/models/User.ts | 2 +- packages/shared/src/models/index.ts | 8 +- packages/shared/src/utils/dateHelpers.ts | 9 +- packages/shared/src/utils/index.ts | 2 +- 63 files changed, 786 insertions(+), 542 deletions(-) diff --git a/apps/client/src/Themes.tsx b/apps/client/src/Themes.tsx index e98c1db..3fc16d4 100644 --- a/apps/client/src/Themes.tsx +++ b/apps/client/src/Themes.tsx @@ -1,17 +1,17 @@ type Theme = { - chatBot: string, - primeFg: string, - primeBg: string, - messageBorderBg: string, - placeholderBg: string, - calenderBg: string, - confirmButton: string, - rejectButton: string, - disabledButton: string, - buttonText: string, - textSecondary: string, - textMuted: string, -} + chatBot: string; + primeFg: string; + primeBg: string; + messageBorderBg: string; + placeholderBg: string; + calenderBg: string; + confirmButton: string; + rejectButton: string; + disabledButton: string; + buttonText: string; + textSecondary: string; + textMuted: string; +}; const defaultLight: Theme = { chatBot: "#DE6C20", @@ -26,7 +26,7 @@ const defaultLight: Theme = { buttonText: "#fff", textSecondary: "#666", textMuted: "#888", -} +}; let currentTheme: Theme = defaultLight; export default currentTheme; diff --git a/apps/client/src/app/(tabs)/_layout.tsx b/apps/client/src/app/(tabs)/_layout.tsx index 378e1ac..d74930f 100644 --- a/apps/client/src/app/(tabs)/_layout.tsx +++ b/apps/client/src/app/(tabs)/_layout.tsx @@ -4,24 +4,30 @@ import theme from "../../Themes"; export default function TabLayout() { return ( - + , + title: "Chat", + tabBarIcon: ({ color }) => ( + + ), }} /> , + title: "Calendar", + tabBarIcon: ({ color }) => ( + + ), }} /> diff --git a/apps/client/src/app/(tabs)/chat.tsx b/apps/client/src/app/(tabs)/chat.tsx index 8bb6e87..a2adcf1 100644 --- a/apps/client/src/app/(tabs)/chat.tsx +++ b/apps/client/src/app/(tabs)/chat.tsx @@ -40,13 +40,13 @@ const Chat = () => { action: "confirm" | "reject", messageId: string, conversationId: string, - proposedChange?: ProposedEventChange + proposedChange?: ProposedEventChange, ) => { // Mark message as responded (optimistic update) setMessages((prev) => prev.map((msg) => - msg.id === messageId ? { ...msg, respondedAction: action } : msg - ) + msg.id === messageId ? { ...msg, respondedAction: action } : msg, + ), ); try { @@ -58,7 +58,7 @@ const Chat = () => { proposedChange.action, proposedChange.event, proposedChange.eventId, - proposedChange.updates + proposedChange.updates, ) : await ChatService.rejectEvent(conversationId, messageId); @@ -74,8 +74,8 @@ const Chat = () => { // Revert on error setMessages((prev) => prev.map((msg) => - msg.id === messageId ? { ...msg, respondedAction: undefined } : msg - ) + msg.id === messageId ? { ...msg, respondedAction: undefined } : msg, + ), ); } }; @@ -119,7 +119,12 @@ const Chat = () => { proposedChange={item.proposedChange} respondedAction={item.respondedAction} onConfirm={() => - handleEventResponse("confirm", item.id, item.conversationId!, item.proposedChange) + handleEventResponse( + "confirm", + item.id, + item.conversationId!, + item.proposedChange, + ) } onReject={() => handleEventResponse("reject", item.id, item.conversationId!) diff --git a/apps/client/src/app/event/[id].tsx b/apps/client/src/app/event/[id].tsx index 2fc2feb..742087f 100644 --- a/apps/client/src/app/event/[id].tsx +++ b/apps/client/src/app/event/[id].tsx @@ -1,6 +1,6 @@ -import { View, Text, TextInput, Pressable } from 'react-native'; -import { useLocalSearchParams } from 'expo-router'; -import BaseBackground from '../../components/BaseBackground'; +import { View, Text, TextInput, Pressable } from "react-native"; +import { useLocalSearchParams } from "expo-router"; +import BaseBackground from "../../components/BaseBackground"; const EventDetailScreen = () => { const { id } = useLocalSearchParams<{ id: string }>(); @@ -12,7 +12,7 @@ const EventDetailScreen = () => { // TODO: Delete button -> EventService.delete() // TODO: Link to NoteScreen for this event // TODO: Loading and error states - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/app/login.tsx b/apps/client/src/app/login.tsx index 994dda5..f1e2272 100644 --- a/apps/client/src/app/login.tsx +++ b/apps/client/src/app/login.tsx @@ -1,5 +1,5 @@ -import { View, Text, TextInput, Pressable } from 'react-native'; -import BaseBackground from '../components/BaseBackground'; +import { View, Text, TextInput, Pressable } from "react-native"; +import BaseBackground from "../components/BaseBackground"; const LoginScreen = () => { // TODO: Email input field @@ -8,7 +8,7 @@ const LoginScreen = () => { // TODO: Link to RegisterScreen // TODO: Error handling and display // TODO: Navigate to Calendar on success - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/app/note/[id].tsx b/apps/client/src/app/note/[id].tsx index 3c62e77..5635d00 100644 --- a/apps/client/src/app/note/[id].tsx +++ b/apps/client/src/app/note/[id].tsx @@ -1,6 +1,6 @@ -import { View, Text, TextInput, Pressable } from 'react-native'; -import { useLocalSearchParams } from 'expo-router'; -import BaseBackground from '../../components/BaseBackground'; +import { View, Text, TextInput, Pressable } from "react-native"; +import { useLocalSearchParams } from "expo-router"; +import BaseBackground from "../../components/BaseBackground"; const NoteScreen = () => { const { id } = useLocalSearchParams<{ id: string }>(); @@ -10,7 +10,7 @@ const NoteScreen = () => { // TODO: Auto-save or manual save button // TODO: Save changes -> EventService.update({ note: ... }) // TODO: Loading and error states - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/app/register.tsx b/apps/client/src/app/register.tsx index 3525e46..abc48d2 100644 --- a/apps/client/src/app/register.tsx +++ b/apps/client/src/app/register.tsx @@ -1,5 +1,5 @@ -import { View, Text, TextInput, Pressable } from 'react-native'; -import BaseBackground from '../components/BaseBackground'; +import { View, Text, TextInput, Pressable } from "react-native"; +import BaseBackground from "../components/BaseBackground"; const RegisterScreen = () => { // TODO: Email input field @@ -10,7 +10,7 @@ const RegisterScreen = () => { // TODO: Link to LoginScreen // TODO: Error handling and display // TODO: Navigate to Calendar on success - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/components/BaseBackground.tsx b/apps/client/src/components/BaseBackground.tsx index 8c266e6..0bbcf86 100644 --- a/apps/client/src/components/BaseBackground.tsx +++ b/apps/client/src/components/BaseBackground.tsx @@ -1,11 +1,11 @@ -import { View } from "react-native" -import currentTheme from "../Themes" +import { View } from "react-native"; +import currentTheme from "../Themes"; import { ReactNode } from "react"; type BaseBackgroundProps = { children?: ReactNode; className?: string; -} +}; const BaseBackground = (props: BaseBackgroundProps) => { return ( @@ -17,7 +17,7 @@ const BaseBackground = (props: BaseBackgroundProps) => { > {props.children} - ) -} + ); +}; export default BaseBackground; diff --git a/apps/client/src/components/EventCard.tsx b/apps/client/src/components/EventCard.tsx index d05c156..4e5682f 100644 --- a/apps/client/src/components/EventCard.tsx +++ b/apps/client/src/components/EventCard.tsx @@ -1,5 +1,5 @@ -import { View, Text, Pressable } from 'react-native'; -import { CalendarEvent } from '@caldav/shared'; +import { View, Text, Pressable } from "react-native"; +import { CalendarEvent } from "@caldav/shared"; type EventCardProps = { event: CalendarEvent; @@ -10,7 +10,7 @@ const EventCard = ({ event: _event, onPress: _onPress }: EventCardProps) => { // TODO: Display event title, time, and description preview // TODO: Handle onPress to navigate to EventDetailScreen // TODO: Style based on event type or time of day - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/components/EventConfirmDialog.tsx b/apps/client/src/components/EventConfirmDialog.tsx index f9ce6f3..00a0241 100644 --- a/apps/client/src/components/EventConfirmDialog.tsx +++ b/apps/client/src/components/EventConfirmDialog.tsx @@ -1,5 +1,5 @@ -import { View, Text, Modal, Pressable } from 'react-native'; -import { CreateEventDTO } from '@caldav/shared'; +import { View, Text, Modal, Pressable } from "react-native"; +import { CreateEventDTO } from "@caldav/shared"; type EventConfirmDialogProps = { visible: boolean; @@ -20,7 +20,7 @@ const EventConfirmDialog = ({ // TODO: Confirm button calls onConfirm and closes dialog // TODO: Reject button calls onReject and closes dialog // TODO: Close button or backdrop tap calls onClose - throw new Error('Not implemented'); + throw new Error("Not implemented"); return ( diff --git a/apps/client/src/components/ProposedEventCard.tsx b/apps/client/src/components/ProposedEventCard.tsx index 6e28c98..34d3348 100644 --- a/apps/client/src/components/ProposedEventCard.tsx +++ b/apps/client/src/components/ProposedEventCard.tsx @@ -41,7 +41,10 @@ export const ProposedEventCard = ({ {formatDateTime(event?.startTime)} {event?.description && ( - + {event.description} )} @@ -65,7 +68,10 @@ export const ProposedEventCard = ({ borderColor: currentTheme.confirmButton, }} > - + Annehmen @@ -81,7 +87,10 @@ export const ProposedEventCard = ({ borderColor: currentTheme.rejectButton, }} > - + Ablehnen diff --git a/apps/client/src/services/AuthService.ts b/apps/client/src/services/AuthService.ts index ec79fbe..4c973ff 100644 --- a/apps/client/src/services/AuthService.ts +++ b/apps/client/src/services/AuthService.ts @@ -1,19 +1,19 @@ -import { LoginDTO, CreateUserDTO, AuthResponse } from '@caldav/shared'; +import { LoginDTO, CreateUserDTO, AuthResponse } from "@caldav/shared"; export const AuthService = { login: async (_credentials: LoginDTO): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, register: async (_data: CreateUserDTO): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, logout: async (): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, refresh: async (): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, }; diff --git a/apps/client/src/services/ChatService.ts b/apps/client/src/services/ChatService.ts index 333e561..71c3af0 100644 --- a/apps/client/src/services/ChatService.ts +++ b/apps/client/src/services/ChatService.ts @@ -28,27 +28,32 @@ export const ChatService = { action: EventAction, event?: CreateEventDTO, eventId?: string, - updates?: UpdateEventDTO + updates?: UpdateEventDTO, ): Promise => { const body: ConfirmEventRequest = { action, event, eventId, updates }; - return ApiClient.post(`/chat/confirm/${conversationId}/${messageId}`, body); + return ApiClient.post( + `/chat/confirm/${conversationId}/${messageId}`, + body, + ); }, rejectEvent: async ( conversationId: string, - messageId: string + messageId: string, ): Promise => { - return ApiClient.post(`/chat/reject/${conversationId}/${messageId}`); + return ApiClient.post( + `/chat/reject/${conversationId}/${messageId}`, + ); }, getConversations: async (): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, getConversation: async ( _id: string, - _options?: GetMessagesOptions + _options?: GetMessagesOptions, ): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, }; diff --git a/apps/client/src/services/EventService.ts b/apps/client/src/services/EventService.ts index 0105489..3b14717 100644 --- a/apps/client/src/services/EventService.ts +++ b/apps/client/src/services/EventService.ts @@ -1,27 +1,33 @@ -import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared'; +import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; export const EventService = { getAll: async (): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, getById: async (_id: string): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, - getByDateRange: async (_start: Date, _end: Date): Promise => { - throw new Error('Not implemented'); + getByDateRange: async ( + _start: Date, + _end: Date, + ): Promise => { + throw new Error("Not implemented"); }, create: async (_data: CreateEventDTO): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, - update: async (_id: string, _data: UpdateEventDTO): Promise => { - throw new Error('Not implemented'); + update: async ( + _id: string, + _data: UpdateEventDTO, + ): Promise => { + throw new Error("Not implemented"); }, delete: async (_id: string): Promise => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, }; diff --git a/apps/client/src/services/index.ts b/apps/client/src/services/index.ts index 8556f4c..44a6334 100644 --- a/apps/client/src/services/index.ts +++ b/apps/client/src/services/index.ts @@ -1,4 +1,4 @@ -export { ApiClient, API_BASE_URL } from './ApiClient'; -export { AuthService } from './AuthService'; -export { EventService } from './EventService'; -export { ChatService } from './ChatService'; +export { ApiClient, API_BASE_URL } from "./ApiClient"; +export { AuthService } from "./AuthService"; +export { EventService } from "./EventService"; +export { ChatService } from "./ChatService"; diff --git a/apps/client/src/stores/AuthStore.ts b/apps/client/src/stores/AuthStore.ts index 2ed4466..1856e5b 100644 --- a/apps/client/src/stores/AuthStore.ts +++ b/apps/client/src/stores/AuthStore.ts @@ -1,5 +1,5 @@ -import { create } from 'zustand'; -import { User } from '@caldav/shared'; +import { create } from "zustand"; +import { User } from "@caldav/shared"; interface AuthState { user: User | null; @@ -15,12 +15,12 @@ export const useAuthStore = create((set) => ({ token: null, isAuthenticated: false, login: (_user: User, _token: string) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, logout: () => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, setToken: (_token: string) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, })); diff --git a/apps/client/src/stores/EventsStore.ts b/apps/client/src/stores/EventsStore.ts index 5664f28..b14f5b8 100644 --- a/apps/client/src/stores/EventsStore.ts +++ b/apps/client/src/stores/EventsStore.ts @@ -1,5 +1,5 @@ -import { create } from 'zustand'; -import { CalendarEvent } from '@caldav/shared'; +import { create } from "zustand"; +import { CalendarEvent } from "@caldav/shared"; interface EventsState { events: CalendarEvent[]; @@ -12,15 +12,15 @@ interface EventsState { export const useEventsStore = create((set) => ({ events: [], setEvents: (_events: CalendarEvent[]) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, addEvent: (_event: CalendarEvent) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, updateEvent: (_id: string, _event: Partial) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, deleteEvent: (_id: string) => { - throw new Error('Not implemented'); + throw new Error("Not implemented"); }, })); diff --git a/apps/client/src/stores/index.ts b/apps/client/src/stores/index.ts index f395794..51bdbb1 100644 --- a/apps/client/src/stores/index.ts +++ b/apps/client/src/stores/index.ts @@ -1,2 +1,2 @@ -export { useAuthStore } from './AuthStore'; -export { useEventsStore } from './EventsStore'; +export { useAuthStore } from "./AuthStore"; +export { useEventsStore } from "./EventsStore"; diff --git a/apps/server/src/ai/ClaudeAdapter.ts b/apps/server/src/ai/ClaudeAdapter.ts index 030a3c9..18e55d6 100644 --- a/apps/server/src/ai/ClaudeAdapter.ts +++ b/apps/server/src/ai/ClaudeAdapter.ts @@ -1,18 +1,21 @@ -import Anthropic from '@anthropic-ai/sdk'; -import { AIProvider, AIContext, AIResponse } from '../services/interfaces'; +import Anthropic from "@anthropic-ai/sdk"; +import { AIProvider, AIContext, AIResponse } from "../services/interfaces"; export class ClaudeAdapter implements AIProvider { private client: Anthropic; private model: string; - constructor(apiKey?: string, model: string = 'claude-3-haiku-20240307') { + constructor(apiKey?: string, model: string = "claude-3-haiku-20240307") { this.client = new Anthropic({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY, }); this.model = model; } - async processMessage(message: string, context: AIContext): Promise { - throw new Error('Not implemented'); + async processMessage( + message: string, + context: AIContext, + ): Promise { + throw new Error("Not implemented"); } } diff --git a/apps/server/src/ai/index.ts b/apps/server/src/ai/index.ts index bc63f1a..7938c6d 100644 --- a/apps/server/src/ai/index.ts +++ b/apps/server/src/ai/index.ts @@ -1 +1 @@ -export * from './ClaudeAdapter'; +export * from "./ClaudeAdapter"; diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index d690e34..d9db7a6 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -1,28 +1,35 @@ -import express from 'express'; -import mongoose from 'mongoose'; -import 'dotenv/config' +import express from "express"; +import mongoose from "mongoose"; +import "dotenv/config"; -import { createRoutes } from './routes'; -import { AuthController, ChatController, EventController } from './controllers'; -import { AuthService, ChatService, EventService } from './services'; -import { MongoUserRepository, MongoEventRepository, MongoChatRepository } from './repositories'; -import { ClaudeAdapter } from './ai'; +import { createRoutes } from "./routes"; +import { AuthController, ChatController, EventController } from "./controllers"; +import { AuthService, ChatService, EventService } from "./services"; +import { + MongoUserRepository, + MongoEventRepository, + MongoChatRepository, +} from "./repositories"; +import { ClaudeAdapter } from "./ai"; const app = express(); const port = process.env.PORT || 3000; -const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/caldav'; +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') { +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.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; } @@ -49,34 +56,37 @@ const chatController = new ChatController(chatService); const eventController = new EventController(eventService); // Setup routes -app.use('/api', createRoutes({ - authController, - chatController, - eventController, -})); +app.use( + "/api", + createRoutes({ + authController, + chatController, + eventController, + }), +); // Health check -app.get('/health', (_, res) => { - res.json({ status: 'ok' }); +app.get("/health", (_, res) => { + res.json({ status: "ok" }); }); // AI Test endpoint (for development only) -app.post('/api/ai/test', async (req, res) => { +app.post("/api/ai/test", async (req, res) => { try { const { message } = req.body; if (!message) { - res.status(400).json({ error: 'message is required' }); + res.status(400).json({ error: "message is required" }); return; } const result = await aiProvider.processMessage(message, { - userId: 'test-user', + userId: "test-user", conversationHistory: [], existingEvents: [], currentDate: new Date(), }); res.json(result); } catch (error) { - console.error('AI test error:', error); + console.error("AI test error:", error); res.status(500).json({ error: String(error) }); } }); @@ -85,13 +95,13 @@ app.post('/api/ai/test', async (req, res) => { async function start() { try { await mongoose.connect(mongoUri); - console.log('Connected to MongoDB'); + console.log("Connected to MongoDB"); app.listen(port, () => { console.log(`Server running on port ${port}`); }); } catch (error) { - console.error('Failed to start server:', error); + console.error("Failed to start server:", error); process.exit(1); } } diff --git a/apps/server/src/controllers/AuthController.ts b/apps/server/src/controllers/AuthController.ts index 8393230..46a186d 100644 --- a/apps/server/src/controllers/AuthController.ts +++ b/apps/server/src/controllers/AuthController.ts @@ -1,5 +1,5 @@ -import { Request, Response } from 'express'; -import { AuthService } from '../services'; +import { Request, Response } from "express"; +import { AuthService } from "../services"; export class AuthController { constructor(private authService: AuthService) {} @@ -23,10 +23,10 @@ export class AuthController { } async refresh(req: Request, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async logout(req: Request, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } diff --git a/apps/server/src/controllers/ChatController.ts b/apps/server/src/controllers/ChatController.ts index f69f9dd..52a0110 100644 --- a/apps/server/src/controllers/ChatController.ts +++ b/apps/server/src/controllers/ChatController.ts @@ -1,7 +1,12 @@ -import { Response } from 'express'; -import { SendMessageDTO, CreateEventDTO, UpdateEventDTO, EventAction } from '@caldav/shared'; -import { ChatService } from '../services'; -import { AuthenticatedRequest } from '../middleware'; +import { Response } from "express"; +import { + SendMessageDTO, + CreateEventDTO, + UpdateEventDTO, + EventAction, +} from "@caldav/shared"; +import { ChatService } from "../services"; +import { AuthenticatedRequest } from "../middleware"; export class ChatController { constructor(private chatService: ChatService) {} @@ -13,7 +18,7 @@ export class ChatController { const response = await this.chatService.processMessage(userId, data); res.json(response); } catch (error) { - res.status(500).json({ error: 'Failed to process message' }); + res.status(500).json({ error: "Failed to process message" }); } } @@ -34,11 +39,11 @@ export class ChatController { action, event, eventId, - updates + updates, ); res.json(response); } catch (error) { - res.status(500).json({ error: 'Failed to confirm event' }); + res.status(500).json({ error: "Failed to confirm event" }); } } @@ -46,18 +51,28 @@ export class ChatController { try { const userId = req.user!.userId; const { conversationId, messageId } = req.params; - const response = await this.chatService.rejectEvent(userId, conversationId, messageId); + const response = await this.chatService.rejectEvent( + userId, + conversationId, + messageId, + ); res.json(response); } catch (error) { - res.status(500).json({ error: 'Failed to reject event' }); + res.status(500).json({ error: "Failed to reject event" }); } } - async getConversations(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + async getConversations( + req: AuthenticatedRequest, + res: Response, + ): Promise { + throw new Error("Not implemented"); } - async getConversation(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + async getConversation( + req: AuthenticatedRequest, + res: Response, + ): Promise { + throw new Error("Not implemented"); } } diff --git a/apps/server/src/controllers/EventController.ts b/apps/server/src/controllers/EventController.ts index d6ba38f..0297d40 100644 --- a/apps/server/src/controllers/EventController.ts +++ b/apps/server/src/controllers/EventController.ts @@ -1,31 +1,34 @@ -import { Response } from 'express'; -import { EventService } from '../services'; -import { AuthenticatedRequest } from '../middleware'; +import { Response } from "express"; +import { EventService } from "../services"; +import { AuthenticatedRequest } from "../middleware"; export class EventController { constructor(private eventService: EventService) {} async create(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async getById(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async getAll(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } - async getByDateRange(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + async getByDateRange( + req: AuthenticatedRequest, + res: Response, + ): Promise { + throw new Error("Not implemented"); } async update(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async delete(req: AuthenticatedRequest, res: Response): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } diff --git a/apps/server/src/controllers/index.ts b/apps/server/src/controllers/index.ts index 409a32e..74b22f3 100644 --- a/apps/server/src/controllers/index.ts +++ b/apps/server/src/controllers/index.ts @@ -1,3 +1,3 @@ -export * from './AuthController'; -export * from './ChatController'; -export * from './EventController'; +export * from "./AuthController"; +export * from "./ChatController"; +export * from "./EventController"; diff --git a/apps/server/src/middleware/AuthMiddleware.ts b/apps/server/src/middleware/AuthMiddleware.ts index c52bc21..d1c40fe 100644 --- a/apps/server/src/middleware/AuthMiddleware.ts +++ b/apps/server/src/middleware/AuthMiddleware.ts @@ -1,16 +1,20 @@ -import { Request, Response, NextFunction } from 'express'; -import { verifyToken, TokenPayload } from '../utils/jwt'; +import { Request, Response, NextFunction } from "express"; +import { verifyToken, TokenPayload } from "../utils/jwt"; export interface AuthenticatedRequest extends Request { user?: TokenPayload; } -export function authenticate(req: AuthenticatedRequest, res: Response, next: NextFunction): void { +export function authenticate( + req: AuthenticatedRequest, + res: Response, + next: NextFunction, +): void { // TODO: Implement real JWT verification // Fake user for testing purposes req.user = { - userId: 'fake-user-id', - email: 'test@example.com', + userId: "fake-user-id", + email: "test@example.com", }; next(); } diff --git a/apps/server/src/middleware/index.ts b/apps/server/src/middleware/index.ts index f504f2f..260e539 100644 --- a/apps/server/src/middleware/index.ts +++ b/apps/server/src/middleware/index.ts @@ -1 +1 @@ -export * from './AuthMiddleware'; +export * from "./AuthMiddleware"; diff --git a/apps/server/src/repositories/index.ts b/apps/server/src/repositories/index.ts index b9fe678..92aae12 100644 --- a/apps/server/src/repositories/index.ts +++ b/apps/server/src/repositories/index.ts @@ -1 +1 @@ -export * from './mongo'; +export * from "./mongo"; diff --git a/apps/server/src/repositories/mongo/MongoChatRepository.ts b/apps/server/src/repositories/mongo/MongoChatRepository.ts index 250ca05..bb4b47f 100644 --- a/apps/server/src/repositories/mongo/MongoChatRepository.ts +++ b/apps/server/src/repositories/mongo/MongoChatRepository.ts @@ -1,23 +1,34 @@ -import { ChatMessage, Conversation, CreateMessageDTO, GetMessagesOptions } from '@caldav/shared'; -import { ChatRepository } from '../../services/interfaces'; -import { ChatMessageModel, ConversationModel } from './models'; +import { + ChatMessage, + Conversation, + CreateMessageDTO, + GetMessagesOptions, +} from "@caldav/shared"; +import { ChatRepository } from "../../services/interfaces"; +import { ChatMessageModel, ConversationModel } from "./models"; export class MongoChatRepository implements ChatRepository { // Conversations async getConversationsByUser(userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async createConversation(userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } // Messages (cursor-based pagination) - async getMessages(conversationId: string, options?: GetMessagesOptions): Promise { - throw new Error('Not implemented'); + async getMessages( + conversationId: string, + options?: GetMessagesOptions, + ): Promise { + throw new Error("Not implemented"); } - async createMessage(conversationId: string, message: CreateMessageDTO): Promise { - throw new Error('Not implemented'); + async createMessage( + conversationId: string, + message: CreateMessageDTO, + ): Promise { + throw new Error("Not implemented"); } } diff --git a/apps/server/src/repositories/mongo/MongoEventRepository.ts b/apps/server/src/repositories/mongo/MongoEventRepository.ts index 33ebb6c..d2d304e 100644 --- a/apps/server/src/repositories/mongo/MongoEventRepository.ts +++ b/apps/server/src/repositories/mongo/MongoEventRepository.ts @@ -1,6 +1,6 @@ -import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared'; -import { EventRepository } from '../../services/interfaces'; -import { EventModel } from './models'; +import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; +import { EventRepository } from "../../services/interfaces"; +import { EventModel } from "./models"; export class MongoEventRepository implements EventRepository { async findById(id: string): Promise { @@ -11,15 +11,19 @@ export class MongoEventRepository implements EventRepository { async findByUserId(userId: string): Promise { const events = await EventModel.find({ userId }).sort({ startTime: 1 }); - return events.map(e => e.toJSON() as unknown as CalendarEvent); + return events.map((e) => e.toJSON() as unknown as CalendarEvent); } - async findByDateRange(userId: string, startDate: Date, endDate: Date): Promise { + async findByDateRange( + userId: string, + startDate: Date, + endDate: Date, + ): Promise { const events = await EventModel.find({ userId, - startTime: { $gte: startDate, $lte: endDate } + startTime: { $gte: startDate, $lte: endDate }, }).sort({ startTime: 1 }); - return events.map(e => e.toJSON() as unknown as CalendarEvent); + return events.map((e) => e.toJSON() as unknown as CalendarEvent); } async create(userId: string, data: CreateEventDTO): Promise { @@ -28,7 +32,10 @@ export class MongoEventRepository implements EventRepository { return event.toJSON() as unknown as CalendarEvent; } - async update(id: string, data: UpdateEventDTO): Promise { + async update( + id: string, + data: UpdateEventDTO, + ): Promise { const event = await EventModel.findByIdAndUpdate(id, data, { new: true }); if (!event) return null; return event.toJSON() as unknown as CalendarEvent; diff --git a/apps/server/src/repositories/mongo/MongoUserRepository.ts b/apps/server/src/repositories/mongo/MongoUserRepository.ts index 95cb8e8..b669aa6 100644 --- a/apps/server/src/repositories/mongo/MongoUserRepository.ts +++ b/apps/server/src/repositories/mongo/MongoUserRepository.ts @@ -1,10 +1,10 @@ -import { User } from '@caldav/shared'; -import { UserRepository, CreateUserData } from '../../services/interfaces'; -import { UserModel } from './models'; +import { User } from "@caldav/shared"; +import { UserRepository, CreateUserData } from "../../services/interfaces"; +import { UserModel } from "./models"; export class MongoUserRepository implements UserRepository { async findById(id: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async findByEmail(email: string): Promise { diff --git a/apps/server/src/repositories/mongo/index.ts b/apps/server/src/repositories/mongo/index.ts index 224258a..a8734c4 100644 --- a/apps/server/src/repositories/mongo/index.ts +++ b/apps/server/src/repositories/mongo/index.ts @@ -1,3 +1,3 @@ -export * from './MongoUserRepository'; -export * from './MongoEventRepository'; -export * from './MongoChatRepository'; +export * from "./MongoUserRepository"; +export * from "./MongoEventRepository"; +export * from "./MongoChatRepository"; diff --git a/apps/server/src/repositories/mongo/models/ChatModel.ts b/apps/server/src/repositories/mongo/models/ChatModel.ts index d1fa8bc..ffca712 100644 --- a/apps/server/src/repositories/mongo/models/ChatModel.ts +++ b/apps/server/src/repositories/mongo/models/ChatModel.ts @@ -1,11 +1,18 @@ -import mongoose, { Schema, Document, Model } from 'mongoose'; -import { ChatMessage, Conversation, CreateEventDTO, UpdateEventDTO, ProposedEventChange } from '@caldav/shared'; -import { IdVirtual } from './types'; +import mongoose, { Schema, Document, Model } from "mongoose"; +import { + ChatMessage, + Conversation, + CreateEventDTO, + UpdateEventDTO, + ProposedEventChange, +} from "@caldav/shared"; +import { IdVirtual } from "./types"; -export interface ChatMessageDocument extends Omit, Document { +export interface ChatMessageDocument extends Omit, Document { toJSON(): ChatMessage; } -export interface ConversationDocument extends Omit, Document { +export interface ConversationDocument + extends Omit, Document { toJSON(): Conversation; } @@ -19,7 +26,7 @@ const EventSchema = new Schema( isRecurring: { type: Boolean }, recurrenceRule: { type: String }, }, - { _id: false } + { _id: false }, ); const UpdatesSchema = new Schema( @@ -32,20 +39,30 @@ const UpdatesSchema = new Schema( isRecurring: { type: Boolean }, recurrenceRule: { type: String }, }, - { _id: false } + { _id: false }, ); const ProposedChangeSchema = new Schema( { - action: { type: String, enum: ['create', 'update', 'delete'], required: true }, + action: { + type: String, + enum: ["create", "update", "delete"], + required: true, + }, eventId: { type: String }, event: { type: EventSchema }, updates: { type: UpdatesSchema }, }, - { _id: false } + { _id: false }, ); -const ChatMessageSchema = new Schema, {}, {}, IdVirtual>( +const ChatMessageSchema = new Schema< + ChatMessageDocument, + Model, + {}, + {}, + IdVirtual +>( { conversationId: { type: String, @@ -53,7 +70,7 @@ const ChatMessageSchema = new Schema, {}, {}, IdVirtual>( +const ConversationSchema = new Schema< + ConversationDocument, + Model, + {}, + {}, + IdVirtual +>( { userId: { type: String, @@ -109,8 +132,14 @@ const ConversationSchema = new Schema('ChatMessage', ChatMessageSchema); -export const ConversationModel = mongoose.model('Conversation', ConversationSchema); +export const ChatMessageModel = mongoose.model( + "ChatMessage", + ChatMessageSchema, +); +export const ConversationModel = mongoose.model( + "Conversation", + ConversationSchema, +); diff --git a/apps/server/src/repositories/mongo/models/EventModel.ts b/apps/server/src/repositories/mongo/models/EventModel.ts index efb87b3..e5a3e68 100644 --- a/apps/server/src/repositories/mongo/models/EventModel.ts +++ b/apps/server/src/repositories/mongo/models/EventModel.ts @@ -1,12 +1,18 @@ -import mongoose, { Schema, Document, Model } from 'mongoose'; -import { CalendarEvent } from '@caldav/shared'; -import { IdVirtual } from './types'; +import mongoose, { Schema, Document, Model } from "mongoose"; +import { CalendarEvent } from "@caldav/shared"; +import { IdVirtual } from "./types"; -export interface EventDocument extends Omit, Document { +export interface EventDocument extends Omit, Document { toJSON(): CalendarEvent; } -const EventSchema = new Schema, {}, {}, IdVirtual>( +const EventSchema = new Schema< + EventDocument, + Model, + {}, + {}, + IdVirtual +>( { userId: { type: String, @@ -58,9 +64,9 @@ const EventSchema = new Schema('Event', EventSchema); +export const EventModel = mongoose.model("Event", EventSchema); diff --git a/apps/server/src/repositories/mongo/models/UserModel.ts b/apps/server/src/repositories/mongo/models/UserModel.ts index ba1e40b..0239c1a 100644 --- a/apps/server/src/repositories/mongo/models/UserModel.ts +++ b/apps/server/src/repositories/mongo/models/UserModel.ts @@ -1,12 +1,18 @@ -import mongoose, { Schema, Document, Model } from 'mongoose'; -import { User } from '@caldav/shared'; -import { IdVirtual } from './types'; +import mongoose, { Schema, Document, Model } from "mongoose"; +import { User } from "@caldav/shared"; +import { IdVirtual } from "./types"; -export interface UserDocument extends Omit, Document { +export interface UserDocument extends Omit, Document { toJSON(): User; } -const UserSchema = new Schema, {}, {}, IdVirtual>( +const UserSchema = new Schema< + UserDocument, + Model, + {}, + {}, + IdVirtual +>( { email: { type: String, @@ -43,7 +49,7 @@ const UserSchema = new Schema('User', UserSchema); +export const UserModel = mongoose.model("User", UserSchema); diff --git a/apps/server/src/repositories/mongo/models/index.ts b/apps/server/src/repositories/mongo/models/index.ts index 6ef5ca1..16e6ebb 100644 --- a/apps/server/src/repositories/mongo/models/index.ts +++ b/apps/server/src/repositories/mongo/models/index.ts @@ -1,3 +1,3 @@ -export * from './UserModel'; -export * from './EventModel'; -export * from './ChatModel'; +export * from "./UserModel"; +export * from "./EventModel"; +export * from "./ChatModel"; diff --git a/apps/server/src/routes/auth.routes.ts b/apps/server/src/routes/auth.routes.ts index a3ac2bc..caa047c 100644 --- a/apps/server/src/routes/auth.routes.ts +++ b/apps/server/src/routes/auth.routes.ts @@ -1,13 +1,13 @@ -import { Router } from 'express'; -import { AuthController } from '../controllers'; +import { Router } from "express"; +import { AuthController } from "../controllers"; export function createAuthRoutes(authController: AuthController): Router { const router = Router(); - router.post('/login', (req, res) => authController.login(req, res)); - router.post('/register', (req, res) => authController.register(req, res)); - router.post('/refresh', (req, res) => authController.refresh(req, res)); - router.post('/logout', (req, res) => authController.logout(req, res)); + router.post("/login", (req, res) => authController.login(req, res)); + router.post("/register", (req, res) => authController.register(req, res)); + router.post("/refresh", (req, res) => authController.refresh(req, res)); + router.post("/logout", (req, res) => authController.logout(req, res)); return router; } diff --git a/apps/server/src/routes/chat.routes.ts b/apps/server/src/routes/chat.routes.ts index ba48824..b273e50 100644 --- a/apps/server/src/routes/chat.routes.ts +++ b/apps/server/src/routes/chat.routes.ts @@ -1,17 +1,25 @@ -import { Router } from 'express'; -import { ChatController } from '../controllers'; -import { authenticate } from '../middleware'; +import { Router } from "express"; +import { ChatController } from "../controllers"; +import { authenticate } from "../middleware"; export function createChatRoutes(chatController: ChatController): Router { const router = Router(); router.use(authenticate); - router.post('/message', (req, res) => chatController.sendMessage(req, res)); - router.post('/confirm/:conversationId/:messageId', (req, res) => chatController.confirmEvent(req, res)); - router.post('/reject/:conversationId/:messageId', (req, res) => chatController.rejectEvent(req, res)); - router.get('/conversations', (req, res) => chatController.getConversations(req, res)); - router.get('/conversations/:id', (req, res) => chatController.getConversation(req, res)); + router.post("/message", (req, res) => chatController.sendMessage(req, res)); + router.post("/confirm/:conversationId/:messageId", (req, res) => + chatController.confirmEvent(req, res), + ); + router.post("/reject/:conversationId/:messageId", (req, res) => + chatController.rejectEvent(req, res), + ); + router.get("/conversations", (req, res) => + chatController.getConversations(req, res), + ); + router.get("/conversations/:id", (req, res) => + chatController.getConversation(req, res), + ); return router; } diff --git a/apps/server/src/routes/event.routes.ts b/apps/server/src/routes/event.routes.ts index a7deb5f..c68e47a 100644 --- a/apps/server/src/routes/event.routes.ts +++ b/apps/server/src/routes/event.routes.ts @@ -1,18 +1,18 @@ -import { Router } from 'express'; -import { EventController } from '../controllers'; -import { authenticate } from '../middleware'; +import { Router } from "express"; +import { EventController } from "../controllers"; +import { authenticate } from "../middleware"; export function createEventRoutes(eventController: EventController): Router { const router = Router(); router.use(authenticate); - router.post('/', (req, res) => eventController.create(req, res)); - router.get('/', (req, res) => eventController.getAll(req, res)); - router.get('/range', (req, res) => eventController.getByDateRange(req, res)); - router.get('/:id', (req, res) => eventController.getById(req, res)); - router.put('/:id', (req, res) => eventController.update(req, res)); - router.delete('/:id', (req, res) => eventController.delete(req, res)); + router.post("/", (req, res) => eventController.create(req, res)); + router.get("/", (req, res) => eventController.getAll(req, res)); + router.get("/range", (req, res) => eventController.getByDateRange(req, res)); + router.get("/:id", (req, res) => eventController.getById(req, res)); + router.put("/:id", (req, res) => eventController.update(req, res)); + router.delete("/:id", (req, res) => eventController.delete(req, res)); return router; } diff --git a/apps/server/src/routes/index.ts b/apps/server/src/routes/index.ts index 86bcaac..4920a79 100644 --- a/apps/server/src/routes/index.ts +++ b/apps/server/src/routes/index.ts @@ -1,8 +1,12 @@ -import { Router } from 'express'; -import { createAuthRoutes } from './auth.routes'; -import { createChatRoutes } from './chat.routes'; -import { createEventRoutes } from './event.routes'; -import { AuthController, ChatController, EventController } from '../controllers'; +import { Router } from "express"; +import { createAuthRoutes } from "./auth.routes"; +import { createChatRoutes } from "./chat.routes"; +import { createEventRoutes } from "./event.routes"; +import { + AuthController, + ChatController, + EventController, +} from "../controllers"; export interface Controllers { authController: AuthController; @@ -13,13 +17,13 @@ export interface Controllers { export function createRoutes(controllers: Controllers): Router { const router = Router(); - router.use('/auth', createAuthRoutes(controllers.authController)); - router.use('/chat', createChatRoutes(controllers.chatController)); - router.use('/events', createEventRoutes(controllers.eventController)); + router.use("/auth", createAuthRoutes(controllers.authController)); + router.use("/chat", createChatRoutes(controllers.chatController)); + router.use("/events", createEventRoutes(controllers.eventController)); return router; } -export * from './auth.routes'; -export * from './chat.routes'; -export * from './event.routes'; +export * from "./auth.routes"; +export * from "./chat.routes"; +export * from "./event.routes"; diff --git a/apps/server/src/services/AuthService.ts b/apps/server/src/services/AuthService.ts index 2a20496..3d02033 100644 --- a/apps/server/src/services/AuthService.ts +++ b/apps/server/src/services/AuthService.ts @@ -1,7 +1,7 @@ -import { User, CreateUserDTO, LoginDTO, AuthResponse } from '@caldav/shared'; -import { UserRepository } from './interfaces'; -import * as jwt from '../utils/jwt'; -import * as password from '../utils/password'; +import { User, CreateUserDTO, LoginDTO, AuthResponse } from "@caldav/shared"; +import { UserRepository } from "./interfaces"; +import * as jwt from "../utils/jwt"; +import * as password from "../utils/password"; export class AuthService { constructor(private userRepo: UserRepository) {} @@ -9,21 +9,21 @@ export class AuthService { async login(data: LoginDTO): Promise { const user = await this.userRepo.findByEmail(data.email); if (!user || !user.passwordHash) { - throw new Error('Invalid credentials'); + throw new Error("Invalid credentials"); } const isValid = await password.compare(data.password, user.passwordHash); if (!isValid) { - throw new Error('Invalid credentials'); + throw new Error("Invalid credentials"); } - return { user, accessToken: '' }; + return { user, accessToken: "" }; } async register(data: CreateUserDTO): Promise { const existingUser = await this.userRepo.findByEmail(data.email); if (existingUser) { - throw new Error('Email already exists'); + throw new Error("Email already exists"); } const passwordHash = await password.hash(data.password); @@ -33,14 +33,14 @@ export class AuthService { passwordHash, }); - return { user, accessToken: '' }; + return { user, accessToken: "" }; } async refreshToken(refreshToken: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async logout(userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } diff --git a/apps/server/src/services/ChatService.ts b/apps/server/src/services/ChatService.ts index 24f8806..9b77a4d 100644 --- a/apps/server/src/services/ChatService.ts +++ b/apps/server/src/services/ChatService.ts @@ -1,6 +1,17 @@ -import { ChatMessage, ChatResponse, SendMessageDTO, ConversationSummary, GetMessagesOptions, ProposedEventChange, getDay, CreateEventDTO, UpdateEventDTO, EventAction } from '@caldav/shared'; -import { ChatRepository, EventRepository, AIProvider } from './interfaces'; -import { getWeeksOverview, getMonthOverview } from '../utils/eventFormatters'; +import { + ChatMessage, + ChatResponse, + SendMessageDTO, + ConversationSummary, + GetMessagesOptions, + ProposedEventChange, + getDay, + CreateEventDTO, + UpdateEventDTO, + EventAction, +} from "@caldav/shared"; +import { ChatRepository, EventRepository, AIProvider } from "./interfaces"; +import { getWeeksOverview, getMonthOverview } from "../utils/eventFormatters"; type TestResponse = { content: string; proposedChange?: ProposedEventChange }; @@ -9,139 +20,147 @@ let responseIndex = 8; // Static test responses (event proposals) const staticResponses: TestResponse[] = [ - // {{{ - // Response 0: 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 0: 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 1: 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 1: 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 2: 2-week overview (DYNAMIC - placeholder) - { content: '' }, - // Response 3: Delete "Meeting mit Jens" (DYNAMIC - placeholder) - { content: '' }, - // Response 4: Doctor appointment with description - { - content: "Ich habe dir einen Arzttermin eingetragen. Denk daran, deine Versichertenkarte mitzunehmen!", - proposedChange: { - action: 'create', - event: { - title: "Arzttermin Dr. Müller", - startTime: getDay('Wednesday', 1, 9, 30), - endTime: getDay('Wednesday', 1, 10, 30), - description: "Routineuntersuchung - Versichertenkarte nicht vergessen", - } - } + }, + // Response 2: 2-week overview (DYNAMIC - placeholder) + { content: "" }, + // Response 3: Delete "Meeting mit Jens" (DYNAMIC - placeholder) + { content: "" }, + // Response 4: Doctor appointment with description + { + content: + "Ich habe dir einen Arzttermin eingetragen. Denk daran, deine Versichertenkarte mitzunehmen!", + proposedChange: { + action: "create", + event: { + title: "Arzttermin Dr. Müller", + startTime: getDay("Wednesday", 1, 9, 30), + endTime: getDay("Wednesday", 1, 10, 30), + description: "Routineuntersuchung - Versichertenkarte nicht vergessen", + }, }, - // Response 5: Birthday - yearly recurring - { - content: "Geburtstage vergisst man leicht - aber nicht mit mir! Ich habe Mamas Geburtstag eingetragen:", - proposedChange: { - action: 'create', - event: { - title: "Mamas Geburtstag", - startTime: getDay('Thursday', 2, 0, 0), - endTime: getDay('Thursday', 2, 23, 59), - isRecurring: true, - recurrenceRule: "FREQ=YEARLY", - } - } + }, + // Response 5: Birthday - yearly recurring + { + content: + "Geburtstage vergisst man leicht - aber nicht mit mir! Ich habe Mamas Geburtstag eingetragen:", + proposedChange: { + action: "create", + event: { + title: "Mamas Geburtstag", + startTime: getDay("Thursday", 2, 0, 0), + endTime: getDay("Thursday", 2, 23, 59), + isRecurring: true, + recurrenceRule: "FREQ=YEARLY", + }, }, - // Response 6: Gym - recurring for 2 months (8 weeks) - { - content: "Perfekt! Ich habe dein Probetraining eingetragen - jeden Dienstag für die nächsten 2 Monate:", - proposedChange: { - action: 'create', - event: { - title: "Fitnessstudio Probetraining", - startTime: getDay('Tuesday', 1, 18, 0), - endTime: getDay('Tuesday', 1, 19, 30), - isRecurring: true, - recurrenceRule: "FREQ=WEEKLY;BYDAY=TU;COUNT=8", - } - } + }, + // Response 6: Gym - recurring for 2 months (8 weeks) + { + content: + "Perfekt! Ich habe dein Probetraining eingetragen - jeden Dienstag für die nächsten 2 Monate:", + proposedChange: { + action: "create", + event: { + title: "Fitnessstudio Probetraining", + startTime: getDay("Tuesday", 1, 18, 0), + endTime: getDay("Tuesday", 1, 19, 30), + isRecurring: true, + recurrenceRule: "FREQ=WEEKLY;BYDAY=TU;COUNT=8", + }, }, - // Response 7: 1-week overview (DYNAMIC - placeholder) - { content: '' }, - // Response 8: Help response (text only) - { - content: "Ich bin dein Kalender-Assistent! Du kannst mir einfach sagen, welche Termine du erstellen, ändern oder löschen möchtest. Zum Beispiel:\n\n" + - "• \"Erstelle einen Termin für morgen um 15 Uhr\"\n" + - "• \"Was habe ich nächste Woche vor?\"\n" + - "• \"Verschiebe das Meeting auf Donnerstag\"\n\n" + - "Wie kann ich dir helfen?", + }, + // Response 7: 1-week overview (DYNAMIC - placeholder) + { content: "" }, + // Response 8: Help response (text only) + { + content: + "Ich bin dein Kalender-Assistent! Du kannst mir einfach sagen, welche Termine du erstellen, ändern oder löschen möchtest. Zum Beispiel:\n\n" + + '• "Erstelle einen Termin für morgen um 15 Uhr"\n' + + '• "Was habe ich nächste Woche vor?"\n' + + '• "Verschiebe das Meeting auf Donnerstag"\n\n' + + "Wie kann ich dir helfen?", + }, + // Response 9: Phone call - short appointment + { + content: + "Alles klar! Ich habe das Telefonat mit deiner Mutter eingetragen:", + proposedChange: { + action: "create", + event: { + title: "Telefonat mit Mama", + startTime: getDay("Sunday", 0, 11, 0), + endTime: getDay("Sunday", 0, 11, 30), + }, }, - // Response 9: Phone call - short appointment - { - content: "Alles klar! Ich habe das Telefonat mit deiner Mutter eingetragen:", - proposedChange: { - action: 'create', - event: { - title: "Telefonat mit Mama", - startTime: getDay('Sunday', 0, 11, 0), - endTime: getDay('Sunday', 0, 11, 30), - } - } + }, + // Response 10: Update "Telefonat mit Mama" +2 days (DYNAMIC - placeholder) + { content: "" }, + // Response 11: Birthday party - evening event + { + content: "Super! Die Geburtstagsfeier ist eingetragen. Viel Spaß!", + proposedChange: { + action: "create", + event: { + title: "Geburtstagsfeier Lisa", + startTime: getDay("Saturday", 2, 19, 0), + endTime: getDay("Saturday", 2, 23, 0), + description: "Geschenk: Buch über Fotografie", + }, }, - // Response 10: Update "Telefonat mit Mama" +2 days (DYNAMIC - placeholder) - { content: '' }, - // Response 11: Birthday party - evening event - { - content: "Super! Die Geburtstagsfeier ist eingetragen. Viel Spaß!", - proposedChange: { - action: 'create', - event: { - title: "Geburtstagsfeier Lisa", - startTime: getDay('Saturday', 2, 19, 0), - endTime: getDay('Saturday', 2, 23, 0), - description: "Geschenk: Buch über Fotografie", - } - } + }, + // Response 12: Language course - limited to 8 weeks + { + content: + "Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag für die nächsten 8 Wochen:", + proposedChange: { + action: "create", + event: { + title: "Spanischkurs VHS", + startTime: getDay("Thursday", 1, 19, 0), + endTime: getDay("Thursday", 1, 20, 30), + isRecurring: true, + recurrenceRule: "FREQ=WEEKLY;BYDAY=TH;COUNT=8", + }, }, - // Response 12: Language course - limited to 8 weeks - { - content: "Dein Spanischkurs ist eingetragen! Er läuft jeden Donnerstag für die nächsten 8 Wochen:", - proposedChange: { - action: 'create', - event: { - title: "Spanischkurs VHS", - startTime: getDay('Thursday', 1, 19, 0), - endTime: getDay('Thursday', 1, 20, 30), - isRecurring: true, - recurrenceRule: "FREQ=WEEKLY;BYDAY=TH;COUNT=8", - } - } - }, - // Response 13: Monthly overview (DYNAMIC - placeholder) - { content: '' }, - // }}} + }, + // Response 13: Monthly overview (DYNAMIC - placeholder) + { content: "" }, + // }}} ]; async function getTestResponse( index: number, eventRepo: EventRepository, - userId: string + userId: string, ): Promise { const responseIdx = index % staticResponses.length; @@ -153,14 +172,15 @@ async function getTestResponse( if (responseIdx === 3) { // Delete "Meeting mit Jens" const events = await eventRepo.findByUserId(userId); - const jensEvent = events.find(e => e.title === 'Meeting mit Jens'); + const jensEvent = events.find((e) => e.title === "Meeting mit Jens"); if (jensEvent) { return { - content: "Alles klar, ich lösche den Termin 'Meeting mit Jens' für dich:", + content: + "Alles klar, ich lösche den Termin 'Meeting mit Jens' für dich:", proposedChange: { - action: 'delete', + action: "delete", eventId: jensEvent.id, - } + }, }; } return { content: "Ich konnte keinen Termin 'Meeting mit Jens' finden." }; @@ -173,16 +193,17 @@ async function getTestResponse( if (responseIdx === 10) { // Update "Telefonat mit Mama" +2 days const events = await eventRepo.findByUserId(userId); - const mamaEvent = events.find(e => e.title === 'Telefonat mit Mama'); + const mamaEvent = events.find((e) => e.title === "Telefonat mit Mama"); if (mamaEvent) { const newStart = new Date(mamaEvent.startTime); newStart.setDate(newStart.getDate() + 2); const newEnd = new Date(mamaEvent.endTime); newEnd.setDate(newEnd.getDate() + 2); return { - content: "Alles klar, ich verschiebe das Telefonat mit Mama um 2 Tage nach hinten:", + content: + "Alles klar, ich verschiebe das Telefonat mit Mama um 2 Tage nach hinten:", proposedChange: { - action: 'update', + action: "update", eventId: mamaEvent.id, updates: { startTime: newStart, endTime: newEnd }, // Include event with new times for display @@ -191,8 +212,8 @@ async function getTestResponse( startTime: newStart, endTime: newEnd, description: mamaEvent.description, - } - } + }, + }, }; } return { content: "Ich konnte keinen Termin 'Telefonat mit Mama' finden." }; @@ -200,7 +221,14 @@ async function getTestResponse( if (responseIdx === 13) { const now = new Date(); - return { content: await getMonthOverview(eventRepo, userId, now.getFullYear(), now.getMonth()) }; + return { + content: await getMonthOverview( + eventRepo, + userId, + now.getFullYear(), + now.getMonth(), + ), + }; } return staticResponses[responseIdx]; @@ -210,17 +238,24 @@ export class ChatService { constructor( private chatRepo: ChatRepository, private eventRepo: EventRepository, - private aiProvider: AIProvider + private aiProvider: AIProvider, ) {} - async processMessage(userId: string, data: SendMessageDTO): Promise { - const response = await getTestResponse(responseIndex, this.eventRepo, userId); + async processMessage( + userId: string, + data: SendMessageDTO, + ): Promise { + const response = await getTestResponse( + responseIndex, + this.eventRepo, + userId, + ); responseIndex++; const message: ChatMessage = { id: Date.now().toString(), - conversationId: data.conversationId || 'temp-conv-id', - sender: 'assistant', + conversationId: data.conversationId || "temp-conv-id", + sender: "assistant", content: response.content, proposedChange: response.proposedChange, }; @@ -235,49 +270,57 @@ export class ChatService { action: EventAction, event?: CreateEventDTO, eventId?: string, - updates?: UpdateEventDTO + updates?: UpdateEventDTO, ): Promise { let content: string; - if (action === 'create' && event) { + if (action === "create" && event) { const createdEvent = await this.eventRepo.create(userId, event); content = `Der Termin "${createdEvent.title}" wurde erstellt.`; - } else if (action === 'update' && eventId && updates) { + } else if (action === "update" && eventId && updates) { const updatedEvent = await this.eventRepo.update(eventId, updates); content = updatedEvent ? `Der Termin "${updatedEvent.title}" wurde aktualisiert.` - : 'Termin nicht gefunden.'; - } else if (action === 'delete' && eventId) { + : "Termin nicht gefunden."; + } else if (action === "delete" && eventId) { await this.eventRepo.delete(eventId); - content = 'Der Termin wurde gelöscht.'; + content = "Der Termin wurde gelöscht."; } else { - content = 'Ungültige Aktion.'; + content = "Ungültige Aktion."; } const message: ChatMessage = { id: Date.now().toString(), conversationId, - sender: 'assistant', + sender: "assistant", content, }; return { message, conversationId }; } - async rejectEvent(userId: string, conversationId: string, messageId: string): Promise { + async rejectEvent( + userId: string, + conversationId: string, + messageId: string, + ): Promise { const message: ChatMessage = { id: Date.now().toString(), conversationId, - sender: 'assistant', - content: 'Der Vorschlag wurde abgelehnt.', + sender: "assistant", + content: "Der Vorschlag wurde abgelehnt.", }; return { message, conversationId }; } async getConversations(userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } - async getConversation(userId: string, conversationId: string, options?: GetMessagesOptions): Promise { - throw new Error('Not implemented'); + async getConversation( + userId: string, + conversationId: string, + options?: GetMessagesOptions, + ): Promise { + throw new Error("Not implemented"); } } diff --git a/apps/server/src/services/EventService.ts b/apps/server/src/services/EventService.ts index acbace8..90466b8 100644 --- a/apps/server/src/services/EventService.ts +++ b/apps/server/src/services/EventService.ts @@ -1,30 +1,38 @@ -import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared'; -import { EventRepository } from './interfaces'; +import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; +import { EventRepository } from "./interfaces"; export class EventService { constructor(private eventRepo: EventRepository) {} async create(userId: string, data: CreateEventDTO): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async getById(id: string, userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async getAll(userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } - async getByDateRange(userId: string, startDate: Date, endDate: Date): Promise { - throw new Error('Not implemented'); + async getByDateRange( + userId: string, + startDate: Date, + endDate: Date, + ): Promise { + throw new Error("Not implemented"); } - async update(id: string, userId: string, data: UpdateEventDTO): Promise { - throw new Error('Not implemented'); + async update( + id: string, + userId: string, + data: UpdateEventDTO, + ): Promise { + throw new Error("Not implemented"); } async delete(id: string, userId: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } diff --git a/apps/server/src/services/index.ts b/apps/server/src/services/index.ts index 463f4c5..3f582fe 100644 --- a/apps/server/src/services/index.ts +++ b/apps/server/src/services/index.ts @@ -1,4 +1,4 @@ -export * from './AuthService'; -export * from './ChatService'; -export * from './EventService'; -export * from './interfaces'; +export * from "./AuthService"; +export * from "./ChatService"; +export * from "./EventService"; +export * from "./interfaces"; diff --git a/apps/server/src/services/interfaces/AIProvider.ts b/apps/server/src/services/interfaces/AIProvider.ts index 87337d1..92ae297 100644 --- a/apps/server/src/services/interfaces/AIProvider.ts +++ b/apps/server/src/services/interfaces/AIProvider.ts @@ -1,4 +1,8 @@ -import { CalendarEvent, ChatMessage, ProposedEventChange } from '@caldav/shared'; +import { + CalendarEvent, + ChatMessage, + ProposedEventChange, +} from "@caldav/shared"; export interface AIContext { userId: string; diff --git a/apps/server/src/services/interfaces/ChatRepository.ts b/apps/server/src/services/interfaces/ChatRepository.ts index ca95e5a..6ea9cf5 100644 --- a/apps/server/src/services/interfaces/ChatRepository.ts +++ b/apps/server/src/services/interfaces/ChatRepository.ts @@ -1,4 +1,9 @@ -import { ChatMessage, Conversation, CreateMessageDTO, GetMessagesOptions } from '@caldav/shared'; +import { + ChatMessage, + Conversation, + CreateMessageDTO, + GetMessagesOptions, +} from "@caldav/shared"; export interface ChatRepository { // Conversations @@ -6,6 +11,12 @@ export interface ChatRepository { createConversation(userId: string): Promise; // Messages (cursor-based pagination) - getMessages(conversationId: string, options?: GetMessagesOptions): Promise; - createMessage(conversationId: string, message: CreateMessageDTO): Promise; + getMessages( + conversationId: string, + options?: GetMessagesOptions, + ): Promise; + createMessage( + conversationId: string, + message: CreateMessageDTO, + ): Promise; } diff --git a/apps/server/src/services/interfaces/EventRepository.ts b/apps/server/src/services/interfaces/EventRepository.ts index 5c4342d..1b01c7e 100644 --- a/apps/server/src/services/interfaces/EventRepository.ts +++ b/apps/server/src/services/interfaces/EventRepository.ts @@ -1,9 +1,13 @@ -import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from '@caldav/shared'; +import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@caldav/shared"; export interface EventRepository { findById(id: string): Promise; findByUserId(userId: string): Promise; - findByDateRange(userId: string, startDate: Date, endDate: Date): Promise; + findByDateRange( + userId: string, + startDate: Date, + endDate: Date, + ): Promise; create(userId: string, data: CreateEventDTO): Promise; update(id: string, data: UpdateEventDTO): Promise; delete(id: string): Promise; diff --git a/apps/server/src/services/interfaces/UserRepository.ts b/apps/server/src/services/interfaces/UserRepository.ts index 7103bfe..1276540 100644 --- a/apps/server/src/services/interfaces/UserRepository.ts +++ b/apps/server/src/services/interfaces/UserRepository.ts @@ -1,4 +1,4 @@ -import { User } from '@caldav/shared'; +import { User } from "@caldav/shared"; export interface CreateUserData { email: string; diff --git a/apps/server/src/services/interfaces/index.ts b/apps/server/src/services/interfaces/index.ts index d568496..a30b23d 100644 --- a/apps/server/src/services/interfaces/index.ts +++ b/apps/server/src/services/interfaces/index.ts @@ -1,4 +1,4 @@ -export * from './AIProvider'; -export * from './UserRepository'; -export * from './EventRepository'; -export * from './ChatRepository'; +export * from "./AIProvider"; +export * from "./UserRepository"; +export * from "./EventRepository"; +export * from "./ChatRepository"; diff --git a/apps/server/src/utils/eventFormatters.ts b/apps/server/src/utils/eventFormatters.ts index c879dbf..105070a 100644 --- a/apps/server/src/utils/eventFormatters.ts +++ b/apps/server/src/utils/eventFormatters.ts @@ -4,34 +4,37 @@ import { DAY_TO_GERMAN, DAY_TO_GERMAN_SHORT, MONTH_TO_GERMAN, -} from '@caldav/shared'; -import { EventRepository } from '../services/interfaces'; -import { expandRecurringEvents, ExpandedEvent } from './recurrenceExpander'; +} from "@caldav/shared"; +import { EventRepository } from "../services/interfaces"; +import { expandRecurringEvents, ExpandedEvent } from "./recurrenceExpander"; // Private formatting helpers function formatTime(date: Date): string { - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); + const hours = date.getHours().toString().padStart(2, "0"); + const minutes = date.getMinutes().toString().padStart(2, "0"); return `${hours}:${minutes}`; } function formatDateShort(date: Date): string { - const day = date.getDate().toString().padStart(2, '0'); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); return `${day}.${month}.`; } function getWeekNumber(date: Date): number { - const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const d = new Date( + Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()), + ); const dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); - return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7); } function formatWeeksText(events: ExpandedEvent[], weeks: number): string { - const weeksText = weeks === 1 ? 'die nächste Woche' : `die nächsten ${weeks} Wochen`; + const weeksText = + weeks === 1 ? "die nächste Woche" : `die nächsten ${weeks} Wochen`; if (events.length === 0) { return `Du hast für ${weeksText} keine Termine.`; @@ -47,8 +50,10 @@ function formatWeeksText(events: ExpandedEvent[], weeks: number): string { lines.push(`${weekday}, ${dateStr} - ${timeStr} Uhr: ${event.title}`); } - lines.push(`\nInsgesamt ${events.length} Termin${events.length === 1 ? '' : 'e'}.`); - return lines.join('\n'); + lines.push( + `\nInsgesamt ${events.length} Termin${events.length === 1 ? "" : "e"}.`, + ); + return lines.join("\n"); } function formatMonthText(events: ExpandedEvent[], monthName: string): string { @@ -66,13 +71,17 @@ function formatMonthText(events: ExpandedEvent[], monthName: string): string { weekGroups.get(weekNum)!.push(event); } - const lines: string[] = [`Hier ist deine Monatsübersicht für ${monthName}:\n`]; + const lines: string[] = [ + `Hier ist deine Monatsübersicht für ${monthName}:\n`, + ]; // Sort weeks and format const sortedWeeks = Array.from(weekGroups.keys()).sort((a, b) => a - b); for (const weekNum of sortedWeeks) { const weekEvents = weekGroups.get(weekNum)!; - lines.push(`KW ${weekNum}: ${weekEvents.length} Termin${weekEvents.length === 1 ? '' : 'e'}`); + lines.push( + `KW ${weekNum}: ${weekEvents.length} Termin${weekEvents.length === 1 ? "" : "e"}`, + ); for (const event of weekEvents) { const day = DAY_INDEX_TO_DAY[event.occurrenceStart.getDay()]; @@ -81,11 +90,13 @@ function formatMonthText(events: ExpandedEvent[], monthName: string): string { const timeStr = formatTime(event.occurrenceStart); lines.push(` • ${weekdayShort} ${dateStr}, ${timeStr}: ${event.title}`); } - lines.push(''); + lines.push(""); } - lines.push(`Insgesamt ${events.length} Termin${events.length === 1 ? '' : 'e'} im ${monthName}.`); - return lines.join('\n'); + lines.push( + `Insgesamt ${events.length} Termin${events.length === 1 ? "" : "e"} im ${monthName}.`, + ); + return lines.join("\n"); } // Public API @@ -97,7 +108,7 @@ function formatMonthText(events: ExpandedEvent[], monthName: string): string { export async function getWeeksOverview( eventRepo: EventRepository, userId: string, - weeks: number + weeks: number, ): Promise { const now = new Date(); const endDate = new Date(now.getTime() + weeks * 7 * 24 * 60 * 60 * 1000); @@ -114,7 +125,7 @@ export async function getMonthOverview( eventRepo: EventRepository, userId: string, year: number, - month: number + month: number, ): Promise { const startOfMonth = new Date(year, month, 1); const endOfMonth = new Date(year, month + 1, 0, 23, 59, 59); diff --git a/apps/server/src/utils/index.ts b/apps/server/src/utils/index.ts index 41ed166..dab22a7 100644 --- a/apps/server/src/utils/index.ts +++ b/apps/server/src/utils/index.ts @@ -1,2 +1,2 @@ -export * from './jwt'; -export * from './password'; +export * from "./jwt"; +export * from "./password"; diff --git a/apps/server/src/utils/jwt.ts b/apps/server/src/utils/jwt.ts index c1e571f..e496ff4 100644 --- a/apps/server/src/utils/jwt.ts +++ b/apps/server/src/utils/jwt.ts @@ -1,4 +1,4 @@ -import jwt from 'jsonwebtoken'; +import jwt from "jsonwebtoken"; export interface TokenPayload { userId: string; @@ -10,17 +10,17 @@ export interface JWTConfig { expiresIn: string; } -const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; -const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '1h'; +const JWT_SECRET = process.env.JWT_SECRET || "your-secret-key"; +const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || "1h"; export function signToken(payload: TokenPayload): string { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } export function verifyToken(token: string): TokenPayload { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } export function decodeToken(token: string): TokenPayload | null { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } diff --git a/apps/server/src/utils/password.ts b/apps/server/src/utils/password.ts index 508a567..2f986b6 100644 --- a/apps/server/src/utils/password.ts +++ b/apps/server/src/utils/password.ts @@ -1,4 +1,4 @@ -import bcrypt from 'bcrypt'; +import bcrypt from "bcrypt"; const SALT_ROUNDS = 10; @@ -6,6 +6,9 @@ export async function hash(password: string): Promise { return bcrypt.hash(password, SALT_ROUNDS); } -export async function compare(password: string, hash: string): Promise { +export async function compare( + password: string, + hash: string, +): Promise { return bcrypt.compare(password, hash); } diff --git a/apps/server/src/utils/recurrenceExpander.ts b/apps/server/src/utils/recurrenceExpander.ts index adf16b0..a5e9cdd 100644 --- a/apps/server/src/utils/recurrenceExpander.ts +++ b/apps/server/src/utils/recurrenceExpander.ts @@ -1,5 +1,5 @@ -import { RRule, rrulestr } from 'rrule'; -import { CalendarEvent } from '@caldav/shared'; +import { RRule, rrulestr } from "rrule"; +import { CalendarEvent } from "@caldav/shared"; export interface ExpandedEvent extends CalendarEvent { occurrenceStart: Date; @@ -9,14 +9,16 @@ export interface ExpandedEvent extends CalendarEvent { // Convert local time to "fake UTC" for rrule // rrule interprets all dates as UTC internally, so we need to trick it function toRRuleDate(date: Date): Date { - return new Date(Date.UTC( - date.getFullYear(), - date.getMonth(), - date.getDate(), - date.getHours(), - date.getMinutes(), - date.getSeconds() - )); + return new Date( + Date.UTC( + date.getFullYear(), + date.getMonth(), + date.getDate(), + date.getHours(), + date.getMinutes(), + date.getSeconds(), + ), + ); } // Convert rrule result back to local time @@ -27,7 +29,7 @@ function fromRRuleDate(date: Date): Date { date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), - date.getUTCSeconds() + date.getUTCSeconds(), ); } @@ -38,7 +40,7 @@ function fromRRuleDate(date: Date): Date { export function expandRecurringEvents( events: CalendarEvent[], rangeStart: Date, - rangeEnd: Date + rangeEnd: Date, ): ExpandedEvent[] { const expanded: ExpandedEvent[] = []; @@ -61,13 +63,15 @@ export function expandRecurringEvents( // Recurring event: parse RRULE and expand try { - const rule = rrulestr(`DTSTART:${formatRRuleDateString(startTime)}\nRRULE:${event.recurrenceRule}`); + const rule = rrulestr( + `DTSTART:${formatRRuleDateString(startTime)}\nRRULE:${event.recurrenceRule}`, + ); // Get occurrences within the range (using fake UTC dates) const occurrences = rule.between( toRRuleDate(rangeStart), toRRuleDate(rangeEnd), - true // inclusive + true, // inclusive ); for (const occurrence of occurrences) { @@ -82,7 +86,10 @@ export function expandRecurringEvents( } } catch (error) { // If RRULE parsing fails, include the event as a single occurrence - console.error(`Failed to parse recurrence rule for event ${event.id}:`, error); + console.error( + `Failed to parse recurrence rule for event ${event.id}:`, + error, + ); expanded.push({ ...event, occurrenceStart: startTime, @@ -92,7 +99,9 @@ export function expandRecurringEvents( } // Sort by occurrence start time - expanded.sort((a, b) => a.occurrenceStart.getTime() - b.occurrenceStart.getTime()); + expanded.sort( + (a, b) => a.occurrenceStart.getTime() - b.occurrenceStart.getTime(), + ); return expanded; } @@ -100,10 +109,10 @@ export function expandRecurringEvents( // Format date as RRULE DTSTART string (YYYYMMDDTHHMMSS) function formatRRuleDateString(date: Date): string { const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + const hours = String(date.getHours()).padStart(2, "0"); + const minutes = String(date.getMinutes()).padStart(2, "0"); + const seconds = String(date.getSeconds()).padStart(2, "0"); return `${year}${month}${day}T${hours}${minutes}${seconds}`; } diff --git a/package-lock.json b/package-lock.json index 8fada5e..787ef2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ ], "devDependencies": { "eslint": "^9.25.0", + "prettier": "^3.7.4", "typescript": "~5.9.2" } }, @@ -11757,7 +11758,6 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/package.json b/package.json index 9b6da01..f7fcb8e 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ ], "devDependencies": { "eslint": "^9.25.0", + "prettier": "^3.7.4", "typescript": "~5.9.2" } } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 0e1c231..aa7832e 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,2 +1,2 @@ -export * from './models'; -export * from './utils'; +export * from "./models"; +export * from "./utils"; diff --git a/packages/shared/src/models/ChatMessage.ts b/packages/shared/src/models/ChatMessage.ts index 6d138db..3acda68 100644 --- a/packages/shared/src/models/ChatMessage.ts +++ b/packages/shared/src/models/ChatMessage.ts @@ -1,14 +1,14 @@ -import { CreateEventDTO, UpdateEventDTO } from './CalendarEvent'; +import { CreateEventDTO, UpdateEventDTO } from "./CalendarEvent"; -export type MessageSender = 'user' | 'assistant'; +export type MessageSender = "user" | "assistant"; -export type EventAction = 'create' | 'update' | 'delete'; +export type EventAction = "create" | "update" | "delete"; export interface ProposedEventChange { action: EventAction; - eventId?: string; // Required for update/delete - event?: CreateEventDTO; // Required for create - updates?: UpdateEventDTO; // Required for update + eventId?: string; // Required for update/delete + event?: CreateEventDTO; // Required for create + updates?: UpdateEventDTO; // Required for update } export interface ChatMessage { @@ -39,8 +39,8 @@ export interface CreateMessageDTO { } export interface GetMessagesOptions { - before?: string; // Message ID - load messages before this one - limit?: number; // Default: 20 + before?: string; // Message ID - load messages before this one + limit?: number; // Default: 20 } export interface ChatResponse { diff --git a/packages/shared/src/models/Constants.ts b/packages/shared/src/models/Constants.ts index 42d8203..e117253 100644 --- a/packages/shared/src/models/Constants.ts +++ b/packages/shared/src/models/Constants.ts @@ -39,40 +39,48 @@ export const DAY_INDEX: Record = { }; // Mapping from Date.getDay() index (0=Sunday) to Day type -export const DAY_INDEX_TO_DAY: Day[] = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; +export const DAY_INDEX_TO_DAY: Day[] = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +]; // German translations export const DAY_TO_GERMAN: Record = { - Monday: 'Montag', - Tuesday: 'Dienstag', - Wednesday: 'Mittwoch', - Thursday: 'Donnerstag', - Friday: 'Freitag', - Saturday: 'Samstag', - Sunday: 'Sonntag', + Monday: "Montag", + Tuesday: "Dienstag", + Wednesday: "Mittwoch", + Thursday: "Donnerstag", + Friday: "Freitag", + Saturday: "Samstag", + Sunday: "Sonntag", }; export const DAY_TO_GERMAN_SHORT: Record = { - Monday: 'Mo', - Tuesday: 'Di', - Wednesday: 'Mi', - Thursday: 'Do', - Friday: 'Fr', - Saturday: 'Sa', - Sunday: 'So', + Monday: "Mo", + Tuesday: "Di", + Wednesday: "Mi", + Thursday: "Do", + Friday: "Fr", + Saturday: "Sa", + Sunday: "So", }; export const MONTH_TO_GERMAN: Record = { - January: 'Januar', - February: 'Februar', - March: 'März', - April: 'April', - May: 'Mai', - June: 'Juni', - July: 'Juli', - August: 'August', - September: 'September', - October: 'Oktober', - November: 'November', - December: 'Dezember', + January: "Januar", + February: "Februar", + March: "März", + April: "April", + May: "Mai", + June: "Juni", + July: "Juli", + August: "August", + September: "September", + October: "Oktober", + November: "November", + December: "Dezember", }; diff --git a/packages/shared/src/models/User.ts b/packages/shared/src/models/User.ts index 30485c0..b207330 100644 --- a/packages/shared/src/models/User.ts +++ b/packages/shared/src/models/User.ts @@ -19,7 +19,7 @@ export interface LoginDTO { } export interface AuthResponse { - user: Omit; + user: Omit; accessToken: string; refreshToken?: string; } diff --git a/packages/shared/src/models/index.ts b/packages/shared/src/models/index.ts index f3c584f..de105fb 100644 --- a/packages/shared/src/models/index.ts +++ b/packages/shared/src/models/index.ts @@ -1,4 +1,4 @@ -export * from './User'; -export * from './CalendarEvent'; -export * from './ChatMessage'; -export * from './Constants'; +export * from "./User"; +export * from "./CalendarEvent"; +export * from "./ChatMessage"; +export * from "./Constants"; diff --git a/packages/shared/src/utils/dateHelpers.ts b/packages/shared/src/utils/dateHelpers.ts index 17533a4..a0a94b5 100644 --- a/packages/shared/src/utils/dateHelpers.ts +++ b/packages/shared/src/utils/dateHelpers.ts @@ -1,4 +1,4 @@ -import { Day, DAY_INDEX } from '../models/Constants'; +import { Day, DAY_INDEX } from "../models/Constants"; /** * Get a date for a specific weekday relative to today. @@ -7,7 +7,12 @@ import { Day, DAY_INDEX } from '../models/Constants'; * @param hour - Hour of day (0-23) * @param minute - Minute (0-59) */ -export function getDay(day: Day, offset: number, hour: number, minute: number): Date { +export function getDay( + day: Day, + offset: number, + hour: number, + minute: number, +): Date { const today = new Date(); const currentDay = today.getDay(); const targetDay = DAY_INDEX[day]; diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index 159aee7..dabda35 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -1 +1 @@ -export * from './dateHelpers'; +export * from "./dateHelpers";