Files
music_library/docs/plans/scrobbled-tracks-crud.md
T
Claudio Ortolina 5d507a357b Rename components to better reflect namespace
╭─────────────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code!                               │
│                                                         │
│   /help for help, /status for your current setup        │
│                                                         │
│   cwd: /Users/cloud/github.com/cloud8421/music_library  │
╰─────────────────────────────────────────────────────────╯

> Look at @lib/music_library_web/components/ and you fill find
components there. I would like you to rename the component modules so
that instead
  of being called MusicLibraryWeb.<something>Component, they're called
MusicLibraryWeb.Components.<something>. For example
  MusicLibraryWeb.AddRecordComponent would be called
MusicLibraryWeb.Components.AddRecord. Make sure you also update
references where these
  components are used.
  ⎿ Listed directory lib/music_library_web/components/

⏺ I'll help you rename the component modules and update their
references. Let me first examine the components directory structure and
then create
   a plan.

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components
-name "*.ex" | head -20)
  ⎿
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/pagination_component.ex

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/add_record_component.ex

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/release_component.ex
     … +10 lines (ctrl+o to expand)

⏺ Now let me examine a few of these files to understand the current
naming pattern:

⏺ Read(lib/music_library_web/components/add_record_component.ex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Read(lib/music_library_web/components/pagination_component.ex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Read(lib/music_library_web/components/barcode_scanner_component.ex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Now let me search for references to these components across the
codebase:

⏺ Search(pattern:
"MusicLibraryWeb\.(AddRecord|Pagination|BarcodeScanner|Notes|Release)Component")
  ⎿ Found 13 files (ctrl+o to expand)

⏺ Let me also check for other component patterns:

⏺ Search(pattern: "MusicLibraryWeb\.[A-Z][a-zA-Z]*Components?")
  ⎿ Found 34 files (ctrl+o to expand)

⏺ Now let me create a plan for this refactoring task:

⏺ Let me examine all component files to understand the current naming
pattern:

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components
-name "*.ex" -exec grep -l "defmodule MusicLibraryWeb\."
       {} \;)
  ⎿ Interrupted · What should Claude do instead?

> Retry the find command

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components
-name "*.ex" -exec grep -l "defmodule MusicLibraryWeb\."
       {} \;)
  ⎿
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/pagination_component.ex

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/add_record_component.ex

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/release_component.ex
     … +10 lines (ctrl+o to expand)

⏺ Now let me examine the module names in each file to identify which
ones need renaming:

⏺ Bash(grep "defmodule MusicLibraryWeb\."
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/*.ex)
  ⎿
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/add_record_component.ex:defmodule
MusicLibraryWeb.AddRecord
    Component do

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/artist_components.ex:defmodule
MusicLibraryWeb.ArtistCompon
    … +21 lines (ctrl+o to expand)

⏺ Perfect! Now I can see which components need renaming. Based on the
current naming pattern, I need to rename the ones ending with
"Component"
  to be under the "Components" namespace. Let me update my todo list and
complete the identification:

⏺ Now let me start renaming the components. I'll begin with
AddRecordComponent:

⏺ Update(lib/music_library_web/components/add_record_component.ex)
  ⎿ Updated lib/music_library_web/components/add_record_component.ex
with 1 addition and 1 removal
       1 -  defmodule MusicLibraryWeb.AddRecordComponent do
       1 +  defmodule MusicLibraryWeb.Components.AddRecord do
       2      use MusicLibraryWeb, :live_component
       3
       4      import MusicLibraryWeb.RecordComponents, only:
[format_label: 1, type_label: 1]

⏺ Update(lib/music_library_web/components/barcode_scanner_component.ex)
  ⎿ Updated
lib/music_library_web/components/barcode_scanner_component.ex with 1
addition and 1 removal
       1 -  defmodule MusicLibraryWeb.BarcodeScannerComponent do
       1 +  defmodule MusicLibraryWeb.Components.BarcodeScanner do
       2      use MusicLibraryWeb, :live_component
       3
       4      alias MusicBrainz.ReleaseGroupSearchResult

⏺ Update(lib/music_library_web/components/notes_component.ex)
  ⎿ Updated lib/music_library_web/components/notes_component.ex with 1
addition and 1 removal
       1 -  defmodule MusicLibraryWeb.NotesComponent do
       1 +  defmodule MusicLibraryWeb.Components.Notes do
       2      use MusicLibraryWeb, :live_component
       3
       4      alias MusicLibrary.Notes

⏺ Update(lib/music_library_web/components/pagination_component.ex)
  ⎿ Updated lib/music_library_web/components/pagination_component.ex
with 1 addition and 1 removal
       1 -  defmodule MusicLibraryWeb.PaginationComponent do
       1 +  defmodule MusicLibraryWeb.Components.Pagination do
       2      use MusicLibraryWeb, :html
       3
       4      attr :pagination_params, :map, required: true

⏺ Update(lib/music_library_web/components/release_component.ex)
  ⎿ Updated lib/music_library_web/components/release_component.ex with 1
addition and 1 removal
       1 -  defmodule MusicLibraryWeb.ReleaseComponent do
       1 +  defmodule MusicLibraryWeb.Components.Release do
       2      use MusicLibraryWeb, :live_component
       3      use Gettext, backend: MusicLibraryWeb.Gettext
       4

⏺ Update(lib/music_library_web/components/record_form.ex)
  ⎿ Updated lib/music_library_web/components/record_form.ex with 1
addition and 1 removal
       1 -  defmodule MusicLibraryWeb.RecordForm do
       1 +  defmodule MusicLibraryWeb.Components.RecordForm do
       2      use MusicLibraryWeb, :live_component
       3
       4      import MusicLibraryWeb.RecordComponents,

⏺ Now I need to update all the references across the codebase. Let me
start this process:

⏺ Now let me search for and update all references to these components.
Let me start with AddRecordComponent:

⏺ Search(pattern: "AddRecordComponent", output_mode: "content")
  ⎿ Found 3 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/artist_live/show.html.heex)
  ⎿ File must be read first

⏺ Read(lib/music_library_web/live/artist_live/show.html.heex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/artist_live/show.html.heex)
  ⎿ Updated lib/music_library_web/live/artist_live/show.html.heex with 1
addition and 1 removal
       237
on_close={JS.patch(~p"/artists/#{@artist.musicbrainz_id}")}
       238      >
       239        <.live_component
       240 -        module={MusicLibraryWeb.AddRecordComponent}
       240 +        module={MusicLibraryWeb.Components.AddRecord}
       241          id={:search}
       242          title={@page_title}
       243          action={@live_action}

⏺ Read(lib/music_library_web/live/wishlist_live/index.html.heex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/wishlist_live/index.html.heex)
  ⎿ Updated lib/music_library_web/live/wishlist_live/index.html.heex
with 1 addition and 1 removal
       70        on_close={JS.patch(back_path(@record_list_params))}
       71      >
       72        <.live_component
       73 -        module={MusicLibraryWeb.AddRecordComponent}
       73 +        module={MusicLibraryWeb.Components.AddRecord}
       74          id={:search}
       75          title={@page_title}
       76          action={@live_action}

⏺ Read(lib/music_library_web/live/collection_live/index.html.heex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/collection_live/index.html.heex)
  ⎿ Updated lib/music_library_web/live/collection_live/index.html.heex
with 1 addition and 1 removal
       82        on_close={JS.patch(back_path(@record_list_params))}
       83      >
       84        <.live_component
       85 -        module={MusicLibraryWeb.AddRecordComponent}
       85 +        module={MusicLibraryWeb.Components.AddRecord}
       86          id={:search}
       87          title={@page_title}
       88          action={@live_action}

⏺ Now let me search for BarcodeScannerComponent references:

⏺ Search(pattern: "BarcodeScannerComponent", output_mode: "content")
  ⎿ Found 2 lines (ctrl+o to expand)

⏺ Read(lib/music_library_web/live/collection_live/index.ex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ I notice there are also imports of PaginationComponent in the same
file. Let me update multiple references in this file:

⏺ Update(lib/music_library_web/live/collection_live/index.ex)
  ⎿ Updated lib/music_library_web/live/collection_live/index.ex with 2
additions and 2 removals
       1    defmodule MusicLibraryWeb.CollectionLive.Index do
       2      use MusicLibraryWeb, :live_view
       3
       4 -    import MusicLibraryWeb.BarcodeScannerComponent, only:
[barcode_icon: 1]
       5 -    import MusicLibraryWeb.PaginationComponent
       4 +    import MusicLibraryWeb.Components.BarcodeScanner, only:
[barcode_icon: 1]
       5 +    import MusicLibraryWeb.Components.Pagination
       6      import MusicLibraryWeb.RecordComponents
       7
       8      alias MusicLibrary.Collection

⏺ Read(lib/music_library_web/live/collection_live/index.html.heex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/collection_live/index.html.heex)
  ⎿ Updated lib/music_library_web/live/collection_live/index.html.heex
with 1 addition and 1 removal
        99        on_close={JS.patch(back_path(@record_list_params))}
       100      >
       101        <.live_component
       102 -        module={MusicLibraryWeb.BarcodeScannerComponent}
       102 +        module={MusicLibraryWeb.Components.BarcodeScanner}
       103          id={:barcode_scanner}
       104          title={@page_title}
       105          action={@live_action}

⏺ Let me continue searching for other component references. Let me
search for all the components I renamed:

⏺ Search(pattern:
"MusicLibraryWeb\.(NotesComponent|PaginationComponent|ReleaseComponent|RecordForm)",
output_mode: "content")
  ⎿ Found 18 lines (ctrl+o to expand)

⏺ Let me use a more efficient approach and update all the references at
once. First, let me read the files that need updating and then apply the
  changes:

⏺ Read(lib/music_library_web/live/wishlist_live/index.ex)
  ⎿ Read 80 lines (ctrl+o to expand)

⏺ Update(lib/music_library_web/live/wishlist_live/index.ex)
  ⎿ Updated lib/music_library_web/live/wishlist_live/index.ex with 2
additions and 2 removals
       1    defmodule MusicLibraryWeb.WishlistLive.Index do
       2      use MusicLibraryWeb, :live_view
       3
       4 -    import MusicLibraryWeb.PaginationComponent
       4 +    import MusicLibraryWeb.Components.Pagination
       5      import MusicLibraryWeb.RecordComponents
       6
       7      alias MusicLibrary.Records
     ...
       70      end
       71
       72      @impl true
       73 -    def handle_info({MusicLibraryWeb.RecordForm, {:saved,
_record}}, socket) do
       73 +    def handle_info({MusicLibraryWeb.Components.RecordForm,
{:saved, _record}}, socket) do
       74        {:noreply, load_and_assign_records(socket,
socket.assigns.record_list_params)}
       75      end
       76

⏺ Let me continue with the other files. I'll use grep and replace to do
this more efficiently:

⏺ Bash(grep -r "MusicLibraryWeb\.RecordForm"
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/
--exclude-dir=.git)
  ⎿
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/live/wishlist_live/index.html.heex:
module={MusicLibraryWeb.Recor
    dForm}

/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/live/wishlist_live/show.ex:
def handle_info({MusicLibraryWeb.RecordFo
    … +11 lines (ctrl+o to expand)

⏺ Let me update these files with the correct module references:

⏺ Read(lib/music_library_web/live/wishlist_live/index.html.heex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Let me use a more efficient approach by using sed to replace all
occurrences in all files at once:

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web
-name "*.ex" -o -name "*.heex" | xargs sed -i ''
      's/MusicLibraryWeb\.RecordForm/Music…)
  ⎿ (No content)

⏺ Perfect! Now let me continue with the other components. Let me update
NotesComponent references:

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web
-name "*.ex" -o -name "*.heex" | xargs sed -i ''
      's/MusicLibraryWeb\.NotesComponent/M…)
  ⎿ (No content)

⏺ Now let me update ReleaseComponent references:

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web
-name "*.ex" -o -name "*.heex" | xargs sed -i ''
      's/MusicLibraryWeb\.ReleaseComponent…)
  ⎿ (No content)

⏺ Finally, let me update PaginationComponent references:

⏺ Bash(find
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web
-name "*.ex" -o -name "*.heex" | xargs sed -i ''
      's/MusicLibraryWeb\.PaginationCompon…)
  ⎿ (No content)

⏺ Read(lib/music_library_web/live/scrobbled_tracks_live/index.ex)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Great! Now let me verify that all the references have been updated.
Let me search for any remaining old component references:

⏺ Search(pattern:
"MusicLibraryWeb\.(AddRecordComponent|BarcodeScannerComponent|NotesComponent|PaginationComponent|ReleaseComponent|RecordForm)(?!\.C
        omponents)", output_mode: "content")
  ⎿ Found 0 lines

⏺ Excellent! Let me also check if there are any references in the
documentation that I should update:

⏺ Search(pattern: "MusicLibraryWeb\.PaginationComponent", path: "docs",
output_mode: "content")
  ⎿ Found 2 lines (ctrl+o to expand)

⏺ Let me update the documentation references as well:

⏺ Read(docs/plans/scrobbled-tracks-crud.md)
  ⎿ Read 10 lines (ctrl+o to expand)

⏺ Update(docs/plans/scrobbled-tracks-crud.md)
  ⎿ Updated docs/plans/scrobbled-tracks-crud.md with 2 additions and 2
removals
       84      - Album title
       85      - Scrobbled date/time
       86      - Edit/Delete action buttons
       87 -  - Pagination component at bottom using
`MusicLibraryWeb.PaginationComponent`
       87 +  - Pagination component at bottom using
`MusicLibraryWeb.Components.Pagination`
       88    - Empty state message when no tracks found
       89
       90    ### Form Component Template
     ...
       127    ### Pagination Integration
       128
       129    - **Pattern**: Follow `CollectionLive.Index` pagination
approach
       130 -  - **Component**: Reuse
`MusicLibraryWeb.PaginationComponent`
       130 +  - **Component**: Reuse
`MusicLibraryWeb.Components.Pagination`
       131    - **Parameters**: Support `query`, `page`, `page_size`,
and `order`
       132    - **URL Structure**:
`/scrobbled-tracks?page=2&query=artist&page_size=50`
       133

⏺ Now let me mark the reference update task as completed and run tests
to verify everything works:

⏺ Bash(mix test --max-failures=5)
  ⎿ Compiling 13 files (.ex)
     Generated music_library app
     Running ExUnit with seed: 515502, max_cases: 20
     … +5 lines (ctrl+o to expand)

⏺ Perfect! All tests pass. Let me also run the format and static checks
to make sure everything is properly formatted:

⏺ Bash(mix do format --check-formatted, credo --strict)
  ⎿ Checking 193 source files (this might take a while) ...

     Please report incorrect results:
https://github.com/rrrene/credo/issues
     … +5 lines (ctrl+o to expand)

⏺ Excellent! All static checks pass as well. Let me complete the final
todo:

⏺ Perfect! I have successfully completed the component renaming task.
Here's a summary of what was accomplished:

  Component Renaming Summary

  Renamed Components:
  - MusicLibraryWeb.AddRecordComponent →
MusicLibraryWeb.Components.AddRecord
  - MusicLibraryWeb.BarcodeScannerComponent →
MusicLibraryWeb.Components.BarcodeScanner
  - MusicLibraryWeb.NotesComponent → MusicLibraryWeb.Components.Notes
  - MusicLibraryWeb.PaginationComponent →
MusicLibraryWeb.Components.Pagination
  - MusicLibraryWeb.ReleaseComponent →
MusicLibraryWeb.Components.Release
  - MusicLibraryWeb.RecordForm → MusicLibraryWeb.Components.RecordForm

  Files Updated:
  - Updated module definitions in all component files
  - Updated imports in LiveView modules
  - Updated module={...} references in templates
  - Updated handle_info callback references
  - Updated function calls in templates
  - Updated documentation references

  Verification:
  - All tests pass (198 tests, 0 failures)
  - Static checks pass (format and credo)
  - No remaining old component references found

  The component modules are now properly organized under the
MusicLibraryWeb.Components namespace as requested.

> Note that the file names of the renamed components need to be updated
as well

⏺ You're absolutely right! I need to rename the actual files to match
the new module structure. Let me update the filenames as well.

⏺ Bash(mv
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/add_record_component.ex
      /Users/cloud/github.com/cloud8421/music_library/lib/…)
  ⎿ (No content)

⏺ Bash(mv
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/barcode_scanner_component.ex
      /Users/cloud/github.com/cloud8421/music_library…)
  ⎿ (No content)

⏺ Bash(mv
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/notes_component.ex
      /Users/cloud/github.com/cloud8421/music_library/lib/music…)
  ⎿ (No content)

⏺ Bash(mv
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/pagination_component.ex
      /Users/cloud/github.com/cloud8421/music_library/lib/…)
  ⎿ (No content)

⏺ Bash(mv
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/release_component.ex
      /Users/cloud/github.com/cloud8421/music_library/lib/mus…)
  ⎿ (No content)

⏺ Now let me verify that all files have been renamed correctly:

⏺ Bash(ls -la
/Users/cloud/github.com/cloud8421/music_library/lib/music_library_web/components/)
  ⎿ .rw-r--r--@ 6.4k cloud 23 Sep 09:51 add_record.ex
     .rw-r--r--@  682 cloud 23 Sep 09:51 artist_components.ex
     .rw-r--r--@  15k cloud 23 Sep 09:51 barcode_scanner.ex
     … +11 lines (ctrl+o to expand)

⏺ Perfect! Now let me run tests to verify everything still works after
the file renames:

⏺ Bash(mix test --max-failures=5)
  ⎿ Compiling 8 files (.ex)
     Generated music_library app
     Running ExUnit with seed: 143097, max_cases: 20
     … +5 lines (ctrl+o to expand)

⏺ Excellent! All tests still pass. Let me also run the static checks:

⏺ Bash(mix do format --check-formatted, credo --strict)
  ⎿ Checking 193 source files (this might take a while) ...

     Please report incorrect results:
https://github.com/rrrene/credo/issues
     … +5 lines (ctrl+o to expand)

⏺ Perfect! Let me complete the final todo:

⏺ Complete Component Renaming Summary

  I have successfully completed the component renaming task, including
updating both the module names and the file names:

  Module and File Renames:
  - add_record_component.ex → add_record.ex
(MusicLibraryWeb.AddRecordComponent →
MusicLibraryWeb.Components.AddRecord)
  - barcode_scanner_component.ex → barcode_scanner.ex
(MusicLibraryWeb.BarcodeScannerComponent →
MusicLibraryWeb.Components.BarcodeScanner)
  - notes_component.ex → notes.ex (MusicLibraryWeb.NotesComponent →
MusicLibraryWeb.Components.Notes)
  - pagination_component.ex → pagination.ex
(MusicLibraryWeb.PaginationComponent →
MusicLibraryWeb.Components.Pagination)
  - release_component.ex → release.ex (MusicLibraryWeb.ReleaseComponent
→ MusicLibraryWeb.Components.Release)
  - record_form.ex → kept same filename (MusicLibraryWeb.RecordForm →
MusicLibraryWeb.Components.RecordForm)

  What was updated:
  - Module definitions in component files
  - All import statements across the codebase
  - LiveView template references (module={...})
  - Function calls in templates
  - handle_info callback references in LiveViews
  - Documentation references

  Verification:
  - All tests pass (198 tests, 0 failures)
  - Static checks pass (format and credo)
  - Application compiles successfully

  All component modules are now properly organized under the
MusicLibraryWeb.Components namespace with matching file names.
2025-09-23 09:55:43 +03:00

279 lines
7.8 KiB
Markdown

# Scrobbled Tracks Management UI Plan
## Overview
Build a complete UI for managing scrobbled tracks with index, edit, and delete functionality. The interface will be accessible through the "More" menu in the top navigation and will follow existing patterns from collection and wishlist management.
## 1. Database & Context Layer
### Existing Schema
The `LastFm.Track` schema (`lib/last_fm/track.ex`) already exists with:
- Primary key: `scrobbled_at_uts` (Unix timestamp)
- Fields: `musicbrainz_id`, `title`, `cover_url`, `scrobbled_at_label`
- Embedded schemas: `artist` and `album`
- Raw `last_fm_data` map
### Context Functions Needed
Add to `LastFm` module (`lib/last_fm.ex`):
- `list_tracks/1` - Paginated track listing with search support
- `get_track!/1` - Individual track retrieval by `scrobbled_at_uts`
- `update_track/2` - Track metadata updates
- `delete_track/1` - Track deletion
- `search_tracks_count/1` - Count for pagination totals
## 2. LiveView Components
### Index LiveView
**File**: `lib/music_library_web/live/scrobbled_tracks_live/index.ex`
**Pattern**: Follow `CollectionLive.Index` structure
**Features**:
- Paginated list (200 items per page)
- Search functionality by track title/artist name
- Stream-based updates for performance (`phx-update="stream"`)
- Delete confirmation modals
- Edit links to individual track editing
**Default Parameters**:
```elixir
@default_tracks_list_params %{
query: "",
page: 1,
page_size: 200,
order: :scrobbled_at
}
```
### Edit Form Component
**File**: `lib/music_library_web/live/scrobbled_tracks_live/form_component.ex`
**Pattern**: Follow existing form components (e.g., `ScrobbleRulesLive.FormComponent`)
**Editable Fields**:
- Track title
- Artist name
- Album title
- Scrobbled date/time
- Cover URL (optional)
## 3. Templates & UI Design
### Index Template
**File**: `lib/music_library_web/live/scrobbled_tracks_live/index.html.heex`
**Layout**: Responsive card/list layout similar to collection
**Components**:
- Search bar at top with debounced input
- Track cards displaying:
- Cover image (with fallback for missing covers)
- Track title
- Artist name
- Album title
- Scrobbled date/time
- Edit/Delete action buttons
- Pagination component at bottom using `MusicLibraryWeb.Components.Pagination`
- Empty state message when no tracks found
### Form Component Template
**Features**:
- Form inputs for editable track metadata
- Client-side and server-side validation
- Cancel/Save buttons with proper form handling
- Error display for validation failures
## 4. Routing & Navigation
### Router Updates
**File**: `lib/music_library_web/router.ex`
Add to the logged-in scope around line 65:
```elixir
live "/scrobbled-tracks", ScrobbledTracksLive.Index, :index
live "/scrobbled-tracks/:scrobbled_at_uts/edit", ScrobbledTracksLive.Index, :edit
```
### Navigation Integration
**File**: `lib/music_library_web/components/layouts/app.html.heex`
Add to the "More" dropdown menu (after line 58):
```elixir
<.dropdown_link href={~p"/scrobbled-tracks"}>
<.icon name="hero-musical-note" class="h-4 w-4 mr-2" aria-hidden="true" data-slot="icon" />
{gettext("Scrobbled Tracks")}
</.dropdown_link>
```
## 5. Implementation Details
### Pagination Integration
- **Pattern**: Follow `CollectionLive.Index` pagination approach
- **Component**: Reuse `MusicLibraryWeb.Components.Pagination`
- **Parameters**: Support `query`, `page`, `page_size`, and `order`
- **URL Structure**: `/scrobbled-tracks?page=2&query=artist&page_size=50`
### Stream Implementation
- Use `stream(:tracks, tracks)` for efficient rendering of large track lists
- Implement `stream_delete(socket, :tracks, track)` for track deletion
- Handle pagination with `reset: true` for filtered results
- Use unique DOM IDs based on `scrobbled_at_uts`
### Search Functionality
- Search across track title, artist name, and album title
- Use database LIKE queries for basic string matching
- Debounced search input with LiveView `phx-change` events
- Reset to page 1 when search query changes
### Delete Confirmation
- Use modal overlay for delete confirmation
- Show track details (title, artist, date) in confirmation modal
- Implement optimistic UI updates with `stream_delete`
- Handle deletion errors gracefully
### Error Handling
- Validate track existence before edit operations
- Handle database constraint violations
- Display user-friendly error messages
- Graceful fallbacks for missing track data
## 6. File Structure
```
lib/music_library_web/live/scrobbled_tracks_live/
├── index.ex # Main LiveView controller
├── index.html.heex # Index template with track listing
└── form_component.ex # Edit form component
docs/plans/
└── scrobbled-tracks-crud.md # This plan document
```
## 7. Implementation Steps
1. **Create context functions** in `MusicLibrary.ScrobbleActivity` module for CRUD operations
2. **Implement pagination logic** following existing `CollectionLive.Index` patterns
3. **Create LiveView** with stream-based track listing and search
4. **Add search functionality** with query parameter handling and debouncing
5. **Implement edit form** with validation and error handling
6. **Add delete functionality** with confirmation modal
7. **Update navigation** to include "Scrobbled Tracks" link in More dropdown
8. **Add routing** for index and edit actions
9. **Style components** using Tailwind CSS and Fluxon UI components
10. **Add comprehensive tests** following existing test patterns
## 8. Technical Considerations
### Primary Key Handling
- Use `scrobbled_at_uts` as unique identifier in routes and database queries
- Handle potential timestamp collisions gracefully
- Consider compound keys if needed for uniqueness
### Performance Optimization
- Use LiveView streams for efficient rendering of large lists
- Implement database indexes on searchable fields
- Paginate results to avoid memory issues
- Use debounced search to reduce database load
### Data Integrity
- Validate required fields (track title, artist name)
- Handle embedded schema updates (artist, album data)
- Preserve Last.fm API data integrity
- Consider soft deletes vs hard deletes
### User Experience
- Consistent styling with existing collection/wishlist pages
- Responsive design for mobile/tablet viewing
- Loading states during pagination and search
- Clear feedback for user actions (edit, delete)
- Accessible keyboard navigation
### Security & Permissions
- Ensure proper authentication via `:logged_in` pipeline
- Validate user permissions for track modifications
- Sanitize input data to prevent XSS
- Use Phoenix CSRF protection
## 9. Testing Strategy
### Unit Tests
- Context function tests for CRUD operations
- LiveView event handling tests
- Form validation tests
- Pagination logic tests
### Integration Tests
- Full user flow tests (search, edit, delete)
- Navigation integration tests
- Error handling scenarios
- Responsive layout tests
### Test Files
```
test/music_library_web/live/scrobbled_tracks_live_test.exs
test/last_fm_test.exs (extend existing)
test/support/fixtures/scrobbled_tracks_fixtures.ex
```
## 10. Localization
Add gettext entries for:
- Page titles and headings
- Form labels and placeholders
- Error messages
- Action button text
- Empty state messages
- Navigation menu item
## 11. Future Enhancements
### Potential Extensions
- Bulk operations (delete multiple tracks)
- Export functionality (CSV, JSON)
- Advanced filtering (date ranges, specific artists)
- Scrobble statistics and analytics
- Integration with music streaming services
- Track matching with collection records
### Performance Improvements
- Virtual scrolling for very large lists
- Background track synchronization
- Caching for frequently accessed data
- Search index optimization
This plan ensures consistency with existing codebase patterns while providing a complete, user-friendly interface for scrobbled tracks management.