1
- import { useCallback , useEffect , useReducer , useRef , useState } from "react" ;
2
- import { Modal } from "../../../components/Modal/Modal" ;
3
- import { cn } from "../../../utils/bem" ;
1
+ import { ff } from "@humansignal/core" ;
2
+ import { SampleDatasetSelect } from "@humansignal/core/blocks/SampleDatasetSelect/SampleDatasetSelect" ;
3
+ import { IconError , IconFileUpload , IconInfo , IconTrash , IconUpload } from "@humansignal/icons" ;
4
+ import { Badge } from "@humansignal/shad/components/ui/badge" ;
4
5
import { cn as scn } from "@humansignal/shad/utils" ;
5
6
import { CodeBlock , SimpleCard } from "@humansignal/ui" ;
6
- import { unique } from "../../../utils/helpers" ;
7
- import "./Import.scss" ;
8
- import { IconError , IconFileUpload , IconInfo , IconUpload } from "@humansignal/icons" ;
9
- import { useAPI } from "../../../providers/ApiProvider" ;
10
- import Input from "libs/datamanager/src/components/Common/Input/Input" ;
11
7
import { Button } from "apps/labelstudio/src/components" ;
12
8
import { useAtomValue } from "jotai" ;
9
+ import Input from "libs/datamanager/src/components/Common/Input/Input" ;
10
+ import { useCallback , useEffect , useReducer , useRef , useState } from "react" ;
11
+ import { Modal } from "../../../components/Modal/Modal" ;
12
+ import { useAPI } from "../../../providers/ApiProvider" ;
13
+ import { cn } from "../../../utils/bem" ;
14
+ import { unique } from "../../../utils/helpers" ;
13
15
import { sampleDatasetAtom } from "../utils/atoms" ;
14
- import { ff } from "@humansignal/core" ;
15
-
16
- const testCode = `
17
- import { SimpleCard } from "../simple-card";
18
-
19
- export function CodeBlock({
20
- code,
21
- title,
22
- description,
23
- className,
24
- }: {
25
- title?: string;
26
- description?: string;
27
- code: string;
28
- className?: string;
29
- }) {
30
- return (
31
- <SimpleCard title={title} description={description} className={className}>
32
- <div className="whitespace-pre-wrap font-mono mt-2 p-3 bg-gray-100 rounded-sm">{code}</div>
33
- </SimpleCard>
34
- );
35
- }
36
- ` ;
16
+ import "./Import.scss" ;
17
+ import samples from "./samples.json" ;
18
+ import { importFiles } from "./utils" ;
37
19
38
20
const importClass = cn ( "upload_page" ) ;
39
21
const dropzoneClass = cn ( "dropzone" ) ;
@@ -175,9 +157,11 @@ const ErrorMessage = ({ error }) => {
175
157
176
158
export const ImportPage = ( {
177
159
project,
160
+ sample,
178
161
show = true ,
179
162
onWaiting,
180
163
onFileListUpdate,
164
+ onSampleDatasetSelect,
181
165
highlightCsvHandling,
182
166
dontCommitToProject = false ,
183
167
csvHandling,
@@ -211,7 +195,7 @@ export const ImportPage = ({
211
195
} ;
212
196
213
197
const [ files , dispatch ] = useReducer ( processFiles , { uploaded : [ ] , uploading : [ ] , ids : [ ] } ) ;
214
- const showList = Boolean ( files . uploaded ?. length || files . uploading ?. length ) ;
198
+ const showList = Boolean ( files . uploaded ?. length || files . uploading ?. length || sample ) ;
215
199
216
200
const loadFilesList = useCallback (
217
201
async ( file_upload_ids ) => {
@@ -267,27 +251,18 @@ export const ImportPage = ({
267
251
[ addColumns , loadFilesList , setLoading ] ,
268
252
) ;
269
253
270
- const importFiles = useCallback (
254
+ const importFilesImmediately = useCallback (
271
255
async ( files , body ) => {
272
- dispatch ( { sending : files } ) ;
273
-
274
- const query = dontCommitToProject ? { commit_to_project : "false" } : { } ;
275
- // @todo use json for dataset uploads by URL
276
- const contentType =
277
- body instanceof FormData
278
- ? "multipart/form-data" // usual multipart for usual files
279
- : "application/x-www-form-urlencoded" ; // chad urlencoded for URL uploads
280
- const res = await api . callApi ( "importFiles" , {
281
- params : { pk : project . id , ...query } ,
282
- headers : { "Content-Type" : contentType } ,
256
+ importFiles ( {
257
+ files,
283
258
body,
284
- errorFilter : ( ) => true ,
259
+ project,
260
+ onError,
261
+ onFinish,
262
+ onUploadStart : ( files ) => dispatch ( { sending : files } ) ,
263
+ onUploadFinish : ( files ) => dispatch ( { sent : files } ) ,
264
+ dontCommitToProject,
285
265
} ) ;
286
-
287
- if ( res && ! res . error ) onFinish ?. ( res ) ;
288
- else onError ?. ( res ?. response ) ;
289
-
290
- dispatch ( { sent : files } ) ;
291
266
} ,
292
267
[ project , onFinish ] ,
293
268
) ;
@@ -306,9 +281,9 @@ export const ImportPage = ({
306
281
}
307
282
fd . append ( f . name , f ) ;
308
283
}
309
- return importFiles ( files , fd ) ;
284
+ return importFilesImmediately ( files , fd ) ;
310
285
} ,
311
- [ importFiles , onStart ] ,
286
+ [ importFilesImmediately , onStart ] ,
312
287
) ;
313
288
314
289
const onUpload = useCallback (
@@ -333,9 +308,9 @@ export const ImportPage = ({
333
308
onWaiting ?. ( true ) ;
334
309
const body = new URLSearchParams ( { url } ) ;
335
310
336
- importFiles ( [ { name : url } ] , body ) ;
311
+ importFilesImmediately ( [ { name : url } ] , body ) ;
337
312
} ,
338
- [ importFiles ] ,
313
+ [ importFilesImmediately ] ,
339
314
) ;
340
315
341
316
const openConfig = useCallback (
@@ -375,7 +350,7 @@ export const ImportPage = ({
375
350
{ highlightCsvHandling && < div className = { importClass . elem ( "csv-splash" ) } /> }
376
351
< input id = "file-input" type = "file" name = "file" multiple onChange = { onUpload } style = { { display : "none" } } />
377
352
378
- < header >
353
+ < header className = "flex gap-4" >
379
354
< form className = { `${ importClass . elem ( "url-form" ) } inline-flex` } method = "POST" onSubmit = { onLoadURL } >
380
355
< Input placeholder = "Dataset URL" name = "url" ref = { urlRef } style = { { height : 40 } } />
381
356
< Button type = "submit" look = "primary" >
@@ -391,6 +366,9 @@ export const ImportPage = ({
391
366
< IconUpload width = "16" height = "16" className = { importClass . elem ( "upload-icon" ) } />
392
367
Upload { files . uploaded . length ? "More " : "" } Files
393
368
</ Button >
369
+ { ff . isActive ( ff . FF_SAMPLE_DATASETS ) && (
370
+ < SampleDatasetSelect samples = { samples } sample = { sample } onSampleApplied = { onSampleDatasetSelect } />
371
+ ) }
394
372
< div
395
373
className = { importClass . elem ( "csv-handling" ) . mod ( { highlighted : highlightCsvHandling , hidden : ! csvHandling } ) }
396
374
>
@@ -475,10 +453,33 @@ export const ImportPage = ({
475
453
{ showList && (
476
454
< table >
477
455
< tbody >
456
+ { sample && (
457
+ < tr key = { sample . url } >
458
+ < td >
459
+ < div className = "flex items-center gap-2" >
460
+ { sample . title }
461
+ < Badge variant = "info" className = "h-5 text-xs rounded-sm" >
462
+ Sample
463
+ </ Badge >
464
+ </ div >
465
+ </ td >
466
+ < td > { sample . description } </ td >
467
+ < td >
468
+ < Button
469
+ size = "icon"
470
+ look = "destructive"
471
+ style = { { height : 26 , width : 26 , padding : 0 } }
472
+ onClick = { ( ) => onSampleDatasetSelect ( undefined ) }
473
+ >
474
+ < IconTrash style = { { width : 12 , height : 12 } } />
475
+ </ Button >
476
+ </ td >
477
+ </ tr >
478
+ ) }
478
479
{ files . uploading . map ( ( file , idx ) => (
479
480
< tr key = { `${ idx } -${ file . name } ` } >
480
481
< td > { file . name } </ td >
481
- < td >
482
+ < td colSpan = { 2 } >
482
483
< span className = { importClass . elem ( "file-status" ) . mod ( { uploading : true } ) } />
483
484
</ td >
484
485
</ tr >
0 commit comments