CODEX - Moar Changes

This commit is contained in:
2026-02-22 21:20:16 +02:00
parent dd25f66c16
commit 5a0faad429

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useRef, useMemo } from "react";
import { useState, useEffect, useRef, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import api from "../api/client";
import { useAuth } from "../auth/AuthContext";
@@ -14,6 +14,12 @@ import {
const MELODY_TYPES = ["", "orthodox", "catholic", "all"];
const MELODY_TONES = ["", "normal", "festive", "cheerful", "lamentation"];
const NOTE_LABELS = "ABCDEFGHIJKLMNOP";
const SORTABLE_COLUMN_TO_KEY = {
name: "name",
totalActiveBells: "totalActiveBells",
dateCreated: "dateCreated",
dateEdited: "dateEdited",
};
// All available columns with their defaults
const ALL_COLUMNS = [
@@ -271,7 +277,7 @@ export default function MelodyList() {
switch (key) {
case "name":
return getDisplayName(info.name).toLowerCase();
case "totalBells":
case "totalActiveBells":
return Number(info.totalActiveBells || 0);
case "dateEdited":
return parseDateValue(metadata.dateEdited);
@@ -300,6 +306,24 @@ export default function MelodyList() {
});
}, [melodies, createdByFilter, sortBy, sortDir]); // eslint-disable-line react-hooks/exhaustive-deps
const handleSortClick = (columnKey) => {
const nextSortKey = SORTABLE_COLUMN_TO_KEY[columnKey];
if (!nextSortKey) return;
if (sortBy === nextSortKey) {
setSortDir((prev) => (prev === "asc" ? "desc" : "asc"));
return;
}
setSortBy(nextSortKey);
setSortDir(nextSortKey === "dateCreated" || nextSortKey === "dateEdited" ? "desc" : "asc");
};
const getSortIndicator = (columnKey) => {
const sortKey = SORTABLE_COLUMN_TO_KEY[columnKey];
if (!sortKey) return null;
if (sortBy !== sortKey) return "↕";
return sortDir === "asc" ? "▲" : "▼";
};
const renderCellValue = (key, row) => {
const info = row.information || {};
const ds = row.default_settings || {};
@@ -398,7 +422,44 @@ export default function MelodyList() {
</div>
);
case "duration":
return ds.duration != null ? formatDuration(ds.duration) : "-";
if (ds.duration == null) return "-";
if (Number(ds.duration) === 0) {
return (
<span
className="px-2 py-0.5 text-xs rounded-full"
style={{ backgroundColor: "rgba(88,156,250,0.18)", color: "var(--text-link)" }}
>
Single Run
</span>
);
}
{
const allDurationValues = melodySettings?.duration_values || [];
const nonZeroDurations = allDurationValues.filter((v) => Number(v) > 0);
const idx = nonZeroDurations.indexOf(Number(ds.duration));
const percent = idx >= 0 && nonZeroDurations.length > 0
? Math.round(((idx + 1) / nonZeroDurations.length) * 100)
: 0;
return (
<div className="min-w-28">
<div className="text-xs mb-1" style={{ color: "var(--text-secondary)" }}>
{formatDuration(ds.duration)}
</div>
<div
className="w-full h-2 rounded-full"
style={{ backgroundColor: "var(--bg-primary)", border: "1px solid var(--border-primary)" }}
>
<div
className="h-full rounded-full transition-all"
style={{
width: `${Math.max(0, Math.min(100, percent))}%`,
backgroundColor: speedBarColor(percent),
}}
/>
</div>
</div>
);
}
case "totalRunDuration":
return ds.totalRunDuration ?? "-";
case "pauseDuration":
@@ -417,14 +478,14 @@ export default function MelodyList() {
);
case "noteAssignments":
return ds.noteAssignments?.length > 0 ? (
<div className="flex flex-wrap gap-1.5">
<div className="flex flex-nowrap gap-1 whitespace-nowrap">
{ds.noteAssignments.map((assignedBell, noteIdx) => (
<div
key={noteIdx}
className="flex flex-col items-center rounded-md border"
style={{
minWidth: "30px",
padding: "3px 5px",
minWidth: "26px",
padding: "3px 3px",
backgroundColor: "var(--bg-card-hover)",
borderColor: "var(--border-primary)",
}}
@@ -434,7 +495,7 @@ export default function MelodyList() {
</span>
<div className="w-full my-0.5" style={{ height: "1px", backgroundColor: "var(--border-primary)" }} />
<span className="text-xs leading-tight" style={{ color: "var(--text-muted)" }}>
{assignedBell > 0 ? assignedBell : "—"}
{assignedBell > 0 ? assignedBell : "—"}
</span>
</div>
))}
@@ -551,26 +612,6 @@ export default function MelodyList() {
<option value="draft">Drafts</option>
</select>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className={selectClass}
>
<option value="dateCreated">Sort: Date Created</option>
<option value="dateEdited">Sort: Date Edited</option>
<option value="name">Sort: Name</option>
<option value="totalBells">Sort: Total Bells</option>
</select>
<select
value={sortDir}
onChange={(e) => setSortDir(e.target.value)}
className={selectClass}
>
<option value="desc">Desc</option>
<option value="asc">Asc</option>
</select>
<div className="relative" ref={creatorPickerRef}>
<button
type="button"
@@ -724,10 +765,33 @@ export default function MelodyList() {
{activeColumns.map((col) => (
<th
key={col.key}
className={`px-4 py-3 text-left font-medium ${col.key === "color" ? "w-8 px-2" : ""}`}
className={`px-4 py-3 text-left font-medium ${
col.key === "color" ? "w-8 px-2" : col.key === "noteAssignments" ? "min-w-[420px]" : ""
}`}
style={{ color: "var(--text-muted)" }}
>
{col.key === "color" ? "" : col.label}
{col.key === "color" ? "" : (
<div className="inline-flex items-center gap-1">
<span>{col.label}</span>
{SORTABLE_COLUMN_TO_KEY[col.key] ? (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleSortClick(col.key);
}}
className="text-xs leading-none px-1 rounded cursor-pointer"
style={{
color: sortBy === SORTABLE_COLUMN_TO_KEY[col.key] ? "var(--accent)" : "var(--text-muted)",
backgroundColor: sortBy === SORTABLE_COLUMN_TO_KEY[col.key] ? "rgba(116,184,22,0.14)" : "transparent",
}}
title={`Sort by ${col.label}`}
>
{getSortIndicator(col.key)}
</button>
) : null}
</div>
)}
</th>
))}
{canEdit && (
@@ -748,7 +812,9 @@ export default function MelodyList() {
{activeColumns.map((col) => (
<td
key={col.key}
className={`px-4 py-3 ${col.key === "color" ? "w-8 px-2" : ""}`}
className={`px-4 py-3 ${
col.key === "color" ? "w-8 px-2" : col.key === "noteAssignments" ? "min-w-[420px]" : ""
}`}
style={{ color: "var(--text-primary)" }}
>
{renderCellValue(col.key, row)}
@@ -819,3 +885,4 @@ export default function MelodyList() {
</div>
);
}