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

feat: tempcommit

feat: tempcommit

feat: tempcommit

feat: tempcommit

feat: tempcommit

feat: tempcommit (broken input)

feat: tempcommit

feat: tempcommit

feat: tempcommit
This commit is contained in:
Maximilian Liebmann 2025-06-23 11:20:09 +02:00 committed by Maximilian Liebmann
parent 53cc8cb2b7
commit 7ede267762
25 changed files with 1422 additions and 552 deletions

View file

@ -62,18 +62,20 @@ export function AppSidebar() {
<>
<Sidebar collapsible='icon' variant='sidebar'>
<SidebarHeader className='overflow-hidden'>
<Logo
colorType='colored'
logoType='combo'
height={50}
className='group-data-[collapsible=icon]:hidden min-w-[203px]'
></Logo>
<Logo
colorType='colored'
logoType='submark'
height={50}
className='group-data-[collapsible=]:hidden group-data-[mobile=true]/mobile:hidden'
></Logo>
<Link href='/home'>
<Logo
colorType='colored'
logoType='combo'
height={50}
className='group-data-[collapsible=icon]:hidden min-w-[203px]'
></Logo>
<Logo
colorType='colored'
logoType='submark'
height={50}
className='group-data-[collapsible=]:hidden group-data-[mobile=true]/mobile:hidden'
></Logo>
</Link>
</SidebarHeader>
<SidebarContent className='grid grid-rows-[auto_1fr_auto] overflow-hidden'>
<Collapsible defaultOpen className='group/collapsible'>

View file

@ -1,29 +1,54 @@
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 +58,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>