mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 11:03:38 +02:00
feat(calendar): add birthday integration from contacts service
- Add birthdaysStore to fetch and manage birthdays from contacts API - Add BirthdayPopover component with contact details and link to contacts app - Integrate birthdays into WeekView, MonthView, and DayView as all-day events - Add settings for showBirthdays and showBirthdayAge toggles - Add reactive $effect in layout to load birthdays when setting is enabled - Add /contacts/birthdays endpoint to contacts backend - Configure PUBLIC_CONTACTS_API_URL env variable for calendar app 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4b6a4c73ae
commit
cdc3cd3ec8
12 changed files with 995 additions and 85 deletions
|
|
@ -234,6 +234,16 @@ export class ContactController {
|
|||
return { contacts, total };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all contacts with birthdays (for calendar integration)
|
||||
* Returns lightweight data: id, displayName, firstName, lastName, birthday, photoUrl
|
||||
*/
|
||||
@Get('birthdays')
|
||||
async getBirthdays(@CurrentUser() user: CurrentUserData) {
|
||||
const contacts = await this.contactService.findWithBirthdays(user.userId);
|
||||
return { contacts };
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@CurrentUser() user: CurrentUserData, @Param('id', ParseUUIDPipe) id: string) {
|
||||
const contact = await this.contactService.findById(id, user.userId);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
import { Injectable, Inject, NotFoundException } from '@nestjs/common';
|
||||
import { eq, and, or, ilike, desc, sql } from 'drizzle-orm';
|
||||
import { eq, and, or, ilike, desc, sql, isNotNull } from 'drizzle-orm';
|
||||
import { DATABASE_CONNECTION } from '../db/database.module';
|
||||
import { Database } from '../db/connection';
|
||||
import { contacts } from '../db/schema';
|
||||
import type { Contact, NewContact } from '../db/schema';
|
||||
|
||||
export interface ContactBirthdaySummary {
|
||||
id: string;
|
||||
displayName: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
birthday: string;
|
||||
photoUrl: string | null;
|
||||
}
|
||||
|
||||
export interface ContactFilters {
|
||||
search?: string;
|
||||
isFavorite?: boolean;
|
||||
|
|
@ -148,4 +157,34 @@ export class ContactService {
|
|||
|
||||
return Number(result[0]?.count || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all contacts with birthdays (for calendar integration)
|
||||
* Returns only essential fields for lightweight transfer
|
||||
*/
|
||||
async findWithBirthdays(userId: string): Promise<ContactBirthdaySummary[]> {
|
||||
const result = await this.db
|
||||
.select({
|
||||
id: contacts.id,
|
||||
displayName: contacts.displayName,
|
||||
firstName: contacts.firstName,
|
||||
lastName: contacts.lastName,
|
||||
birthday: contacts.birthday,
|
||||
photoUrl: contacts.photoUrl,
|
||||
})
|
||||
.from(contacts)
|
||||
.where(
|
||||
and(
|
||||
eq(contacts.userId, userId),
|
||||
eq(contacts.isArchived, false),
|
||||
isNotNull(contacts.birthday)
|
||||
)
|
||||
)
|
||||
.orderBy(contacts.lastName, contacts.firstName);
|
||||
|
||||
return result.map((c) => ({
|
||||
...c,
|
||||
birthday: c.birthday || '',
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue