···8282- Sometimes `author`, sometimes `createdBy`, sometimes `authorId`, sometimes `authorHandle`
8383- Inconsistent inclusion of `cardCount`, `createdAt`, `updatedAt`
84848585+### GetLibrariesForUrlResponse - Missing Card Data
8686+8787+**Current Structure** (responses.ts:331-340):
8888+```tsx
8989+{
9090+ libraries: {
9191+ userId: string;
9292+ name: string;
9393+ handle: string;
9494+ avatarUrl?: string;
9595+ }[];
9696+ // ...
9797+}
9898+```
9999+100100+**Issue**: When showing "who has this URL in their library", we only show user info but not their specific card. This means we can't display:
101101+- The user's note on the URL
102102+- When they saved it
103103+- Their specific card metadata
104104+105105+**Proposed Enhancement**: Return both user and card data to enable richer UI display.
106106+85107---
8610887109## Proposed Changes to Response Types
···249271#### GetLibrariesForUrlResponse
250272```tsx
251273export interface GetLibrariesForUrlResponse {
252252- libraries: User[]; // Changed from inline type, enriched with full user data
274274+ libraries: {
275275+ user: User; // The user who has this URL in their library
276276+ card: UrlCard; // Their specific card (may include a note)
277277+ }[];
253278 pagination: Pagination;
254279 sorting: CardSorting;
255280}
···330355331356#### Update LibraryForUrlDTO
332357```tsx
333333-// Keep minimal - will be enriched in use case
358358+// Repository returns card data - will be enriched with user profile in use case
334359export interface LibraryForUrlDTO {
335360 userId: string;
336361 cardId: string;
337337- name: string; // NEW - include from repository
338338- handle: string; // NEW - include from repository
339339- avatarUrl?: string; // NEW - include from repository
362362+ // Card data
363363+ url: string;
364364+ cardContent: {
365365+ url: string;
366366+ title?: string;
367367+ description?: string;
368368+ author?: string;
369369+ thumbnailUrl?: string;
370370+ };
371371+ libraryCount: number;
372372+ urlLibraryCount: number;
373373+ urlInLibrary?: boolean;
374374+ createdAt: Date;
375375+ updatedAt: Date;
376376+ note?: {
377377+ id: string;
378378+ text: string;
379379+ };
380380+ // Note: userId is the card author in this context (it's their card in their library)
340381}
341382```
342383343384**Implementation Impact**:
344344-- Update SQL queries in `DrizzleCardQueryRepository` to include `cards.curatorId as authorId`
345345-- Update SQL queries for `getLibrariesForUrl` to join with profiles table
385385+- Update SQL queries in `DrizzleCardQueryRepository` to include `cards.curatorId as authorId` in all `UrlCardView` queries
386386+- Update `getLibrariesForUrl` query to return full card data (similar to how `getUrlCardsOfUser` works)
387387+ - No need to join with profiles table - enrichment happens in use case (following the pattern from `GetCollectionsForUrlUseCase`)
346388347389### 2. ICollectionQueryRepository
348390···571613**File**: `src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts`
572614573615**Changes**:
574574-- Enrich library data with full user profiles (name, handle, avatarUrl)
575575-- OR: Update repository to join with profiles table and return full data
616616+- Repository now returns full card data in `LibraryForUrlDTO`
617617+- Enrich with user profiles for each library owner
618618+- Transform to return both `user` and `card` objects
576619577577-**Option 1: Enrich in Use Case**
620620+**Updated Code** (following pattern from `GetCollectionsForUrlUseCase`):
578621```tsx
579579-// After fetching from repository
580580-const uniqueUserIds = Array.from(
581581- new Set(result.items.map(lib => lib.userId))
582582-);
622622+async execute(
623623+ query: GetLibrariesForUrlQuery,
624624+): Promise<Result<GetLibrariesForUrlResult>> {
625625+ // Validate URL
626626+ const urlResult = URL.create(query.url);
627627+ if (urlResult.isErr()) {
628628+ return err(
629629+ new ValidationError(`Invalid URL: ${urlResult.error.message}`),
630630+ );
631631+ }
632632+633633+ // Set defaults
634634+ const page = query.page || 1;
635635+ const limit = Math.min(query.limit || 20, 100);
636636+ const sortBy = query.sortBy || CardSortField.UPDATED_AT;
637637+ const sortOrder = query.sortOrder || SortOrder.DESC;
638638+639639+ try {
640640+ // Execute query to get libraries with full card data
641641+ const result = await this.cardQueryRepo.getLibrariesForUrl(query.url, {
642642+ page,
643643+ limit,
644644+ sortBy,
645645+ sortOrder,
646646+ });
647647+648648+ // Enrich with user profiles
649649+ const uniqueUserIds = Array.from(
650650+ new Set(result.items.map((item) => item.userId)),
651651+ );
652652+653653+ const profilePromises = uniqueUserIds.map((userId) =>
654654+ this.profileService.getProfile(userId, query.callingUserId),
655655+ );
656656+657657+ const profileResults = await Promise.all(profilePromises);
658658+659659+ // Create a map of profiles
660660+ const profileMap = new Map<string, User>();
661661+662662+ for (let i = 0; i < uniqueUserIds.length; i++) {
663663+ const profileResult = profileResults[i];
664664+ const userId = uniqueUserIds[i];
665665+ if (!profileResult || !userId) {
666666+ return err(new Error('Missing profile result or user ID'));
667667+ }
668668+ if (profileResult.isErr()) {
669669+ return err(
670670+ new Error(
671671+ `Failed to fetch user profile: ${profileResult.error instanceof Error ? profileResult.error.message : 'Unknown error'}`,
672672+ ),
673673+ );
674674+ }
675675+ const profile = profileResult.value;
676676+ profileMap.set(userId, {
677677+ id: profile.id,
678678+ name: profile.name,
679679+ handle: profile.handle,
680680+ avatarUrl: profile.avatarUrl,
681681+ description: profile.bio,
682682+ });
683683+ }
583684584584-const userProfiles = new Map<string, User>();
585585-const profileResults = await Promise.all(
586586- uniqueUserIds.map(id => this.profileService.getProfile(id))
587587-);
685685+ // Map items with enriched user data and card data
686686+ const enrichedLibraries = result.items.map((item) => {
687687+ const user = profileMap.get(item.userId);
688688+ if (!user) {
689689+ throw new Error(`Profile not found for user ${item.userId}`);
690690+ }
588691589589-// Build user map and transform to User[]
590590-const enrichedLibraries = result.items.map(lib => {
591591- const profile = userProfiles.get(lib.userId);
592592- return {
593593- id: profile.id,
594594- name: profile.name,
595595- handle: profile.handle,
596596- avatarUrl: profile.avatarUrl,
597597- };
598598-});
599599-```
692692+ // Build card object
693693+ // Note: userId is the card author (it's their card in their library)
694694+ const card: UrlCard = {
695695+ id: item.cardId,
696696+ type: 'URL',
697697+ url: item.url,
698698+ cardContent: item.cardContent,
699699+ libraryCount: item.libraryCount,
700700+ urlLibraryCount: item.urlLibraryCount,
701701+ urlInLibrary: item.urlInLibrary,
702702+ createdAt: item.createdAt.toISOString(),
703703+ updatedAt: item.updatedAt.toISOString(),
704704+ author: user, // Card author is same as library user
705705+ note: item.note,
706706+ };
600707601601-**Option 2: Update Repository** (Preferred - less overhead)
602602-- Update `getLibrariesForUrl` query to join with profiles
603603-- Return full user data in `LibraryForUrlDTO`
708708+ return {
709709+ user,
710710+ card,
711711+ };
712712+ });
713713+714714+ return ok({
715715+ libraries: enrichedLibraries,
716716+ pagination: {
717717+ currentPage: page,
718718+ totalPages: Math.ceil(result.totalCount / limit),
719719+ totalCount: result.totalCount,
720720+ hasMore: page * limit < result.totalCount,
721721+ limit,
722722+ },
723723+ sorting: {
724724+ sortBy,
725725+ sortOrder,
726726+ },
727727+ });
728728+ } catch (error) {
729729+ return err(
730730+ new Error(
731731+ `Failed to retrieve libraries for URL: ${error instanceof Error ? error.message : 'Unknown error'}`,
732732+ ),
733733+ );
734734+ }
735735+}
736736+```
604737605738**Dependencies**:
606606-- Option 1: Needs `IProfileService` injected
607607-- Option 2: Update repository implementation
739739+- Needs `IProfileService` injected (add if not already present)
740740+- Repository must return full card data in `LibraryForUrlDTO`
608741609742### 7. GetNoteCardsForUrlUseCase
610743···758891759892### Phase 1: Repository Updates
7608931. Update `UrlCardView` in `ICardQueryRepository.ts` to include `authorId`
761761-2. Update `DrizzleCardQueryRepository` to include `cards.curatorId as authorId` in all queries returning `UrlCardView`
762762-3. (Optional) Update `getLibrariesForUrl` to join with profiles and return enriched data
894894+2. Update `LibraryForUrlDTO` in `ICardQueryRepository.ts` to include full card data (url, cardContent, libraryCount, etc.)
895895+3. Update `DrizzleCardQueryRepository` to:
896896+ - Include `cards.curatorId as authorId` in all queries returning `UrlCardView`
897897+ - Update `getLibrariesForUrl` query to return full card data (similar to `getUrlCardsOfUser`)
763898764899### Phase 2: Response Type Updates
7659001. Add unified `User` interface in `responses.ts`
···7739081. **GetProfileUseCase** - Add `description` field (minimal change)
7749092. **GetLibrariesForCardUseCase** - Add `description` to user DTOs
7759103. **GetNoteCardsForUrlUseCase** - Add `description` to author
776776-4. **GetLibrariesForUrlUseCase** - Enrich with full user data OR wait for repository update
911911+4. **GetLibrariesForUrlUseCase** - Enrich with user profiles, return `{ user, card }[]` instead of just users
7779125. **GetCollectionsUseCase** - Change `createdBy` to `author`, add `description`
7789136. **GetCollectionsForUrlUseCase** - Add collection dates/cardCount, author description
7799147. **GetUrlStatusForMyLibraryUseCase** - Enrich collections with full data
···8369712. **Performance**: Enriching authors for all cards in list views will add N+1 queries.
837972 - **Mitigation**: Batch profile fetches with `Promise.all`, cache frequently accessed profiles, or update repository to join with profiles table.
838973839839-3. **LibrariesForUrl**: Should we enrich in use case or update repository?
840840- - **Recommendation**: Update repository to join with profiles - more efficient, cleaner code.
974974+3. **LibrariesForUrl**: Return both user and card data in response
975975+ - **Decision**: Response now returns `{ user: User, card: UrlCard }[]` to show both who saved the URL and their specific card (with potential note)
976976+ - **Implementation**: Repository returns full card data, use case enriches with user profiles (following pattern from `GetCollectionsForUrlUseCase`)
8419778429784. **Collection enrichment**: When displaying collections in UrlCard, do we need full Collection objects?
843979 - **Current approach**: Keep minimal `{id, name, authorId}[]` to avoid over-fetching
···8529881. **User**: Single interface with optional `description`
8539892. **UrlCard**: Add `author` field (currently missing!)
8549903. **Collection**: Standardize to `author` (not `createdBy`), ensure complete data
991991+4. **GetLibrariesForUrl**: Enhanced to return both `user` and `card` objects, enabling display of who saved a URL and their specific card (with potential note)
855992856856-Most changes are in the API client response types and use cases. Repository changes are minimal (add `authorId` to card queries). Use cases will enrich data by fetching author profiles using the existing `IProfileService`.
993993+Most changes are in the API client response types and use cases. Repository changes are minimal (add `authorId` to card queries, expand `getLibrariesForUrl` to return full card data). Use cases will enrich data by fetching author profiles using the existing `IProfileService`.