Merge branch 'main' into feat/typefully

This commit is contained in:
Adithya Krishna
2024-03-05 16:42:29 +05:30
committed by GitHub
6 changed files with 46 additions and 243 deletions

View File

@ -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(

View File

@ -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>
);
};

View File

@ -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,20 +35,18 @@ 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" className={cn(
className={cn( 'w-full justify-start',
'w-full justify-start', pathname?.startsWith('/settings/teams') && 'bg-secondary',
pathname?.startsWith('/settings/teams') && 'bg-secondary', )}
)} >
> <Users className="mr-2 h-5 w-5" />
<Users className="mr-2 h-5 w-5" /> Teams
Teams </Button>
</Button> </Link>
</Link>
)}
<Link href="/settings/security"> <Link href="/settings/security">
<Button <Button

View File

@ -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,20 +38,18 @@ 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" className={cn(
className={cn( 'w-full justify-start',
'w-full justify-start', pathname?.startsWith('/settings/teams') && 'bg-secondary',
pathname?.startsWith('/settings/teams') && 'bg-secondary', )}
)} >
> <Users className="mr-2 h-5 w-5" />
<Users className="mr-2 h-5 w-5" /> Teams
Teams </Button>
</Button> </Link>
</Link>
)}
<Link href="/settings/security"> <Link href="/settings/security">
<Button <Button

View File

@ -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,

View File

@ -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({