diff --git a/bims/static/react/js/components/ContextFilterView.jsx b/bims/static/react/js/components/ContextFilterView.jsx index 0ad147201..f9da5e27f 100644 --- a/bims/static/react/js/components/ContextFilterView.jsx +++ b/bims/static/react/js/components/ContextFilterView.jsx @@ -3,7 +3,7 @@ import axios from "axios"; import SortableList, {SortableItem, SortableKnob} from "react-easy-sort"; import { arrayMoveImmutable } from "array-move"; import "bootstrap-icons/font/bootstrap-icons.css"; -import {Button, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader} from "reactstrap"; +import {Button, Form, FormGroup, Input, Label, Modal, ModalBody, ModalFooter, ModalHeader} from "reactstrap"; import AddContextGroup from "./AddContextGroup"; @@ -17,6 +17,9 @@ const ContextFilterView = (props) => { const [isAddNewGroupModalOpen, setIsAddNewGroupModalOpen] = useState(false); const [selectedFilter, setSelectedFilter] = useState(null); const [contextGroups, setContextGroups] = useState([]); + const [isAddNewFilterModalOpen, setIsAddNewFilterModalOpen] = useState(false); + const [newFilterName, setNewFilterName] = useState(''); + const [savingNewFilter, setSavingNewFilter] = useState(false); const contextLayerFilterAPI = '/api/context-filter/'; const contextLayerGroupAPI = '/api/context-layer-group/'; @@ -43,6 +46,24 @@ const ContextFilterView = (props) => { } }; + const deleteContextFilter = async (filterId) => { + try { + const deleteUrl = `${contextLayerFilterAPI}?filter_id=${filterId}`; + await axios.delete(deleteUrl, { + headers: { + 'X-CSRFToken': props.csrfToken + } + }); + console.log(`Filter ${filterId} deleted successfully.`); + + // Optionally, refresh the context filters after deletion + setContextFilters([]); + fetchContextFilters(); + } catch (error) { + console.error(`Failed to delete filter ${filterId}:`, error); + } + }; + const fetchContextGroups = async () => { setContextGroups([]) try { @@ -79,6 +100,27 @@ const ContextFilterView = (props) => { } }; + const createContextFilter = async (newFilter) => { + try { + const response = await axios.post(contextLayerFilterAPI, newFilter, { + headers: { + 'X-CSRFToken': props.csrfToken, + 'Content-Type': 'application/json' + } + }); + console.log("New filter created successfully:", response.data.filter); + + setIsAddNewFilterModalOpen(false); + + setContextFilters([]); + fetchContextFilters(); + } catch (error) { + console.error("Failed to create new filter:", error); + } finally { + setSavingNewFilter(false); + } + }; + useEffect(() => { fetchContextFilters(); }, []); @@ -96,7 +138,7 @@ const ContextFilterView = (props) => { display_order: index + 1 })); - updateOrder(updatedFilters, []) + updateOrder(updatedFilters, {}) setContextFilters(arrayMoveImmutable(contextFilters, oldIndex, newIndex)) }; @@ -118,6 +160,16 @@ const ContextFilterView = (props) => { setFilterText(e.target.value); }; + const handleSaveAddNewFilter = () => { + const newFilter = { + display_order: contextFilters.length + 1, + location_context_groups: [], + title: newFilterName + } + setSavingNewFilter(true); + createContextFilter(newFilter); + } + const filteredContextFilters = contextFilters.filter(group => group.title.toLowerCase().includes(filterText.toLowerCase()) ); @@ -126,6 +178,13 @@ const ContextFilterView = (props) => { setIsAddNewGroupModalOpen(!isAddNewGroupModalOpen) } + const toggleAddNewFilterModal = () => { + if (!isAddNewFilterModalOpen) { + setNewFilterName(''); + } + setIsAddNewFilterModalOpen(!isAddNewFilterModalOpen) + } + const handleAddNewGroup = (e, contextFilter) => { e.stopPropagation(); toggleAddNewGropModal(); @@ -135,6 +194,11 @@ const ContextFilterView = (props) => { setSelectedFilter(contextFilter); } + const handleDeleteFilter = (e, contextFilter) => { + e.stopPropagation(); + deleteContextFilter(contextFilter.id); + } + const handleRemoveGroup = (filter, group) => { let groupId = group.group.id; const updatedGroups = { @@ -166,6 +230,11 @@ const ContextFilterView = (props) => { className="form-control mb-2" /> +
+ +
{filteredContextFilters.map((contextFilter) => ( @@ -185,6 +254,12 @@ const ContextFilterView = (props) => { style={{ float: 'right', right: 0, marginTop: -5 }}> +
{expandedGroups[contextFilter.id] && ( onSortChild(contextFilter.id, oldIndex, newIndex)} draggedItemClassName={'dragged'}> @@ -219,6 +294,29 @@ const ContextFilterView = (props) => { + + + Add new filter section + + +
+ + + setNewFilterName(e.target.value)}/> + +
+
+ + + + +
); diff --git a/bims/views/context_layers.py b/bims/views/context_layers.py index d00dcef7c..025fa1bcd 100644 --- a/bims/views/context_layers.py +++ b/bims/views/context_layers.py @@ -70,7 +70,7 @@ def get_location_context_groups(self, obj): class Meta: model = LocationContextFilter - fields = '__all__' + exclude = ['site',] @@ -130,6 +130,38 @@ def get(self, request, *args): ) return Response(context_filter_data.data) + def post(self, request, *args): + data = request.data + serializer = LocationFilterSerializer(data=data) + + if serializer.is_valid(): + serializer.save() + return Response( + { + "message": "Filter created successfully.", + "filter": serializer.data + }, status=201) + else: + return Response(serializer.errors, status=400) + + def delete(self, request, *args): + filter_id = request.query_params.get('filter_id') + + if not filter_id: + return Response({"error": "filter_id is required."}, status=400) + + filter_to_delete = LocationContextFilter.objects.filter( + id=filter_id + ) + if filter_to_delete.exists(): + filter_to_delete.delete() + return Response({ + "message": f"Filter {filter_id} deleted successfully."}, + status=204) + else: + return Response({ + "error": f"Filter with id {filter_id} not found."}, status=404) + def put(self, request, *args): """Update the order of context filters and groups.""" data = request.data @@ -140,7 +172,8 @@ def put(self, request, *args): for filter_data in filters_data: filter_id = filter_data.get('id') new_order = filter_data.get('display_order') - LocationContextFilter.objects.filter(id=filter_id).update(display_order=new_order) + LocationContextFilter.objects.filter( + id=filter_id).update(display_order=new_order) # Update group order within each filter for filter_id, groups in groups_data.items(): @@ -156,7 +189,8 @@ def put(self, request, *args): if remove: context_filter_group.delete() else: - context_filter_group.update(group_display_order=new_group_order) + context_filter_group.update( + group_display_order=new_group_order) else: if not remove: LocationContextFilterGroupOrder.objects.create( @@ -168,7 +202,8 @@ def put(self, request, *args): tenant_id = get_tenant(request).id generate_spatial_scale_filter.delay(tenant_id) - return Response({"message": "Order updated successfully."}, status=200) + return Response( + {"message": "Order updated successfully."}, status=200) class CloudNativeLayerAutoCompleteAPI(APIView):