11<template >
2- <div ref =" modalElement" class =" modal fade" tabindex =" -1" role =" dialog" aria-hidden =" true" >
3- <div class =" modal-dialog modal-lg" role =" document" >
4- <div class =" modal-content" >
5- <div class =" modal-header" >
6- <h5 class =" modal-title" >Export Sample and Related Items</h5 >
7- <button type =" button" class =" close" data-dismiss =" modal" aria-label =" Close" >
8- <span aria-hidden =" true" >× ; </span >
9- </button >
2+ <Modal v-model =" isModalOpen" :is-large =" true" >
3+ <template #header >Export Sample and Related Items</template >
4+
5+ <template #body >
6+ <div v-if =" isLoading" class =" text-center" >
7+ <i class =" fa fa-spinner fa-spin fa-2x" ></i >
8+ <p class =" mt-2" >Loading related samples...</p >
9+ </div >
10+ <div v-else-if =" relatedSamples.length === 0" >
11+ <p >No related samples found for this item.</p >
12+ </div >
13+ <div v-else >
14+ <p >Select the samples you want to include in the export:</p >
15+ <div class =" form-check" >
16+ <input
17+ id =" select-all"
18+ v-model =" selectAll"
19+ type =" checkbox"
20+ class =" form-check-input"
21+ @change =" toggleSelectAll"
22+ />
23+ <label class =" form-check-label" for =" select-all" >
24+ <strong >Select All</strong >
25+ </label >
1026 </div >
11- <div class =" modal-body" >
12- <div v-if =" isLoading" class =" text-center" >
13- <i class =" fa fa-spinner fa-spin fa-2x" ></i >
14- <p class =" mt-2" >Loading related samples...</p >
15- </div >
16- <div v-else-if =" relatedSamples.length === 0" >
17- <p >No related samples found for this item.</p >
27+ <hr />
28+ <div v-for =" sample in relatedSamples" :key =" sample.id" class =" form-check" >
29+ <input
30+ :id =" `sample-${sample.id}`"
31+ v-model =" selectedSampleIds"
32+ type =" checkbox"
33+ class =" form-check-input"
34+ :value =" sample.id"
35+ />
36+ <label class =" form-check-label" :for =" `sample-${sample.id}`" >
37+ {{ sample.name }} ({{ sample.id }})
38+ <span v-if =" sample.type" class =" badge badge-secondary ml-2" >
39+ {{ sample.type }}
40+ </span >
41+ </label >
42+ </div >
43+ <hr />
44+ <div class =" form-check" >
45+ <input
46+ id =" create-collection"
47+ v-model =" createCollection"
48+ type =" checkbox"
49+ class =" form-check-input"
50+ />
51+ <label class =" form-check-label" for =" create-collection" >
52+ <strong >Create a collection from selected samples</strong >
53+ </label >
54+ </div >
55+ <div v-if =" createCollection" class =" mt-3" >
56+ <div class =" form-group" >
57+ <label for =" collection-id" >Collection ID</label >
58+ <input
59+ id =" collection-id"
60+ v-model =" collectionId"
61+ type =" text"
62+ class =" form-control"
63+ placeholder =" Enter collection ID"
64+ />
1865 </div >
19- <div v-else >
20- <p >Select the samples you want to include in the export:</p >
21- <div class =" form-check" >
22- <input
23- id =" select-all"
24- v-model =" selectAll"
25- type =" checkbox"
26- class =" form-check-input"
27- @change =" toggleSelectAll"
28- />
29- <label class =" form-check-label" for =" select-all" >
30- <strong >Select All</strong >
31- </label >
32- </div >
33- <hr />
34- <div v-for =" sample in relatedSamples" :key =" sample.id" class =" form-check" >
35- <input
36- :id =" `sample-${sample.id}`"
37- v-model =" selectedSampleIds"
38- type =" checkbox"
39- class =" form-check-input"
40- :value =" sample.id"
41- />
42- <label class =" form-check-label" :for =" `sample-${sample.id}`" >
43- {{ sample.name }} ({{ sample.id }})
44- <span v-if =" sample.type" class =" badge badge-secondary ml-2" >
45- {{ sample.type }}
46- </span >
47- </label >
48- </div >
66+ <div class =" form-group" >
67+ <label for =" collection-title" >Collection Title</label >
68+ <input
69+ id =" collection-title"
70+ v-model =" collectionTitle"
71+ type =" text"
72+ class =" form-control"
73+ placeholder =" Enter collection title"
74+ />
4975 </div >
5076 </div >
51- <div class =" modal-footer" >
52- <button type =" button" class =" btn btn-secondary" data-dismiss =" modal" >Cancel</button >
53- <button
54- type =" button"
55- class =" btn btn-primary"
56- :disabled =" isExporting || selectedSampleIds.length === 0"
57- @click =" handleExport"
58- >
59- <span v-if =" !isExporting" >
60- <i class =" fa fa-download" ></i > Export Selected ({{ selectedSampleIds.length }})
61- </span >
62- <span v-else > <i class =" fa fa-spinner fa-spin" ></i > Exporting... </span >
63- </button >
64- </div >
6577 </div >
66- </div >
67- </div >
78+ </template >
79+
80+ <template #footer >
81+ <button
82+ type =" button"
83+ class =" btn btn-primary"
84+ :disabled ="
85+ isExporting ||
86+ selectedSampleIds.length === 0 ||
87+ (createCollection && (!collectionId || !collectionTitle))
88+ "
89+ @click =" handleExport"
90+ >
91+ <span v-if =" !isExporting" >
92+ <i class =" fa fa-download" ></i > Export Selected ({{ selectedSampleIds.length }})
93+ </span >
94+ <span v-else > <i class =" fa fa-spinner fa-spin" ></i > Exporting... </span >
95+ </button >
96+ <button type =" button" class =" btn btn-secondary" @click =" isModalOpen = false" >Cancel</button >
97+ </template >
98+ </Modal >
6899</template >
69100
70101<script >
102+ import Modal from " @/components/Modal" ;
71103import {
72104 getItemGraph ,
73105 startSampleExport ,
74106 getExportStatus ,
75107 getExportDownloadUrl ,
108+ createNewCollection ,
76109} from " @/server_fetch_utils" ;
77110import { DialogService } from " @/services/DialogService" ;
78111
79112export default {
80113 name: " SampleGraphExportModal" ,
114+ components: {
115+ Modal,
116+ },
81117 props: {
82118 itemId: {
83119 type: String ,
@@ -86,25 +122,32 @@ export default {
86122 },
87123 data () {
88124 return {
125+ isModalOpen: false ,
89126 isLoading: false ,
90127 isExporting: false ,
91128 relatedSamples: [],
92129 selectedSampleIds: [],
93130 selectAll: false ,
94131 pollInterval: null ,
95- modalInstance: null ,
132+ createCollection: false ,
133+ collectionId: " " ,
134+ collectionTitle: " " ,
96135 };
97136 },
98- mounted () {
99- this .modalInstance = new window.bootstrap.Modal (this .$refs .modalElement );
137+ watch: {
138+ isModalOpen (newValue ) {
139+ if (newValue) {
140+ this .loadRelatedSamples ();
141+ } else {
142+ this .relatedSamples = [];
143+ this .selectedSampleIds = [];
144+ }
145+ },
100146 },
101147 beforeUnmount () {
102148 if (this .pollInterval ) {
103149 clearInterval (this .pollInterval );
104150 }
105- if (this .modalInstance ) {
106- this .modalInstance .dispose ();
107- }
108151 },
109152 methods: {
110153 async loadRelatedSamples () {
@@ -122,6 +165,9 @@ export default {
122165
123166 this .selectedSampleIds = this .relatedSamples .map ((s ) => s .id );
124167 this .selectAll = true ;
168+ this .createCollection = false ;
169+ this .collectionId = ` ${ this .itemId } -collection` ;
170+ this .collectionTitle = ` ${ this .itemId } and related samples` ;
125171 } catch (error) {
126172 console .error (" Failed to load related samples:" , error);
127173 DialogService .error ({
@@ -145,6 +191,40 @@ export default {
145191 try {
146192 this .isExporting = true ;
147193
194+ if (this .createCollection ) {
195+ if (! this .collectionId .trim () || ! this .collectionTitle .trim ()) {
196+ DialogService .error ({
197+ title: " Validation Error" ,
198+ message: " Please enter both collection ID and title." ,
199+ });
200+ this .isExporting = false ;
201+ return ;
202+ }
203+
204+ try {
205+ const startingMembers = [
206+ { item_id: this .itemId },
207+ ... this .selectedSampleIds .map ((id ) => ({ item_id: id })),
208+ ];
209+
210+ await createNewCollection (this .collectionId , this .collectionTitle , {
211+ starting_members: startingMembers,
212+ });
213+
214+ DialogService .alert ({
215+ title: " Collection Created" ,
216+ message: ` Collection "${ this .collectionTitle } " created successfully with ${ startingMembers .length } items.` ,
217+ type: " info" ,
218+ });
219+ } catch (error) {
220+ console .error (" Failed to create collection:" , error);
221+ DialogService .error ({
222+ title: " Collection Creation Failed" ,
223+ message: " Failed to create collection. Continuing with export..." ,
224+ });
225+ }
226+ }
227+
148228 const response = await startSampleExport (this .itemId , {
149229 include_related: true ,
150230 related_item_ids: this .selectedSampleIds ,
@@ -171,7 +251,7 @@ export default {
171251 clearInterval (this .pollInterval );
172252 this .downloadExport (taskId);
173253 this .isExporting = false ;
174- this .modalInstance . hide () ;
254+ this .isModalOpen = false ;
175255 DialogService .alert ({
176256 title: " Export Complete" ,
177257 message: " Your samples have been exported successfully." ,
@@ -204,8 +284,7 @@ export default {
204284 },
205285
206286 show () {
207- this .modalInstance .show ();
208- this .loadRelatedSamples ();
287+ this .isModalOpen = true ;
209288 },
210289 },
211290};
0 commit comments