feat: Integrate react-select for multi-select fields and add support for file upload field types.

This commit is contained in:
pradeepkumar
2026-01-24 20:47:00 +05:30
parent 5e2b3d8e83
commit 0f84e3d4d7
3 changed files with 465 additions and 78 deletions

View File

@@ -1,6 +1,7 @@
'use client';
import React from 'react';
import Select, { MultiValue, StylesConfig } from 'react-select';
import { ProfileField } from '@/services/profile-sections.service';
import { FormInput } from './FormInput';
import { FormCheckbox } from './FormCheckbox';
@@ -8,6 +9,12 @@ import { FormTextarea } from './FormTextarea';
import { FormSelect } from './FormSelect';
import { TagInput } from './TagInput';
// Type for react-select options
interface SelectOption {
value: string;
label: string;
}
interface DynamicFieldProps {
field: ProfileField;
value: unknown;
@@ -240,19 +247,94 @@ function RadioGroup({ field, value, onChange }: RadioGroupProps) {
);
}
// Multi-Select Component
// Multi-Select Component using react-select
interface MultiSelectProps {
field: ProfileField;
value: string[];
onChange: (value: string[]) => void;
}
// Custom styles for react-select to match the design
const multiSelectStyles: StylesConfig<SelectOption, true> = {
control: (base, state) => ({
...base,
borderRadius: '15px',
borderColor: state.isFocused ? '#E58625' : 'rgba(0, 41, 61, 0.2)',
boxShadow: state.isFocused ? '0 0 0 1px #E58625' : 'none',
padding: '4px',
minHeight: '48px',
fontFamily: 'inherit',
'&:hover': {
borderColor: '#E58625',
},
}),
placeholder: (base) => ({
...base,
color: 'rgba(0, 41, 61, 0.4)',
fontSize: '14px',
}),
input: (base) => ({
...base,
fontSize: '14px',
color: '#00293D',
}),
multiValue: (base) => ({
...base,
backgroundColor: 'rgba(229, 134, 37, 0.1)',
borderRadius: '20px',
}),
multiValueLabel: (base) => ({
...base,
color: '#E58625',
fontSize: '12px',
padding: '4px 8px',
}),
multiValueRemove: (base) => ({
...base,
color: '#E58625',
borderRadius: '0 20px 20px 0',
'&:hover': {
backgroundColor: 'rgba(229, 134, 37, 0.2)',
color: '#E58625',
},
}),
option: (base, state) => ({
...base,
fontSize: '14px',
color: '#00293D',
backgroundColor: state.isSelected
? 'rgba(229, 134, 37, 0.1)'
: state.isFocused
? 'rgba(229, 134, 37, 0.05)'
: 'white',
'&:active': {
backgroundColor: 'rgba(229, 134, 37, 0.15)',
},
}),
menu: (base) => ({
...base,
borderRadius: '15px',
overflow: 'hidden',
boxShadow: '0px 10px 20px rgba(217, 217, 217, 0.5)',
}),
menuList: (base) => ({
...base,
padding: '8px',
}),
};
function MultiSelect({ field, value, onChange }: MultiSelectProps) {
const handleToggle = (optionValue: string) => {
const newValue = value.includes(optionValue)
? value.filter(v => v !== optionValue)
: [...value, optionValue];
onChange(newValue);
// Convert field options to react-select format
const options: SelectOption[] = field.options?.map(opt => ({
value: opt.value,
label: opt.label,
})) || [];
// Convert current value to react-select format
const selectedOptions = options.filter(opt => value.includes(opt.value));
const handleChange = (newValue: MultiValue<SelectOption>) => {
onChange(newValue.map(opt => opt.value));
};
return (
@@ -261,44 +343,19 @@ function MultiSelect({ field, value, onChange }: MultiSelectProps) {
{field.name}
{field.isRequired && <span className="text-red-500 ml-1">*</span>}
</label>
<div className="border border-[#00293D]/20 rounded-[15px] p-3 max-h-[200px] overflow-y-auto">
{field.options?.map((option) => (
<label
key={option.value}
className="flex items-center gap-2 cursor-pointer py-1 hover:bg-gray-50 px-2 rounded"
>
<input
type="checkbox"
checked={value.includes(option.value)}
onChange={() => handleToggle(option.value)}
className="w-4 h-4 rounded border-[#00293D]/20 text-[#E58625] focus:ring-[#E58625]"
/>
<span className="text-[14px] font-serif text-[#00293D]">{option.label}</span>
</label>
))}
</div>
{value.length > 0 && (
<div className="flex flex-wrap gap-2 mt-2">
{value.map((v) => {
const option = field.options?.find(o => o.value === v);
return (
<span
key={v}
className="inline-flex items-center gap-1 px-2 py-1 bg-[#E58625]/10 text-[#E58625] rounded-full text-[12px]"
>
{option?.label || v}
<button
type="button"
onClick={() => handleToggle(v)}
className="hover:text-[#E58625]/70"
>
×
</button>
</span>
);
})}
</div>
)}
<Select<SelectOption, true>
isMulti
options={options}
value={selectedOptions}
onChange={handleChange}
placeholder={field.placeholder || `Select ${field.name.toLowerCase()}...`}
styles={multiSelectStyles}
classNamePrefix="react-select"
isClearable
isSearchable
closeMenuOnSelect={false}
noOptionsMessage={() => 'No options available'}
/>
</div>
);
}