Skip to content

Commit

Permalink
feat(core): add team workspace related UI
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmFly committed Dec 3, 2024
1 parent 6873a94 commit 2beff12
Show file tree
Hide file tree
Showing 53 changed files with 2,436 additions and 921 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export const root = style({
flexDirection: 'column',
fontSize: cssVar('fontBase'),
position: 'relative',
background: cssVar('backgroundPrimaryColor'),
backgroundColor: cssVar('backgroundPrimaryColor'),
backgroundSize: 'cover',
});
export const affineLogo = style({
color: 'inherit',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Button } from '@affine/component/ui/button';
import { useI18n } from '@affine/i18n';
import { Logo1Icon } from '@blocksuite/icons/rc';
import { useTheme } from 'next-themes';
import { type ReactNode, useCallback } from 'react';

import dotBgDark from './assets/dot-bg.dark.png';
import dotBgLight from './assets/dot-bg.light.png';
import { DesktopNavbar } from './desktop-navbar';
import * as styles from './index.css';
import { MobileNavbar } from './mobile-navbar';
Expand All @@ -18,8 +21,15 @@ export const AffineOtherPageLayout = ({
open(BUILD_CONFIG.downloadUrl, '_blank');
}, []);

const { resolvedTheme } = useTheme();
const backgroundImage =
resolvedTheme === 'dark' && dotBgDark ? dotBgDark : dotBgLight;

return (
<div className={styles.root}>
<div
className={styles.root}
style={{ backgroundImage: `url(${backgroundImage})` }}
>
{BUILD_CONFIG.isElectron ? (
<div className={styles.draggableHeader} />
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type { FC, PropsWithChildren, ReactNode } from 'react';

import { Empty } from '../../ui/empty';
import { ThemedImg } from '../../ui/themed-img';
import { AffineOtherPageLayout } from '../affine-other-page-layout';
import { authPageContainer, hideInSmallScreen } from './share.css';
import illustrationDark from '../affine-other-page-layout/assets/other-page.dark.png';
import illustrationLight from '../affine-other-page-layout/assets/other-page.light.png';
import {
authPageContainer,
hideInSmallScreen,
illustration,
} from './share.css';

export const AuthPageContainer: FC<
PropsWithChildren<{
Expand All @@ -20,7 +26,12 @@ export const AuthPageContainer: FC<
{children}
</div>
<div className={hideInSmallScreen}>
<Empty />
<ThemedImg
draggable={false}
className={illustration}
lightSrc={illustrationLight}
darkSrc={illustrationDark}
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,33 @@ export const authPageContainer = style({
globalStyle(`${authPageContainer} .wrapper`, {
display: 'flex',
alignItems: 'center',
width: '100%',
justifyContent: 'center',
overflow: 'hidden',
'@media': {
'screen and (max-width: 1024px)': {
flexDirection: 'column',
justifyContent: 'flex-start',
},
},
});
globalStyle(`${authPageContainer} .content`, {
maxWidth: '700px',
maxWidth: '810px',
'@media': {
'screen and (min-width: 1024px)': {
marginLeft: '200px',
minWidth: '500px',
marginRight: '60px',
flexGrow: 1,
flexShrink: 0,
flexBasis: 0,
},
'screen and (max-width: 1024px)': {
maxWidth: '600px',
width: '100%',
margin: 'auto',
},
},
});
globalStyle(`${authPageContainer} .title`, {
fontSize: cssVar('fontTitle'),
Expand Down Expand Up @@ -202,3 +221,8 @@ export const hideInSmallScreen = style({
},
},
});

export const illustration = style({
flexShrink: 0,
width: '670px',
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './accept-invite-page';
export * from './invite-modal';
export * from './invite-team-modal';
export * from './member-limit-modal';
export * from './pagination';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useI18n } from '@affine/i18n';
import { ExportIcon } from '@blocksuite/icons/rc';
import { cssVar } from '@toeverything/theme';

import { Button } from '../../../ui/button';
import Input from '../../../ui/input';
import * as styles from './styles.css';

export const EmailInvite = ({
inviteEmail,
setInviteEmail,
handleConfirm,
isMutating,
isValidEmail,
}: {
inviteEmail: string;
setInviteEmail: (value: string) => void;
handleConfirm: () => void;
isMutating: boolean;
isValidEmail: boolean;
}) => {
const t = useI18n();
return (
<>
<div className={styles.modalSubTitle}>
{t['com.affine.payment.member.team.invite.email-invite']()}
</div>
<div>
<Input
inputStyle={{ fontSize: cssVar('fontXs') }}
disabled={isMutating}
placeholder={t[
'com.affine.payment.member.team.invite.email-placeholder'
]()}
value={inviteEmail}
onChange={setInviteEmail}
onEnter={handleConfirm}
size="large"
/>
{!isValidEmail ? (
<div className={styles.errorHint}>
{t['com.affine.auth.sign.email.error']()}
</div>
) : null}
</div>
<div>
<Button className={styles.importButton} prefix={<ExportIcon />}>
{t['com.affine.payment.member.team.invite.import-csv']()}
</Button>
</div>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { emailRegex } from '@affine/component/auth-components';
import { Permission } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { useCallback, useEffect, useState } from 'react';

import { ConfirmModal } from '../../../ui/modal';
import { notify } from '../../../ui/notification';
import { type InviteMethodType, ModalContent } from './modal-content';
import * as styles from './styles.css';

export interface InviteTeamMemberModalProps {
open: boolean;
setOpen: (value: boolean) => void;
onConfirm: (params: { email: string; permission: Permission }) => void;
isMutating: boolean;
}

export const InviteTeamMemberModal = ({
open,
setOpen,
onConfirm,
isMutating,
}: InviteTeamMemberModalProps) => {
const t = useI18n();
const [inviteEmail, setInviteEmail] = useState('');
const [permission] = useState(Permission.Write);
const [isValidEmail, setIsValidEmail] = useState(true);
const [inviteMethod, setInviteMethod] = useState<InviteMethodType>('email');

const handleConfirm = useCallback(() => {
if (inviteMethod === 'link') {
setOpen(false);
return;
}
if (!emailRegex.test(inviteEmail)) {
setIsValidEmail(false);
return;
}
setIsValidEmail(true);

onConfirm({
email: inviteEmail,
permission,
});
notify.success({
title: t['com.affine.payment.member.team.invite.notify.title'](),
message: t['com.affine.payment.member.team.invite.notify.message'](),
});
}, [inviteEmail, inviteMethod, onConfirm, permission, setOpen, t]);

useEffect(() => {
if (!open) {
setInviteEmail('');
setIsValidEmail(true);
}
}, [open]);

return (
<ConfirmModal
width={480}
open={open}
onOpenChange={setOpen}
title={t['com.affine.payment.member.team.invite.title']()}
cancelText={t['com.affine.inviteModal.button.cancel']()}
contentOptions={{
['data-testid' as string]: 'invite-modal',
style: {
padding: '20px 24px',
},
}}
confirmText={
inviteMethod === 'email'
? t['com.affine.payment.member.team.invite.send-invites']()
: t['com.affine.payment.member.team.invite.done']()
}
confirmButtonOptions={{
loading: isMutating,
variant: 'primary',
}}
onConfirm={handleConfirm}
childrenContentClassName={styles.contentStyle}
>
<ModalContent
inviteEmail={inviteEmail}
setInviteEmail={setInviteEmail}
handleConfirm={handleConfirm}
isMutating={isMutating}
isValidEmail={isValidEmail}
inviteMethod={inviteMethod}
onInviteMethodChange={setInviteMethod}
/>
</ConfirmModal>
);
};
Loading

0 comments on commit 2beff12

Please sign in to comment.