feat: universal reorder grip handles and compact UI density
- Add generic move_in_order() to BaseRepository for reorder support - Add reusable reorder_arrows.html partial with grip dot handles - Add reorder routes to all 9 list routers (tasks, notes, links, contacts, meetings, decisions, appointments, lists, focus) - Compact row padding (6px 12px, gap 8px) on .list-row and .focus-item - Reduce font size to 0.80rem on row titles, sidebar nav, domain tree Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -248,3 +248,72 @@ class BaseRepository:
|
||||
text(f"UPDATE {self.table} SET sort_order = :order WHERE id = :id"),
|
||||
{"order": (i + 1) * 10, "id": str(id)}
|
||||
)
|
||||
|
||||
async def swap_sort_order(self, id_a: str, id_b: str) -> None:
|
||||
"""Swap sort_order between two rows."""
|
||||
result = await self.db.execute(
|
||||
text(f"SELECT id, sort_order FROM {self.table} WHERE id IN (:a, :b)"),
|
||||
{"a": str(id_a), "b": str(id_b)},
|
||||
)
|
||||
rows = {str(r._mapping["id"]): r._mapping["sort_order"] for r in result}
|
||||
if len(rows) != 2:
|
||||
return
|
||||
await self.db.execute(
|
||||
text(f"UPDATE {self.table} SET sort_order = :order WHERE id = :id"),
|
||||
{"order": rows[str(id_b)], "id": str(id_a)},
|
||||
)
|
||||
await self.db.execute(
|
||||
text(f"UPDATE {self.table} SET sort_order = :order WHERE id = :id"),
|
||||
{"order": rows[str(id_a)], "id": str(id_b)},
|
||||
)
|
||||
|
||||
async def move_in_order(self, item_id: str, direction: str, filters: dict | None = None) -> None:
|
||||
"""Move an item up or down within its sort group.
|
||||
|
||||
Handles lazy initialization (all sort_order=0) and swaps with neighbor.
|
||||
filters: optional dict to scope the group (e.g. {"list_id": some_id}).
|
||||
"""
|
||||
where_clauses = ["is_deleted = false"]
|
||||
params: dict[str, Any] = {}
|
||||
if filters:
|
||||
for i, (key, value) in enumerate(filters.items()):
|
||||
if value is None:
|
||||
where_clauses.append(f"{key} IS NULL")
|
||||
else:
|
||||
param_name = f"mf_{i}"
|
||||
where_clauses.append(f"{key} = :{param_name}")
|
||||
params[param_name] = value
|
||||
|
||||
where_sql = " AND ".join(where_clauses)
|
||||
|
||||
result = await self.db.execute(
|
||||
text(f"SELECT id, sort_order FROM {self.table} WHERE {where_sql} ORDER BY sort_order, created_at"),
|
||||
params,
|
||||
)
|
||||
items = [dict(r._mapping) for r in result]
|
||||
if len(items) < 2:
|
||||
return
|
||||
|
||||
# Lazy init: if all sort_order are 0, assign incremental values
|
||||
if all(r["sort_order"] == 0 for r in items):
|
||||
for i, r in enumerate(items):
|
||||
await self.db.execute(
|
||||
text(f"UPDATE {self.table} SET sort_order = :order WHERE id = :id"),
|
||||
{"order": (i + 1) * 10, "id": str(r["id"])},
|
||||
)
|
||||
# Re-fetch
|
||||
result = await self.db.execute(
|
||||
text(f"SELECT id, sort_order FROM {self.table} WHERE {where_sql} ORDER BY sort_order, created_at"),
|
||||
params,
|
||||
)
|
||||
items = [dict(r._mapping) for r in result]
|
||||
|
||||
ids = [str(r["id"]) for r in items]
|
||||
if item_id not in ids:
|
||||
return
|
||||
|
||||
idx = ids.index(item_id)
|
||||
if direction == "up" and idx > 0:
|
||||
await self.swap_sort_order(ids[idx], ids[idx - 1])
|
||||
elif direction == "down" and idx < len(ids) - 1:
|
||||
await self.swap_sort_order(ids[idx], ids[idx + 1])
|
||||
|
||||
Reference in New Issue
Block a user