feat: Implement settings dropdown and page components
- Added `SettingsDropdown` component for selecting settings sections with icons and descriptions. - Created `SettingsPage` component to manage user settings, including account details, notifications, calendar availability, privacy, and appearance. - Introduced `SettingsSwitcher` for selecting options within settings. - Integrated command and dialog components for improved user interaction. - Updated `UserDropdown` to include links for settings and logout. - Refactored button styles and card footer layout for consistency. - Added popover functionality for dropdown menus. - Updated dependencies in `yarn.lock` for new components. feat: tempcommit feat: tempcommit
This commit is contained in:
parent
53cc8cb2b7
commit
9191eb3df0
25 changed files with 1476 additions and 552 deletions
|
@ -1,29 +1,56 @@
|
|||
import { Input, Textarea } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import React, { ForwardRefExoticComponent, RefAttributes } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Eye, EyeOff, LucideProps } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function LabeledInput({
|
||||
type,
|
||||
label,
|
||||
subtext,
|
||||
placeholder,
|
||||
value,
|
||||
defaultValue,
|
||||
name,
|
||||
icon,
|
||||
variantSize = 'default',
|
||||
autocomplete,
|
||||
error,
|
||||
...rest
|
||||
}: {
|
||||
type: 'text' | 'email' | 'password';
|
||||
label: string;
|
||||
subtext?: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
name?: string;
|
||||
icon?: ForwardRefExoticComponent<
|
||||
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
|
||||
>;
|
||||
variantSize?: 'default' | 'big' | 'textarea';
|
||||
autocomplete?: string;
|
||||
error?: string;
|
||||
} & React.InputHTMLAttributes<HTMLInputElement>) {
|
||||
const [passwordVisible, setPasswordVisible] = React.useState(false);
|
||||
const [inputValue, setInputValue] = React.useState(
|
||||
value || defaultValue || '',
|
||||
);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
if (rest.onChange) {
|
||||
rest.onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label htmlFor={name}>{label}</Label>
|
||||
{subtext && (
|
||||
<Label className='text-sm text-muted-foreground' htmlFor={name}>
|
||||
{subtext}
|
||||
</Label>
|
||||
)}
|
||||
{variantSize === 'textarea' ? (
|
||||
<Textarea
|
||||
placeholder={placeholder}
|
||||
|
@ -33,20 +60,48 @@ export default function LabeledInput({
|
|||
rows={3}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
id={name}
|
||||
name={name}
|
||||
className={
|
||||
variantSize === 'big'
|
||||
? 'h-12 file:h-10 text-lg gplaceholder:text-lg sm:text-2xl sm:placeholder:text-2xl'
|
||||
: ''
|
||||
}
|
||||
autoComplete={autocomplete}
|
||||
{...rest}
|
||||
/>
|
||||
<span className='relative'>
|
||||
<Input
|
||||
className={cn(
|
||||
type === 'password' ? 'pr-[50px]' : '',
|
||||
variantSize === 'big'
|
||||
? 'h-12 file:h-10 text-lg placeholder:text-lg sm:text-2xl sm:placeholder:text-2xl'
|
||||
: '',
|
||||
icon && inputValue === '' ? 'pl-10' : '',
|
||||
'transition-all duration-300 ease-in-out',
|
||||
)}
|
||||
type={passwordVisible ? 'text' : type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={inputValue}
|
||||
id={name}
|
||||
name={name}
|
||||
autoComplete={autocomplete}
|
||||
{...rest}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{icon && (
|
||||
<span
|
||||
className={cn(
|
||||
'absolute left-3 top-1/2 -translate-y-1/2 text-muted-input transition-all duration-300 ease-in-out',
|
||||
inputValue === ''
|
||||
? 'opacity-100 scale-100'
|
||||
: 'opacity-0 scale-75 pointer-events-none',
|
||||
)}
|
||||
>
|
||||
{React.createElement(icon)}
|
||||
</span>
|
||||
)}
|
||||
{type === 'password' && (
|
||||
<Button
|
||||
className='absolute right-0 top-0 w-[36px] h-[36px]'
|
||||
type='button'
|
||||
variant={'outline_muted'}
|
||||
onClick={() => setPasswordVisible((visible) => !visible)}
|
||||
>
|
||||
{passwordVisible ? <Eye /> : <EyeOff />}
|
||||
</Button>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{error && <p className='text-red-500 text-sm mt-1'>{error}</p>}
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue