diff --git a/CHANGELOG.md b/CHANGELOG.md index 314e65bd..1537a2b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,3 +100,12 @@ For each PR made, an entry should be added to this changelog. It should contain - Added universal search functionality tests - Created search pane filter tests - Added pattern application form tests with validation checks + +- affected-urls-page + - Description: Added functionality to view affected URLs (both Delta and Curated URLs) for each pattern type (Include, Exclude, Title, Document Type) in a modal view. + - Changes: + - Created new API endpoints for each pattern type to fetch affected URLs + - Added `BaseAffectedURLsViewSet` and pattern-specific views for API endpoints + - Implemented modal-based display for affected URLs with dynamic sizing + - Enhanced pattern list views with clickable eye icons that shows "Affected URLs" modal + - Improved user experience by keeping users on the same page while viewing affected URLs diff --git a/sde_collections/urls.py b/sde_collections/urls.py index 9ee77759..dcba1d94 100644 --- a/sde_collections/urls.py +++ b/sde_collections/urls.py @@ -16,6 +16,21 @@ router.register(r"document-type-patterns", views.DocumentTypePatternViewSet) router.register(r"division-patterns", views.DivisionPatternViewSet) router.register(r"environmental-justice", EnvironmentalJusticeRowViewSet) +router.register( + r"exclude-pattern-affected-urls", views.ExcludePatternAffectedURLsViewSet, basename="exclude-pattern-affected-urls" +) +router.register( + r"include-pattern-affected-urls", views.IncludePatternAffectedURLsViewSet, basename="include-pattern-affected-urls" +) +router.register( + r"title-pattern-affected-urls", views.TitlePatternAffectedURLsViewSet, basename="title-pattern-affected-urls" +) +router.register( + r"documenttype-pattern-affected-urls", + views.DocumentTypePatternAffectedURLsViewSet, + basename="documenttype-pattern-affected-urls", +) + app_name = "sde_collections" diff --git a/sde_collections/views.py b/sde_collections/views.py index fb268170..91b67888 100644 --- a/sde_collections/views.py +++ b/sde_collections/views.py @@ -632,3 +632,41 @@ def get(self, request, *args, **kwargs): "resolved_title_errors": resolved_title_errors, } return render(request, "sde_collections/titles_and_errors_list.html", context) + + +class BaseAffectedURLsViewSet(CollectionFilterMixin, viewsets.ModelViewSet): + + pattern_model = None + pattern_type = None + + def get_serializer_class(self): + url_type = self.request.GET.get("url_type") + return DeltaURLSerializer if url_type == "delta" else CuratedURLSerializer + + def get_queryset(self): + pattern_id = self.request.GET.get("pattern_id") + url_type = self.request.GET.get("url_type") + self.pattern = self.pattern_model.objects.get(id=pattern_id) + return ( + self.pattern.get_matching_delta_urls() if url_type == "delta" else self.pattern.get_matching_curated_urls() + ) + + +class ExcludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaExcludePattern + pattern_type = "Exclude" + + +class IncludePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaIncludePattern + pattern_type = "Include" + + +class TitlePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaTitlePattern + pattern_type = "Title" + + +class DocumentTypePatternAffectedURLsViewSet(BaseAffectedURLsViewSet): + pattern_model = DeltaDocumentTypePattern + pattern_type = "Document Type" diff --git a/sde_indexing_helper/static/css/delta_url_list.css b/sde_indexing_helper/static/css/delta_url_list.css index 06689207..65865064 100644 --- a/sde_indexing_helper/static/css/delta_url_list.css +++ b/sde_indexing_helper/static/css/delta_url_list.css @@ -451,3 +451,50 @@ div.dt-container div.dt-paging ul.pagination { max-width: 100%; min-width: 100%; } + +/* Specific styling for affected URLs modal only */ +#affectedURLsModal { + padding: 0 !important; +} + +#affectedURLsModal .modal-dialog { + max-width: 90%; + height: auto; + margin: 1.75rem auto; +} + +#affectedURLsModal .modal-content { + background-color: #15232E; + color: white; + width: 100%; + height: auto; + max-height: 80vh; + display: flex; + flex-direction: column; +} + +#affectedURLsModal .modal-header { + border-bottom: 1px solid #3F4A58; + margin-bottom: 0px; +} + +#affectedURLsModal .modal-body { + flex: 0 1 auto; + overflow: auto; + padding: 1rem; +} + +#affectedURLsModal .close { + color: white; +} + +#affectedURLsModal .dataTables_wrapper { + display: flex; + flex-direction: column; +} + +#affectedURLsModal .col-md-auto { + display: flex; + align-items: center; + gap: 1rem; +} diff --git a/sde_indexing_helper/static/js/delta_url_list.js b/sde_indexing_helper/static/js/delta_url_list.js index fb214cc1..7464d4a9 100644 --- a/sde_indexing_helper/static/js/delta_url_list.js +++ b/sde_indexing_helper/static/js/delta_url_list.js @@ -103,6 +103,23 @@ function modalContents(tableName) { }); } +function renderCountWithViewButton(count, patternType, urlType, rowId) { + return ` +
+ + ${count} + + +
+ `; +} + function initializeDataTable() { var true_icon = 'check'; var false_icon = 'close'; @@ -604,11 +621,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'exclude', 'delta', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'exclude', 'curated', row.id); + }, }, { data: null, @@ -690,11 +713,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'include', 'delta', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'include', 'curated', row.id); + }, }, { data: null, @@ -773,11 +802,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'title', 'delta', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'title', 'curated', row.id); + }, }, { data: null, @@ -856,11 +891,17 @@ function initializeDataTable() { data: "delta_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'document-type', 'delta', row.id); + }, }, { data: "curated_urls_count", class: "text-center whiteText", sortable: true, + render: function (data, type, row) { + return renderCountWithViewButton(data, 'document-type', 'curated', row.id); + }, }, { data: null, @@ -998,6 +1039,7 @@ function setupClickHandlers() { handleDivisionSelect(); handleExcludeIndividualUrlClick(); handleNewTitleChange(); + handleShowAffectedURLsListButtonClick(); handleUrlLinkClick(); handleTabsClick(); @@ -2251,3 +2293,98 @@ function handleReindexingStatusSelect() { }); }); } + +function handleShowAffectedURLsListButtonClick() { + const PATTERN_ENDPOINTS = { + 'exclude': 'exclude-pattern-affected-urls', + 'include': 'include-pattern-affected-urls', + 'title': 'title-pattern-affected-urls', + 'document-type': 'documenttype-pattern-affected-urls' + }; + + $("body").on("click", ".view-pattern-urls", function() { + const matchPatternId = $(this).data("row-id"); + const patternType = $(this).data("pattern-type"); + const urlType = $(this).data("url-type"); + const patternName = $(this).closest('tr').find('td:first').text(); + const urlCount = $(this).prev('.urlCount').text().trim(); + + // Update modal title and pattern info + const capitalize = str => str[0].toUpperCase() + str.slice(1); + $("#affectedURLsModalTitle").text(`Affected ${capitalize(urlType)} URLs`); + $("#patternInfo").text(` + ${urlCount} affected URL${urlCount === '1' ? '' : 's'} for ${patternType} pattern: + `).append($('').css('color', '#65B1EF').text(patternName)); + + if ($.fn.DataTable.isDataTable('#affectedURLsModalTable')) { + $('#affectedURLsModalTable').DataTable().destroy(); + } + + initializeModalDataTable(PATTERN_ENDPOINTS[patternType], matchPatternId, urlType); + $("#affectedURLsModal").modal('show'); + + }); +} + +function initializeModalDataTable(endpoint, patternId, urlType) { + + affected_urls_table = $("#affectedURLsModalTable").DataTable({ + processing: true, + pageLength: 100, + colReorder: true, + stateSave: true, + serverSide: true, + orderCellsTop: true, + pagingType: "input", + paging: true, + stateSave: false, + rowId: "url", + layout: { + bottomEnd: "inputPaging", + topEnd: null, + topStart: { + info: true, + pageLength: { + menu: [ + [25, 50, 100, 500], + ["Show 25", "Show 50", "Show 100", "Show 500"], + ], + }, + }, + }, + columnDefs: [ + { orderable: true, targets: "_all" }, + { orderable: false, targets: "filter-row" }, + ], + orderCellsTop: true, + ajax: { + url: `/api/${endpoint}/?format=datatables&url_type=${urlType}&pattern_id=${patternId}`, + data: function (d) {}, + complete: function (xhr, status) {}, + }, + + columns: [ + getURLColumn(), + ], + }); + + $("#affectedURLsFilter").on( + "beforeinput", + DataTable.util.debounce(function (val) { + affected_urls_table.columns(0).search(this.value).draw(); + }, 200) + ); +} + +function getURLColumn() { + return { + data: "url", + width: "30%", + render: function (data, type, row) { + return `
${data} + + open_in_new +
`; + }, + }; +} diff --git a/sde_indexing_helper/templates/sde_collections/delta_urls_list.html b/sde_indexing_helper/templates/sde_collections/delta_urls_list.html index e17317a3..c286bbcf 100644 --- a/sde_indexing_helper/templates/sde_collections/delta_urls_list.html +++ b/sde_indexing_helper/templates/sde_collections/delta_urls_list.html @@ -762,6 +762,32 @@ + + {% endblock content %} {% block javascripts %}