4.0 KiB
4.0 KiB
title, type, permalink
| title | type | permalink |
|---|---|---|
| collections-and-sharing | note | voyage/knowledge/domain/collections-and-sharing |
Collections & Sharing Domain
Collection Sharing Architecture
Data Model
Collection.shared_with-ManyToManyField(User, related_name='shared_with', blank=True)- the primary access grantCollectionInvite- staging table for pending invites:(collection FK, invited_user FK, unique_together); prevents self-invite and double-invite- Both models in
backend/server/adventures/models.py
Access Flow (Invite -> Accept -> Membership)
- Owner calls
POST /api/collections/{id}/share/{uuid}/-> createsCollectionInvite - Invited user sees pending invites via
GET /api/collections/invites/ - Invited user calls
POST /api/collections/{id}/accept-invite/-> adds toshared_with, deletes invite - Owner revokes:
POST /api/collections/{id}/revoke-invite/{uuid}/ - User declines:
POST /api/collections/{id}/decline-invite/ - Owner removes:
POST /api/collections/{id}/unshare/{uuid}/- removes user's locations from collection - User self-removes:
POST /api/collections/{id}/leave/- removes their locations
Permission Classes
CollectionShared- full access for owner andshared_withmembers; invite actions for invitees; anonymous read foris_publicIsOwnerOrSharedWithFullAccess- child-object viewsets; traversesobj.collections/obj.collectionto checkshared_withContentImagePermission- delegates toIsOwnerOrSharedWithFullAccessoncontent_object
Key Design Constraints
- No role differentiation - all shared users have same write access
- On unshare/leave, departing user's locations are removed from collection (not deleted)
duplicateaction creates a private copy with noshared_withtransfer
Chat Agent Tool Access
get_trip_detailsandadd_to_itinerarytools authorize usingQ(user=user) | Q(shared_with=user)— shared members can use AI chat tools on shared collections.list_tripsremains owner-only (shared collections not listed).add_to_itineraryassignsLocation.user = shared_user(shared users own their contributed locations), consistent with REST API behavior.- See patterns/chat-and-llm.md.
Itinerary Architecture
Primary Component
CollectionItineraryPlanner.svelte (~3818 lines) - monolith rendering the entire itinerary view and all modals.
The "Add" Button
DaisyUI dropdown at bottom of each day card with "Link existing item" and "Create new" submenu (Location, Lodging, Transportation, Note, Checklist).
Day Suggestions Modal (WS3)
- Component:
ItinerarySuggestionModal.svelte - Props:
collection,user,targetDate,displayDate - Events:
close,addItem { type: 'location', itemId, updateDate } - 3-step UX: category selection -> filters -> results from
POST /api/chat/suggestions/day/
User Recommendation Preference Profile
Backend-only feature: model, API, and system-prompt integration exist, but no frontend UI built yet.
Auto-learned preference updates
backend/server/integrations/utils/auto_profile.pyderives profile from user history- Fields:
interests(from activities + locations),trip_style(from lodging),notes(frequently visited regions) ChatViewSet.send_message()invokesupdate_auto_preference_profile(request.user)at method start
Model
UserRecommendationPreferenceProfile (OneToOne -> CustomUser): cuisines, interests (JSONField), trip_style, notes, timestamps.
System Prompt Integration
- Single-user: labeled as auto-inferred with structured markdown section
- Shared collections:
get_aggregated_preferences(collection)appends per-participant preferences - Missing profiles skipped gracefully
Frontend Gap
- No settings tab for manual preference editing
- TypeScript type available as
UserRecommendationPreferenceProfileinsrc/lib/types.ts - See Plan: AI travel agent redesign