feat: tempcommit
This commit is contained in:
parent
5d81288479
commit
13a99e9dc4
6 changed files with 113 additions and 174 deletions
|
@ -52,7 +52,7 @@ const items = [
|
|||
},
|
||||
{
|
||||
title: 'Events',
|
||||
url: '#',
|
||||
url: '/events',
|
||||
icon: CalendarClock,
|
||||
},
|
||||
];
|
||||
|
@ -114,7 +114,7 @@ export function AppSidebar() {
|
|||
<SidebarFooter>
|
||||
<SidebarMenuItem className='pl-[8px]'>
|
||||
<Link
|
||||
href='/event/new'
|
||||
href='/events/new'
|
||||
className='flex items-center gap-2 text-xl font-label'
|
||||
>
|
||||
<CalendarPlus className='size-8' />
|
||||
|
|
|
@ -57,19 +57,19 @@ const settingsSections: SettingsSection[] = [
|
|||
{
|
||||
label: 'Calendar',
|
||||
value: 'calendarAvailability',
|
||||
description: 'Manage calendar display and availability',
|
||||
description: 'Manage calendar display, availability and iCal integration',
|
||||
icon: Calendar,
|
||||
},
|
||||
{
|
||||
label: 'Privacy',
|
||||
value: 'sharingPrivacy',
|
||||
description: 'Control who can see your calendar',
|
||||
description: 'Control who can see your calendar and book time with you',
|
||||
icon: Shield,
|
||||
},
|
||||
{
|
||||
label: 'Appearance',
|
||||
value: 'appearance',
|
||||
description: 'Customize the look and feel',
|
||||
description: 'Customize the look and feel of the application',
|
||||
icon: Palette,
|
||||
},
|
||||
];
|
||||
|
@ -99,7 +99,7 @@ export function SettingsDropdown({
|
|||
variant='outline_muted'
|
||||
role='combobox'
|
||||
aria-expanded={open}
|
||||
className='w-full justify-between bg-white text-black h-auto py-3'
|
||||
className='w-full justify-between bg-popover text-text h-auto py-3'
|
||||
>
|
||||
<div className='flex items-center gap-3'>
|
||||
<CurrentIcon className='h-4 w-4 text-muted-foreground' />
|
||||
|
|
|
@ -23,10 +23,15 @@ import {
|
|||
} from '@/components/ui/select';
|
||||
import { SettingsDropdown } from '@/components/misc/settings-dropdown';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useGetApiUserMe } from '@/generated/api/user/user';
|
||||
import { ThemePicker } from './theme-picker';
|
||||
import LabeledInput from '../custom-ui/labeled-input';
|
||||
import { GroupWrapper } from '../wrappers/group-wrapper';
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
const [currentSection, setCurrentSection] = useState('general');
|
||||
const { data } = useGetApiUserMe();
|
||||
|
||||
const renderSettingsContent = () => {
|
||||
switch (currentSection) {
|
||||
|
@ -36,28 +41,79 @@ export default function SettingsPage() {
|
|||
<ScrollableSettingsWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>Account Settings</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your account details and preferences.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='displayName'>Display Name</Label>
|
||||
<Input id='displayName' placeholder='Your Name' />
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='email'>Email Address</Label>
|
||||
<Input
|
||||
id='email'
|
||||
type='email'
|
||||
placeholder='your.email@example.com'
|
||||
readOnly
|
||||
value='user-email@example.com'
|
||||
/>
|
||||
<p className='text-sm text-muted-foreground'>
|
||||
Email is managed by your SSO provider.
|
||||
</p>
|
||||
</div>
|
||||
<CardContent className='space-y-6 mt-2'>
|
||||
<GroupWrapper legend='General Settings'>
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-center justify-evenly'>
|
||||
<div>
|
||||
<Label htmlFor='displayName'>First Name</Label>
|
||||
<Input
|
||||
id='displayName'
|
||||
placeholder='Your Name'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor='displayName'>Last Name</Label>
|
||||
<Input
|
||||
id='displayName'
|
||||
placeholder='Your Name'
|
||||
defaultValue={data?.data.user.last_name ?? ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='displayName'>Display Name</Label>
|
||||
<Input
|
||||
id='displayName'
|
||||
placeholder='Your Name'
|
||||
defaultValue={data?.data.user.name}
|
||||
/>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='email'>Email Address</Label>
|
||||
<Input
|
||||
id='email'
|
||||
type='email'
|
||||
placeholder='your.email@example.com'
|
||||
readOnly
|
||||
defaultValue={data?.data.user.email}
|
||||
/>
|
||||
<p className='text-sm text-muted-foreground'>
|
||||
Email might be managed by your SSO provider.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
<GroupWrapper legend='Reset Password'>
|
||||
<div className='flex items-center justify-evenly'>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Current Password'
|
||||
placeholder='Current Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='New Password'
|
||||
placeholder='New Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Repeat Password'
|
||||
placeholder='Repeat Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='profilePicture'>Profile Picture</Label>
|
||||
<Input id='profilePicture' type='file' />
|
||||
|
@ -67,7 +123,11 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='timezone'>Timezone</Label>
|
||||
<Input id='displayName' placeholder='Europe/Berlin' />
|
||||
<Input
|
||||
id='displayName'
|
||||
placeholder='Europe/Berlin'
|
||||
defaultValue={data?.data.user.timezone}
|
||||
/>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='language'>Language</Label>
|
||||
|
@ -98,9 +158,6 @@ export default function SettingsPage() {
|
|||
<ScrollableSettingsWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>Notification Preferences</CardTitle>
|
||||
<CardDescription>
|
||||
Choose how you want to be notified.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<div className='flex items-center justify-between space-x-2 p-3 rounded-md border'>
|
||||
|
@ -175,10 +232,6 @@ export default function SettingsPage() {
|
|||
<ScrollableSettingsWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>Calendar & Availability</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your calendar display, default availability, and iCal
|
||||
integrations.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<fieldset className='space-y-4 p-4 border rounded-md'>
|
||||
|
@ -298,9 +351,6 @@ export default function SettingsPage() {
|
|||
<ScrollableSettingsWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>Sharing & Privacy</CardTitle>
|
||||
<CardDescription>
|
||||
Control who can see your calendar and book time with you.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<div className='space-y-2'>
|
||||
|
@ -386,23 +436,11 @@ export default function SettingsPage() {
|
|||
<ScrollableSettingsWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>Appearance</CardTitle>
|
||||
<CardDescription>
|
||||
Customize the look and feel of the application.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='theme'>Theme</Label>
|
||||
<Select>
|
||||
<SelectTrigger id='theme'>
|
||||
<SelectValue placeholder='Select theme' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value='light'>Light</SelectItem>
|
||||
<SelectItem value='dark'>Dark</SelectItem>
|
||||
<SelectItem value='system'>System Default</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ThemePicker />
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='dateFormat'>Date Format</Label>
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/command';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Check, ChevronDown } from 'lucide-react';
|
||||
|
||||
interface SettingsOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface SettingsSwitcherProps {
|
||||
title: string;
|
||||
options: SettingsOption[];
|
||||
defaultValue?: string;
|
||||
onValueChange?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
searchPlaceholder?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function SettingsSwitcher({
|
||||
title,
|
||||
options,
|
||||
defaultValue,
|
||||
onValueChange,
|
||||
placeholder = 'Select option...',
|
||||
searchPlaceholder = 'Search options...',
|
||||
className,
|
||||
}: SettingsSwitcherProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedValue, setSelectedValue] = useState(
|
||||
defaultValue || options[0]?.value || '',
|
||||
);
|
||||
|
||||
const selectedOption = options.find(
|
||||
(option) => option.value === selectedValue,
|
||||
);
|
||||
|
||||
const handleSelect = (value: string) => {
|
||||
setSelectedValue(value);
|
||||
setOpen(false);
|
||||
onValueChange?.(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-2', className)}>
|
||||
<label className='text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'>
|
||||
{title}
|
||||
</label>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline_muted'
|
||||
role='combobox'
|
||||
aria-expanded={open}
|
||||
className='w-full justify-between bg-white text-black'
|
||||
>
|
||||
<div className='flex flex-col items-start'>
|
||||
<span>{selectedOption?.label || placeholder}</span>
|
||||
{selectedOption?.description && (
|
||||
<span className='text-xs text-muted-foreground'>
|
||||
{selectedOption.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ChevronDown className='ml-2 h-4 w-4 shrink-0 opacity-50' />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-full p-0' align='start'>
|
||||
<Command>
|
||||
<CommandInput placeholder={searchPlaceholder} />
|
||||
<CommandList>
|
||||
<CommandEmpty>No option found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
onSelect={() => handleSelect(option.value)}
|
||||
className='flex items-center justify-between'
|
||||
>
|
||||
<div className='flex flex-col'>
|
||||
<span>{option.label}</span>
|
||||
{option.description && (
|
||||
<span className='text-xs text-muted-foreground'>
|
||||
{option.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Check
|
||||
className={cn(
|
||||
'ml-2 h-4 w-4',
|
||||
selectedValue === option.value
|
||||
? 'opacity-100'
|
||||
: 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -17,6 +17,7 @@ import UserCard from '@/components/misc/user-card';
|
|||
|
||||
export default function UserDropdown() {
|
||||
const { data } = useGetApiUserMe();
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
23
src/components/wrappers/group-wrapper.tsx
Normal file
23
src/components/wrappers/group-wrapper.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { cn } from '@/lib/utils';
|
||||
import type * as React from 'react';
|
||||
|
||||
interface ScrollableSettingsWrapperProps {
|
||||
className?: string;
|
||||
legend?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function GroupWrapper({
|
||||
className,
|
||||
legend,
|
||||
children,
|
||||
}: ScrollableSettingsWrapperProps) {
|
||||
return (
|
||||
<fieldset
|
||||
className={cn('space-t-4 p-4 border rounded-md shadow-md', className)}
|
||||
>
|
||||
<legend className='text-sm font-medium px-1'>{legend}</legend>
|
||||
{children}
|
||||
</fieldset>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue