* improved team setting UI

* fixed demo header style
This commit is contained in:
Zai Shi 2024-08-15 16:47:56 -07:00 committed by GitHub
parent 5b6116e57e
commit df9dd2c4dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 35 deletions

View File

@ -9,7 +9,7 @@ export default function Header() {
return (
<>
<div className="fixed w-full z-50 p-4 h-12 flex items-center py-4 border-b justify-between">
<div className="fixed w-full z-50 p-4 h-12 flex items-center py-4 border-b justify-between bg-white dark:bg-black">
<Link href="/" className="font-semibold">
Stack Demo
</Link>

View File

@ -7,8 +7,8 @@ import { yupObject, yupString } from '@stackframe/stack-shared/dist/schema-field
import { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';
import { throwErr } from '@stackframe/stack-shared/dist/utils/errors';
import { runAsynchronously, runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';
import { Button, Container, EditableText, Input, Label, PasswordInput, SimpleTooltip, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@stackframe/stack-ui';
import { CirclePlus, Contact, Settings, ShieldCheck } from 'lucide-react';
import { Button, EditableText, Input, Label, PasswordInput, SimpleTooltip, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@stackframe/stack-ui';
import { CirclePlus, Contact, LogOut, ShieldCheck } from 'lucide-react';
import { TOTPController, createTOTPKeyURI } from "oslo/otp";
import * as QRCode from 'qrcode';
import { useEffect, useState } from 'react';
@ -16,6 +16,7 @@ import { useForm } from 'react-hook-form';
import * as yup from "yup";
import { CurrentUser, MessageCard, Project, Team, useStackApp, useUser } from '..';
import { FormWarningText } from '../components/elements/form-warning';
import { MaybeFullPage } from "../components/elements/maybe-full-page";
import { SidebarLayout } from '../components/elements/sidebar-layout';
import { UserAvatar } from '../components/elements/user-avatar';
import { TeamIcon } from '../components/team-icon';
@ -49,10 +50,10 @@ export function AccountSettings({ fullPage=false }: { fullPage?: boolean }) {
),
},
{
title: 'Settings',
subpath: '/settings',
title: 'Sign Out',
subpath: '/sign-out',
type: 'item',
icon: Settings,
icon: LogOut,
content: <SignOutSection />,
},
...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{
@ -88,15 +89,11 @@ export function AccountSettings({ fullPage=false }: { fullPage?: boolean }) {
basePath='/handler/account-settings'
/>;
if (fullPage) {
return (
<Container size={1000} className='stack-scope'>
{inner}
</Container>
);
} else {
return inner;
}
return (
<MaybeFullPage fullPage={fullPage} size={800} fullVertical containerClassName="sm:border-r sm:border-l">
{inner}
</MaybeFullPage>
);
}
function ProfileSection() {
@ -118,6 +115,10 @@ function EmailVerificationSection() {
const user = useUser({ or: 'redirect' });
const [emailSent, setEmailSent] = useState(false);
if (!user.primaryEmail) {
return null;
}
return (
<>
<div>
@ -125,19 +126,22 @@ function EmailVerificationSection() {
{user.primaryEmailVerified ? (
<Typography variant='success'>Your email has been verified.</Typography>
) : (
<Typography variant='destructive'>Your email has not been verified.</Typography>
<>
<Typography variant='destructive'>Your email has not been verified.</Typography>
<div className='flex mt-4'>
<Button
disabled={emailSent}
onClick={async () => {
await user.sendVerificationEmail();
setEmailSent(true);
}}
>
{emailSent ? 'Email sent!' : 'Send Verification Email'}
</Button>
</div>
</>
)}
<div className='flex mt-4'>
<Button
disabled={emailSent}
onClick={async () => {
await user.sendVerificationEmail();
setEmailSent(true);
}}
>
{emailSent ? 'Email sent!' : 'Send Verification Email'}
</Button>
</div>
</div>
</>
);

View File

@ -1,15 +1,21 @@
"use client";
import { Container } from "@stackframe/stack-ui";
import { Container, cn } from "@stackframe/stack-ui";
import React, { useId } from "react";
import { SsrScript } from "./ssr-layout-effect";
export function MaybeFullPage({
children,
fullPage=true
fullPage=true,
size=380,
fullVertical=false,
containerClassName,
}: {
children: React.ReactNode,
fullPage?: boolean,
size?: number,
fullVertical?: boolean,
containerClassName?: string,
}) {
const uniqueId = useId();
const id = `stack-card-frame-${uniqueId}`;
@ -34,12 +40,12 @@ export function MaybeFullPage({
minHeight: '100vh',
alignSelf: 'stretch',
display: 'flex',
alignItems: 'center',
alignItems: fullVertical ? 'stretch' : 'center',
justifyContent: 'center',
}}
className="stack-scope"
>
<Container size={380} style={{ padding: '1rem 1rem' }}>
<Container size={size} className={cn(fullVertical ? undefined : 'p-4', containerClassName)}>
{children}
</Container>
</div>

View File

@ -15,7 +15,7 @@ export type SidebarItem = {
contentTitle?: React.ReactNode,
}
export function SidebarLayout(props: { items: SidebarItem[], title?: ReactNode, basePath: string }) {
export function SidebarLayout(props: { items: SidebarItem[], title?: ReactNode, basePath: string, className?: string }) {
const pathname = usePathname();
const selectedIndex = props.items.findIndex(item => item.subpath && (props.basePath + item.subpath === pathname));
const router = useRouter();
@ -25,10 +25,10 @@ export function SidebarLayout(props: { items: SidebarItem[], title?: ReactNode,
return (
<>
<div className="hidden sm:flex">
<div className={cn("hidden sm:flex h-full", props.className)}>
<DesktopLayout items={props.items} title={props.title} selectedIndex={selectedIndex} basePath={props.basePath} />
</div>
<div className="sm:hidden">
<div className={cn("sm:hidden h-full", props.className)}>
<MobileLayout items={props.items} title={props.title} selectedIndex={selectedIndex} basePath={props.basePath} />
</div>
</>
@ -68,7 +68,7 @@ function DesktopLayout(props: { items: SidebarItem[], title?: ReactNode, selecte
const selectedItem = props.items[props.selectedIndex === -1 ? 0 : props.selectedIndex];
return (
<div className="stack-scope flex p-2 w-full">
<div className="stack-scope flex w-full h-full">
<div className="flex w-[200px] border-r flex-col items-stretch gap-2 p-2">
{props.title && <div className='mb-2'>
<Typography type='h2' className="text-lg font-semibold text-zinc-800 dark:text-zinc-300">{props.title}</Typography>