PozoApp/src/AdminPanel/AdminPanelForms/HeroSectionForm.jsx

416 lines
19 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import { Popconfirm, message } from 'antd'
import { useAdminPanel } from '../AdminPanelContext'
import { DefaultModal } from '../../Components/Modal/DefaultModal'
import "../Styles/HeroSectionForm.scss"
const HeroSectionForm = () => {
const { sectionData, updateSectionData } = useAdminPanel()
const heroData = sectionData.HeroSectionForm || []
const [isModalOpen, setIsModalOpen] = useState(false)
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [subsections, setSubsections] = useState([{ id: 1, image: null, previewUrl: null, heading: '', subheading: '' }])
const [editingIndex, setEditingIndex] = useState(-1)
const [titleError, setTitleError] = useState('')
const [descriptionError, setDescriptionError] = useState('')
// UUID generator fallback for browser compatibility
const generateUUID = () => {
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return crypto.randomUUID()
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0
const v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
}
useEffect(() => {
return () => {
subsections.forEach(s => { if (s.previewUrl) URL.revokeObjectURL(s.previewUrl) })
}
}, [])
const openModal = () => {
setIsModalOpen(true)
}
const closeModal = () => {
setIsModalOpen(false)
setTitle('')
setDescription('')
setSubsections([{ id: 1, image: null, previewUrl: null, heading: '', subheading: '' }])
setEditingIndex(-1)
setTitleError('')
setDescriptionError('')
}
const handleClear = () => {
setTitle('')
setDescription('')
setSubsections([{ id: 1, image: null, previewUrl: null, heading: '', subheading: '' }])
setTitleError('')
setDescriptionError('')
}
const addSubsection = () => {
if (subsections.length >= 3) {
message.warning('Only 3 subsections allowed')
return
}
const newId = Math.max(...subsections.map(s => s.id)) + 1
setSubsections([...subsections, { id: newId, image: null, previewUrl: null, heading: '', subheading: '' }])
}
const removeSubsection = (id) => {
if (subsections.length === 1) {
message.warning('At least one subsection is required')
return
}
const toRemove = subsections.find(s => s.id === id)
if (toRemove?.previewUrl) {
URL.revokeObjectURL(toRemove.previewUrl)
}
setSubsections(subsections.filter(s => s.id !== id))
}
const handleImageUpload = (id, file) => {
setSubsections(subsections.map(s => {
if (s.id !== id) return s
if (s.previewUrl) {
URL.revokeObjectURL(s.previewUrl)
}
const nextUrl = file ? URL.createObjectURL(file) : null
return { ...s, image: file || null, previewUrl: nextUrl }
}))
}
const handleSubsectionChange = (id, field, value) => {
setSubsections(subsections.map(s =>
s.id === id ? { ...s, [field]: value } : s
))
}
const handleSave = () => {
let hasError = false
if (!title.trim()) {
setTitleError('Title is required')
hasError = true
} else {
setTitleError('')
}
if (!description.trim()) {
setDescriptionError('Description is required')
hasError = true
} else {
setDescriptionError('')
}
if (hasError) {
message.error('Please fix the highlighted fields')
return
}
const newHero = {
id: editingIndex >= 0 ? heroData[editingIndex].id : generateUUID(),
title: title.trim(),
description: description.trim(),
subsections: subsections.map(s => ({
id: s.id,
heading: s.heading,
subheading: s.subheading,
displayUrl: s.previewUrl || (s.image ? URL.createObjectURL(s.image) : null),
})),
active: true,
createdAt: editingIndex >= 0 ? heroData[editingIndex].createdAt : new Date().toLocaleDateString()
}
if (editingIndex >= 0) {
const updatedHeros = heroData.map((hero, index) => index === editingIndex ? newHero : hero)
updateSectionData('HeroSectionForm', updatedHeros)
setEditingIndex(-1)
message.success('Hero section updated!')
} else {
updateSectionData('HeroSectionForm', [...heroData, newHero])
message.success('Hero section added!')
}
closeModal()
}
const handleDelete = (index) => {
const updatedHeros = heroData.filter((_, i) => i !== index)
updateSectionData('HeroSectionForm', updatedHeros)
message.success('Hero section deleted')
}
const handleEdit = (index) => {
setTitle(heroData[index].title)
setDescription(heroData[index].description)
if (heroData[index].subsections && heroData[index].subsections.length > 0) {
setSubsections(heroData[index].subsections.map(ss => ({
id: ss.id,
image: null,
previewUrl: ss.displayUrl || null,
heading: ss.heading || '',
subheading: ss.subheading || ''
})))
}
setEditingIndex(index)
openModal()
}
const handleToggleActive = (index) => {
const updated = [...heroData]
updated[index].active = !updated[index].active
updateSectionData('HeroSectionForm', updated)
message.success('Status updated')
}
return (
<div className='hero-section-form-master'>
{/* Header Section */}
<div className='hero-section-form-header'>
<div className='hero-section-header-left'>
<div className='hero-section-header-content'>
<h2>Hero Section Management</h2>
<p>Manage and organize your hero section content and settings.</p>
</div>
</div>
<button onClick={openModal} className='hero-section-create-btn'>
<span>+</span> Create Hero Section
</button>
</div>
{/* Tab Navigation */}
<div className='hero-section-tab-navigation'>
<div className='hero-section-tab-buttons'>
<button className='hero-section-tab-btn hero-section-active'>
Hero Sections ({heroData.length})
</button>
<button className='hero-section-tab-btn'>
All Status
</button>
</div>
<div className='hero-section-tab-controls'>
<div className='hero-section-sort-dropdown'>
<select>
<option value="newest">Newest First</option>
<option value="oldest">Oldest First</option>
<option value="alphabetical">Alphabetical</option>
</select>
</div>
<div className='hero-section-view-toggle'>
<button className='hero-section-view-btn hero-section-active'>
<span>⊞</span>
</button>
<button className='hero-section-view-btn'>
<span>☰</span>
</button>
</div>
</div>
</div>
{/* Content Area */}
{heroData.length > 0 ? (
<div className='hero-section-display'>
<div className='hero-section-grid'>
{heroData.map((hero, index) => (
<div key={hero.id} className={`hero-section-card ${!hero.active ? 'hero-section-inactive' : ''}`}>
<div className='hero-section-card-header'>
<h3>{hero.title}</h3>
<div className='hero-section-card-actions'>
<button className='hero-section-edit-btn' onClick={() => handleEdit(index)}>Edit</button>
<Popconfirm
title="Delete Hero Section"
description="Are you sure you want to delete this hero section?"
onConfirm={() => handleDelete(index)}
okText="Yes"
cancelText="No"
>
<button className='hero-section-delete-btn'>Delete</button>
</Popconfirm>
<button
className={`hero-section-status-btn ${hero.active ? 'hero-section-deactivate' : 'hero-section-activate'}`}
onClick={() => handleToggleActive(index)}
>
{hero.active ? 'Deactivate' : 'Activate'}
</button>
</div>
</div>
<div className='hero-section-card-content'>
<p><strong>Description:</strong> {hero.description || 'No description'}</p>
<p className='hero-section-created-date'>Created: {hero.date || hero.createdAt}</p>
<p className={`hero-section-status ${hero.active ? 'hero-section-active' : 'hero-section-inactive'}`}>
Status: {hero.active ? 'Active' : 'Inactive'}
</p>
{hero.subsections && hero.subsections.length > 0 && (
<div className='hero-section-subsections'>
<strong>Subsections:</strong>
{hero.subsections.map(ss => (
<div key={ss.id} className='hero-section-subsection-item'>
{ss.displayUrl && (
<div className='hero-section-thumb'>
<img src={ss.displayUrl} alt={ss.heading || 'Subsection'} />
</div>
)}
<div className='hero-section-texts'>
{ss.heading && <div className='hero-section-heading'>{ss.heading}</div>}
{ss.subheading && <div className='hero-section-subheading'>{ss.subheading}</div>}
</div>
</div>
))}
</div>
)}
</div>
</div>
))}
</div>
</div>
) : (
<div className='hero-section-placeholder-content'>
<div className='hero-section-placeholder-icon'>
<span>🎯</span>
</div>
<h2>No Hero Sections Found</h2>
<p>Get started by creating your first hero section</p>
</div>
)
}
{/* Modal */}
<DefaultModal
open={isModalOpen}
title={editingIndex >= 0 ? 'Edit Hero Section' : 'Create New Hero Section'}
handleCancel={closeModal}
handleSubmit={handleSave}
buttonText={editingIndex >= 0 ? 'Update Hero' : 'Add Hero'}
width={700}
destroyOnClose={true}
>
<div className='hero-section-form'>
<div className='hero-section-form-group'>
<label htmlFor='heroTitle'>Title</label>
<input
type='text'
id='heroTitle'
value={title}
onChange={(e) => {
setTitle(e.target.value)
if (titleError) setTitleError('')
}}
placeholder='Enter hero title'
className={titleError ? 'error' : ''}
autoFocus
/>
{titleError && <span className='error-message'>{titleError}</span>}
</div>
<div className='hero-section-form-group'>
<label htmlFor='heroDescription'>Description</label>
<textarea
id='heroDescription'
value={description}
onChange={(e) => {
setDescription(e.target.value)
if (descriptionError) setDescriptionError('')
}}
placeholder='Enter hero description'
rows='4'
className={descriptionError ? 'error' : ''}
/>
{descriptionError && <span className='error-message'>{descriptionError}</span>}
</div>
<div className='hero-section-subsections-container'>
<div className='hero-section-subsections-header'>
<h3>Subsections</h3>
<p>Add up to 3 subsections with images and content</p>
</div>
{subsections.map((subsection, idx) => (
<div key={subsection.id} className='hero-section-subsection'>
<div className='hero-section-subsection-number'>
Subsection {idx + 1}
{subsections.length > 1 && (
<button
type='button'
className='hero-section-remove-subsection-btn'
onClick={() => removeSubsection(subsection.id)}
>
✕
</button>
)}
</div>
<div className='hero-section-form-group'>
<label>Image Upload</label>
<input
type='file'
accept='image/*'
onChange={(e) => handleImageUpload(subsection.id, e.target.files[0])}
className='hero-section-file-input'
aria-label={`Upload image for subsection ${idx + 1}`}
/>
{subsection.previewUrl && (
<div className='hero-section-image-preview'>
<img src={subsection.previewUrl} alt='Preview' />
</div>
)}
</div>
<div className='hero-section-form-group'>
<label>Heading</label>
<input
type='text'
placeholder='Enter heading'
value={subsection.heading}
onChange={(e) => handleSubsectionChange(subsection.id, 'heading', e.target.value)}
/>
</div>
<div className='hero-section-form-group'>
<label>Sub Heading</label>
<input
type='text'
placeholder='Enter sub heading'
value={subsection.subheading}
onChange={(e) => handleSubsectionChange(subsection.id, 'subheading', e.target.value)}
/>
</div>
</div>
))}
<button
type='button'
className='hero-section-add-subsection-btn'
onClick={addSubsection}
disabled={subsections.length >= 3}
>
+ Add Subsection
</button>
</div>
<div className='hero-section-form-actions'>
<button type='button' onClick={handleSave} className='hero-section-save-btn'>
{editingIndex >= 0 ? 'Update' : 'Save'}
</button>
<button type='button' onClick={closeModal} className='hero-section-cancel-btn'>
Cancel
</button>
<button type='button' onClick={handleClear} className='hero-section-clear-btn'>
Clear
</button>
</div>
</div>
</DefaultModal>
</div>
)
}
export default HeroSectionForm