Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
OchiengPaul442 committed Mar 8, 2025
1 parent 280275e commit 0080ab7
Show file tree
Hide file tree
Showing 6 changed files with 599 additions and 444 deletions.
2 changes: 1 addition & 1 deletion src/platform/src/common/components/Calendar/Calendar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ const Calendar = ({
const CalendarSectionComponent = ({ month, onNextMonth, onPrevMonth }) => (
<div
className={`${
showTwoCalendars ? 'px-6 pt-5 pb-6' : 'px-2 pt-2 pb-5'
showTwoCalendars ? 'px-6 pt-5 pb-5' : 'px-2 pt-2'
} flex flex-col`}
>
<CalendarHeader
Expand Down
97 changes: 73 additions & 24 deletions src/platform/src/common/components/Calendar/DatePicker.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// DatePicker.js
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { format } from 'date-fns';
import { usePopper } from 'react-popper';
import { Transition } from '@headlessui/react';
Expand All @@ -8,15 +7,23 @@ import CalendarIcon from '@/icons/Analytics/calendarIcon';
import TabButtons from '../Button/TabButtons';

/**
* DatePicker component integrates the Calendar and manages its visibility.
* DatePicker component that integrates Calendar with react-popper.
* It manages its open/close state and renders the calendar in a popper with an arrow.
*/
const DatePicker = ({ customPopperStyle, alignment, onChange }) => {
const DatePicker = ({
customPopperStyle = {},
alignment = 'left',
onChange,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [referenceElement, setReferenceElement] = useState(null);
const [popperElement, setPopperElement] = useState(null);
const [arrowElement, setArrowElement] = useState(null);

const [selectedDate, setSelectedDate] = useState({ start: null, end: null });
const popperRef = useRef(null);

// Configure react-popper
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: alignment === 'right' ? 'bottom-end' : 'bottom-start',
modifiers: [
Expand All @@ -38,41 +45,64 @@ const DatePicker = ({ customPopperStyle, alignment, onChange }) => {
},
{ name: 'computeStyles', options: { adaptive: false } },
{ name: 'eventListeners', options: { scroll: true, resize: true } },
{ name: 'arrow', options: { padding: 8 } },
{
name: 'arrow',
options: {
element: arrowElement, // attach arrow element
padding: 8,
},
},
],
});

const handleToggle = () => {
/**
* Toggles the calendar's open/close state.
*/
const toggleOpen = useCallback(() => {
setIsOpen((prev) => !prev);
};
}, []);

const handleValueChange = (newValue) => {
setSelectedDate(newValue);
onChange(newValue);
};
/**
* Called whenever the user selects a date range in the Calendar.
*/
const handleValueChange = useCallback(
(newValue) => {
setSelectedDate(newValue);
onChange?.(newValue);
},
[onChange],
);

const handleClickOutside = (event) => {
if (
popperRef.current &&
!popperRef.current.contains(event.target) &&
!referenceElement.contains(event.target)
) {
setIsOpen(false);
}
};
/**
* Closes the popper when clicking outside of it.
*/
const handleClickOutside = useCallback(
(event) => {
if (
popperRef.current &&
!popperRef.current.contains(event.target) &&
referenceElement &&
!referenceElement.contains(event.target)
) {
setIsOpen(false);
}
},
[referenceElement],
);

// Attach/detach outside click handler
useEffect(() => {
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}

return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen, referenceElement]);
}, [isOpen, handleClickOutside]);

// Format the selected date range for display
const formattedStartDate = selectedDate.start
? format(selectedDate.start, 'MMM d, yyyy')
: '';
Expand All @@ -86,19 +116,22 @@ const DatePicker = ({ customPopperStyle, alignment, onChange }) => {

return (
<div className="relative">
{/* The button that toggles the calendar */}
<TabButtons
Icon={<CalendarIcon />}
btnText={btnText}
tabButtonClass="w-full"
dropdown
onClick={handleToggle}
onClick={toggleOpen}
id="datePicker"
type="button"
btnStyle="w-full bg-white border-gray-750 px-4 py-2"
tabRef={setReferenceElement}
aria-haspopup="dialog"
aria-expanded={isOpen}
/>

{/* Transition for the popper (calendar container) */}
<Transition
show={isOpen}
enter="ease-out duration-300"
Expand All @@ -107,7 +140,6 @@ const DatePicker = ({ customPopperStyle, alignment, onChange }) => {
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
className="absolute z-50"
>
<div
ref={(node) => {
Expand All @@ -116,7 +148,24 @@ const DatePicker = ({ customPopperStyle, alignment, onChange }) => {
}}
style={{ ...styles.popper, ...customPopperStyle }}
{...attributes.popper}
className="z-50"
>
{/* The arrow element for popper */}
<div
ref={setArrowElement}
style={{
...styles.arrow,
width: 0,
height: 0,
borderLeft: '6px solid transparent',
borderRight: '6px solid transparent',
borderBottom: '6px solid white',
position: 'absolute',
}}
{...attributes.arrow}
/>
{/* Calendar container with reduced height */}

<Calendar
showTwoCalendars={false}
handleValueChange={handleValueChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,17 @@ import CustomDropdown from '../../../Dropdowns/CustomDropdown';
import DatePicker from '../../../Calendar/DatePicker';

/**
* Formats the name by replacing underscores and hyphens with spaces and adjusting case.
* Formats a string by replacing underscores/hyphens with spaces and adjusting its case.
*/
const formatName = (name, textFormat = 'lowercase') => {
if (typeof name !== 'string' || !name) return name;
const formatted = name.replace(/[_-]/g, ' ');
return textFormat === 'uppercase' ? formatted.toUpperCase() : formatted;
};

/**
* Defines the rules for formatting the field value based on the field id.
* Removes hyphens and formats in uppercase for display
* Retains hyphens in the stored value
*/
const FIELD_FORMAT_RULES = {
organization: {
display: (value) => formatName(value.replace(/[_-]/g, ' '), 'uppercase'),
display: (value) => formatName(value, 'uppercase'),
store: (value) => value,
},
default: {
Expand All @@ -38,7 +33,7 @@ const formatFieldValue = (value, fieldId, textFormat, display = false) => {

/**
* CustomFields Component
* Renders different types of input fields based on props.
* Renders different types of input fields based on the props.
*/
const CustomFields = ({
field = false,
Expand All @@ -53,13 +48,10 @@ const CustomFields = ({
defaultOption,
textFormat = 'lowercase',
}) => {
const [selectedOption, setSelectedOption] = useState(
defaultOption || (options.length > 0 ? options[0] : { id: '', name: '' }),
);
const initialOption =
defaultOption || (options.length > 0 ? options[0] : { id: '', name: '' });
const [selectedOption, setSelectedOption] = useState(initialOption);

/**
* Handles the selection of an option.
*/
const handleSelect = useCallback(
(option) => {
const formattedOption = {
Expand All @@ -73,26 +65,23 @@ const CustomFields = ({
);

return (
<div className="w-full h-auto flex flex-col gap-2 justify-start">
<label className="w-[280px] h-auto p-0 m-0 text-[#7A7F87]">{title}</label>

<div className="w-full flex flex-col gap-2">
<label className="w-[280px] text-[#7A7F87]">{title}</label>
{field ? (
<input
className="bg-transparent text-[16px] font-medium leading-6 p-0 m-0 w-full h-auto border-none"
type="text"
name={id}
className="bg-transparent text-[16px] font-medium leading-6 w-full border-none p-0 m-0"
value={formatFieldValue(selectedOption.name, id, textFormat, true)}
onChange={(e) =>
handleSelect({ ...selectedOption, name: e.target.value })
}
type="text"
name={id}
disabled={!edit}
/>
) : useCalendar ? (
<DatePicker
customPopperStyle={{ left: '-7px' }}
onChange={(date) => {
handleSelect({ name: date });
}}
onChange={(date) => handleSelect({ name: date })}
/>
) : (
<CustomDropdown
Expand Down Expand Up @@ -121,9 +110,7 @@ const CustomFields = ({
<span className="flex items-center space-x-2">
<span>{formatName(option.name, textFormat)}</span>
</span>
{selectedOption.id === option.id && (
<CheckIcon fill={'#145FFF'} />
)}
{selectedOption.id === option.id && <CheckIcon fill="#145FFF" />}
</span>
))}
</CustomDropdown>
Expand Down
Loading

0 comments on commit 0080ab7

Please sign in to comment.