116 lines
6.0 KiB
React
116 lines
6.0 KiB
React
import React, { useState } from 'react'
|
|
import { useContent } from '../hooks/useContent'
|
|
import { useFormSubmit } from '../hooks/useFormSubmit'
|
|
import { validateResourceDownload } from '../utils/validation'
|
|
import SEOHead from '../components/SEOHead'
|
|
|
|
const inputStyle = {
|
|
width: '100%',
|
|
padding: '12px',
|
|
background: 'var(--bg-elevated)',
|
|
border: '1px solid var(--glass-border)',
|
|
borderRadius: 'var(--radius-sm)',
|
|
color: 'var(--text-primary)',
|
|
fontSize: '1rem',
|
|
}
|
|
|
|
function ResourceRequestForm({ resource, onCancel }) {
|
|
const resourceId = resource.id || resource.slug
|
|
const [form, setForm] = useState({ name: '', email: '', company: '', resource_id: resourceId, company_website: '' })
|
|
const { submit, loading, success, errors, serverError } = useFormSubmit('/api/resource-download', validateResourceDownload)
|
|
|
|
function updateField(event) {
|
|
setForm(prev => ({ ...prev, [event.target.name]: event.target.value }))
|
|
}
|
|
|
|
async function handleSubmit(event) {
|
|
event.preventDefault()
|
|
await submit(form)
|
|
}
|
|
|
|
return (
|
|
<div className="glass-panel" style={{ padding: '24px', marginTop: '24px' }}>
|
|
{success ? (
|
|
<p role="status" aria-live="polite" style={{ margin: 0, color: 'var(--text-primary)' }}>
|
|
Danke! Wir prüfen die Anfrage und melden uns bei dir.
|
|
</p>
|
|
) : (
|
|
<form onSubmit={handleSubmit} noValidate>
|
|
<h2 style={{ fontSize: '1.25rem', marginBottom: '8px' }}>Material anfragen</h2>
|
|
<p style={{ color: 'var(--text-secondary)', marginTop: 0 }}>
|
|
Wir prüfen die Anfrage. Bei positiver Prüfung verschickt André ein aktuelles Profil persönlich.
|
|
</p>
|
|
|
|
<div hidden>
|
|
<label htmlFor="resource-company-website">Firmen-Website (bitte leer lassen)</label>
|
|
<input id="resource-company-website" name="company_website" tabIndex={-1} autoComplete="off" value={form.company_website} onChange={updateField} />
|
|
</div>
|
|
|
|
<input type="hidden" name="resource_id" value={resourceId} />
|
|
|
|
<label htmlFor="resource-name">Name</label>
|
|
<input id="resource-name" name="name" value={form.name} onChange={updateField} style={inputStyle} autoComplete="name" aria-invalid={Boolean(errors.name)} aria-describedby={errors.name ? 'resource-name-error' : undefined} />
|
|
{errors.name && <p id="resource-name-error" role="alert" style={{ color: '#ff4444', margin: '4px 0 12px' }}>{errors.name}</p>}
|
|
|
|
<label htmlFor="resource-email">E-Mail</label>
|
|
<input id="resource-email" name="email" type="email" value={form.email} onChange={updateField} style={inputStyle} autoComplete="email" aria-invalid={Boolean(errors.email)} aria-describedby={errors.email ? 'resource-email-error' : undefined} />
|
|
{errors.email && <p id="resource-email-error" role="alert" style={{ color: '#ff4444', margin: '4px 0 12px' }}>{errors.email}</p>}
|
|
|
|
<label htmlFor="resource-company">Unternehmen</label>
|
|
<input id="resource-company" name="company" value={form.company} onChange={updateField} style={inputStyle} autoComplete="organization" aria-invalid={Boolean(errors.company)} aria-describedby={errors.company ? 'resource-company-error' : undefined} />
|
|
{errors.company && <p id="resource-company-error" role="alert" style={{ color: '#ff4444', margin: '4px 0 12px' }}>{errors.company}</p>}
|
|
|
|
{serverError && <p role="alert" aria-live="assertive" style={{ color: '#ff4444' }}>{serverError}</p>}
|
|
{errors.resource_id && <p role="alert" style={{ color: '#ff4444' }}>{errors.resource_id}</p>}
|
|
|
|
<div style={{ display: 'flex', gap: '12px', marginTop: '20px', flexWrap: 'wrap' }}>
|
|
<button type="submit" className="primary-button" disabled={loading}>
|
|
{loading ? 'Wird gesendet...' : 'Anfrage senden'}
|
|
</button>
|
|
<button type="button" className="secondary-button" onClick={onCancel}>Abbrechen</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function Resources() {
|
|
const { items } = useContent('resources', { pageSize: 20 })
|
|
const [selectedResource, setSelectedResource] = useState(null)
|
|
|
|
return (
|
|
<main style={{ paddingTop: '120px', minHeight: '100vh' }}>
|
|
<SEOHead title="Ressourcen" description="Ausgewählte Materialien und Profile von Dr. André Knie auf Anfrage." url="/resources" />
|
|
<div className="app-container">
|
|
<div className="section-header">
|
|
<h1 className="section-title"><span className="text-gradient">Ressourcen</span></h1>
|
|
<p className="section-subtitle">Materialien auf Anfrage. Jede Anfrage wird zunächst geprüft.</p>
|
|
</div>
|
|
|
|
<div style={{ display: 'grid', gap: '20px', maxWidth: '700px', margin: '0 auto' }}>
|
|
{items.map(resource => {
|
|
const resourceId = resource.id || resource.slug
|
|
const selected = selectedResource === resourceId
|
|
return (
|
|
<div key={resourceId} className="glass-panel" style={{ padding: '24px' }}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '16px', flexWrap: 'wrap' }}>
|
|
<div>
|
|
<h2 style={{ marginBottom: '4px', fontSize: '1.1rem' }}>{resource.title}</h2>
|
|
<p style={{ color: 'var(--text-secondary)', margin: 0, fontSize: '0.9rem' }}>{resource.description}</p>
|
|
</div>
|
|
<button type="button" className="primary-button" onClick={() => setSelectedResource(selected ? null : resourceId)} style={{ padding: '10px 20px', fontSize: '0.9rem', whiteSpace: 'nowrap' }}>
|
|
{selected ? 'Schließen' : 'Material anfragen'}
|
|
</button>
|
|
</div>
|
|
{selected && <ResourceRequestForm key={resourceId} resource={resource} onCancel={() => setSelectedResource(null)} />}
|
|
</div>
|
|
)
|
|
})}
|
|
{items.length === 0 && <p style={{ textAlign: 'center', color: 'var(--text-secondary)' }}>Ressourcen werden vorbereitet.</p>}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|