From 6a5ad338babdb1adb72ba63694ea1bccabcdfaf3 Mon Sep 17 00:00:00 2001 From: SomeCodecat <88855796+SomeCodecat@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:01:20 +0200 Subject: [PATCH 01/13] feat: add Radix UI components and implement sidebar functionality - Added new Radix UI components: Dialog, Tooltip, Separator, and updated existing components. - Introduced a Sidebar component with collapsible functionality and mobile responsiveness. - Implemented a custom hook `useIsMobile` to manage mobile state. - Updated package dependencies in package.json and yarn.lock for new components. - Created utility components such as Button, Skeleton, and Input for consistent styling. feat: add AppSidebar component with collapsible functionality and sidebar menu - Introduced AppSidebar component for a customizable sidebar layout. - Implemented collapsible sections using Radix UI's Collapsible component. - Added sidebar menu items with icons and links for navigation. - Created Sidebar UI components including SidebarHeader, SidebarFooter, and SidebarMenu. - Integrated ThemePicker for theme selection within the sidebar. - Updated sidebar styles and layout for better responsiveness. chore: add @radix-ui/react-collapsible dependency - Added @radix-ui/react-collapsible package to manage collapsible UI elements. --- src/components/ui/sidebar.tsx | 725 ++++++++++++++++++++++++++++++++++ yarn.lock | 18 +- 2 files changed, 734 insertions(+), 9 deletions(-) create mode 100644 src/components/ui/sidebar.tsx diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx new file mode 100644 index 0000000..6b68b8f --- /dev/null +++ b/src/components/ui/sidebar.tsx @@ -0,0 +1,725 @@ +'use client'; + +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, VariantProps } from 'class-variance-authority'; +import { PanelLeftIcon } from 'lucide-react'; + +import { useIsMobile } from '@/hooks/use-mobile'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Separator } from '@/components/ui/separator'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from '@/components/ui/sheet'; +import { Skeleton } from '@/components/ui/skeleton'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; + +const SIDEBAR_COOKIE_NAME = 'sidebar_state'; +const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +const SIDEBAR_WIDTH = '16rem'; +const SIDEBAR_WIDTH_MOBILE = '18rem'; +const SIDEBAR_WIDTH_ICON = '4rem'; +const SIDEBAR_KEYBOARD_SHORTCUT = 'b'; + +type SidebarContextProps = { + state: 'expanded' | 'collapsed'; + open: boolean; + setOpen: (open: boolean) => void; + openMobile: boolean; + setOpenMobile: (open: boolean) => void; + isMobile: boolean; + toggleSidebar: () => void; +}; + +const SidebarContext = React.createContext(null); + +function useSidebar() { + const context = React.useContext(SidebarContext); + if (!context) { + throw new Error('useSidebar must be used within a SidebarProvider.'); + } + + return context; +} + +function SidebarProvider({ + defaultOpen = true, + open: openProp, + onOpenChange: setOpenProp, + className, + style, + children, + ...props +}: React.ComponentProps<'div'> & { + defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; +}) { + const isMobile = useIsMobile(); + const [openMobile, setOpenMobile] = React.useState(false); + + // This is the internal state of the sidebar. + // We use openProp and setOpenProp for control from outside the component. + const [_open, _setOpen] = React.useState(defaultOpen); + const open = openProp ?? _open; + const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === 'function' ? value(open) : value; + if (setOpenProp) { + setOpenProp(openState); + } else { + _setOpen(openState); + } + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + }, + [setOpenProp, open], + ); + + // Helper to toggle the sidebar. + const toggleSidebar = React.useCallback(() => { + return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open); + }, [isMobile, setOpen, setOpenMobile]); + + // Adds a keyboard shortcut to toggle the sidebar. + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ( + event.key === SIDEBAR_KEYBOARD_SHORTCUT && + (event.metaKey || event.ctrlKey) + ) { + event.preventDefault(); + toggleSidebar(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [toggleSidebar]); + + // We add a state so that we can do data-state="expanded" or "collapsed". + // This makes it easier to style the sidebar with Tailwind classes. + const state = open ? 'expanded' : 'collapsed'; + + const contextValue = React.useMemo( + () => ({ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + }), + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar], + ); + + return ( + + +
+ {children} +
+
+
+ ); +} + +function Sidebar({ + side = 'left', + variant = 'sidebar', + collapsible = 'offcanvas', + className, + children, + ...props +}: React.ComponentProps<'div'> & { + side?: 'left' | 'right'; + variant?: 'sidebar' | 'floating' | 'inset'; + collapsible?: 'offcanvas' | 'icon' | 'none'; +}) { + const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); + + if (collapsible === 'none') { + return ( +
+ {children} +
+ ); + } + + if (isMobile) { + return ( + + + + Sidebar + Displays the mobile sidebar. + +
{children}
+
+
+ ); + } + + return ( +
+ {/* This is what handles the sidebar gap on desktop */} +
+ +
+ ); +} + +function SidebarTrigger({ + className, + onClick, + ...props +}: React.ComponentProps) { + const { toggleSidebar } = useSidebar(); + + return ( + + ); +} + +function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) { + const { toggleSidebar } = useSidebar(); + + return ( + -

- Permanently delete your account and all associated data. -

-
- - - - - - - - - - - - - - Notification Preferences - - Choose how you want to be notified. - - - -
- - -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - - - -
-
- - - - - - Calendar & Availability - - Manage your calendar display, default availability, and iCal - integrations. - - - -
- - Display - -
- - -
-
- - -
-
- - -
-
- -
- - Availability - -
- -

- Define your typical available hours (e.g., - Monday-Friday, 9 AM - 5 PM). -

- -
-
- -

- Min time before a booking can be made. -

-
- -
-
-
- -

- Max time in advance a booking can be made. -

- -
-
- -
- - iCalendar Integration - -
- - - -
-
- - - -
-
-
-
- - - - -
-
- - - - - - Sharing & Privacy - - Control who can see your calendar and book time with you. - - - -
- - -
-
- -

- (Override for Default Visibility) -
- - This setting will override the default visibility for - your calendar. You can set specific friends or groups to - see your full calendar details. - -

- -
-
- - -
-
- - -

- Prevent specific users from seeing your calendar or - booking time. -

-
-
-
- - - - -
-
- - - - - - Appearance - - Customize the look and feel of the application. - - - -
- - -
-
- - -
-
- - -
-
-
- - - - -
-
- - - - ); +export default function Page() { + return ; } diff --git a/src/components/misc/settings-dropdown.tsx b/src/components/misc/settings-dropdown.tsx new file mode 100644 index 0000000..63a7b20 --- /dev/null +++ b/src/components/misc/settings-dropdown.tsx @@ -0,0 +1,158 @@ +'use client'; + +import type React from 'react'; + +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, + User, + Bell, + Calendar, + Shield, + Palette, +} from 'lucide-react'; + +interface SettingsSection { + label: string; + value: string; + description: string; + icon: React.ComponentType<{ className?: string }>; +} + +interface SettingsDropdownProps { + currentSection: string; + onSectionChange: (section: string) => void; + className?: string; +} + +const settingsSections: SettingsSection[] = [ + { + label: 'Account', + value: 'general', + description: 'Manage your account details and preferences', + icon: User, + }, + { + label: 'Notifications', + value: 'notifications', + description: 'Choose how you want to be notified', + icon: Bell, + }, + { + label: 'Calendar', + value: 'calendarAvailability', + description: 'Manage calendar display and availability', + icon: Calendar, + }, + { + label: 'Privacy', + value: 'sharingPrivacy', + description: 'Control who can see your calendar', + icon: Shield, + }, + { + label: 'Appearance', + value: 'appearance', + description: 'Customize the look and feel', + icon: Palette, + }, +]; + +export function SettingsDropdown({ + currentSection, + onSectionChange, + className, +}: SettingsDropdownProps) { + const [open, setOpen] = useState(false); + + const currentSectionData = settingsSections.find( + (section) => section.value === currentSection, + ); + const CurrentIcon = currentSectionData?.icon || User; + + const handleSelect = (value: string) => { + onSectionChange(value); + setOpen(false); + }; + + return ( +
+ + + + + + + + + No settings found. + + {settingsSections.map((section) => { + const Icon = section.icon; + return ( + handleSelect(section.value)} + className='flex items-center justify-between p-3' + > +
+ +
+ {section.label} + + {section.description} + +
+
+ +
+ ); + })} +
+
+
+
+
+
+ ); +} diff --git a/src/components/misc/settings-page.tsx b/src/components/misc/settings-page.tsx new file mode 100644 index 0000000..fb90614 --- /dev/null +++ b/src/components/misc/settings-page.tsx @@ -0,0 +1,467 @@ +'use client'; + +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + 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'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { SettingsDropdown } from '@/components/misc/settings-dropdown'; +import { useRouter } from 'next/navigation'; + +export default function SettingsPage() { + const router = useRouter(); + const [currentSection, setCurrentSection] = useState('general'); + + const renderSettingsContent = () => { + switch (currentSection) { + case 'general': + return ( + + + + Account Settings + + Manage your account details and preferences. + + + +
+ + +
+
+ + +

+ Email is managed by your SSO provider. +

+
+
+ + +

+ Upload a new profile picture. +

+
+
+ + +
+
+ + +
+
+ +

+ Permanently delete your account and all associated data. +

+
+
+
+
+ ); + + case 'notifications': + return ( + + + + Notification Preferences + + Choose how you want to be notified. + + + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ ); + + case 'calendarAvailability': + return ( + + + + Calendar & Availability + + Manage your calendar display, default availability, and iCal + integrations. + + + +
+ Display +
+ + +
+
+ + +
+
+ + +
+
+ +
+ + Availability + +
+ +

+ Define your typical available hours (e.g., Monday-Friday, + 9 AM - 5 PM). +

+ +
+
+ +

+ Min time before a booking can be made. +

+
+ +
+
+
+ +

+ Max time in advance a booking can be made. +

+ +
+
+ +
+ + iCalendar Integration + +
+ + + +
+
+ + + +
+
+
+
+
+ ); + + case 'sharingPrivacy': + return ( + + + + Sharing & Privacy + + Control who can see your calendar and book time with you. + + + +
+ + +
+
+ +

+ (Override for Default Visibility) +
+ + This setting will override the default visibility for your + calendar. You can set specific friends or groups to see + your full calendar details. + +

+ +
+
+ + +
+
+ + +

+ Prevent specific users from seeing your calendar or booking + time. +

+
+
+
+
+ ); + + case 'appearance': + return ( + + + + Appearance + + Customize the look and feel of the application. + + + +
+ + +
+
+ + +
+
+ + +
+
+
+
+ ); + + default: + return null; + } + }; + + return ( +
+
+
+
+

Settings

+
+ +
+ +
{renderSettingsContent()}
+
+ + + + +
+
+
+ ); +} diff --git a/src/components/misc/settings-switcher.tsx b/src/components/misc/settings-switcher.tsx new file mode 100644 index 0000000..40e6a05 --- /dev/null +++ b/src/components/misc/settings-switcher.tsx @@ -0,0 +1,123 @@ +'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 ( +
+ + + + + + + + + + No option found. + + {options.map((option) => ( + handleSelect(option.value)} + className='flex items-center justify-between' + > +
+ {option.label} + {option.description && ( + + {option.description} + + )} +
+ +
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/src/components/misc/user-card.tsx b/src/components/misc/user-card.tsx index faefc35..457d0fc 100644 --- a/src/components/misc/user-card.tsx +++ b/src/components/misc/user-card.tsx @@ -21,7 +21,7 @@ export default function UserCard() { )}
{data?.data.user.name}
-
+
{data?.data.user.email}
diff --git a/src/components/misc/user-dropdown.tsx b/src/components/misc/user-dropdown.tsx index e55f4bb..03249c6 100644 --- a/src/components/misc/user-dropdown.tsx +++ b/src/components/misc/user-dropdown.tsx @@ -41,11 +41,13 @@ export default function UserDropdown() { - Settings + + Settings + - - Logout - + + Logout + ); diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index a5eec23..b6c034f 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const buttonVariants = cva( - "radius-lg inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-button transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "radius-lg inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-button transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-nonef", { variants: { variant: { diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 7ba53ea..940a845 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -126,7 +126,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
); diff --git a/src/components/wrappers/settings-scroll.tsx b/src/components/wrappers/settings-scroll.tsx index e0f7251..a647493 100644 --- a/src/components/wrappers/settings-scroll.tsx +++ b/src/components/wrappers/settings-scroll.tsx @@ -1,16 +1,16 @@ -import React from 'react'; +import { cn } from '@/lib/utils'; +import type * as React from 'react'; -interface ScrollableContentWrapperProps { - children: React.ReactNode; +interface ScrollableSettingsWrapperProps { className?: string; + children: React.ReactNode; } -export const ScrollableSettingsWrapper: React.FC< - ScrollableContentWrapperProps -> = ({ children, className = '' }) => { +export function ScrollableSettingsWrapper({ + className, + children, +}: ScrollableSettingsWrapperProps) { return ( -
- {children} -
+
{children}
); -}; +} From 13a99e9dc4a083477bb12b17eda5860c692a2506 Mon Sep 17 00:00:00 2001 From: SomeCodecat <88855796+SomeCodecat@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:45:50 +0200 Subject: [PATCH 03/13] feat: tempcommit --- src/components/custom-ui/app-sidebar.tsx | 4 +- src/components/misc/settings-dropdown.tsx | 8 +- src/components/misc/settings-page.tsx | 128 ++++++++++++++-------- src/components/misc/settings-switcher.tsx | 123 --------------------- src/components/misc/user-dropdown.tsx | 1 + src/components/wrappers/group-wrapper.tsx | 23 ++++ 6 files changed, 113 insertions(+), 174 deletions(-) delete mode 100644 src/components/misc/settings-switcher.tsx create mode 100644 src/components/wrappers/group-wrapper.tsx diff --git a/src/components/custom-ui/app-sidebar.tsx b/src/components/custom-ui/app-sidebar.tsx index f823970..ef01ca9 100644 --- a/src/components/custom-ui/app-sidebar.tsx +++ b/src/components/custom-ui/app-sidebar.tsx @@ -52,7 +52,7 @@ const items = [ }, { title: 'Events', - url: '#', + url: '/events', icon: CalendarClock, }, ]; @@ -114,7 +114,7 @@ export function AppSidebar() { diff --git a/src/components/misc/settings-dropdown.tsx b/src/components/misc/settings-dropdown.tsx index 63a7b20..5bf256c 100644 --- a/src/components/misc/settings-dropdown.tsx +++ b/src/components/misc/settings-dropdown.tsx @@ -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' >
diff --git a/src/components/misc/settings-page.tsx b/src/components/misc/settings-page.tsx index fb90614..ce91579 100644 --- a/src/components/misc/settings-page.tsx +++ b/src/components/misc/settings-page.tsx @@ -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() { Account Settings - - Manage your account details and preferences. - - -
- - -
-
- - -

- Email is managed by your SSO provider. -

-
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +

+ Email might be managed by your SSO provider. +

+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
@@ -67,7 +123,11 @@ export default function SettingsPage() {
- +
@@ -98,9 +158,6 @@ export default function SettingsPage() { Notification Preferences - - Choose how you want to be notified. -
@@ -175,10 +232,6 @@ export default function SettingsPage() { Calendar & Availability - - Manage your calendar display, default availability, and iCal - integrations. -
@@ -298,9 +351,6 @@ export default function SettingsPage() { Sharing & Privacy - - Control who can see your calendar and book time with you. -
@@ -386,23 +436,11 @@ export default function SettingsPage() { Appearance - - Customize the look and feel of the application. -
- +
diff --git a/src/components/misc/settings-switcher.tsx b/src/components/misc/settings-switcher.tsx deleted file mode 100644 index 40e6a05..0000000 --- a/src/components/misc/settings-switcher.tsx +++ /dev/null @@ -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 ( -
- - - - - - - - - - No option found. - - {options.map((option) => ( - handleSelect(option.value)} - className='flex items-center justify-between' - > -
- {option.label} - {option.description && ( - - {option.description} - - )} -
- -
- ))} -
-
-
-
-
-
- ); -} diff --git a/src/components/misc/user-dropdown.tsx b/src/components/misc/user-dropdown.tsx index 03249c6..d9af19d 100644 --- a/src/components/misc/user-dropdown.tsx +++ b/src/components/misc/user-dropdown.tsx @@ -17,6 +17,7 @@ import UserCard from '@/components/misc/user-card'; export default function UserDropdown() { const { data } = useGetApiUserMe(); + return ( diff --git a/src/components/wrappers/group-wrapper.tsx b/src/components/wrappers/group-wrapper.tsx new file mode 100644 index 0000000..713b5d6 --- /dev/null +++ b/src/components/wrappers/group-wrapper.tsx @@ -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 ( +
+ {legend} + {children} +
+ ); +} From f658a95b16ca1ed5d24eaab1b84cf796bbed8322 Mon Sep 17 00:00:00 2001 From: SomeCodecat <88855796+SomeCodecat@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:52:00 +0200 Subject: [PATCH 04/13] feat: tempcommit --- src/components/misc/settings-dropdown.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/misc/settings-dropdown.tsx b/src/components/misc/settings-dropdown.tsx index 5bf256c..c49629c 100644 --- a/src/components/misc/settings-dropdown.tsx +++ b/src/components/misc/settings-dropdown.tsx @@ -105,9 +105,9 @@ export function SettingsDropdown({
{currentSectionData?.label} - +

{currentSectionData?.description} - +

@@ -132,9 +132,9 @@ export function SettingsDropdown({
{section.label} - +

{section.description} - +

Date: Thu, 26 Jun 2025 20:25:13 +0200 Subject: [PATCH 05/13] feat: tempcommit --- src/components/custom-ui/labeled-input.tsx | 51 +++++++--- src/components/misc/settings-dropdown.tsx | 4 +- src/components/misc/settings-page.tsx | 107 ++++++++++++--------- src/components/wrappers/group-wrapper.tsx | 6 +- 4 files changed, 102 insertions(+), 66 deletions(-) diff --git a/src/components/custom-ui/labeled-input.tsx b/src/components/custom-ui/labeled-input.tsx index 4746a31..23601d9 100644 --- a/src/components/custom-ui/labeled-input.tsx +++ b/src/components/custom-ui/labeled-input.tsx @@ -1,5 +1,9 @@ import { Input, Textarea } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import React from 'react'; +import { Button } from '../ui/button'; +import { Eye, EyeOff } from 'lucide-react'; +import { cn } from '@/lib/utils'; export default function LabeledInput({ type, @@ -12,7 +16,7 @@ export default function LabeledInput({ error, ...rest }: { - type: 'text' | 'email' | 'password'; + type: 'text' | 'email' | 'password' | 'file'; label: string; placeholder?: string; value?: string; @@ -21,6 +25,8 @@ export default function LabeledInput({ autocomplete?: string; error?: string; } & React.InputHTMLAttributes) { + const [passwordVisible, setPasswordVisible] = React.useState(false); + return (
@@ -33,21 +39,36 @@ export default function LabeledInput({ rows={3} /> ) : ( - + + + + {type === 'password' && ( + + )} + )} + {error &&

{error}

}
); diff --git a/src/components/misc/settings-dropdown.tsx b/src/components/misc/settings-dropdown.tsx index c49629c..6eaae8d 100644 --- a/src/components/misc/settings-dropdown.tsx +++ b/src/components/misc/settings-dropdown.tsx @@ -45,13 +45,13 @@ const settingsSections: SettingsSection[] = [ { label: 'Account', value: 'general', - description: 'Manage your account details and preferences', + description: 'Manage account details', icon: User, }, { label: 'Notifications', value: 'notifications', - description: 'Choose how you want to be notified', + description: 'Choose notification Preferences', icon: Bell, }, { diff --git a/src/components/misc/settings-page.tsx b/src/components/misc/settings-page.tsx index ce91579..34e1dd0 100644 --- a/src/components/misc/settings-page.tsx +++ b/src/components/misc/settings-page.tsx @@ -27,6 +27,9 @@ 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'; +import { Avatar } from '../ui/avatar'; +import Image from 'next/image'; +import { User } from 'lucide-react'; export default function SettingsPage() { const router = useRouter(); @@ -43,51 +46,50 @@ export default function SettingsPage() { Account Settings - +
- - + >
- - + >
- - + >
-
- - + -

+ label='Email Address' + placeholder='Your E-Mail' + defaultValue={data?.data.user.email ?? ''} + > + + Email might be managed by your SSO provider. -

+
- -
+ +
-
- - -

- Upload a new profile picture. -

+
+ + + {data?.data.user.image ? ( + Avatar + ) : ( + + )} +
@@ -143,9 +158,9 @@ export default function SettingsPage() {
-

+ Permanently delete your account and all associated data. -

+
@@ -277,10 +292,10 @@ export default function SettingsPage() {
-

+ Define your typical available hours (e.g., Monday-Friday, 9 AM - 5 PM). -

+ @@ -289,9 +304,9 @@ export default function SettingsPage() { -

+ Min time before a booking can be made. -

+
Booking Window (days in advance) -

+ Max time in advance a booking can be made. -

+ Who Can See Your Full Calendar Details? -

+ (Override for Default Visibility)
@@ -386,7 +401,7 @@ export default function SettingsPage() { calendar. You can set specific friends or groups to see your full calendar details. -

+ + + {data?.data.user.image ? ( + Avatar + ) : ( + + )} + + +
+ + ); +} diff --git a/src/components/misc/settings-page.tsx b/src/components/misc/settings-page.tsx index 34e1dd0..33a083b 100644 --- a/src/components/misc/settings-page.tsx +++ b/src/components/misc/settings-page.tsx @@ -30,6 +30,7 @@ import { GroupWrapper } from '../wrappers/group-wrapper'; import { Avatar } from '../ui/avatar'; import Image from 'next/image'; import { User } from 'lucide-react'; +import ProfilePictureUpload from './profile-picture-upload'; export default function SettingsPage() { const router = useRouter(); @@ -48,24 +49,26 @@ export default function SettingsPage() {
-
-
- + +
+
+ +
+
+ +
-
- -
-
+
-
- - - {data?.data.user.image ? ( - Avatar - ) : ( - - )} - -
-
- - -
-
- - -
-
+ +
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
Permanently delete your account and all associated data. From bf4f0b61ed7c5949951f05b879eb4b6cb7d956f1 Mon Sep 17 00:00:00 2001 From: SomeCodecat <88855796+SomeCodecat@users.noreply.github.com> Date: Fri, 27 Jun 2025 22:24:38 +0200 Subject: [PATCH 07/13] feat: tempcommit --- .../misc/profile-picture-upload.tsx | 7 +++- src/components/misc/settings-page.tsx | 40 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/components/misc/profile-picture-upload.tsx b/src/components/misc/profile-picture-upload.tsx index 9ddf2a7..41b64e0 100644 --- a/src/components/misc/profile-picture-upload.tsx +++ b/src/components/misc/profile-picture-upload.tsx @@ -5,13 +5,18 @@ import { User } from 'lucide-react'; import { Input } from '../ui/input'; -export default function ProfilePictureUpload() { +export default function ProfilePictureUpload({ + className, +}: { + className?: string; +}) { const { data } = useGetApiUserMe(); return ( <>
- -
-
- -
-
- -
-
-
+
+ +
+
+ +
- +
@@ -151,7 +147,7 @@ export default function SettingsPage() {
- + Permanently delete your account and all associated data. From 6b46177dc0cf9ea59accada947d3e9783c17dceb Mon Sep 17 00:00:00 2001 From: Maximilian Liebmann Date: Fri, 27 Jun 2025 23:06:19 +0200 Subject: [PATCH 08/13] feat: tempcommit --- .../misc/profile-picture-upload.tsx | 7 +- src/components/misc/settings-page.tsx | 142 ++++++++++-------- 2 files changed, 82 insertions(+), 67 deletions(-) diff --git a/src/components/misc/profile-picture-upload.tsx b/src/components/misc/profile-picture-upload.tsx index 41b64e0..cdd1673 100644 --- a/src/components/misc/profile-picture-upload.tsx +++ b/src/components/misc/profile-picture-upload.tsx @@ -15,12 +15,7 @@ export default function ProfilePictureUpload({ <>
- + {data?.data.user.image ? (
- +
@@ -127,7 +124,7 @@ export default function SettingsPage() { type='text' label='Timezone' placeholder='Europe/Berlin' - defaultValue={data?.data.user.timezone} + defaultValue={data?.data.user.timezone ?? ''} >
@@ -147,7 +144,12 @@ export default function SettingsPage() {
- + Permanently delete your account and all associated data. @@ -165,67 +167,85 @@ export default function SettingsPage() { Notification Preferences -
- - -
-
-
- - -
-
+ +
- +
-
- - + + + +
+
+ + +
+
+ + +
+
+
+ +
+
+ + +
+
+ +
+
-
- - + + +
+
+ + +
+
+ + +
-
- - -
-
- - -
-
+
From 7d2d5c55e8ffcb22c11bf6cda8d2a978ffbd5d0f Mon Sep 17 00:00:00 2001 From: SomeCodecat <88855796+SomeCodecat@users.noreply.github.com> Date: Sun, 29 Jun 2025 19:03:26 +0200 Subject: [PATCH 09/13] feat: tempcommit --- src/app/api/logout/route.ts | 8 + src/components/custom-ui/labeled-input.tsx | 8 +- src/components/misc/settings-page.tsx | 174 +++++++++++---------- 3 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 src/app/api/logout/route.ts diff --git a/src/app/api/logout/route.ts b/src/app/api/logout/route.ts new file mode 100644 index 0000000..ba89440 --- /dev/null +++ b/src/app/api/logout/route.ts @@ -0,0 +1,8 @@ +import { signOut } from '@/auth'; +import { NextResponse } from 'next/server'; + +export const GET = async () => { + await signOut(); + + return NextResponse.redirect('/login'); +}; diff --git a/src/components/custom-ui/labeled-input.tsx b/src/components/custom-ui/labeled-input.tsx index 23601d9..5ea9caf 100644 --- a/src/components/custom-ui/labeled-input.tsx +++ b/src/components/custom-ui/labeled-input.tsx @@ -8,6 +8,7 @@ import { cn } from '@/lib/utils'; export default function LabeledInput({ type, label, + subtext, placeholder, value, name, @@ -16,8 +17,8 @@ export default function LabeledInput({ error, ...rest }: { - type: 'text' | 'email' | 'password' | 'file'; label: string; + subtext?: string; placeholder?: string; value?: string; name?: string; @@ -30,6 +31,11 @@ export default function LabeledInput({ return (
+ {subtext && ( + + )} {variantSize === 'textarea' ? (