Skip to content

Commit d7d69f1

Browse files
authored
Add ability to create folders (codesandbox#4016)
* create folders * remove columns * add template filters
1 parent 8f6ebab commit d7d69f1

File tree

8 files changed

+126
-33
lines changed

8 files changed

+126
-33
lines changed

packages/app/src/app/overmind/effects/gql/dashboard/mutations.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import {
22
UnmakeSandboxesTemplateMutation,
33
UnmakeSandboxesTemplateMutationVariables,
4-
CreateCommentMutation,
5-
CreateCollectionMutationVariables,
64
RenameFolderMutation,
75
DeleteFolderMutation,
86
DeleteFolderMutationVariables,
@@ -35,6 +33,8 @@ import {
3533
_RenameSandboxMutationVariables,
3634
MakeSandboxesTemplateMutation,
3735
MakeSandboxesTemplateMutationVariables,
36+
CreateFolderMutation,
37+
CreateFolderMutationVariables,
3838
} from 'app/graphql/types';
3939
import gql from 'graphql-tag';
4040
import { Query } from 'overmind-graphql';
@@ -58,8 +58,8 @@ export const createTeam: Query<
5858
`;
5959

6060
export const createFolder: Query<
61-
CreateCommentMutation,
62-
CreateCollectionMutationVariables
61+
CreateFolderMutation,
62+
CreateFolderMutationVariables
6363
> = gql`
6464
mutation createFolder($path: String!, $teamId: ID) {
6565
createCollection(path: $path, teamId: $teamId) {

packages/app/src/app/overmind/namespaces/dashboard/actions.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,51 @@ export const getAllFolders: AsyncAction = withLoadApp(
191191
}
192192
);
193193

194+
export const createFolder: AsyncAction<string> = async (
195+
{ effects, state },
196+
path
197+
) => {
198+
if (!state.dashboard.allCollections) return;
199+
const split = path.split('/');
200+
const oldFolders = state.dashboard.allCollections;
201+
state.dashboard.allCollections = [
202+
{
203+
path,
204+
id: 'FAKE_ID',
205+
sandboxes: 0,
206+
parent: split.slice(0, split.length - 1).find(a => a) || '',
207+
level: split.length - 2,
208+
name: split[split.length - 1],
209+
},
210+
...state.dashboard.allCollections,
211+
];
212+
try {
213+
const { createCollection } = await effects.gql.mutations.createFolder({
214+
// only way to pass, null is a value in the BE
215+
// @ts-ignore
216+
teamId: state.dashboard.activeTeam || undefined,
217+
path,
218+
});
219+
220+
state.dashboard.allCollections = state.dashboard.allCollections.map(
221+
folder => {
222+
if (folder.id === 'FAKE_ID') {
223+
return {
224+
...folder,
225+
id: createCollection.id,
226+
path: createCollection.path,
227+
};
228+
}
229+
230+
return folder;
231+
}
232+
);
233+
} catch {
234+
state.dashboard.allCollections = [...oldFolders];
235+
effects.notificationToast.error('There was a problem creating your folder');
236+
}
237+
};
238+
194239
export const getDrafts: AsyncAction = withLoadApp(
195240
async ({ state, effects }) => {
196241
const { dashboard } = state;
@@ -585,7 +630,9 @@ export const recoverSandboxes: AsyncAction<string[]> = async (
585630
await effects.gql.mutations.addSandboxToFolder({
586631
sandboxIds: ids,
587632
collectionPath: '/',
588-
teamId: state.dashboard.activeTeam,
633+
// only way to pass, null is a value in the BE
634+
// @ts-ignore
635+
teamId: state.dashboard.activeTeam || undefined,
589636
});
590637
} catch (error) {
591638
state.dashboard.sandboxes.DELETED = [...oldDeleted];

packages/app/src/app/pages/NewDashboard/Components/FolderCard/index.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,37 @@ import { join, dirname } from 'path';
77
import { MenuOptions } from './Menu';
88

99
type Props = {
10-
name: string;
11-
sandboxes: number;
12-
path: string;
10+
name?: string;
11+
sandboxes?: number;
12+
path?: string;
13+
newFolder?: (a: string) => void;
1314
};
1415

15-
export const FolderCard = ({ name, path, sandboxes, ...props }: Props) => {
16+
export const FolderCard = ({
17+
newFolder,
18+
name,
19+
path,
20+
sandboxes,
21+
...props
22+
}: Props) => {
1623
const { actions } = useOvermind();
1724
const [edit, setEdit] = useState(false);
1825
const [newName, setNewName] = useState(name);
1926

2027
const editFolderName = async e => {
2128
e.preventDefault();
29+
if (newFolder) {
30+
return newFolder(newName);
31+
}
2232
await actions.dashboard.renameFolder({
2333
path,
2434
newPath: join(dirname(path), newName),
2535
});
26-
setEdit(false);
36+
return setEdit(false);
2737
};
2838

2939
return (
30-
<Link as={LinkBase} to={`/new-dashboard/all` + path}>
40+
<Link as={newFolder ? Element : LinkBase} to={`/new-dashboard/all` + path}>
3141
<Stack
3242
direction="vertical"
3343
gap={2}
@@ -68,7 +78,7 @@ export const FolderCard = ({ name, path, sandboxes, ...props }: Props) => {
6878
</Stack>
6979
<Stack justify="space-between" align="center" marginLeft={4}>
7080
<Element>
71-
{edit ? (
81+
{edit || newFolder ? (
7282
<form onSubmit={editFolderName}>
7383
<Input
7484
value={newName}
@@ -81,10 +91,10 @@ export const FolderCard = ({ name, path, sandboxes, ...props }: Props) => {
8191
</Text>
8292
)}
8393
<Text marginTop={2} size={3} block variant="muted">
84-
{sandboxes} {sandboxes === 1 ? 'sandbox' : 'sandboxes'}
94+
{sandboxes || 0} {sandboxes === 1 ? 'sandbox' : 'sandboxes'}
8595
</Text>
8696
</Element>
87-
<MenuOptions path={path} setEdit={setEdit} />
97+
{path ? <MenuOptions path={path} setEdit={setEdit} /> : null}
8898
</Stack>
8999
</Stack>
90100
</Link>

packages/app/src/app/pages/NewDashboard/Components/Header/index.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
import React from 'react';
2+
import { withRouter } from 'react-router-dom';
23
import css from '@styled-system/css';
3-
import { Stack, Text } from '@codesandbox/components';
4+
import { Stack, Text, Button } from '@codesandbox/components';
45
import { Breadcrumbs } from '../Breadcrumbs';
56
import { Filters } from '../Filters';
67

78
type Props = {
89
templates?: any[];
910
path?: string;
1011
title?: string;
12+
match: any;
13+
createNewFolder: () => void;
1114
};
1215

13-
export const Header = ({ templates, path, title }: Props) => (
16+
export const HeaderComponent = ({
17+
createNewFolder,
18+
match,
19+
templates,
20+
path,
21+
title,
22+
}: Props) => (
1423
<Stack
1524
align="center"
1625
justify="space-between"
@@ -30,7 +39,25 @@ export const Header = ({ templates, path, title }: Props) => (
3039
) : (
3140
<Breadcrumbs param={path} />
3241
)}
33-
34-
{templates && <Filters possibleTemplates={templates} />}
42+
<Stack gap={4} align="center">
43+
{match.path.includes('all') && (
44+
<Button
45+
onClick={createNewFolder}
46+
variant="link"
47+
css={css({
48+
fontSize: 3,
49+
color: 'mutedForeground',
50+
padding: 0,
51+
width: 'auto',
52+
})}
53+
>
54+
+ New Folder
55+
</Button>
56+
)}
57+
{templates && <Filters possibleTemplates={templates} />}
58+
</Stack>
3559
</Stack>
3660
);
61+
62+
// @ts-ignore
63+
export const Header = withRouter(HeaderComponent);

packages/app/src/app/pages/NewDashboard/Components/Sandbox/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { Element } from '@codesandbox/components';
23
import { withRouter } from 'react-router-dom';
34
import { useOvermind } from 'app/overmind';
45
import { SandboxItem } from '../SandboxItem';
@@ -10,7 +11,11 @@ export const SandboxComponent = props => {
1011
} = useOvermind();
1112

1213
if (dashboard.viewMode === 'list' || props.match.path.includes('deleted')) {
13-
return <SandboxItem {...props} />;
14+
return (
15+
<Element style={{ gridColumn: '1/-1' }}>
16+
<SandboxItem {...props} />
17+
</Element>
18+
);
1419
}
1520
return <SandboxCard {...props} />;
1621
};

packages/app/src/app/pages/NewDashboard/Content/routes/All/index.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { SkeletonCard } from '../../../Components/SandboxCard';
1111

1212
export const AllPage = ({ match: { params }, history }) => {
1313
const [level, setLevel] = useState(0);
14+
const [creating, setCreating] = useState(false);
1415
const param = params.path || '';
1516
const cleanParam = param.split(' ').join('');
1617
const {
@@ -43,6 +44,12 @@ export const AllPage = ({ match: { params }, history }) => {
4344
}
4445
}, [param, actions.dashboard, activeTeam]);
4546

47+
const createNewFolder = (name: string) => {
48+
setCreating(false);
49+
const newPath = params.path ? `/${param}/${name}` : `${param}/${name}`;
50+
actions.dashboard.createFolder(newPath);
51+
};
52+
4653
const getFoldersByPath =
4754
allCollections &&
4855
allCollections.filter(
@@ -51,20 +58,21 @@ export const AllPage = ({ match: { params }, history }) => {
5158

5259
return (
5360
<Element style={{ height: '100%', position: 'relative' }}>
54-
<Header path={param} templates={getPossibleTemplates(allCollections)} />
61+
<Header
62+
path={param}
63+
templates={getPossibleTemplates(allCollections)}
64+
createNewFolder={() => setCreating(true)}
65+
/>
5566
{allCollections ? (
5667
<SandboxGrid>
68+
{creating && <FolderCard key="fake" newFolder={createNewFolder} />}
5769
{getFoldersByPath.map(folder => (
58-
<Column>
59-
<FolderCard key={folder.id} {...folder} />
60-
</Column>
70+
<FolderCard key={folder.id} {...folder} />
6171
))}
6272
{sandboxes.ALL &&
6373
sandboxes.ALL[cleanParam] &&
6474
sandboxes.ALL[cleanParam].map(sandbox => (
65-
<Column>
66-
<Sandbox key={sandbox.id} sandbox={sandbox} />
67-
</Column>
75+
<Sandbox key={sandbox.id} sandbox={sandbox} />
6876
))}
6977
</SandboxGrid>
7078
) : (

packages/app/src/app/pages/NewDashboard/Content/routes/Recent/SandboxesGroup.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ export const SandboxesGroup = ({ title, time }) => {
1919
</Text>
2020
<SandboxGrid>
2121
{getFilteredSandboxes(recentSandboxesByTime[time]).map(sandbox => (
22-
<Column>
23-
<Sandbox key={sandbox.id} sandbox={sandbox} />
24-
</Column>
22+
<Sandbox key={sandbox.id} sandbox={sandbox} />
2523
))}
2624
</SandboxGrid>
2725
</>

packages/app/src/app/pages/NewDashboard/Content/routes/Templates/index.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ export const Templates = () => {
2222

2323
return (
2424
<Element css={css({ position: 'relative' })}>
25-
<Header title="Templates" />
25+
<Header title="Templates" templates={[]} />
2626
{sandboxes.TEMPLATES ? (
2727
<SandboxGrid>
2828
{sandboxes.TEMPLATES.map(({ sandbox }) => (
29-
<Column>
30-
<Sandbox template sandbox={sandbox} key={sandbox.id} />
31-
</Column>
29+
<Sandbox template sandbox={sandbox} key={sandbox.id} />
3230
))}
3331
</SandboxGrid>
3432
) : (

0 commit comments

Comments
 (0)