Skip to content

Commit 40b914f

Browse files
Dropdown for sample only or related sample + col
1 parent 2f77f01 commit 40b914f

File tree

2 files changed

+184
-71
lines changed

2 files changed

+184
-71
lines changed

webapp/src/components/SampleGraphExportModal.vue

Lines changed: 149 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,119 @@
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">&times;</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";
71103
import {
72104
getItemGraph,
73105
startSampleExport,
74106
getExportStatus,
75107
getExportDownloadUrl,
108+
createNewCollection,
76109
} from "@/server_fetch_utils";
77110
import { DialogService } from "@/services/DialogService";
78111
79112
export 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
};

webapp/src/components/SampleInformation.vue

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,28 @@
4646
<TinyMceInline v-model="SampleDescription" aria-labelledby="samp-description-label" />
4747
</div>
4848
<div class="col">
49-
<ExportButton :item-id="item_id" />
49+
<div class="dropdown">
50+
<button
51+
class="btn btn-sm btn-outline-primary dropdown-toggle"
52+
type="button"
53+
data-toggle="dropdown"
54+
aria-haspopup="true"
55+
aria-expanded="false"
56+
@click="isExportDropdownVisible = !isExportDropdownVisible"
57+
>
58+
<i class="fa fa-download"></i> Export (as .ELN)
59+
</button>
60+
<div v-show="isExportDropdownVisible" class="dropdown-menu" style="display: block">
61+
<a class="dropdown-item" @click="handleSampleExport">
62+
<i class="fa fa-file"></i> Export Sample Only
63+
</a>
64+
<a class="dropdown-item" @click="handleGraphExport">
65+
<i class="fa fa-project-diagram"></i> Export Related Samples
66+
</a>
67+
</div>
68+
</div>
69+
<ExportButton ref="exportButton" :item-id="item_id" style="display: none" />
70+
<SampleGraphExportModal ref="graphExportModal" :item-id="item_id" />
5071
</div>
5172
</div>
5273
</div>
@@ -72,9 +93,11 @@ import SynthesisInformation from "@/components/SynthesisInformation";
7293
import TableOfContents from "@/components/TableOfContents";
7394
import ItemRelationshipVisualization from "@/components/ItemRelationshipVisualization";
7495
import ExportButton from "@/components/ExportButton";
96+
import SampleGraphExportModal from "@/components/SampleGraphExportModal";
7597
7698
export default {
7799
components: {
100+
SampleGraphExportModal,
78101
ExportButton,
79102
ChemFormulaInput,
80103
TinyMceInline,
@@ -99,6 +122,7 @@ export default {
99122
{ title: "Table of Contents", targetID: "table-of-contents" },
100123
{ title: "Synthesis Information", targetID: "synthesis-information" },
101124
],
125+
isExportDropdownVisible: false,
102126
};
103127
},
104128
computed: {
@@ -111,5 +135,15 @@ export default {
111135
ItemCreators: createComputedSetterForItemField("creators"),
112136
Collections: createComputedSetterForItemField("collections"),
113137
},
138+
methods: {
139+
handleSampleExport() {
140+
this.isExportDropdownVisible = false;
141+
this.$refs.exportButton.handleExport();
142+
},
143+
handleGraphExport() {
144+
this.isExportDropdownVisible = false;
145+
this.$refs.graphExportModal.show();
146+
},
147+
},
114148
};
115149
</script>

0 commit comments

Comments
 (0)