From 2180a6733e47cbbe7941ba930e66de839e776cba Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Thu, 18 Jul 2024 15:16:30 +0100 Subject: [PATCH 1/7] fix(license): change Excavation Reason Type to Excavation Reason --- coral/pkg/graphs/resource_models/License.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coral/pkg/graphs/resource_models/License.json b/coral/pkg/graphs/resource_models/License.json index 8aee4cc88..71705cb55 100644 --- a/coral/pkg/graphs/resource_models/License.json +++ b/coral/pkg/graphs/resource_models/License.json @@ -2984,14 +2984,14 @@ "i18n_properties": [ "placeholder" ], - "label": "Excavation Reason Type", + "label": "Excavation Reason", "placeholder": { "en": "Select an option" } }, "id": "8d4976f6-280b-4227-ac71-c2bb3173df36", "label": { - "en": "Excavation Reason Type" + "en": "Excavation Reason" }, "node_id": "ba8aab44-2d4d-11ef-bbfd-0242ac120006", "sortorder": 4, From fb3075066aeb82780046a8cb34ed5509e922cdc6 Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Thu, 18 Jul 2024 16:04:34 +0100 Subject: [PATCH 2/7] fix(License): change report cardinality to n --- coral/pkg/graphs/resource_models/License.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coral/pkg/graphs/resource_models/License.json b/coral/pkg/graphs/resource_models/License.json index 71705cb55..407eca4a8 100644 --- a/coral/pkg/graphs/resource_models/License.json +++ b/coral/pkg/graphs/resource_models/License.json @@ -7335,7 +7335,7 @@ "parentnodegroup_id": "2749ea5a-48cb-11ee-be76-0242ac140007" }, { - "cardinality": "1", + "cardinality": "n", "legacygroupid": null, "nodegroupid": "f060583a-6120-11ee-9fd1-0242ac120003", "parentnodegroup_id": null From 69fbba76c0874862b5a62ba1569b75ec0be0c2b0 Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Fri, 19 Jul 2024 09:01:28 +0100 Subject: [PATCH 3/7] feat: create custom component --- .../components/plugins/licensing-workflow.js | 4 +-- .../plugins/workflow-builder-loader.js | 1 + .../excavation-report-step.js | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js diff --git a/coral/media/js/views/components/plugins/licensing-workflow.js b/coral/media/js/views/components/plugins/licensing-workflow.js index f2e67ebc2..b435aeaad 100644 --- a/coral/media/js/views/components/plugins/licensing-workflow.js +++ b/coral/media/js/views/components/plugins/licensing-workflow.js @@ -463,13 +463,13 @@ define([ { componentConfigs: [ { - componentName: 'default-card-util', // Has fix for many tiles + componentName: 'excavation-report-step', // Has fix for many tiles uniqueInstanceName: 'report-info', tilesManaged: 'many', parameters: { graphid: 'cc5da227-24e7-4088-bb83-a564c4331efd', nodegroupid: 'f060583a-6120-11ee-9fd1-0242ac120003', - resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']" + resourceid: "['init-step']['app-id'][0]['resourceid']['resourceInstanceId']", } }, { diff --git a/coral/media/js/views/components/plugins/workflow-builder-loader.js b/coral/media/js/views/components/plugins/workflow-builder-loader.js index e7716d503..866e1dcd9 100644 --- a/coral/media/js/views/components/plugins/workflow-builder-loader.js +++ b/coral/media/js/views/components/plugins/workflow-builder-loader.js @@ -20,6 +20,7 @@ define([ 'views/components/workflows/heritage-asset-designation-workflow/ha-summary', 'views/components/workflows/heritage-asset-designation-workflow/start-remap-and-merge', 'views/components/workflows/assign-consultation-workflow/pc-summary', + 'views/components/workflows/excavation-license-workflow/excavation-report-step' ], function (ko, arches, OpenableWorkflow, workflowTemplate) { return ko.components.register('workflow-builder-loader', { viewModel: function (params) { diff --git a/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js b/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js new file mode 100644 index 000000000..6eaedbc67 --- /dev/null +++ b/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js @@ -0,0 +1,34 @@ +define([ + 'underscore', + 'knockout', + 'knockout-mapping', + 'uuid', + 'arches', + '../default-card-util.js', + 'templates/views/components/workflows/default-card-util.htm', + ], function (_, ko, koMapping, uuid, arches, DefaultCardUtilViewModel, componentTemplate) { + function viewModel(params) { + + DefaultCardUtilViewModel.apply(this, [params]); + + this.DOCUMENT_REFERENCE_NODE = "d8f74d42-dc6e-11ee-8def-0242ac120006" + + params.disableAdd(true) + + this.tile.data[this.DOCUMENT_REFERENCE_NODE].subscribe((newValue) => { + if (newValue.en.value != ""){ + console.log(newValue) + params.disableAdd(false) + } + }) + + + } + + ko.components.register('excavation-report-step', { + viewModel: viewModel, + template: componentTemplate + }); + + return viewModel; + }); \ No newline at end of file From 981dd0bd402e652c85c9bd373cd612e77ebca1df Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Fri, 19 Jul 2024 09:02:59 +0100 Subject: [PATCH 4/7] feat: create coral version of workflow-component-abstract for edit --- .../workflows/workflow-component-abstract.js | 856 ++++++++++++++++++ 1 file changed, 856 insertions(+) create mode 100644 coral/media/js/views/components/workflows/workflow-component-abstract.js diff --git a/coral/media/js/views/components/workflows/workflow-component-abstract.js b/coral/media/js/views/components/workflows/workflow-component-abstract.js new file mode 100644 index 000000000..f6ff72873 --- /dev/null +++ b/coral/media/js/views/components/workflows/workflow-component-abstract.js @@ -0,0 +1,856 @@ +define([ + 'underscore', + 'jquery', + 'knockout', + 'knockout-mapping', + 'arches', + 'models/graph', + 'viewmodels/card', + 'viewmodels/provisional-tile', + 'viewmodels/alert', + 'uuid', + 'js-cookie', + 'templates/views/components/workflows/workflow-component-abstract.htm', +], function(_, $, ko, koMapping, arches, GraphModel, CardViewModel, ProvisionalTileViewModel, AlertViewModel, uuid, Cookies, workflowComponentAbstractTemplate) { + function NonTileBasedComponent() { + var self = this; + + + this.initialize = function() { + self.loading(false); + }; + + this.save = function() { + self.complete(false); + self.saving(true); + + self.setToWorkflowHistory('value', self.value()).then(() => { + self.savedData(self.value()); + self.complete(true); + self.saving(false); + }); + }; + + this.reset = function() { + self.value(self.savedData() ? self.savedData() : null); + }; + + this.initialize(); + } + + + function TileBasedComponent() { + var self = this; + + + this.tile = ko.observable(); + this.tiles = ko.observable(); + + this.card = ko.observable(); + this.topCards = ko.observable(); + + this.loadData = function(loadedData) { + let data; + if (!Array.isArray(loadedData)) { + data = [loadedData]; + } + else { + data = loadedData; + } + + /* a flat object of the previously saved data for all tiles */ + var tileDataLookup = data.reduce(function(acc, componentData) { + var parsedTileData = componentData.data || JSON.parse(componentData.tileData); + + Object.keys(parsedTileData).forEach(function(key) { + acc[key] = parsedTileData[key]; + }); + + return acc; + }, {}); + + self.tiles().forEach(function(tile) { + /* force the value of current tile data observables */ + Object.keys(tile.data).forEach(function(key) { + if (ko.isObservable(tile.data[key])) { + tile.data[key](tileDataLookup[key]); + } + }); + tile._tileData(koMapping.toJSON(tile.data)); + + data.forEach(function(datum){ + if (datum.tileData) { + if (JSON.stringify(Object.keys(koMapping.toJS(tile.data)).sort()) + === JSON.stringify(Object.keys(JSON.parse(datum.tileData)).sort())) { + tile.nodegroup_id = datum.nodegroupId; + tile.tileid = datum.tileId; + tile.resourceinstance_id = datum.resourceInstanceId; + } + } + }); + }); + }; + + this.initialize = function() { + if (self.componentData.tilesManaged === "one") { + self.tile.subscribe(function(tile) { + if (!self.tiles()) { + self.tiles([tile]); + } + }); + + self.tiles.subscribe(function(tiles) { + if (tiles && !self.saving()) { + if (self.savedData()) { /* if the refresh after tile save */ + self.loadData(self.savedData()); + } + else if (self.previouslyPersistedComponentData) { /* if previously saved data */ + self.loadData(self.previouslyPersistedComponentData); + } + + } + }); + } + + $.getJSON(( arches.urls.api_card + this.getCardResourceIdOrGraphId() ), function(data) { + var handlers = { + 'after-update': [], + 'tile-reset': [] + }; + var displayname = ko.observable(data.displayname); + var createLookup = function(list, idKey) { + return _.reduce(list, function(lookup, item) { + lookup[item[idKey]] = item; + return lookup; + }, {}); + }; + + self.reviewer = data.userisreviewer; + self.provisionalTileViewModel = new ProvisionalTileViewModel({ + tile: self.tile, + reviewer: data.userisreviewer + }); + + var graphModel = new GraphModel({ + data: { + nodes: data.nodes, + nodegroups: data.nodegroups, + edges: [] + }, + datatypes: data.datatypes + }); + + self.graphModel = graphModel; + + self.topCards = _.filter(data.cards, function(card) { + var nodegroup = _.find(data.nodegroups, function(group) { + return group.nodegroupid === card.nodegroup_id; + }); + return !nodegroup || !nodegroup.parentnodegroup_id; + }).map(function(card) { + self.componentData.parameters.nodegroupid = self.componentData.parameters.nodegroupid || card.nodegroup_id; + return new CardViewModel({ + card: card, + graphModel: graphModel, + tile: null, + resourceId: self.resourceId, + displayname: displayname, + handlers: handlers, + cards: data.cards, + tiles: data.tiles, + provisionalTileViewModel: self.provisionalTileViewModel, + cardwidgets: data.cardwidgets, + userisreviewer: data.userisreviewer, + loading: self.loading + }); + }); + + self.card.subscribe(function(card){ + if (ko.unwrap(card.widgets) && self.componentData.parameters.hiddenNodes) { + card.widgets().forEach(function(widget){ + if (self.componentData.parameters.hiddenNodes.indexOf(widget.node_id()) > -1) { + widget.visible(false); + } + }); + } + }); + + self.topCards.forEach(function(topCard) { + topCard.topCards = self.topCards; + }); + + self.widgetLookup = createLookup( + data.widgets, + 'widgetid' + ); + self.cardComponentLookup = createLookup( + data['card_components'], + 'componentid' + ); + self.nodeLookup = createLookup( + graphModel.get('nodes')(), + 'nodeid' + ); + self.on = function(eventName, handler) { + if (handlers[eventName]) { + handlers[eventName].push(handler); + } + }; + + self.flattenTree(self.topCards, []).forEach(function(item) { + if (item.constructor.name === 'CardViewModel' && item.nodegroupid === ko.unwrap(self.componentData.parameters.nodegroupid)) { + if (ko.unwrap(self.componentData.parameters.parenttileid) && item.parent && ko.unwrap(self.componentData.parameters.parenttileid) !== item.parent.tileid) { + return; + } + if (self.customCardLabel) item.model.name(ko.unwrap(self.customCardLabel)); + self.card(item); + if (ko.unwrap(self.componentData.parameters.tileid)) { + ko.unwrap(item.tiles).forEach(function(tile) { + if (tile.tileid === ko.unwrap(self.componentData.parameters.tileid)) { + self.tile(tile); + } + }); + } else if (ko.unwrap(self.componentData.parameters.createTile) !== false) { + self.tile(item.getNewTile()); + } + } + }); + + self.componentData.parameters.card = self.card(); + self.componentData.parameters.tile = self.tile(); + self.componentData.parameters.loading = self.loading; + self.componentData.parameters.provisionalTileViewModel = self.provisionalTileViewModel; + self.componentData.parameters.reviewer = data.userisreviewer; + self.componentData.parameters.dirty = self.dirty; + self.componentData.parameters.save = self.save; + self.componentData.parameters.tiles = self.tiles; + + self.loading(false); + }); + }; + + this.onSaveSuccess = function(savedData) { // LEGACY -- DO NOT USE + if (!(savedData instanceof Array)) { savedData = [savedData]; } + + self.savedData(savedData.map(function(savedDatum) { + return { + tileData: savedDatum._tileData(), + tileId: savedDatum.tileid, + nodegroupId: savedDatum.nodegroup_id, + resourceInstanceId: savedDatum.resourceinstance_id, + }; + })); + + self.saving(false); + self.complete(true); + }; + + this.reset = function() { + self.tiles().forEach(function(tile) { + tile.reset(); + }); + }; + + this.flattenTree = function(parents, flatList) { + _.each(ko.unwrap(parents), function(parent) { + flatList.push(parent); + var childrenKey = parent.tiles ? 'tiles' : 'cards'; + self.flattenTree( + ko.unwrap(parent[childrenKey]), + flatList + ); + }); + return flatList; + }; + + this.getTiles = function(nodegroupId, tileId) { + var tiles = []; + this.flattenTree(self.topCards, []).forEach(function(item) { + if (item.constructor.name === 'CardViewModel' && item.nodegroupid === nodegroupId) { + tiles = tiles.concat(ko.unwrap(item.tiles)); + } + }); + if (tileId) { + tiles = tiles.filter(function(tile) { + return tile.tileid === tileId; + }); + } + + return tiles; + }; + + this.initialize(); + } + + function AbstractCardAdapter() { // CURRENTLY IN DEVLEOPMENT, USE AT YOUR OWN RISK! + var self = this; + this.cardinality = ko.observable(); + + this.initialize = function() { + self.loading(true); + + $.getJSON(( arches.urls.api_nodegroup(self.componentData.parameters['nodegroupid']) ), function(nodegroupData) { + self.cardinality(nodegroupData.cardinality); + + const resourceInstanceDataPromise = new Promise(function(resolve, _reject) { + const resourceInstanceId = self.componentData.parameters['resourceid']; + + if (resourceInstanceId) { + $.getJSON( + ( arches.urls.resource + `/${resourceInstanceId}/tiles?nodeid=${self.componentData.parameters['nodegroupid']}`), + function(data) { + resolve(data); + } + ); + } + else { + resolve(null); + } + }); + + resourceInstanceDataPromise.then( function(data) { + if (self.cardinality() === '1') { + if (data && data['tiles'].length) { + self.componentData.parameters['tileid'] = data['tiles'][0]['tileid']; + self.complete(true); + } + TileBasedComponent.apply(self); + } + else if (self.cardinality() === 'n') { + MultipleTileBasedComponent.apply(self); + if (data && data['tiles'].length) { + self.complete(true); + } + + self.card.subscribe(function(card) { + if (!card.selected()) { + card.selected(card.tiles().length); + } + }); + + self.onSaveSuccess = function(savedData) { // LEGACY -- DO NOT USE + if (!(savedData instanceof Array)) { savedData = [savedData]; } + + let previouslySavedData = self.savedData(); + if (!previouslySavedData) { + previouslySavedData = []; + } + + self.savedData( + previouslySavedData.concat( + savedData.map(function(savedDatum) { + return { + tileData: savedDatum._tileData(), + tileId: savedDatum.tileid, + nodegroupId: savedDatum.nodegroup_id, + resourceInstanceId: savedDatum.resourceinstance_id, + }; + }) + ) + ); + + self.value(self.savedData()); + + self.componentData.parameters.dirty(false); + self.card().getNewTile(true); // `true` is forceNewTile + self.card().selected(true); + self.dirty(false); + self.saving(false); + self.complete(true); + + /** + * TODO: this is a hack to get around previous data autofilling forms when creating a new tile in cardinality n cards + * It should be removed when we're able to figure out how to prevent that logic + * */ + window.location.reload(); + }; + } + }); + + }); + }; + + this.initialize(); + } + + + function MultipleTileBasedComponent(title) { + var self = this; + + + TileBasedComponent.apply(this); + + this.tileLoadedInEditor = ko.observable(); + + this.itemName = ko.observable(ko.unwrap(title) ? ko.unwrap(title) : 'Items'); + + this.hasDirtyTiles = ko.computed(function() { + var tiles = self.tiles(); + var hasDirtyTiles = false; + + if (!tiles) { + hasDirtyTiles = true; + } + else if (self.savedData() ) { + if (self.savedData().length !== tiles.length) { + hasDirtyTiles = true; + } + + self.savedData().forEach(function(savedDatum) { + var matchingTile = tiles.find(function(tile) { + return tile.tileid === savedDatum.tileid; + }); + + if (!matchingTile) { + hasDirtyTiles = true; + } + else if (!_.isEqual(savedDatum.data, koMapping.toJS(matchingTile.data))) { + hasDirtyTiles = true; + } + }); + } + else if (!self.previouslyPersistedComponentData && tiles.length) { + hasDirtyTiles = true; + } + else if (self.previouslyPersistedComponentData) { + if (self.previouslyPersistedComponentData.length !== tiles.length) { + hasDirtyTiles = true; + } + + self.previouslyPersistedComponentData.forEach(function(persistedComponentData) { + var matchingTile = tiles.find(function(tile) { + return tile.tileid === persistedComponentData.tileid; + }); + + if (!matchingTile) { + hasDirtyTiles = true; + } + else if (!_.isEqual(persistedComponentData.data, koMapping.toJS(matchingTile.data))) { + hasDirtyTiles = true; + } + }); + } + self.multiTileUpdated(hasDirtyTiles); + return hasDirtyTiles; + }); + + this.initialize = function() { + self.loading(true); + + var savingSubscription = self.saving.subscribe(function(saving) { + if (!saving) { + savingSubscription.dispose(); /* self-disposing subscription only runs once */ + self.initialize(); + } + }); + + var multiTileInitSubscription = self.tiles.subscribe(function(tiles) { + /* + at this point in load `self.tiles()` contains a reference to the empty + tile we're using to map values to other tiles. This removes the empty + mapping tile from being tracked + */ + if (tiles.length === 1 && !tiles[0].tileid) { + var savedTiles = []; + + if (self.savedData()) { + var savedData = self.savedData(); + + savedData.forEach(function(savedDatum) { + var newTile = self.card().getNewTile(true); + + newTile.tileid = savedDatum.tileid; + newTile.data = koMapping.fromJS(savedDatum.data); + + savedTiles.unshift(newTile); + }); + } + + multiTileInitSubscription.dispose(); /* self-disposing subscription only runs once */ + self.tiles(savedTiles); + } + }); + + /* load previously persisted data */ + var multiTilePersistedDataSubscription = self.tiles.subscribe(function(tiles) { + if (self.previouslyPersistedComponentData && (!tiles || !tiles.length)) { + var persistedTiles = []; + + self.previouslyPersistedComponentData.forEach(function(persistedComponentData) { + var newTile = self.card().getNewTile(true); + + newTile.tileid = persistedComponentData.tileid; + newTile.data = koMapping.fromJS(persistedComponentData.data); + + persistedTiles.unshift(newTile); + }); + + multiTilePersistedDataSubscription.dispose(); /* self-disposing subscription only runs once */ + self.tiles(persistedTiles); + } + }); + + }; + + this.addOrUpdateTile = function() { + var tiles = self.tiles(); + self.tiles(null); + + /* breaks observable chain */ + var tileData = koMapping.fromJSON( + koMapping.toJSON(self.tile().data) + ); + + var tileLoadedInEditor = self.tileLoadedInEditor(); + + if (tileLoadedInEditor) { + var index = _.findIndex(tiles, tileLoadedInEditor); + + if (index > -1) { + tileLoadedInEditor.data = tileData; + tiles[index] = tileLoadedInEditor; + } + + + self.tileLoadedInEditor(null); + } + else { + var newTile = self.card().getNewTile(true); + newTile.data = tileData; + + tiles.unshift(newTile); + } + + self.tiles(tiles); + self.tile().reset(); + }; + + this.loadTileIntoEditor = function(data) { + self.tileLoadedInEditor(data); + + var tile = self.tile(); + + /* force the value of current tile data observables */ + Object.keys(tile.data).forEach(function(key) { + if (ko.isObservable(tile.data[key])) { + tile.data[key](data.data[key]()); + } + }); + }; + + this.tilesToRemove = ko.observableArray(); + + this.removeTile = function(data) { + var filteredTiles = self.tiles().filter(function(tile) { return tile !== data; }); + if (data.tileid) { + self.tilesToRemove.push(data); + } + self.tiles(filteredTiles); + }; + + this.saveMultiTiles = function() { + self.complete(false); + self.saving(true); + self.previouslyPersistedComponentData = []; + + var unorderedSavedData = ko.observableArray(); + + self.tiles().forEach(function(tile) { + tile.save( + function(){/* onFail */}, + function(savedTileData) { + unorderedSavedData.push(savedTileData); + } + ); + }); + + self.tilesToRemove().forEach(function(tile) { + tile.deleteTile( + function(response) { + self.alert(new AlertViewModel( + 'ep-alert-red', + response.responseJSON.title, + response.responseJSON.message, + null, + function(){ return; } + )); + }, + function() { + self.tilesToRemove.remove(tile); + if ( self.tilesToRemove().length === 0 ) { + self.complete(true); + self.loading(true); + self.saving(false); + } + } + ); + }); + + var saveSubscription = unorderedSavedData.subscribe(function(savedData) { + if (savedData.length === self.tiles().length) { + self.complete(true); + self.loading(true); + self.saving(false); + + var orderedSavedData = self.tiles().map(function(tile) { + return savedData.find(function(datum) { + return datum.tileid === tile.tileid; + }); + }); + + self.savedData(orderedSavedData.reverse()); + + saveSubscription.dispose(); /* self-disposing subscription only runs once */ + } + }); + }; + + this.clearEditor = function() { + self.tile().reset(); + }; + + this.reset = function() { + self.tileLoadedInEditor(null); + self.tile().reset(); + self.initialize(); + self.loading(false); + }; + + this.initialize(); + } + + + function WorkflowComponentAbstract(params) { + var self = this; + + this.workflowId = params.workflowId; + this.workflowName = params.workflowName; + this.componentData = params.componentData; + this.workflowHistory = params.workflowHistory; + this.alert = params.alert; + + this.locked = params.locked; + this.lockExternalStep = params.lockExternalStep; + this.lockableExternalSteps = params.lockableExternalSteps; + + this.id = ko.observable(); + + this.resourceId = ko.observable(); + + this.loading = ko.observable(false); + this.saving = ko.observable(false); + + this.savedComponentPaths = {}; + this.value = ko.observable(null); + + this.complete = ko.observable(false); + this.error = ko.observable(); + this.dirty = ko.observable(); /* user can manually set dirty state */ + + this.AlertViewModel = AlertViewModel; + this.saveOnQuit = ko.observable(); + + this.isStepActive = params.isStepActive; + this.isStepActive.subscribe(function(stepActive) { + if (stepActive) { + self.loadComponent(); + } + }); + + this.savedData = ko.observable(null); + this.savedData.subscribe(function(savedData) { + self.setToWorkflowHistory('value', savedData); + }); + + this.multiTileUpdated = ko.observable(); + this.hasUnsavedData = ko.computed(function() { + var hasUnsavedData = false; + + if (self.componentData.tilesManaged === "many") { + if (self.multiTileUpdated()) { + hasUnsavedData = true; + } + } else { + if (!_.isEqual(self.savedData(), self.value())) { + hasUnsavedData = true; + } + else if (self.dirty()) { + hasUnsavedData = true; + } + } + + return hasUnsavedData; + }); + + this.initialize = function() { + /* cached ID logic */ + if (params.workflowComponentAbstractId) { + self.id(params.workflowComponentAbstractId); + } + else { + self.id(uuid.generate()); + } + + const savedValue = self.getSavedValue(); + if (savedValue) { + self.savedData(savedValue); + self.value(savedValue); + self.complete(true); + } + }; + + this.loadComponent = function() { + self.loading(true); + + /* + Checks format of parameter values for external-component-path-patterned arrays. + If parameter value matches pattern, get external component data and update value in self.componentData + */ + if (self.componentData.parameters) { + Object.keys(self.componentData.parameters).forEach(function(componentDataKey) { + var componentDataValue = self.componentData.parameters[componentDataKey]; + + if (params.isValidComponentPath(componentDataValue)) { + self.componentData.parameters[componentDataKey] = params.getDataFromComponentPath(componentDataValue); + self.savedComponentPaths[componentDataKey] = componentDataValue; + } + else if (self.savedComponentPaths[componentDataKey]) { + self.componentData.parameters[componentDataKey] = params.getDataFromComponentPath(self.savedComponentPaths[componentDataKey]); + } + }); + } + + if (self.savedData()) { + self.value(self.savedData()); + self.complete(true); + } + + if (self.componentData && self.componentData['parameters']) { + self.resourceId(ko.unwrap(self.componentData['parameters']['resourceid'])); + self.componentData['parameters']['renderContext'] = 'workflow'; + } + + if (self.componentData.componentType === 'card') { + let previouslySavedValue = self.getSavedValue(); + let previouslySavedResourceInstanceId; + + if (previouslySavedValue) { + if (!(previouslySavedValue instanceof Array)) { previouslySavedValue = [previouslySavedValue]; } + + if (previouslySavedValue[0]['resourceInstanceId']) { + previouslySavedResourceInstanceId = previouslySavedValue[0]['resourceInstanceId']; + params['componentData']['parameters']['resourceid'] = previouslySavedResourceInstanceId; + } + } + + AbstractCardAdapter.apply(self); + } + + else if (!self.componentData.tilesManaged || self.componentData.tilesManaged === "none") { + NonTileBasedComponent.apply(self); + } + else if (self.componentData.tilesManaged === "one") { + TileBasedComponent.apply(self); + } + else if (self.componentData.tilesManaged === "many") { + MultipleTileBasedComponent.apply(self, [params.title] ); + } + }; + + this.setToWorkflowHistory = async function(key, value) { + const workflowid = self.workflowId; + const workflowname = self.workflowName; + + const workflowHistory = { + workflowid, + workflowname, + completed: false, + componentdata: { + // Django view will patch in this key, keeping existing keys + [self.id()]: { + [key]: value, + }, + }, + }; + + await fetch(arches.urls.workflow_history + workflowid, { + method: 'POST', + credentials: 'include', + headers: { + "X-CSRFToken": Cookies.get('csrftoken') + }, + body: JSON.stringify(workflowHistory), + }); + + }; + + this.getSavedValue = function() { + const savedValue = this.workflowHistory.componentdata?.[self.id()]; + if (savedValue) { + return savedValue['value']; + } + }; + + this.getCardResourceIdOrGraphId = function() { + return (ko.unwrap(self.componentData.parameters.resourceid) || ko.unwrap(self.componentData.parameters.graphid)); + }; + + this.save = function(){}; /* overwritten by inherited components */ + + this._saveComponent = function(componentBasedStepResolve, componentBasedStepReject) { + self.complete(false); + var completeSubscription = self.complete.subscribe(function(complete) { + if (complete) { + + if (componentBasedStepResolve) { + componentBasedStepResolve({ + [self.componentData.uniqueInstanceName]: self.savedData(), + }); + } + completeSubscription.dispose(); /* disposes after save */ + errorSubscription.dispose(); /* disposes after save */ + } + }); + + var errorSubscription = self.error.subscribe(function(error) { + if (error) { + + if (componentBasedStepReject) { + componentBasedStepReject(error); + self.error(null); + } + completeSubscription.dispose(); /* disposes after save */ + errorSubscription.dispose(); /* disposes after save */ + } + }); + + // only saves updated tiles + if (ko.unwrap(self.dirty) || ko.unwrap(self.hasDirtyTiles) || ko.unwrap(self.hasUnsavedData)) { + if (self.componentData.tilesManaged === "many"){ + self.saveMultiTiles(); + } else { + self.save(); + } + } + else { + self.complete(true); + } + }; + + this._resetComponent = function(componentBasedStepResolve) { + if (ko.unwrap(self.tile)) { + ko.unwrap(self.tile).reset(); + } + + componentBasedStepResolve(self.reset()); + }; + + this.initialize(); + } + + ko.components.register('workflow-component-abstract', { + template: workflowComponentAbstractTemplate, + }); + + return WorkflowComponentAbstract; +}); From 1f7516b5bca1ea7a300aaee1defa6dc916404501 Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Fri, 19 Jul 2024 09:03:51 +0100 Subject: [PATCH 5/7] feat: disable add button if mandatory field is not input --- .../components/workflows/workflow-component-abstract.js | 4 ++++ .../components/workflows/workflow-component-abstract.htm | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/coral/media/js/views/components/workflows/workflow-component-abstract.js b/coral/media/js/views/components/workflows/workflow-component-abstract.js index f6ff72873..935ec394a 100644 --- a/coral/media/js/views/components/workflows/workflow-component-abstract.js +++ b/coral/media/js/views/components/workflows/workflow-component-abstract.js @@ -42,6 +42,7 @@ define([ function TileBasedComponent() { var self = this; + this.disableAdd = ko.observable(false); this.tile = ko.observable(); this.tiles = ko.observable(); @@ -493,6 +494,9 @@ define([ }; this.addOrUpdateTile = function() { + + if(this.disableAdd()) return + var tiles = self.tiles(); self.tiles(null); diff --git a/coral/templates/views/components/workflows/workflow-component-abstract.htm b/coral/templates/views/components/workflows/workflow-component-abstract.htm index ee6fb7d51..4b24a4e82 100644 --- a/coral/templates/views/components/workflows/workflow-component-abstract.htm +++ b/coral/templates/views/components/workflows/workflow-component-abstract.htm @@ -13,7 +13,8 @@ ...componentData['parameters'], form: $data, pageVm: $root, - value: $data.value + value: $data.value, + disableAdd: disableAdd } }" > @@ -34,7 +35,8 @@ class="btn btn-workflow-tile btn-success" data-bind="{ click: addOrUpdateTile, - text: tileLoadedInEditor() ? 'Save' : 'Add' + text: tileLoadedInEditor() ? 'Save' : 'Add', + css: {disabled: disableAdd() } }" > From bdfe2a42f92ae8516a7f687a893956b5db9043e3 Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Fri, 19 Jul 2024 09:04:32 +0100 Subject: [PATCH 6/7] fix: edit button for multi cards --- .../workflows/workflow-component-abstract.js | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/coral/media/js/views/components/workflows/workflow-component-abstract.js b/coral/media/js/views/components/workflows/workflow-component-abstract.js index 935ec394a..634511040 100644 --- a/coral/media/js/views/components/workflows/workflow-component-abstract.js +++ b/coral/media/js/views/components/workflows/workflow-component-abstract.js @@ -529,15 +529,35 @@ define([ self.tile().reset(); }; - this.loadTileIntoEditor = function(data) { + self.resolveObservables = function (obj) { + Object.keys(obj).forEach(key => { + if (ko.isObservable(obj[key])) { + // If the property is an observable, call it to get its value + obj[key] = obj[key](); + } else if (typeof obj[key] === 'object' && obj[key] !== null) { + // If the property is an object, recursively process it + obj[key] = self.resolveObservables(obj[key]); + } + }); + return obj; + } + + this.loadTileIntoEditor = (data) => { self.tileLoadedInEditor(data); var tile = self.tile(); /* force the value of current tile data observables */ Object.keys(tile.data).forEach(function(key) { + + if (ko.isObservable(tile.data[key])) { - tile.data[key](data.data[key]()); + if (ko.isObservable(data.data[key])) { + tile.data[key](data.data[key]()); + } else { + self.resolveObservables(data.data[key]) + tile.data[key](data.data[key]); + } } }); }; From 3525f875c6d2de8bb8634da206971ed227a8a5f0 Mon Sep 17 00:00:00 2001 From: Stuart Marshall Date: Fri, 19 Jul 2024 10:45:22 +0100 Subject: [PATCH 7/7] fix: add disableAdd to all functions, remove console logs --- .../excavation-license-workflow/excavation-report-step.js | 2 +- .../components/workflows/workflow-component-abstract.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js b/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js index 6eaedbc67..e03b3ea83 100644 --- a/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js +++ b/coral/media/js/views/components/workflows/excavation-license-workflow/excavation-report-step.js @@ -17,7 +17,7 @@ define([ this.tile.data[this.DOCUMENT_REFERENCE_NODE].subscribe((newValue) => { if (newValue.en.value != ""){ - console.log(newValue) + params.disableAdd(false) } }) diff --git a/coral/media/js/views/components/workflows/workflow-component-abstract.js b/coral/media/js/views/components/workflows/workflow-component-abstract.js index 634511040..6cfb6e158 100644 --- a/coral/media/js/views/components/workflows/workflow-component-abstract.js +++ b/coral/media/js/views/components/workflows/workflow-component-abstract.js @@ -15,6 +15,7 @@ define([ function NonTileBasedComponent() { var self = this; + this.disableAdd = ko.observable(false); this.initialize = function() { self.loading(false); @@ -287,6 +288,8 @@ define([ var self = this; this.cardinality = ko.observable(); + this.disableAdd = ko.observable(false); + this.initialize = function() { self.loading(true); @@ -647,6 +650,8 @@ define([ function WorkflowComponentAbstract(params) { var self = this; + this.disableAdd = ko.observable(false); + this.workflowId = params.workflowId; this.workflowName = params.workflowName; this.componentData = params.componentData;