Skip to content

Commit 279470a

Browse files
committed
fix(page-manager): improve form handling for hero title, datetime, and state management
- Prevent hero title auto-derivation when manually edited by tracking dirty state - Fix datetime-local input handling with proper formatting and parsing - Only include published_at when post is published and has a valid date - Reset posts array when switching between different pages - Fix slug state update order in form submission
1 parent b9cd901 commit 279470a

3 files changed

Lines changed: 59 additions & 13 deletions

File tree

src/components/page-manager/PageForm.jsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
5050
}
5151
return initialData?.title ?? defaultHeroTitle
5252
})
53+
const [heroTitleDirty, setHeroTitleDirty] = useState(false)
5354
const [error, setError] = useState(null)
5455
const formSanitizedSlug = useMemo(() => sanitizeSlug(slug), [slug])
5556
const slugHasInput = slug.trim().length > 0
@@ -69,9 +70,14 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
6970
const trimmedHeroTitle = heroTitle.trim()
7071
const trimmedSlug = slug.trim()
7172
const sanitizedSlug = sanitizeSlug(trimmedSlug)
72-
if (trimmedHeroTitle) {
73-
heroPayload.title = trimmedHeroTitle
74-
} else if (!heroPayload.title) {
73+
if (heroTitleDirty) {
74+
if (trimmedHeroTitle) {
75+
heroPayload.title = trimmedHeroTitle
76+
} else {
77+
delete heroPayload.title
78+
}
79+
}
80+
if (!heroPayload.title) {
7581
heroPayload.title = trimmedTitle
7682
}
7783
if (!trimmedTitle) {
@@ -83,6 +89,7 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
8389
if (!isValidSlug(sanitizedSlug)) {
8490
throw new Error('Slug ist ungültig.')
8591
}
92+
setSlug(sanitizedSlug)
8693
const payload = {
8794
title: trimmedTitle,
8895
slug: sanitizedSlug,
@@ -95,7 +102,6 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
95102
layout: parseJsonField(layout, 'Layout JSON'),
96103
}
97104
await onSubmit(payload)
98-
setSlug(sanitizedSlug)
99105
} catch (err) {
100106
setError(err)
101107
}
@@ -202,6 +208,7 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
202208
onChange={(event) => {
203209
const { value } = event.target
204210
setHeroTitle(value)
211+
setHeroTitleDirty(true)
205212
const trimmedValue = value.trim()
206213
setHero((currentHero) => {
207214
try {
@@ -257,10 +264,12 @@ const PageForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
257264
setHero(value)
258265
try {
259266
const parsed = JSON.parse(value)
260-
const derivedTitle = normalizeTitle(parsed?.title ?? parsed, '').trim()
261-
setHeroTitle((previous) =>
262-
derivedTitle !== previous ? derivedTitle : previous,
263-
)
267+
if (!heroTitleDirty) {
268+
const derivedTitle = normalizeTitle(parsed?.title ?? parsed, '').trim()
269+
setHeroTitle((previous) =>
270+
derivedTitle !== previous ? derivedTitle : previous,
271+
)
272+
}
264273
} catch (err) {
265274
}
266275
}}

src/components/page-manager/PostForm.jsx

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@ import { AlertCircle, FileText, RefreshCw, X } from 'lucide-react'
44
import { sanitizeSlug, isValidSlug } from '../../utils/slug'
55
import { sanitizeInteger } from './formUtils'
66

7+
const formatDateTimeLocal = (value) => {
8+
if (!value) return ''
9+
const date = new Date(value)
10+
if (Number.isNaN(date.getTime())) {
11+
return ''
12+
}
13+
const pad = (unit) => String(unit).padStart(2, '0')
14+
const year = date.getFullYear()
15+
const month = pad(date.getMonth() + 1)
16+
const day = pad(date.getDate())
17+
const hours = pad(date.getHours())
18+
const minutes = pad(date.getMinutes())
19+
return `${year}-${month}-${day}T${hours}:${minutes}`
20+
}
21+
22+
const parseDateTimeLocal = (value) => {
23+
if (!value) return ''
24+
const date = new Date(value)
25+
if (Number.isNaN(date.getTime())) {
26+
return ''
27+
}
28+
return date.toISOString()
29+
}
30+
731
const PostForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
832
const [title, setTitle] = useState(initialData?.title ?? '')
933
const [slug, setSlug] = useState(initialData?.slug ?? '')
@@ -13,6 +37,7 @@ const PostForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
1337
const [isPublished, setIsPublished] = useState(Boolean(initialData?.is_published))
1438
const [publishedAt, setPublishedAt] = useState(initialData?.published_at ?? '')
1539
const [error, setError] = useState(null)
40+
const publishedAtInputValue = useMemo(() => formatDateTimeLocal(publishedAt), [publishedAt])
1641
const sanitizedPostSlug = useMemo(() => sanitizeSlug(slug), [slug])
1742
const postSlugHasInput = slug.trim().length > 0
1843
const postSlugInvalid = postSlugHasInput && !sanitizedPostSlug
@@ -38,17 +63,17 @@ const PostForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
3863
if (!isValidSlug(sanitizedSlug)) {
3964
throw new Error('Slug ist ungültig.')
4065
}
66+
setSlug(sanitizedSlug)
4167
const payload = {
4268
title: title.trim(),
4369
slug: sanitizedSlug,
4470
excerpt: excerpt.trim() || null,
4571
content_markdown: content,
4672
order_index: sanitizeInteger(orderIndex),
4773
is_published: isPublished,
48-
published_at: publishedAt.trim() ? publishedAt : null,
74+
published_at: isPublished && publishedAt ? publishedAt : null,
4975
}
5076
await onSubmit(payload)
51-
setSlug(sanitizedSlug)
5277
} catch (err) {
5378
setError(err)
5479
}
@@ -131,8 +156,8 @@ const PostForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
131156
<input
132157
type="datetime-local"
133158
className="mt-1 w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
134-
value={publishedAt || ''}
135-
onChange={(event) => setPublishedAt(event.target.value)}
159+
value={publishedAtInputValue}
160+
onChange={(event) => setPublishedAt(parseDateTimeLocal(event.target.value))}
136161
disabled={!isPublished}
137162
/>
138163
</label>
@@ -162,7 +187,13 @@ const PostForm = ({ mode, initialData, onSubmit, onCancel, submitting }) => {
162187
type="checkbox"
163188
className="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-slate-600 dark:bg-slate-900"
164189
checked={isPublished}
165-
onChange={(event) => setIsPublished(event.target.checked)}
190+
onChange={(event) => {
191+
const nextValue = event.target.checked
192+
setIsPublished(nextValue)
193+
if (!nextValue) {
194+
setPublishedAt('')
195+
}
196+
}}
166197
/>
167198
Veröffentlicht
168199
</label>

src/components/page-manager/hooks/usePageManagerState.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const usePageManagerState = () => {
1717
const [postsLoading, setPostsLoading] = useState(false)
1818
const [postsError, setPostsError] = useState(null)
1919
const postsRequestRef = useRef(0)
20+
const lastPostsPageIdRef = useRef(null)
2021
const [postFormMode, setPostFormMode] = useState(null)
2122
const [postFormData, setPostFormData] = useState(null)
2223
const [postFormSubmitting, setPostFormSubmitting] = useState(false)
@@ -105,6 +106,7 @@ const usePageManagerState = () => {
105106
postsAbortRef.current.abort()
106107
postsAbortRef.current = null
107108
}
109+
lastPostsPageIdRef.current = null
108110
setPosts([])
109111
setPostsLoading(false)
110112
setPostsError(null)
@@ -115,6 +117,10 @@ const usePageManagerState = () => {
115117
postsAbortRef.current.abort()
116118
}
117119
postsAbortRef.current = controller
120+
if (lastPostsPageIdRef.current !== pageId) {
121+
setPosts([])
122+
}
123+
lastPostsPageIdRef.current = pageId
118124
const requestId = postsRequestRef.current + 1
119125
postsRequestRef.current = requestId
120126
setPostsLoading(true)

0 commit comments

Comments
 (0)