account/sender UI
This commit is contained in:
62
frontend/components/ui/CheckboxInput.tsx
Normal file
62
frontend/components/ui/CheckboxInput.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { CheckboxProps } from '@/app/types'
|
||||
import { HiQuestionMarkCircle } from 'react-icons/hi'
|
||||
|
||||
const CheckboxInput = ({
|
||||
handleChange,
|
||||
label,
|
||||
name,
|
||||
checked,
|
||||
info,
|
||||
enabledText,
|
||||
disabledText,
|
||||
}: CheckboxProps) => {
|
||||
const [showTooltip, setShowTooltip] = useState(false)
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<label className="mb-2 block text-sm font-medium text-gray-500" htmlFor={name}>
|
||||
{label}
|
||||
</label>
|
||||
<div className="flex items-center space-x-3">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={name}
|
||||
checked={checked || false}
|
||||
onChange={handleChange}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="peer peer-checked:bg-orange h-6 w-11 rounded-full bg-gray-200 peer-focus:outline-none after:absolute after:start-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white rtl:peer-checked:after:-translate-x-full"></div>
|
||||
<span
|
||||
className={`ms-3 text-sm select-none ${
|
||||
checked ? 'font-medium text-green-600' : 'text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{checked ? `${enabledText}` : `${disabledText}`}
|
||||
</span>
|
||||
</label>
|
||||
<div className="relative flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
className="text-gray-400 hover:text-gray-600 focus:outline-none"
|
||||
onMouseEnter={() => setShowTooltip(true)}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
onClick={() => setShowTooltip(!showTooltip)}
|
||||
>
|
||||
<HiQuestionMarkCircle className="h-5 w-5" />
|
||||
</button>
|
||||
{showTooltip && (
|
||||
<div className="absolute bottom-full left-1/2 z-10 mb-3 w-max max-w-xs -translate-x-1/2 rounded-xl bg-white px-4 py-2 text-center text-sm text-gray-700 shadow-lg">
|
||||
{info}
|
||||
<div className="absolute -bottom-2 left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 transform bg-white"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CheckboxInput
|
||||
113
frontend/components/ui/Selector.tsx
Normal file
113
frontend/components/ui/Selector.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React from 'react'
|
||||
import Select from 'react-select'
|
||||
import { MultiSelectProps } from '@/app/types'
|
||||
|
||||
const MultiSelect = ({
|
||||
value,
|
||||
handleChange,
|
||||
label,
|
||||
placeholder = 'Выберите опции',
|
||||
name,
|
||||
options,
|
||||
className = '',
|
||||
noOptionsMessage = 'Нет доступных опций',
|
||||
}: MultiSelectProps) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
{label && (
|
||||
<label className="my-2 block text-sm font-medium text-gray-500" htmlFor={name}>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<Select
|
||||
isMulti
|
||||
name={name}
|
||||
id={name}
|
||||
options={options}
|
||||
value={options.filter(option => value.includes(option.id))}
|
||||
onChange={selectedOptions => {
|
||||
handleChange({
|
||||
target: {
|
||||
id: name,
|
||||
value: selectedOptions ? selectedOptions.map(opt => opt.id) : [],
|
||||
},
|
||||
})
|
||||
}}
|
||||
getOptionValue={option => option.id.toString()}
|
||||
className="rounded-lg"
|
||||
classNamePrefix="select"
|
||||
placeholder={placeholder}
|
||||
noOptionsMessage={() => noOptionsMessage}
|
||||
styles={{
|
||||
control: base => ({
|
||||
...base,
|
||||
borderRadius: '0.75rem',
|
||||
backgroundColor: '#F3F4F6',
|
||||
border: '1px solid #E5E7EB',
|
||||
padding: '2px',
|
||||
'&:hover': {
|
||||
borderColor: '#E5E7EB',
|
||||
},
|
||||
'&:focus-within': {
|
||||
backgroundColor: '#FFFFFF',
|
||||
borderColor: '#E5E7EB',
|
||||
boxShadow: '0 0 0 2px rgba(59, 130, 246, 0.5)',
|
||||
},
|
||||
}),
|
||||
menu: base => ({
|
||||
...base,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
zIndex: 9999,
|
||||
marginTop: '4px',
|
||||
borderRadius: '0.75rem',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
menuList: base => ({
|
||||
...base,
|
||||
padding: '4px',
|
||||
}),
|
||||
multiValue: base => ({
|
||||
...base,
|
||||
backgroundColor: '#EFF6FF',
|
||||
borderRadius: '0.5rem',
|
||||
}),
|
||||
multiValueLabel: base => ({
|
||||
...base,
|
||||
color: '#2563EB',
|
||||
}),
|
||||
multiValueRemove: base => ({
|
||||
...base,
|
||||
color: '#2563EB',
|
||||
':hover': {
|
||||
backgroundColor: '#DBEAFE',
|
||||
color: '#1E40AF',
|
||||
},
|
||||
}),
|
||||
menuPortal: base => ({
|
||||
...base,
|
||||
zIndex: 9999,
|
||||
}),
|
||||
option: (base, state) => ({
|
||||
...base,
|
||||
fontSize: '0.875rem',
|
||||
padding: '8px 12px',
|
||||
backgroundColor: state.isSelected ? '#EFF6FF' : state.isFocused ? '#F3F4F6' : 'white',
|
||||
color: state.isSelected ? '#2563EB' : '#1F2937',
|
||||
cursor: 'pointer',
|
||||
'&:active': {
|
||||
backgroundColor: '#DBEAFE',
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: state.isSelected ? '#EFF6FF' : '#F3F4F6',
|
||||
},
|
||||
}),
|
||||
}}
|
||||
menuPortalTarget={document.body}
|
||||
menuPosition="fixed"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MultiSelect
|
||||
Reference in New Issue
Block a user