feat: inline editing for activity mappings (improved UX)
- Edit form now appears at the position of the item being edited - No scrolling needed - stays at same location - Matches ActivityPage inline editing behavior - Visual indicator: Accent border when editing - Create form still appears at top (separate from list) Benefits: - Better UX - no need to scroll to top - Easier to find edited item after saving - Consistent with rest of app Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
829edecbdc
commit
3be82dc8c2
|
|
@ -195,7 +195,6 @@ export default function AdminActivityMappingsPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Create new button */}
|
{/* Create new button */}
|
||||||
{!editingId && (
|
|
||||||
<button
|
<button
|
||||||
onClick={startCreate}
|
onClick={startCreate}
|
||||||
className="btn btn-primary btn-full"
|
className="btn btn-primary btn-full"
|
||||||
|
|
@ -203,14 +202,11 @@ export default function AdminActivityMappingsPage() {
|
||||||
>
|
>
|
||||||
<Plus size={16} /> Neues Mapping anlegen
|
<Plus size={16} /> Neues Mapping anlegen
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Edit form */}
|
{/* New mapping form (only shown when creating) */}
|
||||||
{editingId && formData && (
|
{editingId === 'new' && formData && (
|
||||||
<div className="card" style={{ padding: 16, marginBottom: 16 }}>
|
<div className="card" style={{ padding: 16, marginBottom: 16, border: '2px solid var(--accent)' }}>
|
||||||
<div style={{ fontWeight: 600, marginBottom: 12 }}>
|
<div style={{ fontWeight: 600, marginBottom: 12 }}>➕ Neues Mapping</div>
|
||||||
{editingId === 'new' ? '➕ Neues Mapping' : '✏️ Mapping bearbeiten'}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -220,8 +216,8 @@ export default function AdminActivityMappingsPage() {
|
||||||
value={formData.activity_type}
|
value={formData.activity_type}
|
||||||
onChange={e => setFormData({ ...formData, activity_type: e.target.value })}
|
onChange={e => setFormData({ ...formData, activity_type: e.target.value })}
|
||||||
placeholder="z.B. Traditionelles Krafttraining"
|
placeholder="z.B. Traditionelles Krafttraining"
|
||||||
disabled={editingId !== 'new'}
|
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}>
|
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 4 }}>
|
||||||
Groß-/Kleinschreibung beachten! Muss exakt mit CSV übereinstimmen.
|
Groß-/Kleinschreibung beachten! Muss exakt mit CSV übereinstimmen.
|
||||||
|
|
@ -289,19 +285,101 @@ export default function AdminActivityMappingsPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* List */}
|
{/* List with inline editing */}
|
||||||
{mappings.length === 0 ? (
|
{mappings.length === 0 ? (
|
||||||
<div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--text3)' }}>
|
<div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--text3)' }}>
|
||||||
Keine Mappings gefunden
|
Keine Mappings gefunden
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||||
{mappings.map(mapping => (
|
{mappings.map(mapping => {
|
||||||
|
const isEditing = editingId === mapping.id
|
||||||
|
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
key={mapping.id}
|
key={mapping.id}
|
||||||
className="card"
|
className="card"
|
||||||
style={{ padding: 12 }}
|
style={{
|
||||||
|
padding: 12,
|
||||||
|
border: isEditing ? '2px solid var(--accent)' : undefined
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
{isEditing && formData ? (
|
||||||
|
/* Inline edit form */
|
||||||
|
<div>
|
||||||
|
<div style={{ fontWeight: 600, marginBottom: 12, color: 'var(--accent)' }}>
|
||||||
|
✏️ Mapping bearbeiten
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
|
<div>
|
||||||
|
<div className="form-label">Activity Type (nicht änderbar)</div>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={formData.activity_type}
|
||||||
|
disabled
|
||||||
|
style={{ width: '100%', background: 'var(--surface2)' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="form-label">Training Type *</div>
|
||||||
|
<select
|
||||||
|
className="form-input"
|
||||||
|
value={formData.training_type_id}
|
||||||
|
onChange={e => setFormData({ ...formData, training_type_id: parseInt(e.target.value) })}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
>
|
||||||
|
{trainingTypes.map(type => (
|
||||||
|
<option key={type.id} value={type.id}>
|
||||||
|
{type.icon} {type.name_de} ({type.category})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="form-label">Profil-ID (leer = global)</div>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={formData.profile_id}
|
||||||
|
onChange={e => setFormData({ ...formData, profile_id: e.target.value })}
|
||||||
|
placeholder="Leer lassen für globales Mapping"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
disabled={saving}
|
||||||
|
className="btn btn-primary"
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
>
|
||||||
|
{saving ? (
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6, justifyContent: 'center' }}>
|
||||||
|
<div className="spinner" style={{ width: 14, height: 14 }} />
|
||||||
|
Speichere...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Save size={16} /> Speichern
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={cancelEdit}
|
||||||
|
disabled={saving}
|
||||||
|
className="btn btn-secondary"
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
>
|
||||||
|
<X size={16} /> Abbrechen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
/* Normal view */
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
@ -346,8 +424,10 @@ export default function AdminActivityMappingsPage() {
|
||||||
<Trash2 size={16} />
|
<Trash2 size={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user