feat: modify invite page & 404 page (#1097)

This commit is contained in:
Qi
2023-02-17 17:44:48 +08:00
committed by GitHub
parent 5e6366ba44
commit 67fe1871da
26 changed files with 926 additions and 501 deletions

View File

@@ -1,26 +1,28 @@
import { Button } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { NotFoundTitle, PageContainer } from './styles';
import ErrorImg from '../../../public/imgs/invite-error.svg';
import { StyledContainer } from './styles';
export const NotfoundPage = () => {
const { t } = useTranslation();
const router = useRouter();
return (
<PageContainer>
<NotFoundTitle data-testid="notFound">
{t('404 - Page Not Found')}
<p>
<Button
onClick={() => {
router.push('/workspace');
}}
>
{t('Back Home')}
</Button>
</p>
</NotFoundTitle>
</PageContainer>
<StyledContainer data-testid="notFound">
<Image alt="404" src={ErrorImg}></Image>
<p>{t('404 - Page Not Found')}</p>
<Button
shape="round"
onClick={() => {
router.push('/workspace');
}}
>
{t('Back Home')}
</Button>
</StyledContainer>
);
};

View File

@@ -1,21 +1,19 @@
import { styled } from '@affine/component';
import { displayFlex, styled } from '@affine/component';
export const PageContainer = styled('div')(({ theme }) => {
export const StyledContainer = styled.div(() => {
return {
width: '100%',
height: 'calc(100vh)',
backgroundColor: theme.colors.pageBackground,
};
});
export const NotFoundTitle = styled('h1')(({ theme }) => {
return {
position: 'relative',
top: 'calc(50% - 100px)',
height: '100px',
fontSize: '60px',
lineHeight: '100px',
color: theme.colors.textColor,
textAlign: 'center',
...displayFlex('center', 'center'),
flexDirection: 'column',
height: '100vh',
img: {
width: '360px',
height: '270px',
},
p: {
fontSize: '22px',
fontWeight: 600,
margin: '24px 0',
},
};
});

View File

@@ -4,16 +4,22 @@ import React from 'react';
export const PageListEmpty = (props: { listType?: string }) => {
const { listType } = props;
const { t } = useTranslation();
const getEmptyDescription = () => {
if (listType === 'all') {
return t('emptyAllPages');
}
if (listType === 'favorite') {
return t('emptyFavorite');
}
if (listType === 'trash') {
return t('emptyTrash');
}
};
return (
<div style={{ textAlign: 'center' }}>
<Empty
width={800}
height={300}
sx={{ marginTop: '100px', marginBottom: '30px' }}
/>
{listType === 'all' && <p>{t('emptyAllPages')}</p>}
{listType === 'favorite' && <p>{t('emptyFavorite')}</p>}
{listType === 'trash' && <p>{t('emptyTrash')}</p>}
<div style={{ height: 'calc(100% - 60px)' }}>
<Empty description={getEmptyDescription()} />
</div>
);
};

File diff suppressed because one or more lines are too long

View File

@@ -49,7 +49,7 @@ export const StyledNotFound = styled('div')(({ theme }) => {
'>svg': {
marginTop: '10px',
fontSize: '150px',
height: '200px',
},
};
});

View File

@@ -7,7 +7,7 @@ export const ExportPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
console.log(workspace);
return (
<>
<Wrapper marginBottom="32px"> {t('Export Description')}</Wrapper>
<Wrapper marginBottom="42px"> {t('Export Description')}</Wrapper>
<Button type="light" shape="circle" disabled>
{t('Export AFFiNE backup file')}
</Button>

View File

@@ -32,7 +32,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
if (workspace.published) {
return (
<>
<Wrapper marginBottom="32px">{t('Published Description')}</Wrapper>
<Wrapper marginBottom="42px">{t('Published Description')}</Wrapper>
<Wrapper marginBottom="12px">
<Content weight="500">{t('Share with link')}</Content>
@@ -66,7 +66,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
return (
<>
<Wrapper marginBottom="32px">{t('Publishing Description')}</Wrapper>
<Wrapper marginBottom="42px">{t('Publishing Description')}</Wrapper>
<Button
onClick={async () => {
setLoaded(true);
@@ -84,7 +84,7 @@ export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
return (
<>
<Wrapper marginBottom="32px">{t('Publishing')}</Wrapper>
<Wrapper marginBottom="42px">{t('Publishing')}</Wrapper>
<Button
type="light"
shape="circle"

View File

@@ -53,7 +53,7 @@ export const SyncPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
<Content weight={500}>{t('is a Local Workspace')}</Content>
</FlexWrapper>
<p>{t('Local Workspace Description')}</p>
<Wrapper marginTop="32px">
<Wrapper marginTop="42px">
<Button
type="light"
shape="circle"

View File

@@ -42,7 +42,7 @@ export const StyledInputContent = styled('div')(({ theme }) => {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
margin: '32px 0',
margin: '42px 0',
fontSize: theme.font.base,
};
});
@@ -52,7 +52,7 @@ export const StyledButtonContent = styled('div')(() => {
position: 'absolute',
left: '50%',
transform: 'translateX(-50%)',
bottom: '32px',
bottom: '42px',
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',

View File

@@ -1,7 +1,6 @@
import { Wrapper } from '@affine/component';
import { Button, IconButton } from '@affine/component';
import { Menu, MenuItem } from '@affine/component';
import { Empty } from '@affine/component';
import { toast } from '@affine/component';
import { FlexWrapper } from '@affine/component';
import { WorkspaceUnit } from '@affine/datacenter';
@@ -37,121 +36,126 @@ export const MembersPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
if (workspace.provider === 'affine') {
return (
<StyledMemberContainer>
<StyledMemberListContainer>
{!loaded && (
<FlexWrapper justifyContent="center">
<Loading size={25} />
</FlexWrapper>
)}
{loaded && members.length === 0 && (
<Empty width={648} sx={{ marginTop: '60px' }} height={300} />
)}
{loaded && members.length > 0 && (
<>
<StyledMemberTitleContainer>
<StyledMemberNameContainer>
{t('Users')} ({members.length})
</StyledMemberNameContainer>
<StyledMemberRoleContainer>
{t('Access level')}
</StyledMemberRoleContainer>
<div style={{ width: '24px', paddingRight: '48px' }}></div>
</StyledMemberTitleContainer>
{members.map((member, index) => {
const user = Object.assign(
{
avatar_url: '',
email: '',
id: '',
name: '',
},
member.user
);
return (
<StyledMemberListItem key={index}>
<StyledMemberNameContainer>
<StyledMemberAvatar
alt="member avatar"
src={user.avatar_url}
>
<EmailIcon />
</StyledMemberAvatar>
<>
<StyledMemberContainer>
<ul>
<StyledMemberTitleContainer>
<StyledMemberNameContainer>
{t('Users')} ({members.length})
</StyledMemberNameContainer>
<StyledMemberRoleContainer>
{t('Access level')}
</StyledMemberRoleContainer>
<div style={{ width: '24px', paddingRight: '48px' }}></div>
</StyledMemberTitleContainer>
</ul>
<StyledMemberInfo>
<StyledMemberName>{user.name}</StyledMemberName>
<StyledMemberEmail>
{member.user.email}
</StyledMemberEmail>
</StyledMemberInfo>
</StyledMemberNameContainer>
<StyledMemberRoleContainer>
{member.accepted
? member.type !== 99
? t('Member')
: t('Owner')
: t('Pending')}
</StyledMemberRoleContainer>
<StyledMoreVerticalButton>
{member.type === 99 ? (
<></>
) : (
<Menu
content={
<>
<MenuItem
onClick={async () => {
await removeMember(member.id);
toast(
t('Member has been removed', {
name: user.name,
})
);
}}
icon={<TrashIcon />}
>
{t('Remove from workspace')}
</MenuItem>
</>
}
placement="bottom-end"
disablePortal={true}
<StyledMemberListContainer>
{!loaded && (
<FlexWrapper justifyContent="center">
<Loading size={25} />
</FlexWrapper>
)}
{loaded && members.length > 0 && (
<>
{members.map((member, index) => {
const user = Object.assign(
{
avatar_url: '',
email: '',
id: '',
name: '',
},
member.user
);
return (
<StyledMemberListItem key={index}>
<StyledMemberNameContainer>
<StyledMemberAvatar
alt="member avatar"
src={user.avatar_url}
>
<IconButton>
<MoreVerticalIcon />
</IconButton>
</Menu>
)}
</StyledMoreVerticalButton>
</StyledMemberListItem>
);
})}
</>
)}
</StyledMemberListContainer>
<StyledMemberButtonContainer>
<Button
onClick={() => {
setIsInviteModalShow(true);
}}
type="primary"
shape="circle"
>
{t('Invite Members')}
</Button>
<InviteMemberModal
onClose={() => {
setIsInviteModalShow(false);
}}
onInviteSuccess={() => {
setIsInviteModalShow(false);
// refreshMembers();
}}
workspaceId={workspace.id}
open={isInviteModalShow}
></InviteMemberModal>
</StyledMemberButtonContainer>
</StyledMemberContainer>
<EmailIcon />
</StyledMemberAvatar>
<StyledMemberInfo>
<StyledMemberName>{user.name}</StyledMemberName>
<StyledMemberEmail>
{member.user.email}
</StyledMemberEmail>
</StyledMemberInfo>
</StyledMemberNameContainer>
<StyledMemberRoleContainer>
{member.accepted
? member.type !== 99
? t('Member')
: t('Owner')
: t('Pending')}
</StyledMemberRoleContainer>
<StyledMoreVerticalButton>
{member.type === 99 ? (
<></>
) : (
<Menu
content={
<>
<MenuItem
onClick={async () => {
// FIXME: remove ignore
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
await removeMember(member.id);
toast(
t('Member has been removed', {
name: user.name,
})
);
}}
icon={<TrashIcon />}
>
{t('Remove from workspace')}
</MenuItem>
</>
}
placement="bottom-end"
disablePortal={true}
>
<IconButton>
<MoreVerticalIcon />
</IconButton>
</Menu>
)}
</StyledMoreVerticalButton>
</StyledMemberListItem>
);
})}
</>
)}
</StyledMemberListContainer>
<StyledMemberButtonContainer>
<Button
onClick={() => {
setIsInviteModalShow(true);
}}
type="primary"
shape="circle"
>
{t('Invite Members')}
</Button>
</StyledMemberButtonContainer>
</StyledMemberContainer>
<InviteMemberModal
onClose={() => {
setIsInviteModalShow(false);
}}
onInviteSuccess={() => {
setIsInviteModalShow(false);
// refreshMembers();
}}
workspaceId={workspace.id}
open={isInviteModalShow}
></InviteMemberModal>
</>
);
}

View File

@@ -5,7 +5,7 @@ export const StyledMemberTitleContainer = styled('li')(() => {
return {
display: 'flex',
fontWeight: '500',
marginBottom: '32px',
marginBottom: '42px',
flex: 1,
};
});
@@ -14,6 +14,7 @@ export const StyledMemberContainer = styled('div')(() => {
display: 'flex',
height: '100%',
flexDirection: 'column',
overflow: 'hidden',
};
});
@@ -40,8 +41,8 @@ export const StyledMemberRoleContainer = styled('div')(() => {
export const StyledMemberListContainer = styled('ul')(() => {
return {
overflowY: 'scroll',
width: '100%',
flex: 1,
flexGrow: 1,
paddingBottom: '58px',
};
});
@@ -80,9 +81,8 @@ export const StyledMemberEmail = styled('div')(({ theme }) => {
export const StyledMemberButtonContainer = styled('div')(() => {
return {
position: 'absolute',
bottom: '0',
marginBottom: '20px',
position: 'fixed',
bottom: '20px',
};
});

View File

@@ -4,9 +4,8 @@ export const StyledSettingContainer = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'column',
padding: '0 0 20px 48px',
height: '100vh',
marginTop: '48px',
padding: '48px 0 20px 48px',
height: 'calc(100vh - 60px)',
};
});
@@ -55,7 +54,7 @@ export const StyledSettingKey = styled.div(({ theme }) => {
});
export const StyledRow = styled(FlexWrapper)(() => {
return {
marginBottom: '32px',
marginBottom: '42px',
};
});

View File

@@ -56,10 +56,9 @@ export const StyledListItem = styled.div<{
disabled?: boolean;
}>(({ theme, active, disabled }) => {
return {
width: '296px',
height: '32px',
marginTop: '12px',
color: active ? theme.colors.primaryColor : theme.colors.popoverColor,
color: active ? theme.colors.primaryColor : theme.colors.textColor,
paddingLeft: '12px',
borderRadius: '5px',
cursor: 'pointer',

View File

@@ -46,11 +46,7 @@ export const useWorkspaceHelper = () => {
};
const acceptInvite = async (inviteCode: string) => {
let inviteInfo;
if (inviteCode) {
inviteInfo = await dataCenter.acceptInvitation(inviteCode);
}
return inviteInfo;
return dataCenter.acceptInvitation(inviteCode);
};
return {

View File

@@ -1,5 +1,4 @@
import '../../public/globals.css';
import '../../public/variable.css';
import './temporary.css';
import '@fontsource/space-mono';
import '@fontsource/poppins';

View File

@@ -1,145 +1,104 @@
import { styled } from '@affine/component';
import { Empty } from '@affine/component';
import { displayFlex, styled } from '@affine/component';
import { Button } from '@affine/component';
import { Permission } from '@affine/datacenter';
import {
SucessfulDuotoneIcon,
UnsucessfulDuotoneIcon,
} from '@blocksuite/icons';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { PageLoading } from '@/components/loading';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { useGlobalState } from '@/store/app';
// const User = ({ name, avatar }: { name: string; avatar?: string }) => {
// return (
// <UserContent>
// {avatar ? (
// <Avatar src={avatar}></Avatar>
// ) : (
// <UserIcon>{name.slice(0, 1)}</UserIcon>
// )}
// <span>{name}</span>
// </UserContent>
// );
// };
import inviteError from '../../../public/imgs/invite-error.svg';
import inviteSuccess from '../../../public/imgs/invite-success.svg';
export default function DevPage() {
const [loading, setLoading] = useState(true);
const router = useRouter();
const [successInvited, setSuccessInvited] = useState<boolean>(false);
const [inviteData, setInviteData] = useState<Permission | null>(null);
const { acceptInvite } = useWorkspaceHelper();
const dataCenter = useGlobalState(store => store.dataCenter);
useEffect(() => {
if (router.query.invite_code) {
acceptInvite(router.query.invite_code as string)
.then(data => {
if (data && data.accepted) {
setSuccessInvited(true);
}
})
.finally(() => {
setLoading(false);
});
} else {
const init = async () => {
const data = await dataCenter.acceptInvitation(
router.query.invite_code as string
);
setInviteData(data as Permission);
setLoading(false);
}
}, [router, acceptInvite]);
};
init();
}, [router, acceptInvite, dataCenter]);
return loading ? (
<PageLoading />
) : (
<Invited>
<div>
<Empty width={310} height={310}></Empty>
if (loading) {
return <PageLoading />;
}
<Content>
{/* TODO add inviteInfo*/}
{/* <User name={inviteData?.name ? inviteData.name : '-'}></User> invited */}
{/* you to join */}
{/* <User
name={inviteData?.workspaceName ? inviteData.workspaceName : '-'}
></User> */}
{successInvited ? (
<Status>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle opacity="0.14" cx="12" cy="12" r="9" fill="#6880FF" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM16.6783 8.2652C17.0841 8.6398 17.1094 9.27246 16.7348 9.67828L11.1963 15.6783C11.007 15.8834 10.7406 16 10.4615 16C10.1824 16 9.91604 15.8834 9.72674 15.6783L7.2652 13.0116C6.89059 12.6058 6.9159 11.9731 7.32172 11.5985C7.72754 11.2239 8.3602 11.2492 8.7348 11.6551L10.4615 13.5257L15.2652 8.32172C15.6398 7.9159 16.2725 7.89059 16.6783 8.2652Z"
fill="#6880FF"
/>
</svg>
Successfully joined
</Status>
) : (
<Status>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
opacity="0.14"
x="2"
y="7"
width="12"
height="10"
rx="5"
fill="#6880FF"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2.29289 2.29289C2.68342 1.90237 3.31658 1.90237 3.70711 2.29289L7.70678 6.29256C7.707 6.29278 7.70655 6.29234 7.70678 6.29256L21.7071 20.2929C22.0976 20.6834 22.0976 21.3166 21.7071 21.7071C21.3166 22.0976 20.6834 22.0976 20.2929 21.7071L16.5858 18H16.5C15.9477 18 15.5 17.5523 15.5 17C15.5 16.9722 15.5011 16.9447 15.5033 16.9176L13.961 15.3752C12.8815 16.959 11.0631 18 9 18H7C3.68629 18 1 15.3137 1 12C1 9.40763 2.64407 7.19925 4.94642 6.36064L2.29289 3.70711C1.90237 3.31658 1.90237 2.68342 2.29289 2.29289ZM6.60504 8.01925C4.5813 8.21761 3 9.92414 3 12C3 14.2091 4.79086 16 7 16H9C10.511 16 11.8282 15.1618 12.5087 13.9229L10.9367 12.3509C10.7946 12.7301 10.4288 13 10 13C9.44772 13 9 12.5523 9 12C9 11.5127 9.0583 11.0379 9.16858 10.5828L6.60504 8.01925ZM15 8C14.4436 8 13.9162 8.11302 13.4375 8.31642C12.9291 8.53238 12.342 8.29538 12.1261 7.78707C11.9101 7.27876 12.1471 6.69162 12.6554 6.47566C13.377 6.1691 14.17 6 15 6H17C20.3137 6 23 8.68629 23 12C23 13.4573 22.4792 14.7955 21.6146 15.8349C21.2615 16.2595 20.6309 16.3174 20.2063 15.9642C19.7817 15.6111 19.7239 14.9806 20.077 14.556C20.6539 13.8624 21 12.973 21 12C21 9.79086 19.2091 8 17 8H15Z"
fill="#6880FF"
/>
</svg>
The link has expired
</Status>
)}
</Content>
</div>
</Invited>
);
if (inviteData?.accepted) {
return (
<StyledContainer>
<Image src={inviteSuccess} alt="" />
<Button
type="primary"
shape="round"
onClick={() => {
router.push(`/workspace/${inviteData?.workspace_id}/all`);
}}
>
Go to Workspace
</Button>
<p>
<SucessfulDuotoneIcon />
Successfully joined
</p>
</StyledContainer>
);
}
if (inviteData?.accepted === false) {
return (
<StyledContainer>
<Image src={inviteError} alt="" />
<Button
shape="round"
onClick={() => {
router.push(`/`);
}}
>
Back to Home
</Button>
<p>
<UnsucessfulDuotoneIcon />
The link has expired
</p>
</StyledContainer>
);
}
}
// const UserIcon = styled('div')({
// display: 'inline-block',
// width: '28px',
// height: '28px',
// borderRadius: '50%',
// backgroundColor: '#FFF5AB',
// textAlign: 'center',
// color: '#896406',
// lineHeight: '28px',
// });
const Invited = styled('div')(({ theme }) => {
const StyledContainer = styled('div')(({ theme }) => {
return {
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
color: theme.colors.textColor,
...displayFlex('center', 'center'),
flexDirection: 'column',
backgroundColor: theme.colors.pageBackground,
};
});
const Content = styled('div')({
fontSize: '16px',
marginTop: '35px',
});
const Status = styled('div')(() => {
return {
marginTop: '16px',
svg: {
verticalAlign: 'middle',
marginRight: '12px',
img: {
width: '300px',
height: '300px',
},
p: {
...displayFlex('center', 'center'),
marginTop: '24px',
svg: {
color: theme.colors.primaryColor,
fontSize: '24px',
marginRight: '12px',
},
},
};
});