feat: tempcommit (broken input)

This commit is contained in:
Maximilian Liebmann 2025-06-29 22:03:01 +02:00
parent 7d2d5c55e8
commit fa3b2da69c
6 changed files with 328 additions and 202 deletions

View file

@ -1,19 +1,20 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { LucideProps } from 'lucide-react';
import { IconProp } from '@fortawesome/fontawesome-svg-core'; import React, { ForwardRefExoticComponent, RefAttributes } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export function IconButton({ export function IconButton({
icon, icon,
children, children,
...props ...props
}: { }: {
icon: IconProp; icon?: ForwardRefExoticComponent<
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
>;
children: React.ReactNode; children: React.ReactNode;
} & React.ComponentProps<typeof Button>) { } & React.ComponentProps<typeof Button>) {
return ( return (
<Button type='button' variant='secondary' {...props}> <Button type='button' variant='secondary' {...props}>
<FontAwesomeIcon icon={icon} className='mr-2' /> {icon && React.createElement(icon, { className: 'mr-2' })}
{children} {children}
</Button> </Button>
); );

View file

@ -1,6 +1,6 @@
import { signIn } from '@/auth'; import { signIn } from '@/auth';
import { IconButton } from '@/components/buttons/icon-button'; import { IconButton } from '@/components/buttons/icon-button';
import { faOpenid } from '@fortawesome/free-brands-svg-icons'; import { Fingerprint, ScanEye } from 'lucide-react';
export default function SSOLogin({ export default function SSOLogin({
provider, provider,
@ -22,7 +22,7 @@ export default function SSOLogin({
className='w-full' className='w-full'
type='submit' type='submit'
variant='secondary' variant='secondary'
icon={faOpenid} icon={Fingerprint}
{...props} {...props}
> >
Login with {providerDisplayName} Login with {providerDisplayName}

View file

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

View file

@ -1,8 +1,8 @@
import { Input, Textarea } from '@/components/ui/input'; import { Input, Textarea } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import React from 'react'; import React, { ForwardRefExoticComponent, RefAttributes } from 'react';
import { Button } from '../ui/button'; import { Button } from '../ui/button';
import { Eye, EyeOff } from 'lucide-react'; import { Eye, EyeOff, LucideProps } from 'lucide-react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
export default function LabeledInput({ export default function LabeledInput({
@ -12,6 +12,7 @@ export default function LabeledInput({
placeholder, placeholder,
value, value,
name, name,
icon,
variantSize = 'default', variantSize = 'default',
autocomplete, autocomplete,
error, error,
@ -22,11 +23,22 @@ export default function LabeledInput({
placeholder?: string; placeholder?: string;
value?: string; value?: string;
name?: string; name?: string;
icon?: ForwardRefExoticComponent<
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
>;
variantSize?: 'default' | 'big' | 'textarea'; variantSize?: 'default' | 'big' | 'textarea';
autocomplete?: string; autocomplete?: string;
error?: string; error?: string;
} & React.InputHTMLAttributes<HTMLInputElement>) { } & React.InputHTMLAttributes<HTMLInputElement>) {
const [passwordVisible, setPasswordVisible] = React.useState(false); const [passwordVisible, setPasswordVisible] = React.useState(false);
const [inputValue, setInputValue] = React.useState(value || '');
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
if (rest.onChange) {
rest.onChange(e);
}
};
return ( return (
<div className='grid grid-cols-1 gap-1'> <div className='grid grid-cols-1 gap-1'>
@ -50,18 +62,24 @@ export default function LabeledInput({
className={cn( className={cn(
type === 'password' ? 'pr-[50px]' : '', type === 'password' ? 'pr-[50px]' : '',
variantSize === 'big' variantSize === 'big'
? 'h-12 file:h-10 text-lg gplaceholder:text-lg sm:text-2xl sm:placeholder:text-2xl' ? 'h-12 file:h-10 text-lg placeholder:text-lg sm:text-2xl sm:placeholder:text-2xl'
: '', : '',
icon && inputValue === '' ? 'pl-10' : '',
)} )}
type={passwordVisible ? 'text' : type} type={passwordVisible ? 'text' : type}
placeholder={placeholder} placeholder={placeholder}
defaultValue={value} value={inputValue}
id={name} id={name}
name={name} name={name}
autoComplete={autocomplete} autoComplete={autocomplete}
onChange={handleInputChange}
{...rest} {...rest}
/> />
{icon && inputValue === '' && (
<span className='absolute left-3 top-1/2 -translate-y-1/2 text-muted-input'>
{React.createElement(icon)}
</span>
)}
{type === 'password' && ( {type === 'password' && (
<Button <Button
className='absolute right-0 top-0 w-[36px] h-[36px]' className='absolute right-0 top-0 w-[36px] h-[36px]'
@ -74,7 +92,6 @@ export default function LabeledInput({
)} )}
</span> </span>
)} )}
{error && <p className='text-red-500 text-sm mt-1'>{error}</p>} {error && <p className='text-red-500 text-sm mt-1'>{error}</p>}
</div> </div>
); );

View file

@ -9,6 +9,18 @@ import useZodForm from '@/lib/hooks/useZodForm';
import { loginSchema, registerSchema } from '@/lib/auth/validation'; import { loginSchema, registerSchema } from '@/lib/auth/validation';
import { loginAction } from '@/lib/auth/login'; import { loginAction } from '@/lib/auth/login';
import { registerAction } from '@/lib/auth/register'; import { registerAction } from '@/lib/auth/register';
import { IconButton } from '../buttons/icon-button';
import {
FileKey,
FileKey2,
LogIn,
MailOpen,
RotateCcwKey,
UserCheck,
UserPen,
UserPlus,
UserRoundPen,
} from 'lucide-react';
function LoginFormElement({ function LoginFormElement({
setIsSignUp, setIsSignUp,
@ -56,6 +68,7 @@ function LoginFormElement({
<LabeledInput <LabeledInput
type='text' type='text'
label='E-Mail or Username' label='E-Mail or Username'
icon={UserCheck}
placeholder='What you are known as' placeholder='What you are known as'
error={formState.errors.email?.message} error={formState.errors.email?.message}
{...register('email')} {...register('email')}
@ -64,16 +77,22 @@ function LoginFormElement({
<LabeledInput <LabeledInput
type='password' type='password'
label='Password' label='Password'
icon={FileKey}
placeholder="Let's hope you remember it" placeholder="Let's hope you remember it"
error={formState.errors.password?.message} error={formState.errors.password?.message}
{...register('password')} {...register('password')}
data-cy='password-input' data-cy='password-input'
/> />
<div className='grid grid-rows-2 gap-2'> <div className='grid grid-rows-2 gap-2'>
<Button type='submit' variant='primary' data-cy='login-button'> <IconButton
type='submit'
variant='primary'
data-cy='login-button'
icon={LogIn}
>
Login Login
</Button> </IconButton>
<Button <IconButton
type='button' type='button'
variant='outline_primary' variant='outline_primary'
onClick={() => { onClick={() => {
@ -81,9 +100,10 @@ function LoginFormElement({
setIsSignUp((v) => !v); setIsSignUp((v) => !v);
}} }}
data-cy='register-switch' data-cy='register-switch'
icon={UserPlus}
> >
Sign Up Sign Up
</Button> </IconButton>
</div> </div>
<div> <div>
{formState.errors.root?.message && ( {formState.errors.root?.message && (
@ -159,6 +179,7 @@ function RegisterFormElement({
<LabeledInput <LabeledInput
type='email' type='email'
label='E-Mail' label='E-Mail'
icon={MailOpen}
placeholder='Your email address' placeholder='Your email address'
autocomplete='email' autocomplete='email'
error={formState.errors.email?.message} error={formState.errors.email?.message}
@ -168,6 +189,7 @@ function RegisterFormElement({
<LabeledInput <LabeledInput
type='text' type='text'
label='Username' label='Username'
icon={UserPen}
placeholder='Your username' placeholder='Your username'
autocomplete='username' autocomplete='username'
error={formState.errors.username?.message} error={formState.errors.username?.message}
@ -177,6 +199,7 @@ function RegisterFormElement({
<LabeledInput <LabeledInput
type='password' type='password'
label='Password' label='Password'
icon={FileKey2}
placeholder='Create a password' placeholder='Create a password'
autocomplete='new-password' autocomplete='new-password'
error={formState.errors.password?.message} error={formState.errors.password?.message}
@ -186,6 +209,7 @@ function RegisterFormElement({
<LabeledInput <LabeledInput
type='password' type='password'
label='Confirm Password' label='Confirm Password'
icon={RotateCcwKey}
placeholder='Repeat your password' placeholder='Repeat your password'
autocomplete='new-password' autocomplete='new-password'
error={formState.errors.confirmPassword?.message} error={formState.errors.confirmPassword?.message}
@ -193,19 +217,25 @@ function RegisterFormElement({
data-cy='confirm-password-input' data-cy='confirm-password-input'
/> />
<div className='grid grid-rows-2 gap-2'> <div className='grid grid-rows-2 gap-2'>
<Button type='submit' variant='primary' data-cy='register-button'> <IconButton
type='submit'
variant='primary'
data-cy='register-button'
icon={UserPlus}
>
Sign Up Sign Up
</Button> </IconButton>
<Button <IconButton
type='button' type='button'
variant='outline_primary' variant='outline_primary'
onClick={() => { onClick={() => {
formRef?.current?.reset(); formRef?.current?.reset();
setIsSignUp((v) => !v); setIsSignUp((v) => !v);
}} }}
icon={LogIn}
> >
Back to Login Back to Login
</Button> </IconButton>
</div> </div>
<div> <div>
{formState.errors.root?.message && ( {formState.errors.root?.message && (

View file

@ -9,7 +9,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/components/ui/card'; } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { ScrollableSettingsWrapper } from '@/components/wrappers/settings-scroll'; import { ScrollableSettingsWrapper } from '@/components/wrappers/settings-scroll';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
@ -28,8 +28,23 @@ import LabeledInput from '../custom-ui/labeled-input';
import { GroupWrapper } from '../wrappers/group-wrapper'; import { GroupWrapper } from '../wrappers/group-wrapper';
import ProfilePictureUpload from './profile-picture-upload'; import ProfilePictureUpload from './profile-picture-upload';
import { LogOut } from 'lucide-react'; import {
import { signOut } from '@/auth'; CalendarArrowDown,
CalendarArrowUp,
CalendarCheck,
CalendarClock,
CalendarCog,
CalendarPlus,
CalendarPlus2,
ClockAlert,
ClockFading,
FileKey,
FileKey2,
MailOpen,
RotateCcwKey,
UserLock,
} from 'lucide-react';
import { IconButton } from '../buttons/icon-button';
export default function SettingsPage() { export default function SettingsPage() {
const router = useRouter(); const router = useRouter();
@ -46,7 +61,8 @@ export default function SettingsPage() {
<CardHeader> <CardHeader>
<CardTitle>Account Settings</CardTitle> <CardTitle>Account Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-6 mt-2'> <CardContent className='space-y-6 my-2'>
{/*-------------------- General Settings --------------------*/}
<GroupWrapper title='General Settings'> <GroupWrapper title='General Settings'>
<div className='space-y-4'> <div className='space-y-4'>
<div> <div>
@ -77,6 +93,7 @@ export default function SettingsPage() {
<LabeledInput <LabeledInput
type='email' type='email'
label='Email Address' label='Email Address'
icon={MailOpen}
placeholder='Your E-Mail' placeholder='Your E-Mail'
defaultValue={data?.data.user.email ?? ''} defaultValue={data?.data.user.email ?? ''}
></LabeledInput> ></LabeledInput>
@ -87,39 +104,42 @@ export default function SettingsPage() {
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- General Settings --------------------*/}
{/*-------------------- Reset Password --------------------*/}
<GroupWrapper title='Reset Password'> <GroupWrapper title='Reset Password'>
<div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'> <div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'>
<div> <div>
<LabeledInput <LabeledInput
type='password' type='password'
label='Current Password' label='Current Password'
placeholder='Current Password' icon={FileKey}
defaultValue={data?.data.user.first_name ?? ''}
></LabeledInput> ></LabeledInput>
</div> </div>
<div> <div>
<LabeledInput <LabeledInput
type='password' type='password'
label='New Password' label='New Password'
placeholder='New Password' icon={FileKey2}
defaultValue={data?.data.user.first_name ?? ''}
></LabeledInput> ></LabeledInput>
</div> </div>
<div> <div>
<LabeledInput <LabeledInput
type='password' type='password'
label='Repeat Password' label='Repeat Password'
placeholder='Repeat Password' icon={RotateCcwKey}
defaultValue={data?.data.user.first_name ?? ''}
></LabeledInput> ></LabeledInput>
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- Reset Password --------------------*/}
{/*-------------------- Profile Picture --------------------*/}
<GroupWrapper title='Profile Picture'> <GroupWrapper title='Profile Picture'>
<div className='space-y-2 grid grid-cols-[1fr_auto]'> <div className='space-y-2 grid grid-cols-[1fr_auto]'>
<ProfilePictureUpload className='file:border file:rounded-md file:hover:bg-disabled-destructive' /> <ProfilePictureUpload className='file:border file:rounded-md file:hover:bg-disabled-destructive' />
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- Profile Picture --------------------*/}
{/*-------------------- Regional Settings --------------------*/}
<GroupWrapper title='Regional Settings'> <GroupWrapper title='Regional Settings'>
<div className='space-y-2 grid sm:grid-cols-[1fr_auto] sm:flex-row gap-4'> <div className='space-y-2 grid sm:grid-cols-[1fr_auto] sm:flex-row gap-4'>
<div className='grid gap-1'> <div className='grid gap-1'>
@ -127,6 +147,7 @@ export default function SettingsPage() {
type='text' type='text'
label='Timezone' label='Timezone'
placeholder='Europe/Berlin' placeholder='Europe/Berlin'
icon={CalendarClock}
defaultValue={data?.data.user.timezone ?? ''} defaultValue={data?.data.user.timezone ?? ''}
></LabeledInput> ></LabeledInput>
</div> </div>
@ -146,23 +167,26 @@ export default function SettingsPage() {
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
<div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'> {/*-------------------- Regional Settings --------------------*/}
<Button <GroupWrapper title='DANGER ZONE - INSTANT DELETE - NO CONFIRMATION'>
onClick={() => { <div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'>
deleteUser.mutate(undefined, { <Button
onSuccess: () => { onClick={() => {
router.push('/api/logout'); deleteUser.mutate(undefined, {
}, onSuccess: () => {
}); router.push('/api/logout');
}} },
variant='destructive' });
> }}
Delete Account variant='destructive'
</Button> >
<span className='text-sm text-muted-foreground pt-1'> Delete Account
Permanently delete your account and all associated data. </Button>
</span> <span className='text-sm text-muted-foreground pt-1'>
</div> Permanently delete your account and all associated data.
</span>
</div>
</GroupWrapper>
</CardContent> </CardContent>
</ScrollableSettingsWrapper> </ScrollableSettingsWrapper>
</Card> </Card>
@ -175,8 +199,9 @@ export default function SettingsPage() {
<CardHeader> <CardHeader>
<CardTitle>Notification Preferences</CardTitle> <CardTitle>Notification Preferences</CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-6'> <CardContent className='space-y-6 my-2'>
<GroupWrapper> {/*-------------------- All --------------------*/}
<GroupWrapper title='All'>
<div className='flex items-center justify-between'> <div className='flex items-center justify-between'>
<Label <Label
htmlFor='masterEmailNotifications' htmlFor='masterEmailNotifications'
@ -187,7 +212,8 @@ export default function SettingsPage() {
<Switch id='masterEmailNotifications' /> <Switch id='masterEmailNotifications' />
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- All --------------------*/}
{/*-------------------- Meetings --------------------*/}
<GroupWrapper title='Meetings'> <GroupWrapper title='Meetings'>
<div className='space-y-4'> <div className='space-y-4'>
<div className='flex items-center justify-between space-x-2'> <div className='flex items-center justify-between space-x-2'>
@ -239,6 +265,8 @@ export default function SettingsPage() {
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- Meetings --------------------*/}
{/*-------------------- Social --------------------*/}
<GroupWrapper title='Social'> <GroupWrapper title='Social'>
<div className='space-y-4'> <div className='space-y-4'>
<div className='flex items-center justify-between space-x-2'> <div className='flex items-center justify-between space-x-2'>
@ -255,6 +283,7 @@ export default function SettingsPage() {
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*-------------------- Social --------------------*/}
</CardContent> </CardContent>
</ScrollableSettingsWrapper> </ScrollableSettingsWrapper>
</Card> </Card>
@ -267,8 +296,39 @@ export default function SettingsPage() {
<CardHeader> <CardHeader>
<CardTitle>Calendar & Availability</CardTitle> <CardTitle>Calendar & Availability</CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-6'> <CardContent className='space-y-6 my-2'>
{/*--------------------* Calendar --------------------*/} {/*-------------------- Date & Time Format --------------------*/}
<GroupWrapper title='Date & Time Format'>
<div className='flex flex-col space-y-4'>
<div className='grid grid-cols-1 gap-1'>
<Label htmlFor='dateFormat'>Date Format</Label>
<Select>
<SelectTrigger id='dateFormat'>
<SelectValue placeholder='Select date format' />
</SelectTrigger>
<SelectContent>
<SelectItem value='ddmmyyyy'>DD/MM/YYYY</SelectItem>
<SelectItem value='mmddyyyy'>MM/DD/YYYY</SelectItem>
<SelectItem value='yyyymmdd'>YYYY-MM-DD</SelectItem>
</SelectContent>
</Select>
</div>
<div className='grid grid-cols-1 gap-1'>
<Label htmlFor='timeFormat'>Time Format</Label>
<Select>
<SelectTrigger id='timeFormat'>
<SelectValue placeholder='Select time format' />
</SelectTrigger>
<SelectContent>
<SelectItem value='24h'>24-hour</SelectItem>
<SelectItem value='12h'>12-hour</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</GroupWrapper>
{/*-------------------- Date & Time Format --------------------*/}
{/*-------------------- Calendar --------------------*/}
<GroupWrapper title='Calendar'> <GroupWrapper title='Calendar'>
<div className='space-y-4'> <div className='space-y-4'>
<div className='grid grid-cols-1 gap-1'> <div className='grid grid-cols-1 gap-1'>
@ -306,9 +366,8 @@ export default function SettingsPage() {
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*--------------------* Calendar --------------------*/} {/*-------------------- Calendar --------------------*/}
{/*-------------------- Availability --------------------*/}
{/*--------------------* Availability --------------------*/}
<GroupWrapper title='Availability'> <GroupWrapper title='Availability'>
<div className='space-y-4'> <div className='space-y-4'>
<div className='space-y-2'> <div className='space-y-2'>
@ -325,8 +384,9 @@ export default function SettingsPage() {
<LabeledInput <LabeledInput
type='text' type='text'
label='Minimum Notice for Bookings' label='Minimum Notice for Bookings'
icon={ClockAlert}
subtext='Min time before a booking can be made.' subtext='Min time before a booking can be made.'
placeholder='e.g., 1h' placeholder='e.g. 1h'
defaultValue={''} defaultValue={''}
></LabeledInput> ></LabeledInput>
</div> </div>
@ -334,43 +394,65 @@ export default function SettingsPage() {
<LabeledInput <LabeledInput
type='text' type='text'
label='Booking Window (days in advance)' label='Booking Window (days in advance)'
icon={ClockFading}
subtext='Max time in advance a booking can be made.' subtext='Max time in advance a booking can be made.'
placeholder='e.g., 30d' placeholder='e.g. 30d'
defaultValue={''} defaultValue={''}
></LabeledInput> ></LabeledInput>
</div> </div>
</div> </div>
</GroupWrapper> </GroupWrapper>
{/*--------------------* Availability --------------------*/} {/*-------------------- Availability --------------------*/}
{/*-------------------- iCalendar Integration --------------------*/}
{/*--------------------* iCalendar Integration --------------------*/} <GroupWrapper title='iCalendar Integration'>
<fieldset className='space-y-4 p-4 border rounded-md'> <div className='space-y-4'>
<legend className='text-sm font-medium px-1'> <form
iCalendar Integration onSubmit={(e) => {
</legend> e.preventDefault();
<div className='space-y-2'> }}
<LabeledInput className='space-y-2'
type='url' >
label='Import iCal Feed URL' <LabeledInput
subtext='Max time in advance a booking can be made.' type='url'
placeholder='https://calendar.example.com/feed.ics' label='Import iCal Feed URL'
defaultValue={''} icon={CalendarCheck}
></LabeledInput> placeholder='https://calendar.example.com/feed.ics'
<Button size='sm' className='mt-1'> defaultValue={''}
Add Feed name='icalUrl'
</Button> required
></LabeledInput>
<IconButton
type='submit'
size='sm'
className='mt-1'
variant={'secondary'}
icon={CalendarPlus}
title='Submit iCal URL'
>
Add Feed
</IconButton>
</form>
<div className='space-y-2'>
<Label>Export Your Calendar</Label>
<IconButton
variant='outline_muted'
size='sm'
icon={CalendarArrowUp}
>
Get iCal Export URL
</IconButton>
<IconButton
variant='outline_muted'
size='sm'
className='ml-2'
icon={CalendarArrowDown}
>
Download .ics File
</IconButton>
</div>
</div> </div>
<div className='space-y-2'> </GroupWrapper>
<Label>Export Your Calendar</Label> {/*-------------------- iCalendar Integration --------------------*/}
<Button variant='outline_muted' size='sm'>
Get iCal Export URL
</Button>
<Button variant='outline_muted' size='sm' className='ml-2'>
Download .ics File
</Button>
</div>
</fieldset>
{/*--------------------* iCalendar Integration --------------------*/}
</CardContent> </CardContent>
</ScrollableSettingsWrapper> </ScrollableSettingsWrapper>
</Card> </Card>
@ -383,79 +465,94 @@ export default function SettingsPage() {
<CardHeader> <CardHeader>
<CardTitle>Sharing & Privacy</CardTitle> <CardTitle>Sharing & Privacy</CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-6'> <CardContent className='space-y-6 my-2'>
<div className='space-y-2'> {/*-------------------- Privacy Settigs --------------------*/}
<Label htmlFor='defaultVisibility'> <GroupWrapper title='Privacy Settings'>
Default Calendar Visibility <div className='flex flex-col space-y-4'>
</Label> <div className='grid grid-cols-1 gap-1'>
<Select> <Label htmlFor='defaultVisibility'>
<SelectTrigger id='defaultVisibility'> Default Calendar Visibility
<SelectValue placeholder='Select visibility' /> </Label>
</SelectTrigger> <span className='text-sm text-muted-foreground'>
<SelectContent> Default setting for new friends.
<SelectItem value='private'> </span>
Private (Only You) <Select>
</SelectItem> <SelectTrigger id='defaultVisibility'>
<SelectItem value='freebusy'> <SelectValue placeholder='Select visibility' />
Free/Busy for Friends </SelectTrigger>
</SelectItem> <SelectContent>
<SelectItem value='fulldetails'> <SelectItem value='private'>
Full Details for Friends Private (Only You)
</SelectItem> </SelectItem>
</SelectContent> <SelectItem value='freebusy'>Free/Busy</SelectItem>
</Select> <SelectItem value='fulldetails'>
</div> Full Details
<div className='space-y-2'> </SelectItem>
<Label htmlFor='whoCanSeeFull'> </SelectContent>
Who Can See Your Full Calendar Details? </Select>
</Label> </div>
<span className='text-sm text-muted-foreground'> <div className='grid grid-cols-1 gap-1'>
(Override for Default Visibility) <Label htmlFor='whoCanSeeFull'>
<br /> Who Can See Your Full Calendar Details?
<span className='text-sm text-muted-foreground'> </Label>
This setting will override the default visibility for your <span className='text-sm text-muted-foreground'>
calendar. You can set specific friends or groups to see (Override for Default Visibility)
your full calendar details. <br />
</span> <span className='text-sm text-muted-foreground'>
</span> This setting will override the default visibility for
<Select> your calendar. You can set specific friends or groups
<SelectTrigger id='whoCanSeeFull'> to see your full calendar details.
<SelectValue placeholder='Select audience' /> </span>
</SelectTrigger> </span>
<SelectContent> <Select>
<SelectItem value='me'>Only Me</SelectItem> <SelectTrigger id='whoCanSeeFull'>
<SelectItem value='friends'>My Friends</SelectItem> <SelectValue placeholder='Select audience' />
<SelectItem value='specific'> </SelectTrigger>
Specific Friends/Groups (manage separately) <SelectContent>
</SelectItem> <SelectItem value='me'>Only Me</SelectItem>
</SelectContent> <SelectItem value='friends'>My Friends</SelectItem>
</Select> <SelectItem value='specific'>
</div> Specific Friends/Groups (manage separately)
<div className='space-y-2'> </SelectItem>
<Label htmlFor='whoCanBook'> </SelectContent>
Who Can Book Time With You? </Select>
</Label> </div>
<Select> <div className='grid grid-cols-1 gap-1'>
<SelectTrigger id='whoCanBook'> <Label htmlFor='whoCanBook'>
<SelectValue placeholder='Select audience' /> Who Can Book Time With You?
</SelectTrigger> </Label>
<SelectContent> <Select>
<SelectItem value='none'>No One</SelectItem> <SelectTrigger id='whoCanBook'>
<SelectItem value='friends'>My Friends</SelectItem> <SelectValue placeholder='Select audience' />
<SelectItem value='specific'> </SelectTrigger>
Specific Friends/Groups (manage separately) <SelectContent>
</SelectItem> <SelectItem value='none'>No One</SelectItem>
</SelectContent> <SelectItem value='friends'>My Friends</SelectItem>
</Select> <SelectItem value='specific'>
</div> Specific Friends/Groups (manage separately)
<div className='space-y-2'> </SelectItem>
<Label>Blocked Users</Label> </SelectContent>
<Button variant='outline_muted'>Manage Blocked Users</Button> </Select>
<span className='text-sm text-muted-foreground'> </div>
Prevent specific users from seeing your calendar or booking <div className='space-y-4'>
time. <div className='grid grid-cols-1 gap-1'>
</span> <Label>Blocked Users</Label>
</div> <span className='text-sm text-muted-foreground'>
Prevent specific users from seeing your calendar or
booking time.
</span>
<IconButton
variant='outline_muted'
size='sm'
icon={UserLock}
>
Manage Blocked Users
</IconButton>
</div>
</div>
</div>
</GroupWrapper>
{/*-------------------- Privacy Settigs --------------------*/}
</CardContent> </CardContent>
</ScrollableSettingsWrapper> </ScrollableSettingsWrapper>
</Card> </Card>
@ -468,36 +565,15 @@ export default function SettingsPage() {
<CardHeader> <CardHeader>
<CardTitle>Appearance</CardTitle> <CardTitle>Appearance</CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-6'> <CardContent className='space-y-6 my-2'>
<div className='space-y-2'> {/*-------------------- Change Theme --------------------*/}
<Label htmlFor='theme'>Theme</Label> <GroupWrapper title='Change Theme'>
<ThemePicker /> <div className='space-y-2'>
</div> <Label htmlFor='theme'>Theme</Label>
<div className='space-y-2'> <ThemePicker />
<Label htmlFor='dateFormat'>Date Format</Label> </div>
<Select> </GroupWrapper>
<SelectTrigger id='dateFormat'> {/*-------------------- Change Theme --------------------*/}
<SelectValue placeholder='Select date format' />
</SelectTrigger>
<SelectContent>
<SelectItem value='ddmmyyyy'>DD/MM/YYYY</SelectItem>
<SelectItem value='mmddyyyy'>MM/DD/YYYY</SelectItem>
<SelectItem value='yyyymmdd'>YYYY-MM-DD</SelectItem>
</SelectContent>
</Select>
</div>
<div className='space-y-2'>
<Label htmlFor='timeFormat'>Time Format</Label>
<Select>
<SelectTrigger id='timeFormat'>
<SelectValue placeholder='Select time format' />
</SelectTrigger>
<SelectContent>
<SelectItem value='24h'>24-hour</SelectItem>
<SelectItem value='12h'>12-hour</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent> </CardContent>
</ScrollableSettingsWrapper> </ScrollableSettingsWrapper>
</Card> </Card>