alpha
Login
or
Join now
nandi.uk
/
semble
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
This repository has no description
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
chore: adjust drawer sizing
author
pdelfan
date
5 months ago
(Feb 26, 2026, 4:24 PM -0800)
commit
a907ef66
a907ef66d13d01a261378fd00a811f6afe4a342c
parent
76f2ca6c
76f2ca6c769cdbc6c8d56e5d95e2eb6047d41f93
+308
-411
3 changed files
Expand all
Collapse all
Unified
Split
src
webapp
features
cards
components
addCardDrawer
AddCardDrawer.tsx
AddCardForm.tsx
collections
components
createCollectionDrawer
CreateCollectionDrawer.tsx
+13
-274
src/webapp/features/cards/components/addCardDrawer/AddCardDrawer.tsx
View file
Reviewed
···
1
1
-
import {
2
2
-
Button,
3
3
-
Container,
4
4
-
Drawer,
5
5
-
Flex,
6
6
-
Group,
7
7
-
Input,
8
8
-
ScrollArea,
9
9
-
Stack,
10
10
-
Text,
11
11
-
Textarea,
12
12
-
TextInput,
13
13
-
ThemeIcon,
14
14
-
VisuallyHidden,
15
15
-
} from '@mantine/core';
16
16
-
import { useForm } from '@mantine/form';
17
17
-
import { notifications } from '@mantine/notifications';
18
18
-
import useAddCard from '../../lib/mutations/useAddCard';
19
19
-
import CollectionSelector from '@/features/collections/components/collectionSelector/CollectionSelector';
20
20
-
import { Suspense, useEffect, useState } from 'react';
21
21
-
import CollectionSelectorSkeleton from '@/features/collections/components/collectionSelector/Skeleton.CollectionSelector';
22
22
-
import { useDisclosure } from '@mantine/hooks';
23
23
-
import { BiCollection } from 'react-icons/bi';
24
24
-
import { IoMdCheckmark, IoMdLink } from 'react-icons/io';
1
1
+
'use client';
2
2
+
3
3
+
import { Container, Drawer } from '@mantine/core';
25
4
import { DEFAULT_OVERLAY_PROPS } from '@/styles/overlays';
26
26
-
import { track } from '@vercel/analytics';
27
27
-
import useMyCollections from '@/features/collections/lib/queries/useMyCollections';
28
28
-
import { isMarginUri, getMarginUrl } from '@/lib/utils/margin';
29
29
-
import MarginLogo from '@/components/MarginLogo';
30
30
-
import { Collection, CollectionAccessType } from '@semble/types';
31
31
-
import { FaSeedling } from 'react-icons/fa6';
32
32
-
import { CardSaveSource } from '@/features/analytics/types';
33
33
-
import { usePathname } from 'next/navigation';
5
5
+
import AddCardForm from './AddCardForm';
6
6
+
import { Collection } from '@semble/types';
34
7
35
8
interface Props {
36
9
isOpen: boolean;
···
40
13
}
41
14
42
15
export default function AddCardDrawer(props: Props) {
43
43
-
const pathname = usePathname();
44
44
-
const [collectionSelectorOpened, { toggle: toggleCollectionSelector }] =
45
45
-
useDisclosure(false);
46
46
-
const initialCollections = props.selectedCollection
47
47
-
? [props.selectedCollection]
48
48
-
: [];
49
49
-
const [selectedCollections, setSelectedCollections] =
50
50
-
useState(initialCollections);
51
51
-
52
52
-
const { data: collections } = useMyCollections({ limit: 30 });
53
53
-
const allCollections =
54
54
-
collections?.pages.flatMap((page) => page.collections ?? []) ?? [];
55
55
-
56
56
-
// Put selectedCollection first, then others
57
57
-
const myCollections = props.selectedCollection
58
58
-
? [
59
59
-
props.selectedCollection,
60
60
-
...allCollections.filter((c) => c.id !== props.selectedCollection?.id),
61
61
-
]
62
62
-
: allCollections;
63
63
-
64
64
-
const addCard = useAddCard({
65
65
-
saveSource: CardSaveSource.ADD_CARD_DRAWER,
66
66
-
pagePath: pathname,
67
67
-
});
68
68
-
69
69
-
const form = useForm({
70
70
-
initialValues: {
71
71
-
url: props.initialUrl || '',
72
72
-
note: '',
73
73
-
collections: selectedCollections,
74
74
-
},
75
75
-
});
76
76
-
77
77
-
const MAX_NOTE_LENGTH = 500;
78
78
-
const { note } = form.getValues();
79
79
-
80
80
-
useEffect(() => {
81
81
-
if (props.initialUrl) {
82
82
-
form.setValues({ url: props.initialUrl });
83
83
-
}
84
84
-
}, [props.initialUrl]);
85
85
-
86
86
-
const handleAddCard = (e: React.FormEvent) => {
87
87
-
e.preventDefault();
88
88
-
track('add new card');
89
89
-
90
90
-
addCard.mutate(
91
91
-
{
92
92
-
url: form.getValues().url,
93
93
-
note: form.getValues().note,
94
94
-
collectionIds: selectedCollections.map((c) => c.id),
95
95
-
},
96
96
-
{
97
97
-
onSuccess: () => {
98
98
-
setSelectedCollections(initialCollections);
99
99
-
props.onClose();
100
100
-
window.history.replaceState({}, '', window.location.pathname);
101
101
-
},
102
102
-
onError: () => {
103
103
-
notifications.show({
104
104
-
message: 'Could not add card.',
105
105
-
});
106
106
-
},
107
107
-
onSettled: () => {
108
108
-
form.reset();
109
109
-
},
110
110
-
},
111
111
-
);
112
112
-
};
113
113
-
114
16
return (
115
17
<Drawer
116
18
opened={props.isOpen}
117
117
-
onClose={() => {
118
118
-
props.onClose();
119
119
-
}}
19
19
+
onClose={props.onClose}
120
20
withCloseButton={false}
121
121
-
size={'29rem'}
21
21
+
size={'30.5rem'}
22
22
+
padding={'sm'}
122
23
position="bottom"
123
24
overlayProps={DEFAULT_OVERLAY_PROPS}
124
25
>
···
128
29
</Drawer.Title>
129
30
</Drawer.Header>
130
31
<Container size={'sm'} p={0}>
131
131
-
<form onSubmit={handleAddCard}>
132
132
-
<Stack gap={'lg'}>
133
133
-
<Stack>
134
134
-
<Stack>
135
135
-
<TextInput
136
136
-
id="url"
137
137
-
label="URL"
138
138
-
type="url"
139
139
-
placeholder="https://www.example.com"
140
140
-
variant="filled"
141
141
-
required
142
142
-
size="md"
143
143
-
leftSection={<IoMdLink size={22} />}
144
144
-
key={form.key('url')}
145
145
-
{...form.getInputProps('url')}
146
146
-
/>
147
147
-
148
148
-
<Stack gap={0}>
149
149
-
<Flex justify="space-between">
150
150
-
<Input.Label size="md" htmlFor="note">
151
151
-
Note
152
152
-
</Input.Label>
153
153
-
<Text c={'gray'} aria-hidden>
154
154
-
{form.getValues().note.length} / {MAX_NOTE_LENGTH}
155
155
-
</Text>
156
156
-
</Flex>
157
157
-
158
158
-
<Textarea
159
159
-
id="note"
160
160
-
placeholder="Add a note about this card"
161
161
-
variant="filled"
162
162
-
size="md"
163
163
-
rows={3}
164
164
-
maxLength={MAX_NOTE_LENGTH}
165
165
-
aria-describedby="note-char-remaining"
166
166
-
key={form.key('note')}
167
167
-
{...form.getInputProps('note')}
168
168
-
/>
169
169
-
<VisuallyHidden id="note-char-remaining" aria-live="polite">
170
170
-
{`${MAX_NOTE_LENGTH - form.getValues().note.length} characters remaining`}
171
171
-
</VisuallyHidden>
172
172
-
</Stack>
173
173
-
</Stack>
174
174
-
175
175
-
<Stack gap={5}>
176
176
-
<Text fw={500}>
177
177
-
Add to collections{' '}
178
178
-
{selectedCollections.length > 0 &&
179
179
-
`(${selectedCollections.length})`}
180
180
-
</Text>
181
181
-
<ScrollArea.Autosize
182
182
-
type="hover"
183
183
-
scrollbars="x"
184
184
-
offsetScrollbars={true}
185
185
-
>
186
186
-
<Group gap={'xs'} wrap="nowrap">
187
187
-
<Button
188
188
-
onClick={toggleCollectionSelector}
189
189
-
variant="light"
190
190
-
color={'blue'}
191
191
-
leftSection={<BiCollection size={22} />}
192
192
-
>
193
193
-
{myCollections.length === 0
194
194
-
? 'Create a collection'
195
195
-
: 'Manage & Create'}
196
196
-
</Button>
197
197
-
198
198
-
{myCollections.map((col) => {
199
199
-
const marginUrl = getMarginUrl(
200
200
-
col.uri,
201
201
-
col.author?.handle,
202
202
-
);
203
203
-
return (
204
204
-
<Button
205
205
-
key={col.id}
206
206
-
variant="light"
207
207
-
color={
208
208
-
selectedCollections.some((c) => c.id === col.id)
209
209
-
? 'grape'
210
210
-
: 'gray'
211
211
-
}
212
212
-
rightSection={
213
213
-
selectedCollections.some((c) => c.id === col.id) ? (
214
214
-
<IoMdCheckmark />
215
215
-
) : null
216
216
-
}
217
217
-
leftSection={
218
218
-
isMarginUri(col.uri) ? (
219
219
-
<MarginLogo size={12} marginUrl={marginUrl} />
220
220
-
) : col.accessType === CollectionAccessType.OPEN ? (
221
221
-
<ThemeIcon
222
222
-
variant="light"
223
223
-
radius={'xl'}
224
224
-
size={'xs'}
225
225
-
color="green"
226
226
-
>
227
227
-
<FaSeedling size={8} />
228
228
-
</ThemeIcon>
229
229
-
) : undefined
230
230
-
}
231
231
-
onClick={() => {
232
232
-
setSelectedCollections((prev) => {
233
233
-
// already selected, remove
234
234
-
if (prev.some((c) => c.id === col.id)) {
235
235
-
return prev.filter((c) => c.id !== col.id);
236
236
-
}
237
237
-
// not selected, add it
238
238
-
return [...prev, col];
239
239
-
});
240
240
-
}}
241
241
-
>
242
242
-
{col.name}
243
243
-
</Button>
244
244
-
);
245
245
-
})}
246
246
-
</Group>
247
247
-
</ScrollArea.Autosize>
248
248
-
</Stack>
249
249
-
250
250
-
<Drawer
251
251
-
opened={collectionSelectorOpened}
252
252
-
onClose={toggleCollectionSelector}
253
253
-
withCloseButton={false}
254
254
-
position="bottom"
255
255
-
size={'29rem'}
256
256
-
overlayProps={DEFAULT_OVERLAY_PROPS}
257
257
-
>
258
258
-
<Drawer.Header>
259
259
-
<Drawer.Title fz={'xl'} fw={600} mx={'auto'}>
260
260
-
Add to collections
261
261
-
</Drawer.Title>
262
262
-
</Drawer.Header>
263
263
-
<Container size={'xs'} p={0}>
264
264
-
<Suspense fallback={<CollectionSelectorSkeleton />}>
265
265
-
<CollectionSelector
266
266
-
isOpen={collectionSelectorOpened}
267
267
-
onCancel={() => {
268
268
-
setSelectedCollections(initialCollections);
269
269
-
toggleCollectionSelector();
270
270
-
}}
271
271
-
onClose={toggleCollectionSelector}
272
272
-
onSave={toggleCollectionSelector}
273
273
-
selectedCollections={selectedCollections}
274
274
-
onSelectedCollectionsChange={setSelectedCollections}
275
275
-
/>
276
276
-
</Suspense>
277
277
-
</Container>
278
278
-
</Drawer>
279
279
-
</Stack>
280
280
-
<Group justify="space-between" gap={'xs'} grow>
281
281
-
<Button
282
282
-
variant="light"
283
283
-
size="md"
284
284
-
color={'gray'}
285
285
-
onClick={() => {
286
286
-
props.onClose();
287
287
-
setSelectedCollections(initialCollections);
288
288
-
}}
289
289
-
>
290
290
-
Cancel
291
291
-
</Button>
292
292
-
<Button type="submit" size="md" loading={addCard.isPending}>
293
293
-
Add card
294
294
-
</Button>
295
295
-
</Group>
296
296
-
</Stack>
297
297
-
</form>
32
32
+
<AddCardForm
33
33
+
onClose={props.onClose}
34
34
+
selectedCollection={props.selectedCollection}
35
35
+
initialUrl={props.initialUrl}
36
36
+
/>
298
37
</Container>
299
38
</Drawer>
300
39
);
+282
src/webapp/features/cards/components/addCardDrawer/AddCardForm.tsx
View file
Reviewed
···
1
1
+
'use client';
2
2
+
3
3
+
import {
4
4
+
Button,
5
5
+
Drawer,
6
6
+
Flex,
7
7
+
Group,
8
8
+
Input,
9
9
+
ScrollArea,
10
10
+
Stack,
11
11
+
Text,
12
12
+
Textarea,
13
13
+
TextInput,
14
14
+
ThemeIcon,
15
15
+
VisuallyHidden,
16
16
+
Container,
17
17
+
} from '@mantine/core';
18
18
+
import { useForm } from '@mantine/form';
19
19
+
import { notifications } from '@mantine/notifications';
20
20
+
import useAddCard from '../../lib/mutations/useAddCard';
21
21
+
import CollectionSelector from '@/features/collections/components/collectionSelector/CollectionSelector';
22
22
+
import { Suspense, useEffect, useState } from 'react';
23
23
+
import CollectionSelectorSkeleton from '@/features/collections/components/collectionSelector/Skeleton.CollectionSelector';
24
24
+
import { useDisclosure } from '@mantine/hooks';
25
25
+
import { BiCollection } from 'react-icons/bi';
26
26
+
import { IoMdCheckmark, IoMdLink } from 'react-icons/io';
27
27
+
import { DEFAULT_OVERLAY_PROPS } from '@/styles/overlays';
28
28
+
import { track } from '@vercel/analytics';
29
29
+
import useMyCollections from '@/features/collections/lib/queries/useMyCollections';
30
30
+
import { isMarginUri, getMarginUrl } from '@/lib/utils/margin';
31
31
+
import MarginLogo from '@/components/MarginLogo';
32
32
+
import { Collection, CollectionAccessType } from '@semble/types';
33
33
+
import { FaSeedling } from 'react-icons/fa6';
34
34
+
import { CardSaveSource } from '@/features/analytics/types';
35
35
+
import { usePathname } from 'next/navigation';
36
36
+
37
37
+
interface Props {
38
38
+
onClose: () => void;
39
39
+
selectedCollection?: Collection;
40
40
+
initialUrl?: string;
41
41
+
}
42
42
+
43
43
+
export default function AddCardForm(props: Props) {
44
44
+
const pathname = usePathname();
45
45
+
const [collectionSelectorOpened, { toggle: toggleCollectionSelector }] =
46
46
+
useDisclosure(false);
47
47
+
const initialCollections = props.selectedCollection
48
48
+
? [props.selectedCollection]
49
49
+
: [];
50
50
+
const [selectedCollections, setSelectedCollections] =
51
51
+
useState(initialCollections);
52
52
+
53
53
+
const { data: collections } = useMyCollections({ limit: 30 });
54
54
+
const allCollections =
55
55
+
collections?.pages.flatMap((page) => page.collections ?? []) ?? [];
56
56
+
57
57
+
// Put selectedCollection first, then others
58
58
+
const myCollections = props.selectedCollection
59
59
+
? [
60
60
+
props.selectedCollection,
61
61
+
...allCollections.filter((c) => c.id !== props.selectedCollection?.id),
62
62
+
]
63
63
+
: allCollections;
64
64
+
65
65
+
const addCard = useAddCard({
66
66
+
saveSource: CardSaveSource.ADD_CARD_DRAWER,
67
67
+
pagePath: pathname,
68
68
+
});
69
69
+
70
70
+
const form = useForm({
71
71
+
initialValues: {
72
72
+
url: props.initialUrl || '',
73
73
+
note: '',
74
74
+
collections: selectedCollections,
75
75
+
},
76
76
+
});
77
77
+
78
78
+
const MAX_NOTE_LENGTH = 500;
79
79
+
80
80
+
useEffect(() => {
81
81
+
if (props.initialUrl) {
82
82
+
form.setValues({ url: props.initialUrl });
83
83
+
}
84
84
+
}, [props.initialUrl]);
85
85
+
86
86
+
const handleAddCard = (e: React.FormEvent) => {
87
87
+
e.preventDefault();
88
88
+
track('add new card');
89
89
+
90
90
+
addCard.mutate(
91
91
+
{
92
92
+
url: form.getValues().url,
93
93
+
note: form.getValues().note,
94
94
+
collectionIds: selectedCollections.map((c) => c.id),
95
95
+
},
96
96
+
{
97
97
+
onSuccess: () => {
98
98
+
setSelectedCollections(initialCollections);
99
99
+
props.onClose();
100
100
+
window.history.replaceState({}, '', window.location.pathname);
101
101
+
},
102
102
+
onError: () => {
103
103
+
notifications.show({
104
104
+
message: 'Could not add card.',
105
105
+
});
106
106
+
},
107
107
+
onSettled: () => {
108
108
+
form.reset();
109
109
+
},
110
110
+
},
111
111
+
);
112
112
+
};
113
113
+
114
114
+
return (
115
115
+
<>
116
116
+
<form onSubmit={handleAddCard}>
117
117
+
<Stack gap={'xl'}>
118
118
+
<TextInput
119
119
+
id="url"
120
120
+
label="URL"
121
121
+
type="url"
122
122
+
placeholder="https://www.example.com"
123
123
+
variant="filled"
124
124
+
required
125
125
+
size="md"
126
126
+
leftSection={<IoMdLink size={22} />}
127
127
+
key={form.key('url')}
128
128
+
{...form.getInputProps('url')}
129
129
+
/>
130
130
+
131
131
+
<Stack gap={0}>
132
132
+
<Flex justify="space-between">
133
133
+
<Input.Label size="md" htmlFor="note">
134
134
+
Note
135
135
+
</Input.Label>
136
136
+
<Text c={'gray'} aria-hidden>
137
137
+
{form.getValues().note.length} / {MAX_NOTE_LENGTH}
138
138
+
</Text>
139
139
+
</Flex>
140
140
+
141
141
+
<Textarea
142
142
+
id="note"
143
143
+
placeholder="Add a note about this card"
144
144
+
variant="filled"
145
145
+
size="md"
146
146
+
rows={3}
147
147
+
maxLength={MAX_NOTE_LENGTH}
148
148
+
aria-describedby="note-char-remaining"
149
149
+
key={form.key('note')}
150
150
+
{...form.getInputProps('note')}
151
151
+
/>
152
152
+
<VisuallyHidden id="note-char-remaining" aria-live="polite">
153
153
+
{`${MAX_NOTE_LENGTH - form.getValues().note.length} characters remaining`}
154
154
+
</VisuallyHidden>
155
155
+
</Stack>
156
156
+
157
157
+
<Stack gap={5}>
158
158
+
<Text fw={500}>
159
159
+
Add to collections{' '}
160
160
+
{selectedCollections.length > 0 &&
161
161
+
`(${selectedCollections.length})`}
162
162
+
</Text>
163
163
+
<ScrollArea.Autosize
164
164
+
type="hover"
165
165
+
scrollbars="x"
166
166
+
offsetScrollbars={true}
167
167
+
>
168
168
+
<Group gap={'xs'} wrap="nowrap">
169
169
+
<Button
170
170
+
disabled={addCard.isPending}
171
171
+
onClick={toggleCollectionSelector}
172
172
+
variant="light"
173
173
+
color={'blue'}
174
174
+
leftSection={<BiCollection size={22} />}
175
175
+
>
176
176
+
{myCollections.length === 0
177
177
+
? 'Create a collection'
178
178
+
: 'Manage & Create'}
179
179
+
</Button>
180
180
+
181
181
+
{myCollections.map((col) => {
182
182
+
const marginUrl = getMarginUrl(col.uri, col.author?.handle);
183
183
+
return (
184
184
+
<Button
185
185
+
key={col.id}
186
186
+
disabled={addCard.isPending}
187
187
+
variant="light"
188
188
+
color={
189
189
+
selectedCollections.some((c) => c.id === col.id)
190
190
+
? 'grape'
191
191
+
: 'gray'
192
192
+
}
193
193
+
rightSection={
194
194
+
selectedCollections.some((c) => c.id === col.id) ? (
195
195
+
<IoMdCheckmark />
196
196
+
) : null
197
197
+
}
198
198
+
leftSection={
199
199
+
isMarginUri(col.uri) ? (
200
200
+
<MarginLogo size={12} marginUrl={marginUrl} />
201
201
+
) : col.accessType === CollectionAccessType.OPEN ? (
202
202
+
<ThemeIcon
203
203
+
variant="light"
204
204
+
radius={'xl'}
205
205
+
size={'xs'}
206
206
+
color="green"
207
207
+
>
208
208
+
<FaSeedling size={8} />
209
209
+
</ThemeIcon>
210
210
+
) : undefined
211
211
+
}
212
212
+
onClick={() => {
213
213
+
setSelectedCollections((prev) => {
214
214
+
// already selected, remove
215
215
+
if (prev.some((c) => c.id === col.id)) {
216
216
+
return prev.filter((c) => c.id !== col.id);
217
217
+
}
218
218
+
// not selected, add it
219
219
+
return [...prev, col];
220
220
+
});
221
221
+
}}
222
222
+
>
223
223
+
{col.name}
224
224
+
</Button>
225
225
+
);
226
226
+
})}
227
227
+
</Group>
228
228
+
</ScrollArea.Autosize>
229
229
+
</Stack>
230
230
+
231
231
+
<Group justify="space-between" gap={'xs'} grow>
232
232
+
<Button
233
233
+
variant="light"
234
234
+
size="md"
235
235
+
color={'gray'}
236
236
+
onClick={() => {
237
237
+
props.onClose();
238
238
+
setSelectedCollections(initialCollections);
239
239
+
}}
240
240
+
>
241
241
+
Cancel
242
242
+
</Button>
243
243
+
<Button type="submit" size="md" loading={addCard.isPending}>
244
244
+
Add card
245
245
+
</Button>
246
246
+
</Group>
247
247
+
</Stack>
248
248
+
</form>
249
249
+
250
250
+
<Drawer
251
251
+
opened={collectionSelectorOpened}
252
252
+
onClose={toggleCollectionSelector}
253
253
+
withCloseButton={false}
254
254
+
position="bottom"
255
255
+
padding={'sm'}
256
256
+
size={'30rem'}
257
257
+
overlayProps={DEFAULT_OVERLAY_PROPS}
258
258
+
>
259
259
+
<Drawer.Header>
260
260
+
<Drawer.Title fz={'xl'} fw={600} mx={'auto'}>
261
261
+
Add to collections
262
262
+
</Drawer.Title>
263
263
+
</Drawer.Header>
264
264
+
<Container size={'xs'} p={0}>
265
265
+
<Suspense fallback={<CollectionSelectorSkeleton />}>
266
266
+
<CollectionSelector
267
267
+
isOpen={collectionSelectorOpened}
268
268
+
onCancel={() => {
269
269
+
setSelectedCollections(initialCollections);
270
270
+
toggleCollectionSelector();
271
271
+
}}
272
272
+
onClose={toggleCollectionSelector}
273
273
+
onSave={toggleCollectionSelector}
274
274
+
selectedCollections={selectedCollections}
275
275
+
onSelectedCollectionsChange={setSelectedCollections}
276
276
+
/>
277
277
+
</Suspense>
278
278
+
</Container>
279
279
+
</Drawer>
280
280
+
</>
281
281
+
);
282
282
+
}
+13
-137
src/webapp/features/collections/components/createCollectionDrawer/CreateCollectionDrawer.tsx
View file
Reviewed
···
1
1
+
'use client';
2
2
+
1
3
import { CollectionAccessType } from '@semble/types';
2
2
-
import {
3
3
-
Button,
4
4
-
Container,
5
5
-
Drawer,
6
6
-
Group,
7
7
-
Select,
8
8
-
Stack,
9
9
-
Textarea,
10
10
-
TextInput,
11
11
-
ThemeIcon,
12
12
-
} from '@mantine/core';
13
13
-
import { useForm } from '@mantine/form';
14
14
-
import useCreateCollection from '../../lib/mutations/useCreateCollection';
15
15
-
import { notifications } from '@mantine/notifications';
4
4
+
import { Container, Drawer } from '@mantine/core';
16
5
import { DEFAULT_OVERLAY_PROPS } from '@/styles/overlays';
17
17
-
import { FaSeedling } from 'react-icons/fa6';
6
6
+
import CreateCollectionForm from './CreateCollectionForm';
18
7
19
8
interface Props {
20
9
isOpen: boolean;
···
24
13
onCreate?: () => void;
25
14
}
26
15
27
27
-
export default function createCollectionDrawer(props: Props) {
28
28
-
const createCollection = useCreateCollection();
29
29
-
const form = useForm({
30
30
-
initialValues: {
31
31
-
name: props.initialName ?? '',
32
32
-
description: '',
33
33
-
accessType: props.initialAccessType ?? CollectionAccessType.CLOSED,
34
34
-
},
35
35
-
});
36
36
-
37
37
-
const handleCreateCollection = (e: React.FormEvent) => {
38
38
-
e.preventDefault();
39
39
-
e.stopPropagation();
40
40
-
41
41
-
createCollection.mutate(
42
42
-
{
43
43
-
name: form.getValues().name,
44
44
-
description: form.getValues().description,
45
45
-
accessType: form.getValues().accessType,
46
46
-
},
47
47
-
{
48
48
-
onSuccess: () => {
49
49
-
props.onClose();
50
50
-
if (props.onCreate) {
51
51
-
props.onCreate();
52
52
-
}
53
53
-
},
54
54
-
onError: () => {
55
55
-
notifications.show({
56
56
-
message: 'Could not create collection.',
57
57
-
});
58
58
-
},
59
59
-
onSettled: () => {
60
60
-
form.reset();
61
61
-
},
62
62
-
},
63
63
-
);
64
64
-
};
65
65
-
16
16
+
export default function CreateCollectionDrawer(props: Props) {
66
17
return (
67
18
<Drawer
68
19
opened={props.isOpen}
69
69
-
onClose={() => {
70
70
-
props.onClose();
71
71
-
form.reset();
72
72
-
}}
20
20
+
onClose={props.onClose}
73
21
withCloseButton={false}
74
22
size={'31rem'}
23
23
+
padding={'sm'}
75
24
position="bottom"
76
25
overlayProps={DEFAULT_OVERLAY_PROPS}
77
26
>
···
82
31
</Drawer.Header>
83
32
84
33
<Container size={'sm'} p={0}>
85
85
-
<form onSubmit={handleCreateCollection}>
86
86
-
<Stack>
87
87
-
<Stack gap={'xl'}>
88
88
-
<TextInput
89
89
-
id="name"
90
90
-
label="Name"
91
91
-
type="text"
92
92
-
placeholder="Collection name"
93
93
-
variant="filled"
94
94
-
size="md"
95
95
-
required
96
96
-
maxLength={100}
97
97
-
key={form.key('name')}
98
98
-
{...form.getInputProps('name')}
99
99
-
/>
100
100
-
101
101
-
<Textarea
102
102
-
id="description"
103
103
-
label="Description"
104
104
-
placeholder="Describe what this collection is about"
105
105
-
variant="filled"
106
106
-
size="md"
107
107
-
rows={3}
108
108
-
maxLength={500}
109
109
-
key={form.key('description')}
110
110
-
{...form.getInputProps('description')}
111
111
-
/>
112
112
-
113
113
-
<Select
114
114
-
variant="filled"
115
115
-
size="md"
116
116
-
label="Collaboration"
117
117
-
leftSection={
118
118
-
form.getValues().accessType === CollectionAccessType.OPEN ? (
119
119
-
<ThemeIcon
120
120
-
size={'md'}
121
121
-
variant="light"
122
122
-
color={'green'}
123
123
-
radius={'xl'}
124
124
-
>
125
125
-
<FaSeedling size={14} />
126
126
-
</ThemeIcon>
127
127
-
) : null
128
128
-
}
129
129
-
allowDeselect={false}
130
130
-
defaultValue={CollectionAccessType.CLOSED}
131
131
-
data={[
132
132
-
{
133
133
-
value: CollectionAccessType.CLOSED,
134
134
-
label: 'Personal — Only you can add',
135
135
-
},
136
136
-
{
137
137
-
value: CollectionAccessType.OPEN,
138
138
-
label: 'Open — Anyone can add',
139
139
-
},
140
140
-
]}
141
141
-
{...form.getInputProps('accessType')}
142
142
-
/>
143
143
-
144
144
-
<Group justify="space-between" gap={'xs'} grow>
145
145
-
<Button
146
146
-
variant="light"
147
147
-
size="md"
148
148
-
color={'gray'}
149
149
-
onClick={props.onClose}
150
150
-
>
151
151
-
Cancel
152
152
-
</Button>
153
153
-
<Button
154
154
-
type="submit"
155
155
-
size="md"
156
156
-
loading={createCollection.isPending}
157
157
-
>
158
158
-
Create
159
159
-
</Button>
160
160
-
</Group>
161
161
-
</Stack>
162
162
-
</Stack>
163
163
-
</form>
34
34
+
<CreateCollectionForm
35
35
+
onClose={props.onClose}
36
36
+
initialName={props.initialName}
37
37
+
initialAccessType={props.initialAccessType}
38
38
+
onCreate={props.onCreate}
39
39
+
/>
164
40
</Container>
165
41
</Drawer>
166
42
);