Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/admin/components/FilterSection.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import serviceData from '../../client/data/serviceData'; // Ensure this path is correct
import staticServiceData from '../../client/data/staticServiceData'; // Ensure this path is correct

const FilterSection = ({ onFilterChange }) => {
const handleFilterChange = (e) => {
Expand All @@ -10,7 +10,7 @@ const FilterSection = ({ onFilterChange }) => {
// Memoize the service options to avoid re-computation on every render
const serviceOptions = useMemo(
() =>
serviceData.map((service) => (
staticServiceData.map((service) => (
<option key={service.id} value={service.title}>
{service.title}
</option>
Expand Down
36 changes: 25 additions & 11 deletions src/admin/components/PageHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
import { Icon } from '@iconify/react';
import { useLocation } from 'react-router-dom';

const PageHeader = ({ onSearch, openModal }) => {
const location = useLocation();
const isEditService = location.pathname === '/admin/services';

const PageHeader = ({ onSearch }) => {
return (
<div className="flex flex-col md:flex-row justify-between mb-5 text-lg items-center">
<h1 className="font-semibold mb-3 md:mb-0 text-center md:text-left">Invoice</h1>
<div className="flex items-center border border-[#E8E9ED] bg-white py-2 px-3 rounded-lg text-[#6E7379] w-full md:w-auto">
<Icon icon="charm:search" className="mr-2 w-5 h-5" />
<input
type="text"
placeholder="Search"
className="w-full md:w-80 focus:outline-none"
onChange={(e) => onSearch(e.target.value)} // Trigger onSearch when input changes
/>
</div>
<h1 className="font-semibold text-xl mb-3 md:mb-0 text-center md:text-left"> { !isEditService ? 'Invoice' : 'Services' }</h1>
{!isEditService ? (
<div className="flex items-center border border-[#E8E9ED] bg-white py-2 px-3 rounded-lg text-[#6E7379] w-full md:w-auto">
<Icon icon="charm:search" className="mr-2 w-5 h-5" />
<input
type="text"
placeholder="Search"
className="w-full md:w-80 focus:outline-none"
onChange={(e) => onSearch(e.target.value)} // Trigger onSearch when input changes
/>
</div>
): (
<button
onClick={openModal}
className="bg-red-600 hover:bg-red-500 text-white py-2 px-4 rounded-lg w-full sm:w-auto"
>
+ Add
</button>
)}

</div>
);
}
Expand Down
140 changes: 140 additions & 0 deletions src/admin/components/ServiceFormModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { useState, useEffect } from 'react';
import { Icon } from '@iconify/react/dist/iconify.js';
import Modal from 'react-modal';

const ServiceFormModal = ({ isOpen, onClose, onSaveService, editingService }) => {
// Initialize service state with default or editingService values
const [service, setService] = useState({
title: '',
description: '',
features: ['', '', ''], // Assume 3 features initially
});

// Update service state when editingService changes
useEffect(() => {
if (editingService) {
setService({
title: editingService.title,
description: editingService.description,
features: editingService.points.map(point => point.title), // Map points to feature titles
});
}
}, [editingService]);

// Prevent body scroll when modal is open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = '';
}

return () => {
document.body.style.overflow = '';
};
}, [isOpen]);

// Handle input changes for both service details and features
const handleChange = (e) => {
const { name, value } = e.target;

if (name.startsWith('feature')) {
// Update the corresponding feature based on index
const index = parseInt(name.replace('feature', '')) - 1;
setService(prevService => {
const updatedFeatures = [...prevService.features];
updatedFeatures[index] = value;
return { ...prevService, features: updatedFeatures };
});
} else {
setService(prevService => ({ ...prevService, [name]: value }));
}
};

// Handle form submission to save the service
const handleSubmit = (e) => {
e.preventDefault();

// Validate that features are unique and not empty
const nonEmptyFeatures = service.features.filter(f => f.trim() !== '');
const featureSet = new Set(nonEmptyFeatures);

if (featureSet.size < nonEmptyFeatures.length) {
alert("Features must be unique and not empty.");
return;
}

// Prepare service data for saving
const updatedService = {
title: service.title,
description: service.description,
points: nonEmptyFeatures.map(feature => ({ title: feature })), // Convert features back to points
};

// Save the service and close the modal
onSaveService(updatedService);
onClose();
};

return (
<Modal
isOpen={isOpen}
onRequestClose={onClose}
overlayClassName="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4 sm:p-0"
className="bg-white p-4 sm:py-7 sm:px-11 rounded-lg shadow-lg w-full sm:max-w-lg relative"
>
<h2 className='text-center text-xl font-semibold mb-4'>
{editingService ? 'Edit' : 'Add A'} Service
</h2>
<button
onClick={onClose}
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
>
<Icon icon='iconamoon:close' className='h-5 w-5' />
</button>
<form onSubmit={handleSubmit}>
<div className='max-h-[480px] overflow-y-auto px-1'>
<div className="mb-4">
<label className="block text-gray-700 mb-2">Name:</label>
<input
type="text"
name="title"
value={service.title}
onChange={handleChange}
className="w-full px-3 py-2 border rounded-md"
required
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 mb-2">Description:</label>
<textarea
name="description"
value={service.description}
onChange={handleChange}
className="w-full px-3 py-2 border rounded-md"
required
></textarea>
</div>
{service.features.map((feature, index) => (
<div className="mb-3" key={index}>
<label className="block text-gray-700 mb-2">Feature {index + 1}:</label>
<input
type="text"
name={`feature${index + 1}`}
value={feature}
onChange={handleChange}
className="w-full px-3 py-2 border rounded-md"
required
/>
</div>
))}
</div>
<button type="submit" className="bg-red-600 text-white py-2 px-4 rounded-lg w-full">
{editingService ? 'Update Service' : 'Add Service'}
</button>
</form>
</Modal>
);
}

export default ServiceFormModal;
Loading