Skip to content

Improve Research Session CRUD — Frontend #89

Description

@uncleJim21

Context

The frontend session list has duplication and lacks management actions. This issue covers the UI work to support delete, rename, deduplication, and eventually pagination.

Depends on backend issue: uncleJim21/pullthatupjamie-backend#69

All frontend work is in src/components/UnifiedSidePanel.tsx unless noted.


2.1 Extract SessionCard component (dedup)

The session card list is rendered identically in two places:

  • Bottom layout: inside the isBottomLayout branch (sessions else-block)
  • Side layout: inside the isSessionsMode branch at the end of the component

Extract into a shared component (can be a local component in the same file or a separate SessionCard.tsx):

interface SessionCardProps {
  session: ResearchSession;
  isActive: boolean;
  activeSessionItemCount?: number;
  onOpen: (sessionId: string, title: string) => void;
  onCopyLink: (sessionId: string) => void;
  onDelete: (sessionId: string, title: string) => void;
  onRename: (sessionId: string, newTitle: string) => void;
  copiedSessionId: string | null;
}

Both layout branches should render <SessionCard ... /> instead of duplicating the card markup.

2.2 Add delete action per session card

  • Add a subtle trash icon button (similar to the existing copy button) on each session card
  • On click, show the existing DeleteConfirmationModal (from src/components/DeleteConfirmationModal.tsx)
  • On confirm, call deleteResearchSession(sessionId) and remove from local sessions state
  • If the deleted session is the activeSessionId, the parent should be notified (add an onDeleteSession callback prop)
  • Prevent deleting while a delete is in-flight (use isDeleting state)

The DeleteConfirmationModal already supports:

  • isDeleting loading state with spinner
  • Custom button text
  • itemType for contextual messaging

2.3 Add inline rename

  • Clicking the session title text switches it to an <input> (controlled)
  • Pre-filled with current displayTitle
  • Save on Enter or blur, cancel on Escape
  • Calls updateResearchSessionTitle(sessionId, newTitle)
  • Optimistically update the local sessions state
  • Follow the inline-edit pattern from src/components/MentionPinManagement.tsx (lines 254-294)
  • Add a small pencil/edit icon on hover to hint at editability

2.4 Wire up service functions

Add to src/services/researchSessionService.ts:

export async function deleteResearchSession(sessionId: string): Promise<{ success: boolean }> {
  const clientId = getOrCreateClientId();
  const url = `${API_URL}/api/research-sessions/${sessionId}?clientId=${encodeURIComponent(clientId)}`;
  const response = await fetch(url, {
    method: 'DELETE',
    headers: getAuthHeaders(),
  });
  if (!response.ok) {
    const errorData = await response.json().catch(() => ({}));
    throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
  }
  return response.json();
}

export async function updateResearchSessionTitle(
  sessionId: string,
  title: string
): Promise<ResearchSessionResponse> {
  const clientId = getOrCreateClientId();
  const response = await fetch(`${API_URL}/api/research-sessions/${sessionId}?clientId=${encodeURIComponent(clientId)}`, {
    method: 'PATCH',
    headers: getAuthHeaders(),
    body: JSON.stringify({ title }),
  });
  if (!response.ok) {
    const errorData = await response.json().catch(() => ({}));
    throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
  }
  return response.json();
}

2.5 Pagination / infinite scroll (Phase 2)

Once the backend supports pagination (1.3):

  • Update fetchAllResearchSessions() to accept page and limit params
  • Implement infinite scroll or "Load more" button in the sessions list
  • Show a loading spinner at the bottom when fetching the next page
  • Track hasMore based on pagination.total vs loaded count

This can be a follow-up PR after the backend pagination is deployed.


Acceptance Criteria

  • Session cards show delete and rename actions in both layouts
  • Deleting a session removes it from the list and calls the backend
  • Renaming a session updates the title inline and persists to the backend
  • Session card rendering is deduplicated into a shared component
  • All actions work on mobile (bottom-sheet layout)

Files likely touched

  • src/components/UnifiedSidePanel.tsx
  • src/services/researchSessionService.ts
  • src/components/DeleteConfirmationModal.tsx (may need minor props)
  • New: src/components/SessionCard.tsx (optional extraction)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions