mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 00:21:26 +02:00
fix(types): resolve TypeScript errors across multiple packages
- bot-services: Add registerAsync to AI, Calendar, Clock, Todo modules - bot-services: Add convenience methods to ClockService for bot handlers - bot-services: Make CreateEventInput.endTime optional with sensible defaults - bot-services: Fix empty interface ESLint errors (use type aliases) - questions-backend: Add missing schema columns (isDefault, sortOrder, deletedAt) - questions-backend: Fix or() return type handling in question service - questions-web: Add guard for undefined question ID in route params - skilltree-web: Fix DBSchema type by not extending idb interface directly - calendar-web: Fix Check icon prop (use weight instead of strokeWidth) - matrix-mana-bot: Update clock handler to use new service methods Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
91143a497b
commit
1733580d05
14 changed files with 314 additions and 37 deletions
|
|
@ -1,8 +1,13 @@
|
|||
import { Module, DynamicModule } from '@nestjs/common';
|
||||
import { Module, DynamicModule, Provider, Type, ModuleMetadata } from '@nestjs/common';
|
||||
import { ClockService } from './clock.service';
|
||||
import { ClockServiceConfig } from './types';
|
||||
|
||||
export interface ClockModuleOptions extends Partial<ClockServiceConfig> {}
|
||||
export type ClockModuleOptions = Partial<ClockServiceConfig>;
|
||||
|
||||
export interface ClockModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
||||
useFactory: (...args: unknown[]) => Promise<ClockModuleOptions> | ClockModuleOptions;
|
||||
inject?: (Type<unknown> | string | symbol)[];
|
||||
}
|
||||
|
||||
@Module({})
|
||||
export class ClockModule {
|
||||
|
|
@ -42,4 +47,29 @@ export class ClockModule {
|
|||
exports: [ClockService],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register asynchronously with factory function
|
||||
*/
|
||||
static registerAsync(options: ClockModuleAsyncOptions): DynamicModule {
|
||||
const configProvider: Provider = {
|
||||
provide: 'CLOCK_SERVICE_CONFIG',
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject || [],
|
||||
};
|
||||
|
||||
return {
|
||||
module: ClockModule,
|
||||
imports: options.imports || [],
|
||||
providers: [
|
||||
configProvider,
|
||||
{
|
||||
provide: ClockService,
|
||||
useFactory: (config: Partial<ClockServiceConfig>) => new ClockService(config),
|
||||
inject: ['CLOCK_SERVICE_CONFIG'],
|
||||
},
|
||||
],
|
||||
exports: [ClockService],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,4 +267,116 @@ export class ClockService {
|
|||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
// ===== Convenience Methods for Bot Handlers =====
|
||||
|
||||
/**
|
||||
* Start a timer from natural language input
|
||||
* Parses duration and optional label from input like "25m Pomodoro"
|
||||
*/
|
||||
async startTimerForUser(userId: string, input: string): Promise<Timer & { name?: string }> {
|
||||
const token = this.getUserToken(userId);
|
||||
if (!token) {
|
||||
throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.');
|
||||
}
|
||||
|
||||
// Parse duration from input
|
||||
const durationSeconds = this.parseDuration(input);
|
||||
if (!durationSeconds) {
|
||||
throw new Error('Ungültiges Dauer-Format. Beispiele: 25m, 1h30m, 90s');
|
||||
}
|
||||
|
||||
// Extract label (everything after duration pattern)
|
||||
const label = input.replace(/\d+\s*[hms]?(?:in)?/gi, '').trim() || null;
|
||||
|
||||
const timer = await this.createTimer({ durationSeconds, label }, token);
|
||||
// Start the timer immediately
|
||||
const started = await this.startTimer(timer.id, token);
|
||||
return { ...started, name: started.label ?? undefined };
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the running timer for a user
|
||||
*/
|
||||
async stopTimerForUser(userId: string, timerName?: string): Promise<Timer & { name?: string }> {
|
||||
const token = this.getUserToken(userId);
|
||||
if (!token) {
|
||||
throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.');
|
||||
}
|
||||
|
||||
const timers = await this.getTimers(token);
|
||||
let timer: Timer | undefined;
|
||||
|
||||
if (timerName) {
|
||||
timer = timers.find(
|
||||
(t) =>
|
||||
(t.status === 'running' || t.status === 'paused') &&
|
||||
t.label?.toLowerCase().includes(timerName.toLowerCase())
|
||||
);
|
||||
} else {
|
||||
timer = timers.find((t) => t.status === 'running' || t.status === 'paused');
|
||||
}
|
||||
|
||||
if (!timer) {
|
||||
throw new Error('Kein aktiver Timer gefunden.');
|
||||
}
|
||||
|
||||
await this.deleteTimer(timer.id, token);
|
||||
return { ...timer, name: timer.label ?? undefined };
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an alarm from natural language input
|
||||
* Parses time and optional label from input like "14:30 Meeting"
|
||||
*/
|
||||
async setAlarmForUser(userId: string, input: string): Promise<Alarm & { name?: string }> {
|
||||
const token = this.getUserToken(userId);
|
||||
if (!token) {
|
||||
throw new Error('Nicht authentifiziert. Bitte zuerst anmelden.');
|
||||
}
|
||||
|
||||
const time = this.parseAlarmTime(input);
|
||||
if (!time) {
|
||||
throw new Error('Ungültiges Zeit-Format. Beispiele: 14:30, 9:00, 14 Uhr 30');
|
||||
}
|
||||
|
||||
// Extract label (everything after time pattern)
|
||||
const label =
|
||||
input
|
||||
.replace(/\d{1,2}:\d{2}(:\d{2})?/g, '')
|
||||
.replace(/\d{1,2}\s*uhr(\s*\d{1,2})?/gi, '')
|
||||
.trim() || null;
|
||||
|
||||
const alarm = await this.createAlarm({ time, label }, token);
|
||||
return { ...alarm, name: alarm.label ?? undefined };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time for a specific city/timezone
|
||||
*/
|
||||
async getWorldClockTime(city: string): Promise<{ city: string; time: string; date: string }> {
|
||||
// Search for timezone
|
||||
const results = await this.searchTimezones(city);
|
||||
if (results.length === 0) {
|
||||
throw new Error(`Stadt "${city}" nicht gefunden.`);
|
||||
}
|
||||
|
||||
const tz = results[0];
|
||||
const now = new Date();
|
||||
|
||||
const time = now.toLocaleTimeString('de-DE', {
|
||||
timeZone: tz.timezone,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
||||
const date = now.toLocaleDateString('de-DE', {
|
||||
timeZone: tz.timezone,
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
});
|
||||
|
||||
return { city: tz.city, time, date };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue