feat: sidebar nav (default), group member management, per-customer ticketing identity mapping
- Navigation: new per-user setting (default "side") renders the page links as a left sidebar while keeping theme/bell/avatar/logout in the top-right; falls back to a top bar under 760px or when set to "top". Profile selector added. - Group conversations: record a creatorId; the creator gets a "Manage members" button to add/remove participants (new GET/POST/DELETE members endpoints, creator-only). Existing groups backfilled. - Customer ticketing: admin can map each assigned consultant to their username in that customer's ticketing system. Per-customer mapping takes precedence over the consultant's global ticketingIdentities during sync. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ type Conversation struct {
|
||||
Kind string `bson:"kind" json:"kind"` // dm | group | project
|
||||
CustomerID string `bson:"customerId,omitempty" json:"customerId,omitempty"`
|
||||
Title string `bson:"title,omitempty" json:"title,omitempty"`
|
||||
CreatorID string `bson:"creatorId,omitempty" json:"creatorId,omitempty"`
|
||||
ParticipantIDs []string `bson:"participantIds" json:"participantIds"`
|
||||
LastMessageAt time.Time `bson:"lastMessageAt" json:"lastMessageAt"`
|
||||
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
|
||||
@@ -104,6 +105,28 @@ func (s *Store) ConversationByID(ctx context.Context, id string) (*Conversation,
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// AddParticipant adds a user to a conversation (idempotent via $addToSet).
|
||||
func (s *Store) AddParticipant(ctx context.Context, convID, userID string) error {
|
||||
_, err := s.DB.Collection("conversations").UpdateOne(ctx,
|
||||
bson.M{"_id": convID},
|
||||
bson.M{"$addToSet": bson.M{"participantIds": userID}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("add participant: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveParticipant removes a user from a conversation.
|
||||
func (s *Store) RemoveParticipant(ctx context.Context, convID, userID string) error {
|
||||
_, err := s.DB.Collection("conversations").UpdateOne(ctx,
|
||||
bson.M{"_id": convID},
|
||||
bson.M{"$pull": bson.M{"participantIds": userID}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove participant: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) ListConversations(ctx context.Context, userID string) ([]Conversation, error) {
|
||||
cur, err := s.DB.Collection("conversations").Find(ctx,
|
||||
bson.M{"participantIds": userID},
|
||||
|
||||
Reference in New Issue
Block a user