mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 02:01:33 +10:00
Merge branch 'main' into feat/typefully
This commit is contained in:
@ -7,7 +7,6 @@ import { useParams } from 'next/navigation';
|
|||||||
|
|
||||||
import { MenuIcon, SearchIcon } from 'lucide-react';
|
import { MenuIcon, SearchIcon } from 'lucide-react';
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
|
||||||
import type { GetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
|
import type { GetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
|
||||||
import { getRootHref } from '@documenso/lib/utils/params';
|
import { getRootHref } from '@documenso/lib/utils/params';
|
||||||
import type { User } from '@documenso/prisma/client';
|
import type { User } from '@documenso/prisma/client';
|
||||||
@ -19,7 +18,6 @@ import { CommandMenu } from '../common/command-menu';
|
|||||||
import { DesktopNav } from './desktop-nav';
|
import { DesktopNav } from './desktop-nav';
|
||||||
import { MenuSwitcher } from './menu-switcher';
|
import { MenuSwitcher } from './menu-switcher';
|
||||||
import { MobileNavigation } from './mobile-navigation';
|
import { MobileNavigation } from './mobile-navigation';
|
||||||
import { ProfileDropdown } from './profile-dropdown';
|
|
||||||
|
|
||||||
export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
|
export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
user: User;
|
user: User;
|
||||||
@ -29,10 +27,6 @@ export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
|
|||||||
export const Header = ({ className, user, teams, ...props }: HeaderProps) => {
|
export const Header = ({ className, user, teams, ...props }: HeaderProps) => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const { getFlag } = useFeatureFlags();
|
|
||||||
|
|
||||||
const isTeamsEnabled = getFlag('app_teams');
|
|
||||||
|
|
||||||
const [isCommandMenuOpen, setIsCommandMenuOpen] = useState(false);
|
const [isCommandMenuOpen, setIsCommandMenuOpen] = useState(false);
|
||||||
const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false);
|
const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false);
|
||||||
const [scrollY, setScrollY] = useState(0);
|
const [scrollY, setScrollY] = useState(0);
|
||||||
@ -47,34 +41,6 @@ export const Header = ({ className, user, teams, ...props }: HeaderProps) => {
|
|||||||
return () => window.removeEventListener('scroll', onScroll);
|
return () => window.removeEventListener('scroll', onScroll);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!isTeamsEnabled) {
|
|
||||||
return (
|
|
||||||
<header
|
|
||||||
className={cn(
|
|
||||||
'supports-backdrop-blur:bg-background/60 bg-background/95 sticky top-0 z-[60] flex h-16 w-full items-center border-b border-b-transparent backdrop-blur duration-200',
|
|
||||||
scrollY > 5 && 'border-b-border',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<div className="mx-auto flex w-full max-w-screen-xl items-center justify-between gap-x-4 px-4 md:justify-normal md:px-8">
|
|
||||||
<Link
|
|
||||||
href="/"
|
|
||||||
className="focus-visible:ring-ring ring-offset-background rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
|
|
||||||
>
|
|
||||||
<Logo className="h-6 w-auto" />
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<DesktopNav />
|
|
||||||
|
|
||||||
<div className="flex gap-x-4 md:ml-8">
|
|
||||||
<ProfileDropdown user={user} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@ -1,177 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Braces,
|
|
||||||
CreditCard,
|
|
||||||
FileSpreadsheet,
|
|
||||||
Lock,
|
|
||||||
LogOut,
|
|
||||||
User as LucideUser,
|
|
||||||
Monitor,
|
|
||||||
Moon,
|
|
||||||
Palette,
|
|
||||||
Sun,
|
|
||||||
UserCog,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { signOut } from 'next-auth/react';
|
|
||||||
import { useTheme } from 'next-themes';
|
|
||||||
import { LuGithub } from 'react-icons/lu';
|
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
|
||||||
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
|
||||||
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
|
||||||
import type { User } from '@documenso/prisma/client';
|
|
||||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuPortal,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuSub,
|
|
||||||
DropdownMenuSubContent,
|
|
||||||
DropdownMenuSubTrigger,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@documenso/ui/primitives/dropdown-menu';
|
|
||||||
|
|
||||||
export type ProfileDropdownProps = {
|
|
||||||
user: User;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProfileDropdown = ({ user }: ProfileDropdownProps) => {
|
|
||||||
const { getFlag } = useFeatureFlags();
|
|
||||||
const { theme, setTheme } = useTheme();
|
|
||||||
const isUserAdmin = isAdmin(user);
|
|
||||||
|
|
||||||
const isBillingEnabled = getFlag('app_billing');
|
|
||||||
|
|
||||||
const avatarFallback = user.name
|
|
||||||
? extractInitials(user.name)
|
|
||||||
: user.email.slice(0, 1).toUpperCase();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
title="Profile Dropdown"
|
|
||||||
className="relative h-10 w-10 rounded-full"
|
|
||||||
>
|
|
||||||
<Avatar className="h-10 w-10">
|
|
||||||
<AvatarFallback>{avatarFallback}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
|
|
||||||
<DropdownMenuContent className="z-[60] w-56" align="end" forceMount>
|
|
||||||
<DropdownMenuLabel>Account</DropdownMenuLabel>
|
|
||||||
|
|
||||||
{isUserAdmin && (
|
|
||||||
<>
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/admin" className="cursor-pointer">
|
|
||||||
<UserCog className="mr-2 h-4 w-4" />
|
|
||||||
Admin
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/settings/profile" className="cursor-pointer">
|
|
||||||
<LucideUser className="mr-2 h-4 w-4" />
|
|
||||||
Profile
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/settings/security" className="cursor-pointer">
|
|
||||||
<Lock className="mr-2 h-4 w-4" />
|
|
||||||
Security
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/settings/tokens" className="cursor-pointer">
|
|
||||||
<Braces className="mr-2 h-4 w-4" />
|
|
||||||
API Tokens
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
{isBillingEnabled && (
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/settings/billing" className="cursor-pointer">
|
|
||||||
<CreditCard className="mr-2 h-4 w-4" />
|
|
||||||
Billing
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link href="/templates" className="cursor-pointer">
|
|
||||||
<FileSpreadsheet className="mr-2 h-4 w-4" />
|
|
||||||
Templates
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger>
|
|
||||||
<Palette className="mr-2 h-4 w-4" />
|
|
||||||
Themes
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuPortal>
|
|
||||||
<DropdownMenuSubContent className="z-[60]">
|
|
||||||
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
|
|
||||||
<DropdownMenuRadioItem value="light">
|
|
||||||
<Sun className="mr-2 h-4 w-4" /> Light
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="dark">
|
|
||||||
<Moon className="mr-2 h-4 w-4" />
|
|
||||||
Dark
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="system">
|
|
||||||
<Monitor className="mr-2 h-4 w-4" />
|
|
||||||
System
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuPortal>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem asChild>
|
|
||||||
<Link
|
|
||||||
href="https://github.com/documenso/documenso"
|
|
||||||
className="cursor-pointer"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<LuGithub className="mr-2 h-4 w-4" />
|
|
||||||
Star on Github
|
|
||||||
</Link>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<DropdownMenuItem
|
|
||||||
onSelect={() =>
|
|
||||||
void signOut({
|
|
||||||
callbackUrl: '/',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<LogOut className="mr-2 h-4 w-4" />
|
|
||||||
Sign Out
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -19,7 +19,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
const { getFlag } = useFeatureFlags();
|
const { getFlag } = useFeatureFlags();
|
||||||
|
|
||||||
const isBillingEnabled = getFlag('app_billing');
|
const isBillingEnabled = getFlag('app_billing');
|
||||||
const isTeamsEnabled = getFlag('app_teams');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex flex-col gap-y-2', className)} {...props}>
|
<div className={cn('flex flex-col gap-y-2', className)} {...props}>
|
||||||
@ -36,7 +35,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{isTeamsEnabled && (
|
|
||||||
<Link href="/settings/teams">
|
<Link href="/settings/teams">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -49,7 +47,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
Teams
|
Teams
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
|
||||||
|
|
||||||
<Link href="/settings/security">
|
<Link href="/settings/security">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -19,7 +19,6 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
const { getFlag } = useFeatureFlags();
|
const { getFlag } = useFeatureFlags();
|
||||||
|
|
||||||
const isBillingEnabled = getFlag('app_billing');
|
const isBillingEnabled = getFlag('app_billing');
|
||||||
const isTeamsEnabled = getFlag('app_teams');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -39,7 +38,6 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{isTeamsEnabled && (
|
|
||||||
<Link href="/settings/teams">
|
<Link href="/settings/teams">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -52,7 +50,6 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
Teams
|
Teams
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
|
||||||
|
|
||||||
<Link href="/settings/security">
|
<Link href="/settings/security">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -22,7 +22,6 @@ export const FEATURE_FLAG_POLL_INTERVAL = 30000;
|
|||||||
*/
|
*/
|
||||||
export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
|
export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
|
||||||
app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true',
|
app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true',
|
||||||
app_teams: true,
|
|
||||||
app_document_page_view_history_sheet: false,
|
app_document_page_view_history_sheet: false,
|
||||||
marketing_header_single_player_mode: false,
|
marketing_header_single_player_mode: false,
|
||||||
marketing_profiles_announcement_bar: true,
|
marketing_profiles_announcement_bar: true,
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import type { QueryClientConfig } from '@tanstack/react-query';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { httpBatchLink } from '@trpc/client';
|
import { httpBatchLink } from '@trpc/client';
|
||||||
import { createTRPCReact } from '@trpc/react-query';
|
import { createTRPCReact } from '@trpc/react-query';
|
||||||
@ -27,7 +28,27 @@ export interface TrpcProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TrpcProvider({ children }: TrpcProviderProps) {
|
export function TrpcProvider({ children }: TrpcProviderProps) {
|
||||||
const [queryClient] = useState(() => new QueryClient());
|
let queryClientConfig: QueryClientConfig | undefined;
|
||||||
|
|
||||||
|
const isDevelopingOffline =
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
window.location.hostname === 'localhost' &&
|
||||||
|
!window.navigator.onLine;
|
||||||
|
|
||||||
|
if (isDevelopingOffline) {
|
||||||
|
queryClientConfig = {
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
networkMode: 'always',
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
networkMode: 'always',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
|
||||||
|
|
||||||
const [trpcClient] = useState(() =>
|
const [trpcClient] = useState(() =>
|
||||||
trpc.createClient({
|
trpc.createClient({
|
||||||
|
|||||||
Reference in New Issue
Block a user