feat: tempcommit (broken input)
This commit is contained in:
parent
7d2d5c55e8
commit
6435bd4a8e
6 changed files with 349 additions and 215 deletions
|
@ -1,19 +1,20 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { LucideProps } from 'lucide-react';
|
||||
import React, { ForwardRefExoticComponent, RefAttributes } from 'react';
|
||||
|
||||
export function IconButton({
|
||||
icon,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
icon: IconProp;
|
||||
icon?: ForwardRefExoticComponent<
|
||||
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
|
||||
>;
|
||||
children: React.ReactNode;
|
||||
} & React.ComponentProps<typeof Button>) {
|
||||
return (
|
||||
<Button type='button' variant='secondary' {...props}>
|
||||
<FontAwesomeIcon icon={icon} className='mr-2' />
|
||||
{icon && React.createElement(icon, { className: 'mr-2' })}
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { signIn } from '@/auth';
|
||||
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({
|
||||
provider,
|
||||
|
@ -22,7 +22,7 @@ export default function SSOLogin({
|
|||
className='w-full'
|
||||
type='submit'
|
||||
variant='secondary'
|
||||
icon={faOpenid}
|
||||
icon={Fingerprint}
|
||||
{...props}
|
||||
>
|
||||
Login with {providerDisplayName}
|
||||
|
|
|
@ -62,6 +62,7 @@ export function AppSidebar() {
|
|||
<>
|
||||
<Sidebar collapsible='icon' variant='sidebar'>
|
||||
<SidebarHeader className='overflow-hidden'>
|
||||
<Link href='/home'>
|
||||
<Logo
|
||||
colorType='colored'
|
||||
logoType='combo'
|
||||
|
@ -74,6 +75,7 @@ export function AppSidebar() {
|
|||
height={50}
|
||||
className='group-data-[collapsible=]:hidden group-data-[mobile=true]/mobile:hidden'
|
||||
></Logo>
|
||||
</Link>
|
||||
</SidebarHeader>
|
||||
<SidebarContent className='grid grid-rows-[auto_1fr_auto]'>
|
||||
<Collapsible defaultOpen className='group/collapsible'>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Input, Textarea } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import React from 'react';
|
||||
import React, { ForwardRefExoticComponent, RefAttributes } from 'react';
|
||||
import { Button } from '../ui/button';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
import { Eye, EyeOff, LucideProps } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function LabeledInput({
|
||||
|
@ -12,6 +12,7 @@ export default function LabeledInput({
|
|||
placeholder,
|
||||
value,
|
||||
name,
|
||||
icon,
|
||||
variantSize = 'default',
|
||||
autocomplete,
|
||||
error,
|
||||
|
@ -22,11 +23,22 @@ export default function LabeledInput({
|
|||
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 || '');
|
||||
|
||||
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'>
|
||||
|
@ -50,18 +62,32 @@ export default function LabeledInput({
|
|||
className={cn(
|
||||
type === 'password' ? 'pr-[50px]' : '',
|
||||
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' : '',
|
||||
'transition-all duration-300 ease-in-out',
|
||||
)}
|
||||
type={passwordVisible ? 'text' : type}
|
||||
placeholder={placeholder}
|
||||
defaultValue={value}
|
||||
value={inputValue}
|
||||
id={name}
|
||||
name={name}
|
||||
autoComplete={autocomplete}
|
||||
onChange={handleInputChange}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
{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]'
|
||||
|
@ -74,7 +100,6 @@ export default function LabeledInput({
|
|||
)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{error && <p className='text-red-500 text-sm mt-1'>{error}</p>}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,11 +4,21 @@ import React, { useState, useRef } from 'react';
|
|||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import LabeledInput from '@/components/custom-ui/labeled-input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import useZodForm from '@/lib/hooks/useZodForm';
|
||||
import { loginSchema, registerSchema } from '@/lib/auth/validation';
|
||||
import { loginAction } from '@/lib/auth/login';
|
||||
import { registerAction } from '@/lib/auth/register';
|
||||
import { IconButton } from '../buttons/icon-button';
|
||||
import {
|
||||
FileKey,
|
||||
FileKey2,
|
||||
LogIn,
|
||||
MailOpen,
|
||||
RotateCcwKey,
|
||||
UserCheck,
|
||||
UserPen,
|
||||
UserPlus,
|
||||
} from 'lucide-react';
|
||||
|
||||
function LoginFormElement({
|
||||
setIsSignUp,
|
||||
|
@ -56,6 +66,7 @@ function LoginFormElement({
|
|||
<LabeledInput
|
||||
type='text'
|
||||
label='E-Mail or Username'
|
||||
icon={UserCheck}
|
||||
placeholder='What you are known as'
|
||||
error={formState.errors.email?.message}
|
||||
{...register('email')}
|
||||
|
@ -64,16 +75,22 @@ function LoginFormElement({
|
|||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
icon={FileKey}
|
||||
placeholder="Let's hope you remember it"
|
||||
error={formState.errors.password?.message}
|
||||
{...register('password')}
|
||||
data-cy='password-input'
|
||||
/>
|
||||
<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
|
||||
</Button>
|
||||
<Button
|
||||
</IconButton>
|
||||
<IconButton
|
||||
type='button'
|
||||
variant='outline_primary'
|
||||
onClick={() => {
|
||||
|
@ -81,9 +98,10 @@ function LoginFormElement({
|
|||
setIsSignUp((v) => !v);
|
||||
}}
|
||||
data-cy='register-switch'
|
||||
icon={UserPlus}
|
||||
>
|
||||
Sign Up
|
||||
</Button>
|
||||
</IconButton>
|
||||
</div>
|
||||
<div>
|
||||
{formState.errors.root?.message && (
|
||||
|
@ -156,27 +174,30 @@ function RegisterFormElement({
|
|||
{...register('lastName')}
|
||||
data-cy='last-name-input'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail'
|
||||
placeholder='Your email address'
|
||||
autocomplete='email'
|
||||
error={formState.errors.email?.message}
|
||||
{...register('email')}
|
||||
data-cy='email-input'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='text'
|
||||
label='Username'
|
||||
icon={UserPen}
|
||||
placeholder='Your username'
|
||||
autocomplete='username'
|
||||
error={formState.errors.username?.message}
|
||||
{...register('username')}
|
||||
data-cy='username-input'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='email'
|
||||
label='E-Mail'
|
||||
icon={MailOpen}
|
||||
placeholder='Your email address'
|
||||
autocomplete='email'
|
||||
error={formState.errors.email?.message}
|
||||
{...register('email')}
|
||||
data-cy='email-input'
|
||||
/>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Password'
|
||||
icon={FileKey2}
|
||||
placeholder='Create a password'
|
||||
autocomplete='new-password'
|
||||
error={formState.errors.password?.message}
|
||||
|
@ -186,6 +207,7 @@ function RegisterFormElement({
|
|||
<LabeledInput
|
||||
type='password'
|
||||
label='Confirm Password'
|
||||
icon={RotateCcwKey}
|
||||
placeholder='Repeat your password'
|
||||
autocomplete='new-password'
|
||||
error={formState.errors.confirmPassword?.message}
|
||||
|
@ -193,19 +215,25 @@ function RegisterFormElement({
|
|||
data-cy='confirm-password-input'
|
||||
/>
|
||||
<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
|
||||
</Button>
|
||||
<Button
|
||||
</IconButton>
|
||||
<IconButton
|
||||
type='button'
|
||||
variant='outline_primary'
|
||||
onClick={() => {
|
||||
formRef?.current?.reset();
|
||||
setIsSignUp((v) => !v);
|
||||
}}
|
||||
icon={LogIn}
|
||||
>
|
||||
Back to Login
|
||||
</Button>
|
||||
</IconButton>
|
||||
</div>
|
||||
<div>
|
||||
{formState.errors.root?.message && (
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { ScrollableSettingsWrapper } from '@/components/wrappers/settings-scroll';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
|
@ -28,8 +28,24 @@ import LabeledInput from '../custom-ui/labeled-input';
|
|||
import { GroupWrapper } from '../wrappers/group-wrapper';
|
||||
|
||||
import ProfilePictureUpload from './profile-picture-upload';
|
||||
import { LogOut } from 'lucide-react';
|
||||
import { signOut } from '@/auth';
|
||||
import {
|
||||
CalendarArrowDown,
|
||||
CalendarArrowUp,
|
||||
CalendarCheck,
|
||||
CalendarClock,
|
||||
CalendarCog,
|
||||
CalendarPlus,
|
||||
CalendarPlus2,
|
||||
ClockAlert,
|
||||
ClockFading,
|
||||
FileKey,
|
||||
FileKey2,
|
||||
MailOpen,
|
||||
RotateCcwKey,
|
||||
UserLock,
|
||||
UserPen,
|
||||
} from 'lucide-react';
|
||||
import { IconButton } from '../buttons/icon-button';
|
||||
|
||||
export default function SettingsPage() {
|
||||
const router = useRouter();
|
||||
|
@ -46,29 +62,31 @@ export default function SettingsPage() {
|
|||
<CardHeader>
|
||||
<CardTitle>Account Settings</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6 mt-2'>
|
||||
<CardContent className='space-y-6 my-2'>
|
||||
{/*-------------------- General Settings --------------------*/}
|
||||
<GroupWrapper title='General Settings'>
|
||||
<div className='space-y-4'>
|
||||
<div>
|
||||
<LabeledInput
|
||||
label='First Name'
|
||||
type='text'
|
||||
label='First Name'
|
||||
placeholder='First Name'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div>
|
||||
<LabeledInput
|
||||
label='Last Name'
|
||||
type='text'
|
||||
label='Last Name'
|
||||
placeholder='Last Name'
|
||||
defaultValue={data?.data.user.last_name ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<LabeledInput
|
||||
label='Display Name'
|
||||
type='text'
|
||||
label='Display Name'
|
||||
icon={UserPen}
|
||||
placeholder='Display Name'
|
||||
defaultValue={data?.data.user.name}
|
||||
></LabeledInput>
|
||||
|
@ -77,6 +95,7 @@ export default function SettingsPage() {
|
|||
<LabeledInput
|
||||
type='email'
|
||||
label='Email Address'
|
||||
icon={MailOpen}
|
||||
placeholder='Your E-Mail'
|
||||
defaultValue={data?.data.user.email ?? ''}
|
||||
></LabeledInput>
|
||||
|
@ -87,39 +106,42 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- General Settings --------------------*/}
|
||||
{/*-------------------- Reset Password --------------------*/}
|
||||
<GroupWrapper title='Reset Password'>
|
||||
<div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Current Password'
|
||||
placeholder='Current Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
icon={FileKey}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='New Password'
|
||||
placeholder='New Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
icon={FileKey2}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
<div>
|
||||
<LabeledInput
|
||||
type='password'
|
||||
label='Repeat Password'
|
||||
placeholder='Repeat Password'
|
||||
defaultValue={data?.data.user.first_name ?? ''}
|
||||
icon={RotateCcwKey}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Reset Password --------------------*/}
|
||||
{/*-------------------- Profile Picture --------------------*/}
|
||||
<GroupWrapper title='Profile Picture'>
|
||||
<div className='space-y-2 grid grid-cols-[1fr_auto]'>
|
||||
<ProfilePictureUpload className='file:border file:rounded-md file:hover:bg-disabled-destructive' />
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Profile Picture --------------------*/}
|
||||
{/*-------------------- Regional Settings --------------------*/}
|
||||
<GroupWrapper title='Regional Settings'>
|
||||
<div className='space-y-2 grid sm:grid-cols-[1fr_auto] sm:flex-row gap-4'>
|
||||
<div className='grid gap-1'>
|
||||
|
@ -127,6 +149,7 @@ export default function SettingsPage() {
|
|||
type='text'
|
||||
label='Timezone'
|
||||
placeholder='Europe/Berlin'
|
||||
icon={CalendarClock}
|
||||
defaultValue={data?.data.user.timezone ?? ''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
|
@ -146,6 +169,8 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Regional Settings --------------------*/}
|
||||
<GroupWrapper title='DANGER ZONE - INSTANT DELETE - NO CONFIRMATION'>
|
||||
<div className='flex items-center justify-evenly sm:flex-row flex-col gap-6'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
@ -163,6 +188,7 @@ export default function SettingsPage() {
|
|||
Permanently delete your account and all associated data.
|
||||
</span>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
</CardContent>
|
||||
</ScrollableSettingsWrapper>
|
||||
</Card>
|
||||
|
@ -175,8 +201,9 @@ export default function SettingsPage() {
|
|||
<CardHeader>
|
||||
<CardTitle>Notification Preferences</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<GroupWrapper>
|
||||
<CardContent className='space-y-6 my-2'>
|
||||
{/*-------------------- All --------------------*/}
|
||||
<GroupWrapper title='All'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label
|
||||
htmlFor='masterEmailNotifications'
|
||||
|
@ -187,7 +214,8 @@ export default function SettingsPage() {
|
|||
<Switch id='masterEmailNotifications' />
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
|
||||
{/*-------------------- All --------------------*/}
|
||||
{/*-------------------- Meetings --------------------*/}
|
||||
<GroupWrapper title='Meetings'>
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-center justify-between space-x-2'>
|
||||
|
@ -239,6 +267,8 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Meetings --------------------*/}
|
||||
{/*-------------------- Social --------------------*/}
|
||||
<GroupWrapper title='Social'>
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-center justify-between space-x-2'>
|
||||
|
@ -255,6 +285,7 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Social --------------------*/}
|
||||
</CardContent>
|
||||
</ScrollableSettingsWrapper>
|
||||
</Card>
|
||||
|
@ -267,8 +298,39 @@ export default function SettingsPage() {
|
|||
<CardHeader>
|
||||
<CardTitle>Calendar & Availability</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
{/*--------------------* Calendar --------------------*/}
|
||||
<CardContent className='space-y-6 my-2'>
|
||||
{/*-------------------- 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'>
|
||||
<div className='space-y-4'>
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
|
@ -306,9 +368,8 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*--------------------* Calendar --------------------*/}
|
||||
|
||||
{/*--------------------* Availability --------------------*/}
|
||||
{/*-------------------- Calendar --------------------*/}
|
||||
{/*-------------------- Availability --------------------*/}
|
||||
<GroupWrapper title='Availability'>
|
||||
<div className='space-y-4'>
|
||||
<div className='space-y-2'>
|
||||
|
@ -325,8 +386,9 @@ export default function SettingsPage() {
|
|||
<LabeledInput
|
||||
type='text'
|
||||
label='Minimum Notice for Bookings'
|
||||
icon={ClockAlert}
|
||||
subtext='Min time before a booking can be made.'
|
||||
placeholder='e.g., 1h'
|
||||
placeholder='e.g. 1h'
|
||||
defaultValue={''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
|
@ -334,43 +396,65 @@ export default function SettingsPage() {
|
|||
<LabeledInput
|
||||
type='text'
|
||||
label='Booking Window (days in advance)'
|
||||
icon={ClockFading}
|
||||
subtext='Max time in advance a booking can be made.'
|
||||
placeholder='e.g., 30d'
|
||||
placeholder='e.g. 30d'
|
||||
defaultValue={''}
|
||||
></LabeledInput>
|
||||
</div>
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*--------------------* Availability --------------------*/}
|
||||
|
||||
{/*--------------------* iCalendar Integration --------------------*/}
|
||||
<fieldset className='space-y-4 p-4 border rounded-md'>
|
||||
<legend className='text-sm font-medium px-1'>
|
||||
iCalendar Integration
|
||||
</legend>
|
||||
<div className='space-y-2'>
|
||||
{/*-------------------- Availability --------------------*/}
|
||||
{/*-------------------- iCalendar Integration --------------------*/}
|
||||
<GroupWrapper title='iCalendar Integration'>
|
||||
<div className='space-y-4'>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
className='space-y-2'
|
||||
>
|
||||
<LabeledInput
|
||||
type='url'
|
||||
label='Import iCal Feed URL'
|
||||
subtext='Max time in advance a booking can be made.'
|
||||
icon={CalendarCheck}
|
||||
placeholder='https://calendar.example.com/feed.ics'
|
||||
defaultValue={''}
|
||||
name='icalUrl'
|
||||
required
|
||||
></LabeledInput>
|
||||
<Button size='sm' className='mt-1'>
|
||||
<IconButton
|
||||
type='submit'
|
||||
size='sm'
|
||||
className='mt-1'
|
||||
variant={'secondary'}
|
||||
icon={CalendarPlus}
|
||||
title='Submit iCal URL'
|
||||
>
|
||||
Add Feed
|
||||
</Button>
|
||||
</div>
|
||||
</IconButton>
|
||||
</form>
|
||||
<div className='space-y-2'>
|
||||
<Label>Export Your Calendar</Label>
|
||||
<Button variant='outline_muted' size='sm'>
|
||||
<IconButton
|
||||
variant='outline_muted'
|
||||
size='sm'
|
||||
icon={CalendarArrowUp}
|
||||
>
|
||||
Get iCal Export URL
|
||||
</Button>
|
||||
<Button variant='outline_muted' size='sm' className='ml-2'>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
variant='outline_muted'
|
||||
size='sm'
|
||||
className='ml-2'
|
||||
icon={CalendarArrowDown}
|
||||
>
|
||||
Download .ics File
|
||||
</Button>
|
||||
</IconButton>
|
||||
</div>
|
||||
</fieldset>
|
||||
{/*--------------------* iCalendar Integration --------------------*/}
|
||||
</div>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- iCalendar Integration --------------------*/}
|
||||
</CardContent>
|
||||
</ScrollableSettingsWrapper>
|
||||
</Card>
|
||||
|
@ -383,11 +467,17 @@ export default function SettingsPage() {
|
|||
<CardHeader>
|
||||
<CardTitle>Sharing & Privacy</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<div className='space-y-2'>
|
||||
<CardContent className='space-y-6 my-2'>
|
||||
{/*-------------------- Privacy Settigs --------------------*/}
|
||||
<GroupWrapper title='Privacy Settings'>
|
||||
<div className='flex flex-col space-y-4'>
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label htmlFor='defaultVisibility'>
|
||||
Default Calendar Visibility
|
||||
</Label>
|
||||
<span className='text-sm text-muted-foreground'>
|
||||
Default setting for new friends.
|
||||
</span>
|
||||
<Select>
|
||||
<SelectTrigger id='defaultVisibility'>
|
||||
<SelectValue placeholder='Select visibility' />
|
||||
|
@ -396,16 +486,14 @@ export default function SettingsPage() {
|
|||
<SelectItem value='private'>
|
||||
Private (Only You)
|
||||
</SelectItem>
|
||||
<SelectItem value='freebusy'>
|
||||
Free/Busy for Friends
|
||||
</SelectItem>
|
||||
<SelectItem value='freebusy'>Free/Busy</SelectItem>
|
||||
<SelectItem value='fulldetails'>
|
||||
Full Details for Friends
|
||||
Full Details
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label htmlFor='whoCanSeeFull'>
|
||||
Who Can See Your Full Calendar Details?
|
||||
</Label>
|
||||
|
@ -413,9 +501,9 @@ export default function SettingsPage() {
|
|||
(Override for Default Visibility)
|
||||
<br />
|
||||
<span className='text-sm text-muted-foreground'>
|
||||
This setting will override the default visibility for your
|
||||
calendar. You can set specific friends or groups to see
|
||||
your full calendar details.
|
||||
This setting will override the default visibility for
|
||||
your calendar. You can set specific friends or groups
|
||||
to see your full calendar details.
|
||||
</span>
|
||||
</span>
|
||||
<Select>
|
||||
|
@ -431,7 +519,7 @@ export default function SettingsPage() {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label htmlFor='whoCanBook'>
|
||||
Who Can Book Time With You?
|
||||
</Label>
|
||||
|
@ -448,14 +536,25 @@ export default function SettingsPage() {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<div className='space-y-4'>
|
||||
<div className='grid grid-cols-1 gap-1'>
|
||||
<Label>Blocked Users</Label>
|
||||
<Button variant='outline_muted'>Manage Blocked Users</Button>
|
||||
<span className='text-sm text-muted-foreground'>
|
||||
Prevent specific users from seeing your calendar or booking
|
||||
time.
|
||||
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>
|
||||
</ScrollableSettingsWrapper>
|
||||
</Card>
|
||||
|
@ -468,36 +567,15 @@ export default function SettingsPage() {
|
|||
<CardHeader>
|
||||
<CardTitle>Appearance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-6'>
|
||||
<CardContent className='space-y-6 my-2'>
|
||||
{/*-------------------- Change Theme --------------------*/}
|
||||
<GroupWrapper title='Change Theme'>
|
||||
<div className='space-y-2'>
|
||||
<Label htmlFor='theme'>Theme</Label>
|
||||
<ThemePicker />
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<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='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>
|
||||
</GroupWrapper>
|
||||
{/*-------------------- Change Theme --------------------*/}
|
||||
</CardContent>
|
||||
</ScrollableSettingsWrapper>
|
||||
</Card>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue