feat(settings): add settings modal with sidebar trigger
This commit is contained in:
parent
6ac1abfa86
commit
a730220aef
@ -1,6 +1,7 @@
|
||||
import { AppSidebar } from "@/components/app-sidebar"
|
||||
import { SiteHeader } from "@/components/site-header"
|
||||
import { CommandMenuProvider } from "@/components/command-menu-provider"
|
||||
import { SettingsProvider } from "@/components/settings-provider"
|
||||
import {
|
||||
SidebarInset,
|
||||
SidebarProvider,
|
||||
@ -15,6 +16,7 @@ export default async function DashboardLayout({
|
||||
const projectList = await getProjects()
|
||||
|
||||
return (
|
||||
<SettingsProvider>
|
||||
<CommandMenuProvider>
|
||||
<SidebarProvider
|
||||
defaultOpen={false}
|
||||
@ -36,5 +38,6 @@ export default async function DashboardLayout({
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
</CommandMenuProvider>
|
||||
</SettingsProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import { NavFiles } from "@/components/nav-files"
|
||||
import { NavProjects } from "@/components/nav-projects"
|
||||
import { NavUser } from "@/components/nav-user"
|
||||
import { useCommandMenu } from "@/components/command-menu-provider"
|
||||
import { useSettings } from "@/components/settings-provider"
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
@ -80,6 +81,7 @@ function SidebarNav({
|
||||
const pathname = usePathname()
|
||||
const { state } = useSidebar()
|
||||
const { open: openSearch } = useCommandMenu()
|
||||
const { open: openSettings } = useSettings()
|
||||
const isExpanded = state === "expanded"
|
||||
const isFilesMode = pathname?.startsWith("/dashboard/files")
|
||||
const isProjectMode = /^\/dashboard\/projects\/[^/]+/.test(
|
||||
@ -94,7 +96,11 @@ function SidebarNav({
|
||||
: "main"
|
||||
|
||||
const secondaryItems = [
|
||||
...data.navSecondary,
|
||||
...data.navSecondary.map((item) =>
|
||||
item.title === "Settings"
|
||||
? { ...item, onClick: openSettings }
|
||||
: item
|
||||
),
|
||||
{ title: "Search", icon: IconSearch, onClick: openSearch },
|
||||
]
|
||||
|
||||
|
||||
166
src/components/settings-modal.tsx
Executable file
166
src/components/settings-modal.tsx
Executable file
@ -0,0 +1,166 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { useTheme } from "next-themes"
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@/components/ui/tabs"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
|
||||
export function SettingsModal({
|
||||
open,
|
||||
onOpenChange,
|
||||
}: {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
}) {
|
||||
const { theme, setTheme } = useTheme()
|
||||
const [emailNotifs, setEmailNotifs] = React.useState(true)
|
||||
const [pushNotifs, setPushNotifs] = React.useState(true)
|
||||
const [weeklyDigest, setWeeklyDigest] = React.useState(false)
|
||||
const [timezone, setTimezone] = React.useState("America/New_York")
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Settings</DialogTitle>
|
||||
<DialogDescription>
|
||||
Manage your app preferences.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Tabs defaultValue="general" className="mt-2">
|
||||
<TabsList>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="notifications">
|
||||
Notifications
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="appearance">Appearance</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="general" className="space-y-4 pt-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="timezone">Timezone</Label>
|
||||
<Select value={timezone} onValueChange={setTimezone}>
|
||||
<SelectTrigger id="timezone" className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="America/New_York">
|
||||
Eastern (ET)
|
||||
</SelectItem>
|
||||
<SelectItem value="America/Chicago">
|
||||
Central (CT)
|
||||
</SelectItem>
|
||||
<SelectItem value="America/Denver">
|
||||
Mountain (MT)
|
||||
</SelectItem>
|
||||
<SelectItem value="America/Los_Angeles">
|
||||
Pacific (PT)
|
||||
</SelectItem>
|
||||
<SelectItem value="Europe/London">
|
||||
London (GMT)
|
||||
</SelectItem>
|
||||
<SelectItem value="Europe/Berlin">
|
||||
Berlin (CET)
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Weekly digest</Label>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Receive a summary of activity each week.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={weeklyDigest}
|
||||
onCheckedChange={setWeeklyDigest}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="notifications"
|
||||
className="space-y-4 pt-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Email notifications</Label>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Get notified about project updates via email.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={emailNotifs}
|
||||
onCheckedChange={setEmailNotifs}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Push notifications</Label>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Receive push notifications in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={pushNotifs}
|
||||
onCheckedChange={setPushNotifs}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="appearance"
|
||||
className="space-y-4 pt-4"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="theme">Theme</Label>
|
||||
<Select
|
||||
value={theme ?? "light"}
|
||||
onValueChange={setTheme}
|
||||
>
|
||||
<SelectTrigger id="theme" className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
32
src/components/settings-provider.tsx
Executable file
32
src/components/settings-provider.tsx
Executable file
@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { SettingsModal } from "@/components/settings-modal"
|
||||
|
||||
const SettingsContext = React.createContext<{
|
||||
open: () => void
|
||||
}>({ open: () => {} })
|
||||
|
||||
export function useSettings() {
|
||||
return React.useContext(SettingsContext)
|
||||
}
|
||||
|
||||
export function SettingsProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
|
||||
const value = React.useMemo(
|
||||
() => ({ open: () => setIsOpen(true) }),
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<SettingsContext.Provider value={value}>
|
||||
{children}
|
||||
<SettingsModal open={isOpen} onOpenChange={setIsOpen} />
|
||||
</SettingsContext.Provider>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user