diff --git a/frontend/src/lib/components/view/book-table.svelte b/frontend/src/lib/components/view/book-table.svelte
index fb45508..ab0d16f 100644
--- a/frontend/src/lib/components/view/book-table.svelte
+++ b/frontend/src/lib/components/view/book-table.svelte
@@ -1,66 +1,294 @@
-
-
-
-
-
- Cover
- Title
- Authors
- Publisher
- Published Date
- Pages
- Tags
- Identifiers
-
-
-
- {#each books as book (book.id)}
+
+
+
+
+ {books.length} shown
+
+
+
+
+
+ Columns
+
+
+
+
+ Show columns
+
+ {#each columns as column (column.key)}
+
+ {
+ if (!column.fixed) column.on = value;
+ }}
+ >
+ {column.label}
+
+ {/each}
+
+
+
+
+
+
+
+
+
- selectedState.toggleSelection(book)}
- />
-
-
-
- {book.title}
-
- {#each book.authors as author}
- {author.name}
- {/each}
-
- {book.publisher}
- {book.published_date}
- {book.pages}
-
- {#each book.tags as tag}
- {tag.name}
- {/each}
-
-
- {#each Object.entries(book.identifiers) as [name, value] (value)}
- {name}
- {/each}
-
+
+ {
+ if (allSelected) selectionState.deselectAll();
+ else selectionState.selectAll(books);
+ }}
+ />
+
+
+
+ {#each visible as column (column.key)}
+
+ {#if column.sort}
+
+
+ {:else}
+ {column.label}
+ {/if}
+
+ {/each}
- {/each}
-
-
+
+
+
+ {#each books as book (book.id)}
+ handleRowClick(event, book)}
+ class={selectionState.selectionModeActive ? 'cursor-pointer' : ''}
+ >
+
+ selectionState.toggleSelection(book)}
+ />
+
+
+
+
+
+ {#each visible as column (column.key)}
+ {#if column.key === 'title'}
+
+ {book.title}
+
+ {:else if column.key === 'authors'}
+
+ {#each book.authors as author (author.id)}
+ {author.name}
+ {/each}
+
+ {:else if column.key === 'series'}
+
+ {book.series ? book.series.title : '—'}
+
+ {:else if column.key === 'publisher'}
+
+ {book.publisher ? book.publisher.name : '—'}
+
+ {:else if column.key === 'published'}
+
+ {book.published_date ? new Date(book.published_date).getFullYear() : '—'}
+
+ {:else if column.key === 'pages'}
+
+ {book.pages ?? '—'}
+
+ {:else if column.key === 'format'}
+ {formats(book)}
+ {:else if column.key === 'size'}
+
+ {formatFileSize(totalSize(book))}
+
+ {:else if column.key === 'added'}
+
+ {addedOn(book)}
+
+ {:else if column.key === 'progress'}
+
+ {#if book.progress?.completed}
+ Finished
+ {:else if book.progress?.percentage}
+
+
+
+
+
+ {Math.round(book.progress.percentage * 100)}%
+
+
+ {:else}
+ Unread
+ {/if}
+
+ {:else if column.key === 'missing'}
+
+ {#each missing(book) as gap (gap)}
+
+ {gap}
+
+ {:else}
+ complete
+ {/each}
+
+ {/if}
+ {/each}
+
+
+
+
+
+ {/each}
+
+
+