feat(admin): add asset picker with favorites, drag-and-drop reordering, preview links, and keyboard shortcuts
- Asset Picker: Visual browser for hero images with Favorites tab (shown first) and All Photos tab. Includes infinite scroll and UUID fallback input for assets not easily found in the grid. - Drag & Drop: All lists (hero images, standalone albums, subpage albums, subpage tiles) are now reorderable via drag handles using @dnd-kit. - Preview Links: 'Preview Site' button in save bar, '↗ Site' in header, and per-subpage preview links with URL slug preview shown inline. - Keyboard Shortcuts: ⌘+S / Ctrl+S to save in both Pages and Settings tabs. - Unsaved Changes Guard: beforeunload warning prevents accidental data loss. - New API route /api/admin/assets for browsing Immich assets with pagination and favorites filter via search/metadata endpoint.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
interface Settings {
|
||||
title?: string;
|
||||
@@ -67,6 +67,37 @@ export default function SettingsEditor() {
|
||||
loadSettings();
|
||||
}, []);
|
||||
|
||||
// ── Keyboard shortcut: ⌘+S / Ctrl+S ─────────────────────────
|
||||
const handleSaveRef = useCallback(() => {
|
||||
if (dirty && !saving) {
|
||||
handleSave();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dirty, saving, settings]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
handleSaveRef();
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [handleSaveRef]);
|
||||
|
||||
// ── Unsaved changes guard ────────────────────────────────────
|
||||
useEffect(() => {
|
||||
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
||||
if (dirty) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
}
|
||||
}
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
}, [dirty]);
|
||||
|
||||
async function loadSettings() {
|
||||
setLoading(true);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user