* feat(schema): add auth, people, and financial tables Add users, organizations, teams, groups, and project members tables. Extend customers/vendors with netsuite fields. Add netsuite schema for invoices, bills, payments, and credit memos. Include all migrations, seeds, new UI primitives, and config updates. * feat(auth): add WorkOS authentication system Add login, signup, password reset, email verification, and invitation flows via WorkOS AuthKit. Includes auth middleware, permission helpers, dev mode fallbacks, and auth page components. * feat(people): add people management system Add user, team, group, and organization management with CRUD actions, dashboard pages, invite dialog, user drawer, and role-based filtering. Includes WorkOS invitation integration. * feat(netsuite): add NetSuite integration and financials Add bidirectional NetSuite REST API integration with OAuth 2.0, rate limiting, sync engine, and conflict resolution. Includes invoices, vendor bills, payments, credit memos CRUD, customer/vendor management pages, and financial dashboard with tabbed views. * feat(ui): add mobile support and dashboard improvements Add mobile bottom nav, FAB, filter bar, search, project switcher, pull-to-refresh, and schedule mobile view. Update sidebar with new nav items, settings modal with integrations tab, responsive dialogs, improved schedule and file components, PWA manifest, and service worker. * ci: retrigger build * ci: retrigger build --------- Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
302 lines
9.3 KiB
TypeScript
Executable File
302 lines
9.3 KiB
TypeScript
Executable File
"use client"
|
|
|
|
import * as React from "react"
|
|
import { useTheme } from "next-themes"
|
|
|
|
import {
|
|
ResponsiveDialog,
|
|
ResponsiveDialogBody,
|
|
} from "@/components/ui/responsive-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"
|
|
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
|
import { SyncControls } from "@/components/netsuite/sync-controls"
|
|
|
|
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")
|
|
|
|
const generalPage = (
|
|
<>
|
|
<div className="space-y-1.5">
|
|
<Label htmlFor="timezone" className="text-xs">
|
|
Timezone
|
|
</Label>
|
|
<Select value={timezone} onValueChange={setTimezone}>
|
|
<SelectTrigger id="timezone" className="w-full h-9">
|
|
<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 gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Weekly digest</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Receive a summary of activity each week.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={weeklyDigest}
|
|
onCheckedChange={setWeeklyDigest}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
</>
|
|
)
|
|
|
|
const notificationsPage = (
|
|
<>
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Email notifications</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Get notified about project updates via email.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={emailNotifs}
|
|
onCheckedChange={setEmailNotifs}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Push notifications</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Receive push notifications in your browser.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={pushNotifs}
|
|
onCheckedChange={setPushNotifs}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
</>
|
|
)
|
|
|
|
const appearancePage = (
|
|
<div className="space-y-1.5">
|
|
<Label htmlFor="theme" className="text-xs">
|
|
Theme
|
|
</Label>
|
|
<Select
|
|
value={theme ?? "light"}
|
|
onValueChange={setTheme}
|
|
>
|
|
<SelectTrigger id="theme" className="w-full h-9">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="light">Light</SelectItem>
|
|
<SelectItem value="dark">Dark</SelectItem>
|
|
<SelectItem value="system">System</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
)
|
|
|
|
const integrationsPage = (
|
|
<>
|
|
<NetSuiteConnectionStatus />
|
|
<SyncControls />
|
|
</>
|
|
)
|
|
|
|
return (
|
|
<ResponsiveDialog
|
|
open={open}
|
|
onOpenChange={onOpenChange}
|
|
title="Settings"
|
|
description="Manage your app preferences."
|
|
className="sm:max-w-xl"
|
|
>
|
|
<ResponsiveDialogBody
|
|
pages={[generalPage, notificationsPage, appearancePage, integrationsPage]}
|
|
>
|
|
<Tabs defaultValue="general" className="w-full">
|
|
<TabsList className="w-full inline-flex justify-start overflow-x-auto">
|
|
<TabsTrigger value="general" className="text-xs sm:text-sm shrink-0">
|
|
General
|
|
</TabsTrigger>
|
|
<TabsTrigger value="notifications" className="text-xs sm:text-sm shrink-0">
|
|
Notifications
|
|
</TabsTrigger>
|
|
<TabsTrigger value="appearance" className="text-xs sm:text-sm shrink-0">
|
|
Appearance
|
|
</TabsTrigger>
|
|
<TabsTrigger value="integrations" className="text-xs sm:text-sm shrink-0">
|
|
Integrations
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="general" className="space-y-3 pt-3">
|
|
<div className="space-y-1.5">
|
|
<Label htmlFor="timezone" className="text-xs">
|
|
Timezone
|
|
</Label>
|
|
<Select value={timezone} onValueChange={setTimezone}>
|
|
<SelectTrigger id="timezone" className="w-full h-9">
|
|
<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 gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Weekly digest</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Receive a summary of activity each week.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={weeklyDigest}
|
|
onCheckedChange={setWeeklyDigest}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent
|
|
value="notifications"
|
|
className="space-y-3 pt-3"
|
|
>
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Email notifications</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Get notified about project updates via email.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={emailNotifs}
|
|
onCheckedChange={setEmailNotifs}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex items-center justify-between gap-4">
|
|
<div className="min-w-0 flex-1">
|
|
<Label className="text-xs">Push notifications</Label>
|
|
<p className="text-muted-foreground text-xs">
|
|
Receive push notifications in your browser.
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
checked={pushNotifs}
|
|
onCheckedChange={setPushNotifs}
|
|
className="shrink-0"
|
|
/>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent
|
|
value="appearance"
|
|
className="space-y-3 pt-3"
|
|
>
|
|
<div className="space-y-1.5">
|
|
<Label htmlFor="theme" className="text-xs">
|
|
Theme
|
|
</Label>
|
|
<Select
|
|
value={theme ?? "light"}
|
|
onValueChange={setTheme}
|
|
>
|
|
<SelectTrigger id="theme" className="w-full h-9">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="light">Light</SelectItem>
|
|
<SelectItem value="dark">Dark</SelectItem>
|
|
<SelectItem value="system">System</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent
|
|
value="integrations"
|
|
className="space-y-3 pt-3"
|
|
>
|
|
<NetSuiteConnectionStatus />
|
|
<SyncControls />
|
|
</TabsContent>
|
|
</Tabs>
|
|
</ResponsiveDialogBody>
|
|
</ResponsiveDialog>
|
|
)
|
|
}
|