|
4 | 4 | import CreateUpdateImageModal from '$lib/components/v2/projects/datasets/CreateUpdateImageModal.svelte'; |
5 | 5 | import Paginator from '$lib/components/common/Paginator.svelte'; |
6 | 6 | import BooleanIcon from 'fractal-components/common/BooleanIcon.svelte'; |
7 | | - import { objectChanged } from '$lib/common/component_utilities'; |
| 7 | + import { deepCopy, objectChanged } from '$lib/common/component_utilities'; |
8 | 8 | import SlimSelect from 'slim-select'; |
9 | 9 | import { onDestroy, tick } from 'svelte'; |
| 10 | + import Tooltip from '$lib/components/common/Tooltip.svelte'; |
10 | 11 |
|
11 | 12 | /** @type {import('fractal-components/types/api').DatasetV2} */ |
12 | 13 | export let dataset; |
|
16 | 17 | export let vizarrViewerUrl; |
17 | 18 | /** @type {boolean} */ |
18 | 19 | export let useDatasetFilters; |
19 | | - export let datasetFiltersChanged = false; |
20 | 20 | /** |
21 | 21 | * Set to true if the table is displayed inside the "Run workflow" modal. |
22 | 22 | * Used to disable some buttons. |
|
27 | 27 | /** @type {{ attribute_filters: { [key: string]: Array<string | number | boolean> | null }, type_filters: { [key: string]: boolean | null }} | null} */ |
28 | 28 | export let initialFilterValues = null; |
29 | 29 |
|
| 30 | + let datasetFiltersChanged = false; |
| 31 | + /** @type {(dataset: import('fractal-components/types/api').DatasetV2) => void} */ |
| 32 | + export let onDatasetsUpdated = () => {}; |
| 33 | +
|
30 | 34 | let showTable = imagePage.total_count > 0; |
31 | 35 |
|
| 36 | + /** @type {Tooltip|undefined} */ |
| 37 | + let currentSelectionTooltip; |
| 38 | +
|
32 | 39 | /** @type {CreateUpdateImageModal|undefined} */ |
33 | 40 | let imageModal = undefined; |
34 | 41 |
|
35 | 42 | let searching = false; |
36 | 43 | let resetting = false; |
| 44 | + let savingDatasetFilters = false; |
37 | 45 |
|
38 | 46 | let reloading = false; |
39 | 47 | /** @type {{ [key: string]: Array<string | number | boolean> | null}} */ |
|
129 | 137 | async function resetSearchFields() { |
130 | 138 | resetBtnActive = false; |
131 | 139 | resetting = true; |
132 | | - attributeFilters = getAttributeFilterBaseValues(imagePage); |
133 | | - typeFilters = getTypeFilterBaseValues(imagePage); |
| 140 | + if (useDatasetFilters) { |
| 141 | + attributeFilters = deepCopy(dataset.attribute_filters); |
| 142 | + typeFilters = deepCopy(dataset.type_filters); |
| 143 | + } else { |
| 144 | + attributeFilters = getAttributeFilterBaseValues(imagePage); |
| 145 | + typeFilters = getTypeFilterBaseValues(imagePage); |
| 146 | + } |
134 | 147 | await tick(); |
135 | 148 | await searchImages(); |
136 | 149 | resetting = false; |
137 | 150 | } |
138 | 151 |
|
139 | 152 | export async function reload() { |
140 | 153 | reloading = true; |
| 154 | + currentSelectionTooltip?.setEnabled(!useDatasetFilters); |
141 | 155 | if (useDatasetFilters) { |
142 | | - attributeFilters = dataset.attribute_filters; |
143 | | - typeFilters = dataset.type_filters; |
| 156 | + attributeFilters = deepCopy(dataset.attribute_filters); |
| 157 | + typeFilters = deepCopy(dataset.type_filters); |
144 | 158 | } else { |
145 | 159 | attributeFilters = getAttributeFilterBaseValues(imagePage); |
146 | 160 | typeFilters = getTypeFilterBaseValues(imagePage); |
|
387 | 401 | } else { |
388 | 402 | errorAlert = displayStandardErrorAlert( |
389 | 403 | await getAlertErrorFromResponse(response), |
390 | | - 'searchError' |
| 404 | + 'datasetImagesError' |
391 | 405 | ); |
392 | 406 | } |
393 | 407 | } |
|
476 | 490 | } |
477 | 491 |
|
478 | 492 | $: if (attributeFilters && typeFilters) { |
| 493 | + //console.log(dataset.attribute_filters, removeNullValues(attributeFilters)) |
479 | 494 | datasetFiltersChanged = |
480 | 495 | !applyBtnActive && |
481 | 496 | (attributesChanged(dataset.attribute_filters, removeNullValues(attributeFilters)) || |
|
510 | 525 | if (oldValue.length !== newValue.length) { |
511 | 526 | return true; |
512 | 527 | } |
513 | | - for (let i = 0; i < oldValue.length; i++) { |
514 | | - if (oldValue[i] !== newValue[i]) { |
| 528 | + for (const ov of oldValue) { |
| 529 | + if (!newValue.includes(ov)) { |
515 | 530 | return true; |
516 | 531 | } |
517 | 532 | } |
|
521 | 536 | } |
522 | 537 | return false; |
523 | 538 | } |
| 539 | +
|
| 540 | + async function saveDatasetFilters() { |
| 541 | + errorAlert?.hide(); |
| 542 | + savingDatasetFilters = true; |
| 543 | + const headers = new Headers(); |
| 544 | + headers.set('Content-Type', 'application/json'); |
| 545 | + const response = await fetch(`/api/v2/project/${dataset.project_id}/dataset/${dataset.id}`, { |
| 546 | + method: 'PATCH', |
| 547 | + credentials: 'include', |
| 548 | + headers, |
| 549 | + body: JSON.stringify({ |
| 550 | + attribute_filters: getAttributeFilters(), |
| 551 | + type_filters: getTypeFilters() |
| 552 | + }) |
| 553 | + }); |
| 554 | + if (response.ok) { |
| 555 | + const result = await response.json(); |
| 556 | + dataset = result; |
| 557 | + onDatasetsUpdated(dataset); |
| 558 | + } else { |
| 559 | + errorAlert = displayStandardErrorAlert( |
| 560 | + await getAlertErrorFromResponse(response), |
| 561 | + 'datasetImagesError' |
| 562 | + ); |
| 563 | + } |
| 564 | + savingDatasetFilters = false; |
| 565 | + } |
524 | 566 | </script> |
525 | 567 |
|
526 | 568 | {#if !showTable} |
|
535 | 577 | <div> |
536 | 578 | <div class="row"> |
537 | 579 | <div class="col"> |
538 | | - <div id="searchError" class="mt-2 mb-2" /> |
| 580 | + <div id="datasetImagesError" class="mt-2 mb-2" /> |
539 | 581 | </div> |
540 | 582 | </div> |
541 | 583 |
|
|
624 | 666 | {/if} |
625 | 667 | Reset |
626 | 668 | </button> |
| 669 | + {#if !runWorkflowModal && useDatasetFilters} |
| 670 | + <ConfirmActionButton |
| 671 | + modalId="confirmSaveDatasetFilters" |
| 672 | + label="Save" |
| 673 | + message="Save dataset filters" |
| 674 | + disabled={!datasetFiltersChanged || savingDatasetFilters} |
| 675 | + callbackAction={async () => { |
| 676 | + await saveDatasetFilters(); |
| 677 | + }} |
| 678 | + /> |
| 679 | + {/if} |
627 | 680 | </th> |
628 | 681 | </tr> |
629 | 682 | </thead> |
|
704 | 757 | disabled={reloading || searching || resetting} |
705 | 758 | /> |
706 | 759 | <label class="btn btn-white btn-outline-primary" for="all-images">All images</label> |
707 | | - <input |
708 | | - type="radio" |
709 | | - class="btn-check" |
710 | | - name="filters-switch" |
711 | | - id="dataset-filters" |
712 | | - autocomplete="off" |
713 | | - value={true} |
714 | | - bind:group={useDatasetFilters} |
715 | | - on:change={reload} |
716 | | - disabled={reloading || searching || resetting} |
717 | | - /> |
718 | | - <label class="btn btn-white btn-outline-primary" for="dataset-filters"> |
719 | | - Dataset filters |
720 | | - </label> |
| 760 | + <Tooltip |
| 761 | + id="current-selection-label" |
| 762 | + title="These are default selection for images on which a workflow will be run" |
| 763 | + placement="bottom" |
| 764 | + bind:this={currentSelectionTooltip} |
| 765 | + > |
| 766 | + <input |
| 767 | + type="radio" |
| 768 | + class="btn-check" |
| 769 | + name="filters-switch" |
| 770 | + id="current-selection" |
| 771 | + autocomplete="off" |
| 772 | + value={true} |
| 773 | + bind:group={useDatasetFilters} |
| 774 | + on:change={reload} |
| 775 | + disabled={reloading || searching || resetting} |
| 776 | + /> |
| 777 | + <label class="btn btn-white btn-outline-primary" for="current-selection"> |
| 778 | + Current selection |
| 779 | + </label> |
| 780 | + </Tooltip> |
721 | 781 | {#if reloading} |
722 | 782 | <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" /> |
723 | 783 | {/if} |
|
0 commit comments