diff --git a/packages/app/src/components/create-workspace/index.tsx b/packages/app/src/components/create-workspace/index.tsx new file mode 100644 index 0000000000..1a04d3ce9a --- /dev/null +++ b/packages/app/src/components/create-workspace/index.tsx @@ -0,0 +1,83 @@ +import { styled } from '@/styles'; +import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal'; +import { Button } from '@/ui/button'; +import { useState } from 'react'; +import { createWorkspace } from '@/hooks/mock-data/mock'; +import Input from '@/ui/input'; + +interface ModalProps { + open: boolean; + onClose: () => void; +} + +export const CreateWorkspaceModal = ({ open, onClose }: ModalProps) => { + const [workspaceName, setWorkspaceName] = useState(''); + const handleCreateWorkspace = () => { + createWorkspace(workspaceName); + onClose(); + }; + return ( +
+ + +
+ New Workspace + { + onClose(); + }} + /> +
+ +

+ Workspace is your virtual space to capture, create and plan as + just one person or together as a team. +

+ { + setWorkspaceName(value); + }} + > + +
+
+
+
+ ); +}; + +const Header = styled('div')({ + position: 'relative', + height: '44px', +}); + +const Content = styled('div')({ + display: 'flex', + padding: '0 48px', + flexDirection: 'column', + alignItems: 'center', + gap: '16px', +}); + +const ContentTitle = styled('span')({ + fontSize: '20px', + lineHeight: '28px', + fontWeight: 600, + textAlign: 'left', + paddingBottom: '16px', +}); + +const Footer = styled('div')({ + height: '70px', + paddingLeft: '24px', + marginTop: '32px', + textAlign: 'center', +}); diff --git a/packages/app/src/components/workspace-modal/index.tsx b/packages/app/src/components/workspace-modal/index.tsx new file mode 100644 index 0000000000..84e56dd60d --- /dev/null +++ b/packages/app/src/components/workspace-modal/index.tsx @@ -0,0 +1,117 @@ +import { styled } from '@/styles'; +import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal'; +import { Button } from '@/ui/button'; +import { useEffect, useState } from 'react'; +import { getWorkspaceList, Workspace } from '@/hooks/mock-data/mock'; +import { CreateWorkspaceModal } from '../create-workspace'; + +interface LoginModalProps { + open: boolean; + onClose: () => void; +} + +export const WorkspaceModal = ({ open, onClose }: LoginModalProps) => { + const [workspaceList, setWorkspaceList] = useState([]); + const [createWorkspaceOpen, setCreateWorkspaceOpen] = useState(false); + + useEffect(() => { + getList(); + }, []); + + const getList = () => { + const data = getWorkspaceList(); + setWorkspaceList(data); + }; + return ( +
+ + +
+ My workspace + { + onClose(); + }} + /> +
+ + + {workspaceList.map(item => { + return ( + + + {item.name} + {item.type === 'local' && local} + {item.type === 'share' && share} + {item.isPublish ? 'public' : 'private'} + {item.isLocal ? 'local' : 'local'} + + ); + })} +
  • + +
  • +
    +
    +
    + +
    + { + setCreateWorkspaceOpen(false); + getList(); + }} + > +
    +
    +
    + ); +}; + +const Header = styled('div')({ + position: 'relative', + height: '44px', +}); + +const Content = styled('div')({ + padding: '0 20px', + flexDirection: 'column', + alignItems: 'center', + gap: '16px', +}); + +const ContentTitle = styled('span')({ + fontSize: '20px', + lineHeight: '28px', + fontWeight: 600, + textAlign: 'left', + paddingBottom: '16px', +}); + +const Footer = styled('div')({ + height: '70px', + paddingLeft: '24px', + marginTop: '32px', + textAlign: 'center', +}); + +const WorkspaceList = styled('div')({ + display: 'grid', + gridRowGap: '10px', + gridColumnGap: '10px', + fontSize: '16px', + gridTemplateColumns: 'repeat(2, 1fr)', +}); + +const WorkspaceItem = styled('div')({ + border: '1px solid #e5e5e5', +}); diff --git a/packages/app/src/components/workspace-modal/styles.ts b/packages/app/src/components/workspace-modal/styles.ts new file mode 100644 index 0000000000..40a8bcc375 --- /dev/null +++ b/packages/app/src/components/workspace-modal/styles.ts @@ -0,0 +1,38 @@ +import { displayFlex, styled } from '@/styles'; + +export const StyledTitle = styled.div(() => { + return { + ...displayFlex('center', 'center'), + fontSize: '20px', + fontWeight: 500, + marginTop: '60px', + lineHeight: 1, + }; +}); + +export const StyledContent = styled.div(() => { + return { + padding: '0 40px', + marginTop: '32px', + fontSize: '18px', + lineHeight: '25px', + 'p:not(last-of-type)': { + marginBottom: '10px', + }, + }; +}); + +export const StyledButton = styled.div(({ theme }) => { + return { + width: '146px', + height: '42px', + background: theme.colors.primaryColor, + color: '#FFFFFF', + fontSize: '18px', + fontWeight: 500, + borderRadius: '21px', + margin: '52px auto 0', + cursor: 'pointer', + ...displayFlex('center', 'center'), + }; +}); diff --git a/packages/app/src/hooks/mock-data/mock.ts b/packages/app/src/hooks/mock-data/mock.ts new file mode 100644 index 0000000000..e484dfcc87 --- /dev/null +++ b/packages/app/src/hooks/mock-data/mock.ts @@ -0,0 +1,75 @@ +export interface Workspace { + name: string; // 名称 + id: string; //唯一标识 + isPublish?: boolean; // 是否公开 + isLocal?: boolean; // 是否全部数据都在本地 + avatar?: string; // 封面 + type: 'local' | 'cloud' | 'share'; // cloud: 云端(本次暂不支持),local: 本地,share: 分享 + workspaceOwner?: User; // 本地工作空间的拥有者 +} + +interface User { + name: string; + id: string; + email: string; + avatar: string; +} + +export function getWorkspaceList(): Workspace[] { + const workspacesMeta = JSON.parse( + localStorage.getItem('affine-workspace') ?? '[]' + ); + return workspacesMeta; +} + +export function getPagesByWorkspaceId(workspaceId: string) { + if (!workspaceId) return []; + const workspacesMeta = []; + for (let i = 0; i < 10; i++) { + workspacesMeta.push({ + id: 'page-' + i, + name: 'page ' + i, + }); + } +} + +export function addWorkSpace(workspaceData: Workspace) { + const workspacesMeta = getWorkspaceList(); + workspacesMeta.push(workspaceData); + localStorage.setItem('affine-workspace', JSON.stringify(workspacesMeta)); +} + +export function deleteWorkspaceById(workspaceId: string) { + const workspacesMeta = getWorkspaceList(); + const newWorkspacesMeta = workspacesMeta.filter(() => { + return workspaceId !== workspaceId; + }); + localStorage.setItem('affine-workspace', JSON.stringify(newWorkspacesMeta)); +} + +export function updateWorkspaceById( + workspaceId: string, + workspaceData: Workspace +) { + const workspacesMeta = getWorkspaceList(); + const newWorkspacesMeta = workspacesMeta.map((workspace: Workspace) => { + if (workspace.id === workspaceId) { + return workspaceData; + } + return workspace; + }); + localStorage.setItem('affine-workspace', JSON.stringify(newWorkspacesMeta)); +} +export function createWorkspace(workspaceName: string) { + const workspaceData = { + name: workspaceName, + id: 'workspace-' + Date.now(), + isPublish: false, + isLocal: true, + avatar: '', + type: 'local', + } as Workspace; + const workspacesMeta = getWorkspaceList(); + workspacesMeta.push(workspaceData); + localStorage.setItem('affine-workspace', JSON.stringify(workspacesMeta)); +} diff --git a/packages/app/src/pages/new-workspace/index.tsx b/packages/app/src/pages/new-workspace/index.tsx new file mode 100644 index 0000000000..b46e99d917 --- /dev/null +++ b/packages/app/src/pages/new-workspace/index.tsx @@ -0,0 +1,47 @@ +import { WorkspaceModal } from '@/components/workspace-modal'; +import { getWorkspaceList } from '@/hooks/mock-data/mock'; +import { useEffect, useState } from 'react'; +import { styled } from '@/styles'; +import Button from '@/ui/button/Button'; + +const Page = () => { + const [open, setOpen] = useState(false); + + useEffect(() => { + const data = getWorkspaceList(); + if (!data.length) { + setOpen(true); + } + }, []); + return ( + +
    workspace
    +
    + +
    + { + setOpen(false); + }} + > +
    + ); +}; +export default Page; + +const Workspace = styled.div(({ theme }) => { + return { + height: '100vh', + background: theme.colors.pageBackground, + color: '#FFFFFF', + fontSize: '18px', + fontWeight: 500, + }; +});