From 6cfa64163c616ff1dd4c49ffe5a571d3a66ff580 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Fri, 2 Aug 2024 14:46:11 +0200 Subject: [PATCH 01/24] Add order to items in Registry --- engine-registry/elm/Registry/Pages/DocumentTemplates.elm | 2 +- engine-registry/elm/Registry/Pages/KnowledgeModels.elm | 2 +- engine-registry/elm/Registry/Pages/Locales.elm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine-registry/elm/Registry/Pages/DocumentTemplates.elm b/engine-registry/elm/Registry/Pages/DocumentTemplates.elm index 390f13312..ebbdd9866 100644 --- a/engine-registry/elm/Registry/Pages/DocumentTemplates.elm +++ b/engine-registry/elm/Registry/Pages/DocumentTemplates.elm @@ -68,7 +68,7 @@ viewDocumentTemplates appState documentTemplates = let documentTemplateView = documentTemplates - |> List.sortBy (Time.toMillis appState.timeZone << .createdAt) + |> List.sortBy ((*) -1 << Time.posixToMillis << .createdAt) |> List.map (ListItem.view appState { toRoute = Routes.documentTemplateDetail << .id }) |> div [] in diff --git a/engine-registry/elm/Registry/Pages/KnowledgeModels.elm b/engine-registry/elm/Registry/Pages/KnowledgeModels.elm index ddf21dd01..7aa37e45a 100644 --- a/engine-registry/elm/Registry/Pages/KnowledgeModels.elm +++ b/engine-registry/elm/Registry/Pages/KnowledgeModels.elm @@ -68,7 +68,7 @@ viewKnowledgeModels appState knowledgeModels = let knowledgeModelsView = knowledgeModels - |> List.sortBy (Time.toMillis appState.timeZone << .createdAt) + |> List.sortBy ((*) -1 << Time.posixToMillis << .createdAt) |> List.map (ListItem.view appState { toRoute = Routes.knowledgeModelDetail << .id }) |> div [] in diff --git a/engine-registry/elm/Registry/Pages/Locales.elm b/engine-registry/elm/Registry/Pages/Locales.elm index 1d7d46e1c..05e228de3 100644 --- a/engine-registry/elm/Registry/Pages/Locales.elm +++ b/engine-registry/elm/Registry/Pages/Locales.elm @@ -68,7 +68,7 @@ viewLocales appState documentTemplates = let localeView = documentTemplates - |> List.sortBy (Time.toMillis appState.timeZone << .createdAt) + |> List.sortBy ((*) -1 << Time.posixToMillis << .createdAt) |> List.map (ListItem.view appState { toRoute = Routes.localeDetail << .id }) |> div [] in From 89a8fa74a3e6344a6cca2ea8791ef7803f3e9ad7 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Fri, 2 Aug 2024 14:53:32 +0200 Subject: [PATCH 02/24] Improve wording for no tags KM selection --- engine-wizard/elm/Wizard/Common/View/Tag.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine-wizard/elm/Wizard/Common/View/Tag.elm b/engine-wizard/elm/Wizard/Common/View/Tag.elm index a0708baa3..68afe8bac 100644 --- a/engine-wizard/elm/Wizard/Common/View/Tag.elm +++ b/engine-wizard/elm/Wizard/Common/View/Tag.elm @@ -155,7 +155,7 @@ selection appState selectionConfig knowledgeModelResult = else viewContent <| Flash.info appState <| - gettext "There are no question tags for this knowledge model." appState.locale + gettext "No need to choose question tags for this knowledge model." appState.locale readOnlyList : AppState -> List String -> List Tag -> Html msg From 60e3f5c2947cd8fac82cbc5f0634de06e74ea2ba Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Mon, 5 Aug 2024 13:32:56 +0200 Subject: [PATCH 03/24] Fix wrongly displayed time in About modal in Registry --- engine-registry/elm/Registry/Components/AboutModal.elm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/engine-registry/elm/Registry/Components/AboutModal.elm b/engine-registry/elm/Registry/Components/AboutModal.elm index d80048b4e..c14f42fbb 100644 --- a/engine-registry/elm/Registry/Components/AboutModal.elm +++ b/engine-registry/elm/Registry/Components/AboutModal.elm @@ -12,9 +12,12 @@ import Gettext exposing (gettext) import Html exposing (Html, a, button, code, div, em, h5, table, tbody, td, text, th, thead, tr) import Html.Attributes exposing (class, classList, colspan, href, target) import Html.Events exposing (onClick) +import Json.Decode as D +import Json.Decode.Extra as D import Registry.Api.BuildInfo as BuildInfoApi import Registry.Components.Page as Page import Registry.Data.AppState exposing (AppState) +import Shared.Common.TimeUtils as TimeUtils import Shared.Data.BuildInfo as BuildInfo exposing (BuildInfo) import Shared.Error.ApiError as ApiError exposing (ApiError) @@ -130,6 +133,11 @@ viewBuildInfo appState name buildInfo extra = [ td [] [ text title ] , td [] [ value ] ] + + buildAtValue = + D.decodeString D.datetime ("\"" ++ buildInfo.builtAt ++ "\"") + |> Result.map (TimeUtils.toReadableDateTime appState.timeZone) + |> Result.withDefault buildInfo.builtAt in table [ class "table table-borderless table-build-info" ] [ thead [] @@ -143,7 +151,7 @@ viewBuildInfo appState name buildInfo extra = ] , tr [] [ td [] [ text (gettext "Built at" appState.locale) ] - , td [] [ em [] [ text buildInfo.builtAt ] ] + , td [] [ em [] [ text buildAtValue ] ] ] ] ++ List.map viewExtraRow extra From 76a70d65fd2d5bd3a4e6bc445b8e70e83673308e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 06:05:47 +0000 Subject: [PATCH 04/24] Bump @codemirror/view from 6.29.0 to 6.29.1 Bumps [@codemirror/view](https://github.com/codemirror/view) from 6.29.0 to 6.29.1. - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/compare/6.29.0...6.29.1) --- updated-dependencies: - dependency-name: "@codemirror/view" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05fe9b274..1bf87c64a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@codemirror/language": "^6.10.2", "@codemirror/legacy-modes": "^6.4.0", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.29.0", + "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.2", "axios-retry": "^4.4.2", @@ -249,9 +249,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "node_modules/@codemirror/view": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.0.tgz", - "integrity": "sha512-ED4ims4fkf7eOA+HYLVP8VVg3NMllt1FPm9PEJBfYFnidKlRITBaua38u68L1F60eNtw2YNcDN5jsIzhKZwWQA==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", + "integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -10722,9 +10722,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "@codemirror/view": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.0.tgz", - "integrity": "sha512-ED4ims4fkf7eOA+HYLVP8VVg3NMllt1FPm9PEJBfYFnidKlRITBaua38u68L1F60eNtw2YNcDN5jsIzhKZwWQA==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", + "integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", "requires": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", diff --git a/package.json b/package.json index a5b9fb5bb..30a9a9b65 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@codemirror/language": "^6.10.2", "@codemirror/legacy-modes": "^6.4.0", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.29.0", + "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.2", "axios-retry": "^4.4.2", From 3c529b81a4aae708c36273eaa74adb00fa86211c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Aug 2024 06:06:08 +0000 Subject: [PATCH 05/24] Bump uglify-js from 3.19.0 to 3.19.1 Bumps [uglify-js](https://github.com/mishoo/UglifyJS) from 3.19.0 to 3.19.1. - [Release notes](https://github.com/mishoo/UglifyJS/releases) - [Commits](https://github.com/mishoo/UglifyJS/compare/v3.19.0...v3.19.1) --- updated-dependencies: - dependency-name: uglify-js dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1bf87c64a..cb33bb4ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "sass": "^1.77.8", "sass-loader": "^16.0.0", "terser-webpack-plugin": "^5.3.10", - "uglify-js": "^3.19.0", + "uglify-js": "^3.19.1", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" @@ -9654,9 +9654,9 @@ } }, "node_modules/uglify-js": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", - "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", + "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", "dev": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -17727,9 +17727,9 @@ } }, "uglify-js": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", - "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", + "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", "dev": true }, "unicorn-magic": { diff --git a/package.json b/package.json index 30a9a9b65..15543633e 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "sass": "^1.77.8", "sass-loader": "^16.0.0", "terser-webpack-plugin": "^5.3.10", - "uglify-js": "^3.19.0", + "uglify-js": "^3.19.1", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" From 6862a5846eeca2e04e5d2470144bcf1e10e8c6c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:56:02 +0000 Subject: [PATCH 06/24] Bump axios-retry from 4.4.2 to 4.5.0 Bumps [axios-retry](https://github.com/softonic/axios-retry) from 4.4.2 to 4.5.0. - [Changelog](https://github.com/softonic/axios-retry/blob/master/CHANGELOG.md) - [Commits](https://github.com/softonic/axios-retry/compare/v4.4.2...v4.5.0) --- updated-dependencies: - dependency-name: axios-retry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb33bb4ea..14183d10b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.2", - "axios-retry": "^4.4.2", + "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", "codemirror": "^6.0.1", @@ -1690,9 +1690,9 @@ } }, "node_modules/axios-retry": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.4.2.tgz", - "integrity": "sha512-2fjo9uDNBQjX8+GMEGOFG5TrLMZ3QijjeRYcgBI2MhrsabnvcIAfLxxIhG7+CCD68vPEQY3IM2llV70ZsrqPvA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", "dependencies": { "is-retry-allowed": "^2.2.0" }, @@ -11916,9 +11916,9 @@ } }, "axios-retry": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.4.2.tgz", - "integrity": "sha512-2fjo9uDNBQjX8+GMEGOFG5TrLMZ3QijjeRYcgBI2MhrsabnvcIAfLxxIhG7+CCD68vPEQY3IM2llV70ZsrqPvA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", "requires": { "is-retry-allowed": "^2.2.0" } diff --git a/package.json b/package.json index 15543633e..1baedd2f1 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.2", - "axios-retry": "^4.4.2", + "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", "codemirror": "^6.0.1", From 03c84891e18c27711ad5b609633df6e20d6e84ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:05:09 +0000 Subject: [PATCH 07/24] Bump axios from 1.7.2 to 1.7.3 Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.7.3. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.3) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 14183d10b..ebf7e9c79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", - "axios": "1.7.2", + "axios": "1.7.3", "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", @@ -1680,9 +1680,9 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -11894,9 +11894,9 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 1baedd2f1..400445174 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", - "axios": "1.7.2", + "axios": "1.7.3", "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", From fb4ae7737586452c3de93c8d80e6d7ed9e24cf5b Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 7 Aug 2024 09:44:46 +0200 Subject: [PATCH 08/24] Fix chapter names are displayed when they have some resolved comments but the switch is turned off --- .../Wizard/Common/Components/Questionnaire.elm | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm index b80d54c9f..ff3e69fe9 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm @@ -2176,10 +2176,18 @@ viewQuestionnaireRightPanelCommentsOverview : AppState -> Model -> Html Msg viewQuestionnaireRightPanelCommentsOverview appState model = let viewChapterComments group = - div [] - [ strong [] [ text group.chapter.title ] - , ul [ class "fa-ul" ] (List.map viewQuestionComments group.comments) - ] + let + anyUnresolvedComments = + List.any (\c -> c.unresolvedComments > 0) group.comments + in + if not model.commentsViewResolved && not anyUnresolvedComments then + emptyNode + + else + div [] + [ strong [] [ text group.chapter.title ] + , ul [ class "fa-ul" ] (List.map viewQuestionComments group.comments) + ] viewQuestionComments comment = if not model.commentsViewResolved && comment.unresolvedComments == 0 then From fb252b3a06801b36b6bef95691de06027995e152 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 7 Aug 2024 10:44:59 +0200 Subject: [PATCH 09/24] Show ongoing migration on all project subpages --- .../Common/Components/Questionnaire.elm | 27 +++------------- .../elm/Wizard/Projects/Detail/View.elm | 31 ++++++++++++++++-- .../Common/Components/_Questionnaire.scss | 8 ----- .../scss/modules/Projects/_Detail.scss | 32 +++++++++++++++++++ 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm index ff3e69fe9..79a6a121c 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm @@ -109,7 +109,7 @@ import Wizard.Common.Components.Questionnaire.UserSuggestionDropdown as UserSugg import Wizard.Common.Components.Questionnaire.VersionModal as VersionModal import Wizard.Common.ElementScrollTop as ElementScrollTop import Wizard.Common.Feature as Feature -import Wizard.Common.Html exposing (illustratedMessage, linkTo, resizableTextarea) +import Wizard.Common.Html exposing (illustratedMessage, resizableTextarea) import Wizard.Common.Html.Attribute exposing (dataCy, grammarlyAttributes, linkToAttributes, tooltip, tooltipLeft, tooltipRight) import Wizard.Common.IntegrationWidgetValue exposing (IntegrationWidgetValue) import Wizard.Common.Integrations as Integrations @@ -1587,34 +1587,17 @@ view appState cfg ctx model = else ( emptyNode, False ) - ( migrationWarning, migrationWarningEnabled ) = - case model.questionnaire.migrationUuid of - Just migrationUuid -> - let - warningLink = - linkTo appState (Routes.projectsMigration migrationUuid) [] [ text (gettext "project migration" appState.locale) ] - - warning = - gettext "There is an ongoing %s. Finish it before you can continue editing this project." appState.locale - |> flip String.formatHtml [ warningLink ] - in - ( div [ class "questionnaire__warning" ] - [ div [ class "alert alert-warning" ] warning ] - , True - ) - - Nothing -> - ( emptyNode, False ) - splitPaneConfig = SplitPane.createViewConfig { toMsg = cfg.wrapMsg << SplitPaneMsg , customSplitter = Nothing } in - div [ class "questionnaire", classList [ ( "toolbar-enabled", toolbarEnabled ), ( "warning-enabled", migrationWarningEnabled ) ] ] + div + [ class "questionnaire" + , classList [ ( "toolbar-enabled", toolbarEnabled ) ] + ] [ toolbar - , migrationWarning , div [ class "questionnaire__body" ] [ SplitPane.view splitPaneConfig (Html.map cfg.wrapMsg <| viewQuestionnaireLeftPanel appState cfg model) diff --git a/engine-wizard/elm/Wizard/Projects/Detail/View.elm b/engine-wizard/elm/Wizard/Projects/Detail/View.elm index 34a76107b..2309fda40 100644 --- a/engine-wizard/elm/Wizard/Projects/Detail/View.elm +++ b/engine-wizard/elm/Wizard/Projects/Detail/View.elm @@ -5,7 +5,7 @@ import Bootstrap.Button as Button import Bootstrap.Dropdown as Dropdown import Gettext exposing (gettext) import Html exposing (Html, button, div, p, text) -import Html.Attributes exposing (class) +import Html.Attributes exposing (class, classList) import Html.Events exposing (onClick) import Shared.Auth.Session as Session import Shared.Components.Badge as Badge @@ -13,6 +13,8 @@ import Shared.Data.PaginationQueryString as PaginationQueryString import Shared.Data.QuestionnaireCommon exposing (QuestionnaireCommon) import Shared.Html exposing (emptyNode, fa, faSet) import Shared.Undraw as Undraw +import Shared.Utils exposing (flip) +import String.Format as String import Wizard.Common.AppState as AppState exposing (AppState) import Wizard.Common.Components.ActionResultView as ActionResultView import Wizard.Common.Components.DetailNavigation as DetailNavigation @@ -20,6 +22,7 @@ import Wizard.Common.Components.Questionnaire as Questionnaire import Wizard.Common.Components.Questionnaire.DefaultQuestionnaireRenderer as DefaultQuestionnaireRenderer import Wizard.Common.Components.SummaryReport as SummaryReport import Wizard.Common.Feature as Features +import Wizard.Common.Html exposing (linkTo) import Wizard.Common.Html.Attribute exposing (dataCy) import Wizard.Common.QuestionnaireUtils as QuestionnaireUtils import Wizard.Common.View.ActionButton as ActionButton @@ -89,6 +92,24 @@ viewError appState = viewProject : ProjectDetailRoute -> AppState -> Model -> QuestionnaireCommon -> Html Msg viewProject route appState model questionnaire = let + ( migrationWarning, migrationWarningEnabled ) = + case questionnaire.migrationUuid of + Just migrationUuid -> + let + warningLink = + linkTo appState (Wizard.Routes.projectsMigration migrationUuid) [] [ text (gettext "project migration" appState.locale) ] + + warningContent = + gettext "There is an ongoing %s. Finish it before you can continue editing this project." appState.locale + |> flip String.formatHtml [ warningLink ] + in + ( div [ class "Projects__Detail__Warning" ] [ div [] warningContent ] + , True + ) + + Nothing -> + ( emptyNode, False ) + navigation = if AppState.isFullscreen appState then emptyNode @@ -107,8 +128,12 @@ viewProject route appState model questionnaire = |> ActionResult.withDefault [] } in - div [ class "Projects__Detail col-full flex-column" ] - [ navigation + div + [ class "Projects__Detail col-full flex-column" + , classList [ ( "Projects__Detail--Warning", migrationWarningEnabled ) ] + ] + [ migrationWarning + , navigation , viewProjectContent appState route model questionnaire , Html.map ShareModalMsg <| ShareModal.view appState model.shareModalModel , Html.map QuestionnaireVersionViewModalMsg <| QuestionnaireVersionViewModal.view modalConfig appState model.questionnaireVersionViewModalModel diff --git a/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss b/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss index f151864d6..26f457f60 100644 --- a/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss +++ b/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss @@ -13,14 +13,6 @@ $toolbar-height: 40px; } } - &.warning-enabled { - position: relative; - - .questionnaire__body { - padding-top: 45px; - } - } - &__warning { position: absolute; width: 100%; diff --git a/engine-wizard/scss/modules/Projects/_Detail.scss b/engine-wizard/scss/modules/Projects/_Detail.scss index 868e78ec4..6baeb1e73 100644 --- a/engine-wizard/scss/modules/Projects/_Detail.scss +++ b/engine-wizard/scss/modules/Projects/_Detail.scss @@ -4,11 +4,14 @@ @import 'Detail/QuestionnaireVersionViewModal'; @import 'Detail/UserSuggestionDropdown'; +$warning-height: 40px; $nav-row-height: 32px; $nav-row-padding: 16px; $header-height: 2 * ($nav-row-height + $nav-row-padding) + 1; $content-height: calc(100vh - #{$header-height}); +$content-with-warning-height: calc(100vh - #{$header-height + $warning-height}); $public-content-height: calc(100vh - #{$header-height + $public-header-height}); +$public-content-with-warning-height: calc(100vh - #{$header-height + $warning-height + $public-header-height}); .Projects__Detail { @@ -18,6 +21,16 @@ $public-content-height: calc(100vh - #{$header-height + $public-header-height}); .questionnaire { height: $public-content-height; } + + &--Warning { + .Projects__Detail__Content { + height: $public-content-with-warning-height !important; + } + + .questionnaire { + height: $public-content-with-warning-height !important; + } + } } .app-fullscreen & { @@ -28,6 +41,25 @@ $public-content-height: calc(100vh - #{$header-height + $public-header-height}); } } + &--Warning { + .Projects__Detail__Content { + height: $content-with-warning-height !important; + } + + .questionnaire { + height: $content-with-warning-height !important; + } + } + + &__Warning { + flex: 0 0 $warning-height; + background: $warning; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + } + &__Content { height: $content-height; overflow: auto; From 083e33e31683559d0ab022c66121c54581b08d34 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Tue, 20 Aug 2024 12:23:27 +0200 Subject: [PATCH 10/24] Add KM appendix --- .../elm/Shared/Api/BookReferences.elm | 10 - .../elm/Shared/Data/BookReference.elm | 22 -- engine-shared/elm/Shared/Data/Event.elm | 76 ++++++ .../Data/Event/AddReferenceEventData.elm | 2 +- .../AddReferenceResourcePageEventData.elm | 8 +- .../Event/AddResourceCollectionEventData.elm | 50 ++++ .../Data/Event/AddResourcePageEventData.elm | 54 ++++ .../Shared/Data/Event/EditEventSetters.elm | 30 +- .../Event/EditKnowledgeModelEventData.elm | 5 + .../Data/Event/EditReferenceEventData.elm | 4 +- .../EditReferenceResourcePageEventData.elm | 8 +- .../Event/EditResourceCollectionEventData.elm | 55 ++++ .../Data/Event/EditResourcePageEventData.elm | 55 ++++ .../elm/Shared/Data/KnowledgeModel.elm | 104 ++++++- .../KnowledgeModel/KnowledgeModelEntities.elm | 40 +++ .../Shared/Data/KnowledgeModel/Reference.elm | 15 +- .../Reference/ResourcePageReferenceData.elm | 16 +- .../KnowledgeModel/ResourceCollection.elm | 31 +++ .../Data/KnowledgeModel/ResourcePage.elm | 25 ++ engine-shared/elm/Shared/Setters.elm | 6 - engine-wizard/elm/Wizard.elm | 7 +- .../DefaultQuestionnaireRenderer.elm | 97 +++++-- .../DiffQuestionnaireRenderer.elm | 2 +- .../Common/Provisioning/DefaultIconSet.elm | 2 + .../Common/Provisioning/DefaultLocale.elm | 1 - .../KMEditor/Editor/Common/EditorBranch.elm | 97 ++++++- .../KMEditor/Editor/Components/KMEditor.elm | 243 ++++++++++++++++- .../Editor/Components/KMEditor/Input.elm | 37 ++- .../Editor/Components/KMEditor/Tree.elm | 60 +++- .../Editor/Components/KMEditor/TreeInput.elm | 2 +- .../KMEditor/Editor/Components/Preview.elm | 6 +- .../elm/Wizard/KMEditor/Migration/View.elm | 203 +++++++++++++- .../KMEditor/Migration/View/DiffTree.elm | 87 +++++- .../elm/Wizard/KnowledgeModels/Models.elm | 6 + .../elm/Wizard/KnowledgeModels/Msgs.elm | 2 + .../Wizard/KnowledgeModels/Preview/View.elm | 5 +- .../KnowledgeModels/ResourcePage/Models.elm | 17 ++ .../KnowledgeModels/ResourcePage/Msgs.elm | 8 + .../KnowledgeModels/ResourcePage/Update.elm | 31 +++ .../KnowledgeModels/ResourcePage/View.elm | 38 +++ .../elm/Wizard/KnowledgeModels/Routes.elm | 1 + .../elm/Wizard/KnowledgeModels/Routing.elm | 14 +- .../Wizard/KnowledgeModels/Subscriptions.elm | 3 + .../elm/Wizard/KnowledgeModels/Update.elm | 12 + .../elm/Wizard/KnowledgeModels/View.elm | 4 + .../QuestionnaireVersionViewModal.elm | 2 +- .../elm/Wizard/Projects/Detail/View.elm | 2 +- .../elm/Wizard/Projects/Import/View.elm | 2 +- .../Wizard/Public/BookReference/Models.elm | 18 -- .../elm/Wizard/Public/BookReference/Msgs.elm | 8 - .../Wizard/Public/BookReference/Update.elm | 31 --- .../elm/Wizard/Public/BookReference/View.elm | 49 ---- engine-wizard/elm/Wizard/Public/Models.elm | 6 - engine-wizard/elm/Wizard/Public/Msgs.elm | 2 - engine-wizard/elm/Wizard/Public/Routes.elm | 1 - engine-wizard/elm/Wizard/Public/Routing.elm | 4 - engine-wizard/elm/Wizard/Public/Update.elm | 12 - engine-wizard/elm/Wizard/Public/View.elm | 5 - engine-wizard/elm/Wizard/Routes.elm | 6 + engine-wizard/img/book-preview.png | Bin 18008 -> 0 bytes engine-wizard/img/crc-logo.png | Bin 45795 -> 0 bytes .../KnowledgeModels/_ResourcePage.scss | 15 + .../scss/modules/Public/_BookReference.scss | 43 --- .../scss/modules/_KnowledgeModels.scss | 1 + engine-wizard/scss/modules/_Public.scss | 1 - tests/Shared/Data/EventTest.elm | 257 +++++++++++++++++- .../KnowledgeModelEntitiesTest.elm | 58 +++- .../Data/KnowledgeModel/ReferenceTest.elm | 4 +- tests/Shared/Data/KnowledgeModelTest.elm | 48 +++- 69 files changed, 1838 insertions(+), 338 deletions(-) delete mode 100644 engine-shared/elm/Shared/Api/BookReferences.elm delete mode 100644 engine-shared/elm/Shared/Data/BookReference.elm create mode 100644 engine-shared/elm/Shared/Data/Event/AddResourceCollectionEventData.elm create mode 100644 engine-shared/elm/Shared/Data/Event/AddResourcePageEventData.elm create mode 100644 engine-shared/elm/Shared/Data/Event/EditResourceCollectionEventData.elm create mode 100644 engine-shared/elm/Shared/Data/Event/EditResourcePageEventData.elm create mode 100644 engine-shared/elm/Shared/Data/KnowledgeModel/ResourceCollection.elm create mode 100644 engine-shared/elm/Shared/Data/KnowledgeModel/ResourcePage.elm create mode 100644 engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Models.elm create mode 100644 engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Msgs.elm create mode 100644 engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Update.elm create mode 100644 engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/View.elm delete mode 100644 engine-wizard/elm/Wizard/Public/BookReference/Models.elm delete mode 100644 engine-wizard/elm/Wizard/Public/BookReference/Msgs.elm delete mode 100644 engine-wizard/elm/Wizard/Public/BookReference/Update.elm delete mode 100644 engine-wizard/elm/Wizard/Public/BookReference/View.elm delete mode 100644 engine-wizard/img/book-preview.png delete mode 100644 engine-wizard/img/crc-logo.png create mode 100644 engine-wizard/scss/modules/KnowledgeModels/_ResourcePage.scss delete mode 100644 engine-wizard/scss/modules/Public/_BookReference.scss diff --git a/engine-shared/elm/Shared/Api/BookReferences.elm b/engine-shared/elm/Shared/Api/BookReferences.elm deleted file mode 100644 index c72d818a8..000000000 --- a/engine-shared/elm/Shared/Api/BookReferences.elm +++ /dev/null @@ -1,10 +0,0 @@ -module Shared.Api.BookReferences exposing (getBookReference) - -import Shared.AbstractAppState exposing (AbstractAppState) -import Shared.Api exposing (ToMsg, httpGet) -import Shared.Data.BookReference as BookReference exposing (BookReference) - - -getBookReference : String -> AbstractAppState a -> ToMsg BookReference msg -> Cmd msg -getBookReference uuid = - httpGet ("/book-references/" ++ uuid) BookReference.decoder diff --git a/engine-shared/elm/Shared/Data/BookReference.elm b/engine-shared/elm/Shared/Data/BookReference.elm deleted file mode 100644 index 25119647c..000000000 --- a/engine-shared/elm/Shared/Data/BookReference.elm +++ /dev/null @@ -1,22 +0,0 @@ -module Shared.Data.BookReference exposing - ( BookReference - , decoder - ) - -import Json.Decode as D exposing (Decoder) -import Json.Decode.Pipeline as D - - -type alias BookReference = - { shortUuid : String - , content : String - , bookChapter : String - } - - -decoder : Decoder BookReference -decoder = - D.succeed BookReference - |> D.required "shortUuid" D.string - |> D.required "content" D.string - |> D.required "bookChapter" D.string diff --git a/engine-shared/elm/Shared/Data/Event.elm b/engine-shared/elm/Shared/Data/Event.elm index 79c931c9e..63a00fea5 100644 --- a/engine-shared/elm/Shared/Data/Event.elm +++ b/engine-shared/elm/Shared/Data/Event.elm @@ -18,6 +18,8 @@ import Shared.Data.Event.AddMetricEventData as AddMetricEventData exposing (AddM import Shared.Data.Event.AddPhaseEventData as AddPhaseEventData exposing (AddPhaseEventData) import Shared.Data.Event.AddQuestionEventData as AddQuestionEventData exposing (AddQuestionEventData) import Shared.Data.Event.AddReferenceEventData as AddReferenceEventData exposing (AddReferenceEventData) +import Shared.Data.Event.AddResourceCollectionEventData as AddResourceCollectionEventData exposing (AddResourceCollectionEventData) +import Shared.Data.Event.AddResourcePageEventData as AddResourcePageEventData exposing (AddResourcePageEventData) import Shared.Data.Event.AddTagEventData as AddTagEventData exposing (AddTagEventData) import Shared.Data.Event.CommonEventData as CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditAnswerEventData as EditAnswerEventData exposing (EditAnswerEventData) @@ -30,6 +32,8 @@ import Shared.Data.Event.EditMetricEventData as EditMetricEventData exposing (Ed import Shared.Data.Event.EditPhaseEventData as EditPhaseEventData exposing (EditPhaseEventData) import Shared.Data.Event.EditQuestionEventData as EditQuestionEventData exposing (EditQuestionEventData) import Shared.Data.Event.EditReferenceEventData as EditReferenceEventData exposing (EditReferenceEventData) +import Shared.Data.Event.EditResourceCollectionEventData as EditResourceCollectionEventData exposing (EditResourceCollectionEventData) +import Shared.Data.Event.EditResourcePageEventData as EditResourcePageEventData exposing (EditResourcePageEventData) import Shared.Data.Event.EditTagEventData as EditTagEventData exposing (EditTagEventData) import Shared.Data.Event.EventField as EventField import Shared.Data.Event.MoveEventData as MoveEventData exposing (MoveEventData) @@ -68,6 +72,12 @@ type Event | AddExpertEvent AddExpertEventData CommonEventData | EditExpertEvent EditExpertEventData CommonEventData | DeleteExpertEvent CommonEventData + | AddResourceCollectionEvent AddResourceCollectionEventData CommonEventData + | EditResourceCollectionEvent EditResourceCollectionEventData CommonEventData + | DeleteResourceCollectionEvent CommonEventData + | AddResourcePageEvent AddResourcePageEventData CommonEventData + | EditResourcePageEvent EditResourcePageEventData CommonEventData + | DeleteResourcePageEvent CommonEventData | MoveQuestionEvent MoveEventData CommonEventData | MoveAnswerEvent MoveEventData CommonEventData | MoveChoiceEvent MoveEventData CommonEventData @@ -180,6 +190,24 @@ decoderByType eventType = "DeleteExpertEvent" -> D.map DeleteExpertEvent CommonEventData.decoder + "AddResourceCollectionEvent" -> + D.map2 AddResourceCollectionEvent AddResourceCollectionEventData.decoder CommonEventData.decoder + + "EditResourceCollectionEvent" -> + D.map2 EditResourceCollectionEvent EditResourceCollectionEventData.decoder CommonEventData.decoder + + "DeleteResourceCollectionEvent" -> + D.map DeleteResourceCollectionEvent CommonEventData.decoder + + "AddResourcePageEvent" -> + D.map2 AddResourcePageEvent AddResourcePageEventData.decoder CommonEventData.decoder + + "EditResourcePageEvent" -> + D.map2 EditResourcePageEvent EditResourcePageEventData.decoder CommonEventData.decoder + + "DeleteResourcePageEvent" -> + D.map DeleteResourcePageEvent CommonEventData.decoder + "MoveQuestionEvent" -> D.map2 MoveQuestionEvent MoveEventData.decoder CommonEventData.decoder @@ -300,6 +328,24 @@ encode event = DeleteExpertEvent commonData -> ( [ ( "eventType", E.string "DeleteExpertEvent" ) ], CommonEventData.encode commonData ) + AddResourceCollectionEvent eventData commonData -> + ( AddResourceCollectionEventData.encode eventData, CommonEventData.encode commonData ) + + EditResourceCollectionEvent eventData commonData -> + ( EditResourceCollectionEventData.encode eventData, CommonEventData.encode commonData ) + + DeleteResourceCollectionEvent commonData -> + ( [ ( "eventType", E.string "DeleteResourceCollectionEvent" ) ], CommonEventData.encode commonData ) + + AddResourcePageEvent eventData commonData -> + ( AddResourcePageEventData.encode eventData, CommonEventData.encode commonData ) + + EditResourcePageEvent eventData commonData -> + ( EditResourcePageEventData.encode eventData, CommonEventData.encode commonData ) + + DeleteResourcePageEvent commonData -> + ( [ ( "eventType", E.string "DeleteResourcePageEvent" ) ], CommonEventData.encode commonData ) + MoveQuestionEvent eventData commonData -> ( MoveEventData.encode "MoveQuestionEvent" eventData, CommonEventData.encode commonData ) @@ -422,6 +468,24 @@ getCommonData event = DeleteExpertEvent commonData -> commonData + AddResourceCollectionEvent _ commonData -> + commonData + + EditResourceCollectionEvent _ commonData -> + commonData + + DeleteResourceCollectionEvent commonData -> + commonData + + AddResourcePageEvent _ commonData -> + commonData + + EditResourcePageEvent _ commonData -> + commonData + + DeleteResourcePageEvent commonData -> + commonData + MoveQuestionEvent _ commonData -> commonData @@ -507,5 +571,17 @@ getEntityVisibleName event = EditExpertEvent eventData _ -> EventField.getValue eventData.name + AddResourceCollectionEvent eventData _ -> + Just eventData.title + + EditResourceCollectionEvent eventData _ -> + EventField.getValue eventData.title + + AddResourcePageEvent eventData _ -> + Just eventData.title + + EditResourcePageEvent eventData _ -> + EventField.getValue eventData.title + _ -> Nothing diff --git a/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm b/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm index 04cd353e7..32850859c 100644 --- a/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm @@ -75,7 +75,7 @@ toReference referenceUuid data = getEntityVisibleName : AddReferenceEventData -> Maybe String getEntityVisibleName = - Just << map .shortUuid .label .targetUuid + Just << map .resourcePageUuid .label .targetUuid map : diff --git a/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm index 0efccdb01..1f24beb27 100644 --- a/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm @@ -13,7 +13,7 @@ import Shared.Data.KnowledgeModel.Reference exposing (Reference(..)) type alias AddReferenceResourcePageEventData = - { shortUuid : String + { resourcePageUuid : String , annotations : List Annotation } @@ -21,14 +21,14 @@ type alias AddReferenceResourcePageEventData = decoder : Decoder AddReferenceResourcePageEventData decoder = D.succeed AddReferenceResourcePageEventData - |> D.required "shortUuid" D.string + |> D.required "resourcePageUuid" D.string |> D.required "annotations" (D.list Annotation.decoder) encode : AddReferenceResourcePageEventData -> List ( String, E.Value ) encode data = [ ( "referenceType", E.string "ResourcePageReference" ) - , ( "shortUuid", E.string data.shortUuid ) + , ( "resourcePageUuid", E.string data.resourcePageUuid ) , ( "annotations", E.list Annotation.encode data.annotations ) ] @@ -37,6 +37,6 @@ toReference : String -> AddReferenceResourcePageEventData -> Reference toReference uuid data = ResourcePageReference { uuid = uuid - , shortUuid = data.shortUuid + , resourcePageUuid = data.resourcePageUuid , annotations = data.annotations } diff --git a/engine-shared/elm/Shared/Data/Event/AddResourceCollectionEventData.elm b/engine-shared/elm/Shared/Data/Event/AddResourceCollectionEventData.elm new file mode 100644 index 000000000..7f606bf8a --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/AddResourceCollectionEventData.elm @@ -0,0 +1,50 @@ +module Shared.Data.Event.AddResourceCollectionEventData exposing + ( AddResourceCollectionEventData + , decoder + , encode + , init + , toResourceCollection + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) + + +type alias AddResourceCollectionEventData = + { title : String + , annotations : List Annotation + } + + +decoder : Decoder AddResourceCollectionEventData +decoder = + D.succeed AddResourceCollectionEventData + |> D.required "title" D.string + |> D.required "annotations" (D.list Annotation.decoder) + + +encode : AddResourceCollectionEventData -> List ( String, E.Value ) +encode eventData = + [ ( "eventType", E.string "AddResourceCollectionEvent" ) + , ( "title", E.string eventData.title ) + , ( "annotations", E.list Annotation.encode eventData.annotations ) + ] + + +init : AddResourceCollectionEventData +init = + { title = "" + , annotations = [] + } + + +toResourceCollection : String -> AddResourceCollectionEventData -> ResourceCollection +toResourceCollection resourceCollectionUuid data = + { uuid = resourceCollectionUuid + , title = data.title + , resourcePageUuids = [] + , annotations = data.annotations + } diff --git a/engine-shared/elm/Shared/Data/Event/AddResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/AddResourcePageEventData.elm new file mode 100644 index 000000000..fe29fba77 --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/AddResourcePageEventData.elm @@ -0,0 +1,54 @@ +module Shared.Data.Event.AddResourcePageEventData exposing + ( AddResourcePageEventData + , decoder + , encode + , init + , toResourcePage + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) + + +type alias AddResourcePageEventData = + { title : String + , content : String + , annotations : List Annotation + } + + +decoder : Decoder AddResourcePageEventData +decoder = + D.succeed AddResourcePageEventData + |> D.required "title" D.string + |> D.required "content" D.string + |> D.required "annotations" (D.list Annotation.decoder) + + +encode : AddResourcePageEventData -> List ( String, E.Value ) +encode eventData = + [ ( "eventType", E.string "AddResourcePageEvent" ) + , ( "title", E.string eventData.title ) + , ( "content", E.string eventData.content ) + , ( "annotations", E.list Annotation.encode eventData.annotations ) + ] + + +init : AddResourcePageEventData +init = + { title = "" + , content = "" + , annotations = [] + } + + +toResourcePage : String -> AddResourcePageEventData -> ResourcePage +toResourcePage resourcePageUuid data = + { uuid = resourcePageUuid + , title = data.title + , content = data.content + , annotations = data.annotations + } diff --git a/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm b/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm index 6a8519d62..f09e68bbb 100644 --- a/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm +++ b/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm @@ -6,6 +6,7 @@ module Shared.Data.Event.EditEventSetters exposing , setChapterUuids , setChoiceUuids , setColor + , setContent , setDescription , setEmail , setExpertUuids @@ -30,10 +31,12 @@ module Shared.Data.Event.EditEventSetters exposing , setRequestMethod , setRequestUrl , setRequiredPhaseUuid + , setResourceCollectionUuids + , setResourcePageUuid + , setResourcePageUuids , setResponseItemId , setResponseItemTemplate , setResponseListField - , setShortUuid , setTagUuids , setText , setTitle @@ -80,6 +83,11 @@ setColor value data = { data | color = EventField.create value True } +setContent : a -> { b | content : EventField a } -> { b | content : EventField a } +setContent value data = + { data | content = EventField.create value True } + + setDescription : a -> { b | description : EventField a } -> { b | description : EventField a } setDescription value data = { data | description = EventField.create value True } @@ -200,6 +208,21 @@ setRequiredPhaseUuid value data = { data | requiredPhaseUuid = EventField.create value True } +setResourceCollectionUuids : a -> { b | resourceCollectionUuids : EventField a } -> { b | resourceCollectionUuids : EventField a } +setResourceCollectionUuids value data = + { data | resourceCollectionUuids = EventField.create value True } + + +setResourcePageUuid : a -> { b | resourcePageUuid : EventField a } -> { b | resourcePageUuid : EventField a } +setResourcePageUuid value data = + { data | resourcePageUuid = EventField.create value True } + + +setResourcePageUuids : a -> { b | resourcePageUuids : EventField a } -> { b | resourcePageUuids : EventField a } +setResourcePageUuids value data = + { data | resourcePageUuids = EventField.create value True } + + setResponseItemId : a -> { b | responseItemId : EventField a } -> { b | responseItemId : EventField a } setResponseItemId value data = { data | responseItemId = EventField.create value True } @@ -215,11 +238,6 @@ setResponseListField value data = { data | responseListField = EventField.create value True } -setShortUuid : a -> { b | shortUuid : EventField a } -> { b | shortUuid : EventField a } -setShortUuid value data = - { data | shortUuid = EventField.create value True } - - setTagUuids : a -> { b | tagUuids : EventField a } -> { b | tagUuids : EventField a } setTagUuids value data = { data | tagUuids = EventField.create value True } diff --git a/engine-shared/elm/Shared/Data/Event/EditKnowledgeModelEventData.elm b/engine-shared/elm/Shared/Data/Event/EditKnowledgeModelEventData.elm index 9df11c9c5..863d79c67 100644 --- a/engine-shared/elm/Shared/Data/Event/EditKnowledgeModelEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditKnowledgeModelEventData.elm @@ -20,6 +20,7 @@ type alias EditKnowledgeModelEventData = , phaseUuids : EventField (List String) , tagUuids : EventField (List String) , integrationUuids : EventField (List String) + , resourceCollectionUuids : EventField (List String) , annotations : EventField (List Annotation) } @@ -32,6 +33,7 @@ decoder = |> D.required "phaseUuids" (EventField.decoder (D.list D.string)) |> D.required "tagUuids" (EventField.decoder (D.list D.string)) |> D.required "integrationUuids" (EventField.decoder (D.list D.string)) + |> D.required "resourceCollectionUuids" (EventField.decoder (D.list D.string)) |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) @@ -43,6 +45,7 @@ encode data = , ( "phaseUuids", EventField.encode (E.list E.string) data.phaseUuids ) , ( "tagUuids", EventField.encode (E.list E.string) data.tagUuids ) , ( "integrationUuids", EventField.encode (E.list E.string) data.integrationUuids ) + , ( "resourceCollectionUuids", EventField.encode (E.list E.string) data.resourceCollectionUuids ) , ( "annotations", EventField.encode (E.list Annotation.encode) data.annotations ) ] @@ -54,6 +57,7 @@ init = , phaseUuids = EventField.empty , tagUuids = EventField.empty , integrationUuids = EventField.empty + , resourceCollectionUuids = EventField.empty , annotations = EventField.empty } @@ -66,5 +70,6 @@ apply eventData km = , phaseUuids = EventField.applyChildren eventData.phaseUuids km.phaseUuids , tagUuids = EventField.applyChildren eventData.tagUuids km.tagUuids , integrationUuids = EventField.applyChildren eventData.integrationUuids km.integrationUuids + , resourceCollectionUuids = EventField.applyChildren eventData.resourceCollectionUuids km.resourceCollectionUuids , annotations = EventField.getValueWithDefault eventData.annotations km.annotations } diff --git a/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm b/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm index 25d2334a0..7a2b4e06d 100644 --- a/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm @@ -61,7 +61,7 @@ apply event reference = EditReferenceResourcePageEvent eventData -> ResourcePageReference { uuid = Reference.getUuid reference - , shortUuid = EventField.getValueWithDefault eventData.shortUuid (Maybe.withDefault "" (Reference.getShortUuid reference)) + , resourcePageUuid = EventField.getValueWithDefault eventData.resourcePageUuid (Maybe.withDefault "" (Reference.getResourcePageUuid reference)) , annotations = EventField.getValueWithDefault eventData.annotations (Reference.getAnnotations reference) } @@ -84,7 +84,7 @@ apply event reference = getEntityVisibleName : EditReferenceEventData -> Maybe String getEntityVisibleName = - EventField.getValue << map .shortUuid .label .targetUuid + EventField.getValue << map .resourcePageUuid .label .targetUuid map : diff --git a/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm index 99571f1a4..6cc259c95 100644 --- a/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm @@ -13,7 +13,7 @@ import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) type alias EditReferenceResourcePageEventData = - { shortUuid : EventField String + { resourcePageUuid : EventField String , annotations : EventField (List Annotation) } @@ -21,20 +21,20 @@ type alias EditReferenceResourcePageEventData = decoder : Decoder EditReferenceResourcePageEventData decoder = D.succeed EditReferenceResourcePageEventData - |> D.required "shortUuid" (EventField.decoder D.string) + |> D.required "resourcePageUuid" (EventField.decoder D.string) |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) encode : EditReferenceResourcePageEventData -> List ( String, E.Value ) encode data = [ ( "referenceType", E.string "ResourcePageReference" ) - , ( "shortUuid", EventField.encode E.string data.shortUuid ) + , ( "resourcePageUuid", EventField.encode E.string data.resourcePageUuid ) , ( "annotations", EventField.encode (E.list Annotation.encode) data.annotations ) ] init : EditReferenceResourcePageEventData init = - { shortUuid = EventField.empty + { resourcePageUuid = EventField.empty , annotations = EventField.empty } diff --git a/engine-shared/elm/Shared/Data/Event/EditResourceCollectionEventData.elm b/engine-shared/elm/Shared/Data/Event/EditResourceCollectionEventData.elm new file mode 100644 index 000000000..13044a36c --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/EditResourceCollectionEventData.elm @@ -0,0 +1,55 @@ +module Shared.Data.Event.EditResourceCollectionEventData exposing + ( EditResourceCollectionEventData + , apply + , decoder + , encode + , init + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Shared.Data.Event.EventField as EventField exposing (EventField) +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) + + +type alias EditResourceCollectionEventData = + { title : EventField String + , resourcePageUuids : EventField (List String) + , annotations : EventField (List Annotation) + } + + +decoder : Decoder EditResourceCollectionEventData +decoder = + D.succeed EditResourceCollectionEventData + |> D.required "title" (EventField.decoder D.string) + |> D.required "resourcePageUuids" (EventField.decoder (D.list D.string)) + |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) + + +encode : EditResourceCollectionEventData -> List ( String, E.Value ) +encode eventData = + [ ( "eventType", E.string "EditResourceCollectionEvent" ) + , ( "title", EventField.encode E.string eventData.title ) + , ( "resourcePageUuids", EventField.encode (E.list E.string) eventData.resourcePageUuids ) + , ( "annotations", EventField.encode (E.list Annotation.encode) eventData.annotations ) + ] + + +init : EditResourceCollectionEventData +init = + { title = EventField.empty + , resourcePageUuids = EventField.empty + , annotations = EventField.empty + } + + +apply : EditResourceCollectionEventData -> ResourceCollection -> ResourceCollection +apply eventData resourceCollection = + { resourceCollection + | title = EventField.getValueWithDefault eventData.title resourceCollection.title + , resourcePageUuids = EventField.applyChildren eventData.resourcePageUuids resourceCollection.resourcePageUuids + , annotations = EventField.getValueWithDefault eventData.annotations resourceCollection.annotations + } diff --git a/engine-shared/elm/Shared/Data/Event/EditResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/EditResourcePageEventData.elm new file mode 100644 index 000000000..6b55bfd77 --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/EditResourcePageEventData.elm @@ -0,0 +1,55 @@ +module Shared.Data.Event.EditResourcePageEventData exposing + ( EditResourcePageEventData + , apply + , decoder + , encode + , init + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Shared.Data.Event.EventField as EventField exposing (EventField) +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) + + +type alias EditResourcePageEventData = + { title : EventField String + , content : EventField String + , annotations : EventField (List Annotation) + } + + +decoder : Decoder EditResourcePageEventData +decoder = + D.succeed EditResourcePageEventData + |> D.required "title" (EventField.decoder D.string) + |> D.required "content" (EventField.decoder D.string) + |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) + + +encode : EditResourcePageEventData -> List ( String, E.Value ) +encode eventData = + [ ( "eventType", E.string "EditResourcePageEvent" ) + , ( "title", EventField.encode E.string eventData.title ) + , ( "content", EventField.encode E.string eventData.content ) + , ( "annotations", EventField.encode (E.list Annotation.encode) eventData.annotations ) + ] + + +init : EditResourcePageEventData +init = + { title = EventField.empty + , content = EventField.empty + , annotations = EventField.empty + } + + +apply : EditResourcePageEventData -> ResourcePage -> ResourcePage +apply eventData resourcePage = + { resourcePage + | title = EventField.getValueWithDefault eventData.title resourcePage.title + , content = EventField.getValueWithDefault eventData.content resourcePage.content + , annotations = EventField.getValueWithDefault eventData.annotations resourcePage.annotations + } diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel.elm b/engine-shared/elm/Shared/Data/KnowledgeModel.elm index 3ab956748..f14e2b4ec 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel.elm @@ -6,6 +6,7 @@ module Shared.Data.KnowledgeModel exposing , empty , filterWithTags , getAllQuestions + , getAllResourcePages , getAnswer , getAnswerFollowupQuestions , getAnswerName @@ -36,6 +37,14 @@ module Shared.Data.KnowledgeModel exposing , getQuestionReferences , getReference , getReferenceName + , getResourceCollection + , getResourceCollectionByResourcePageUuid + , getResourceCollectionName + , getResourceCollectionResourcePages + , getResourceCollectionUuidByResourcePageUuid + , getResourceCollections + , getResourcePage + , getResourcePageName , getTag , getTagName , getTags @@ -48,6 +57,8 @@ module Shared.Data.KnowledgeModel exposing , insertPhase , insertQuestion , insertReference + , insertResourceCollection + , insertResourcePage , insertTag , moveAnswer , moveChoice @@ -63,12 +74,15 @@ module Shared.Data.KnowledgeModel exposing , updatePhase , updateQuestion , updateReference + , updateResourceCollection + , updateResourcePage , updateTag ) import Dict exposing (Dict) import Json.Decode as D exposing (Decoder) import Json.Decode.Pipeline as D +import List.Extra as List import Maybe.Extra as Maybe import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) import Shared.Data.KnowledgeModel.Answer exposing (Answer) @@ -81,6 +95,8 @@ import Shared.Data.KnowledgeModel.Metric exposing (Metric) import Shared.Data.KnowledgeModel.Phase exposing (Phase) import Shared.Data.KnowledgeModel.Question as Question exposing (Question) import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.Utils exposing (nilUuid) import Uuid exposing (Uuid) @@ -93,6 +109,7 @@ type alias KnowledgeModel = , integrationUuids : List String , metricUuids : List String , phaseUuids : List String + , resourceCollectionUuids : List String , entities : KnowledgeModelEntities , annotations : List Annotation } @@ -111,6 +128,7 @@ decoder = |> D.required "integrationUuids" (D.list D.string) |> D.required "metricUuids" (D.list D.string) |> D.required "phaseUuids" (D.list D.string) + |> D.required "resourceCollectionUuids" (D.list D.string) |> D.required "entities" KnowledgeModelEntities.decoder |> D.required "annotations" (D.list Annotation.decoder) @@ -123,6 +141,7 @@ empty = , integrationUuids = [] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , entities = KnowledgeModelEntities.empty , annotations = [] } @@ -189,6 +208,19 @@ insertReference reference parentUuid km = { km | entities = KnowledgeModelEntities.insertReference reference parentUuid km.entities } +insertResourceCollection : ResourceCollection -> String -> KnowledgeModel -> KnowledgeModel +insertResourceCollection resourceCollection _ km = + { km + | resourceCollectionUuids = km.resourceCollectionUuids ++ [ resourceCollection.uuid ] + , entities = KnowledgeModelEntities.insertResourceCollection resourceCollection km.entities + } + + +insertResourcePage : ResourcePage -> String -> KnowledgeModel -> KnowledgeModel +insertResourcePage resourcePage parentUuid km = + { km | entities = KnowledgeModelEntities.insertResourcePage resourcePage parentUuid km.entities } + + insertTag : Tag -> String -> KnowledgeModel -> KnowledgeModel insertTag tag _ km = { km @@ -246,6 +278,16 @@ updateReference reference km = { km | entities = KnowledgeModelEntities.updateReference reference km.entities } +updateResourceCollection : ResourceCollection -> KnowledgeModel -> KnowledgeModel +updateResourceCollection resourceCollection km = + { km | entities = KnowledgeModelEntities.updateResourceCollection resourceCollection km.entities } + + +updateResourcePage : ResourcePage -> KnowledgeModel -> KnowledgeModel +updateResourcePage resourcePage km = + { km | entities = KnowledgeModelEntities.updateResourcePage resourcePage km.entities } + + updateTag : Tag -> KnowledgeModel -> KnowledgeModel updateTag tag km = { km | entities = KnowledgeModelEntities.insertTag tag km.entities } @@ -334,6 +376,33 @@ getReference uuid km = Dict.get uuid km.entities.references +getResourceCollection : String -> KnowledgeModel -> Maybe ResourceCollection +getResourceCollection uuid km = + Dict.get uuid km.entities.resourceCollections + + +getResourceCollectionUuidByResourcePageUuid : String -> KnowledgeModel -> Maybe String +getResourceCollectionUuidByResourcePageUuid resourcePageUuid km = + getResourceCollectionByResourcePageUuid resourcePageUuid km + |> Maybe.map .uuid + + +getResourceCollectionByResourcePageUuid : String -> KnowledgeModel -> Maybe ResourceCollection +getResourceCollectionByResourcePageUuid resourcePageUuid km = + Dict.values km.entities.resourceCollections + |> List.find (\rc -> List.member resourcePageUuid rc.resourcePageUuids) + + +getResourcePage : String -> KnowledgeModel -> Maybe ResourcePage +getResourcePage uuid km = + Dict.get uuid km.entities.resourcePages + + +getAllResourcePages : KnowledgeModel -> List ResourcePage +getAllResourcePages km = + Dict.values km.entities.resourcePages + + getExpert : String -> KnowledgeModel -> Maybe Expert getExpert uuid km = Dict.get uuid km.entities.experts @@ -385,7 +454,17 @@ getExpertName km uuid = getReferenceName : KnowledgeModel -> String -> String getReferenceName km uuid = - Maybe.unwrap "" Reference.getVisibleName <| getReference uuid km + Maybe.unwrap "" (Reference.getVisibleName (getAllResourcePages km)) <| getReference uuid km + + +getResourceCollectionName : KnowledgeModel -> String -> String +getResourceCollectionName km uuid = + Maybe.unwrap "" .title <| getResourceCollection uuid km + + +getResourcePageName : KnowledgeModel -> String -> String +getResourcePageName km uuid = + Maybe.unwrap "" .title <| getResourcePage uuid km getChoiceName : KnowledgeModel -> String -> String @@ -417,6 +496,11 @@ getPhases km = resolveEntities km.entities.phases km.phaseUuids +getResourceCollections : KnowledgeModel -> List ResourceCollection +getResourceCollections km = + resolveEntities km.entities.resourceCollections km.resourceCollectionUuids + + getTags : KnowledgeModel -> List Tag getTags km = resolveEntities km.entities.tags km.tagUuids @@ -457,6 +541,11 @@ getAnswerFollowupQuestions = getEntities .answers .followUpUuids .questions +getResourceCollectionResourcePages : String -> KnowledgeModel -> List ResourcePage +getResourceCollectionResourcePages = + getEntities .resourceCollections .resourcePageUuids .resourcePages + + getEntities : (KnowledgeModelEntities -> Dict String parent) -> (parent -> List String) @@ -497,6 +586,7 @@ createParentMap km = |> insert_ .phaseUuids |> insert_ .tagUuids |> insert_ .integrationUuids + |> insert_ .resourceCollectionUuids processChapter chapter dict = let @@ -526,6 +616,14 @@ createParentMap km = dict |> insert_ .followUpUuids + processResourceCollection resourceCollection dict = + let + insert_ getChildUuids = + insert resourceCollection.uuid (getChildUuids resourceCollection) + in + dict + |> insert_ .resourcePageUuids + processChapters chapters dict = List.foldl processChapter dict <| Dict.values chapters @@ -534,12 +632,16 @@ createParentMap km = processAnswers answers dict = List.foldl processAnswer dict <| Dict.values answers + + processResourceCollections resourceCollections dict = + List.foldl processResourceCollection dict <| Dict.values resourceCollections in Dict.empty |> processKM km |> processChapters km.entities.chapters |> processQuestions km.entities.questions |> processAnswers km.entities.answers + |> processResourceCollections km.entities.resourceCollections getParent : ParentMap -> String -> String diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/KnowledgeModelEntities.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/KnowledgeModelEntities.elm index cc8d7389c..8b642896f 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/KnowledgeModelEntities.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/KnowledgeModelEntities.elm @@ -11,6 +11,8 @@ module Shared.Data.KnowledgeModel.KnowledgeModelEntities exposing , insertPhase , insertQuestion , insertReference + , insertResourceCollection + , insertResourcePage , insertTag , moveAnswer , moveChoice @@ -23,6 +25,8 @@ module Shared.Data.KnowledgeModel.KnowledgeModelEntities exposing , updateQuestion , updateQuestions , updateReference + , updateResourceCollection + , updateResourcePage , updateTags ) @@ -38,6 +42,8 @@ import Shared.Data.KnowledgeModel.Metric as Metric exposing (Metric) import Shared.Data.KnowledgeModel.Phase as Phase exposing (Phase) import Shared.Data.KnowledgeModel.Question as Question exposing (Question) import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference) +import Shared.Data.KnowledgeModel.ResourceCollection as ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage as ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag as Tag exposing (Tag) @@ -48,6 +54,8 @@ type alias KnowledgeModelEntities = , choices : Dict String Choice , experts : Dict String Expert , references : Dict String Reference + , resourceCollections : Dict String ResourceCollection + , resourcePages : Dict String ResourcePage , integrations : Dict String Integration , tags : Dict String Tag , metrics : Dict String Metric @@ -64,6 +72,8 @@ decoder = |> D.required "choices" (D.dict Choice.decoder) |> D.required "experts" (D.dict Expert.decoder) |> D.required "references" (D.dict Reference.decoder) + |> D.required "resourceCollections" (D.dict ResourceCollection.decoder) + |> D.required "resourcePages" (D.dict ResourcePage.decoder) |> D.required "integrations" (D.dict Integration.decoder) |> D.required "tags" (D.dict Tag.decoder) |> D.required "metrics" (D.dict Metric.decoder) @@ -78,6 +88,8 @@ empty = , choices = Dict.empty , experts = Dict.empty , references = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty , integrations = Dict.empty , tags = Dict.empty , metrics = Dict.empty @@ -192,6 +204,24 @@ insertReference reference questionUuid entities = entities +insertResourceCollection : ResourceCollection -> KnowledgeModelEntities -> KnowledgeModelEntities +insertResourceCollection resourceCollection entities = + { entities | resourceCollections = Dict.insert resourceCollection.uuid resourceCollection entities.resourceCollections } + + +insertResourcePage : ResourcePage -> String -> KnowledgeModelEntities -> KnowledgeModelEntities +insertResourcePage resourcePage resourceCollectionUuid entities = + case Dict.get resourceCollectionUuid entities.resourceCollections of + Just resourceCollection -> + { entities + | resourcePages = Dict.insert resourcePage.uuid resourcePage entities.resourcePages + , resourceCollections = Dict.insert resourceCollectionUuid (ResourceCollection.addResourcePageUuid resourcePage.uuid resourceCollection) entities.resourceCollections + } + + Nothing -> + entities + + insertTag : Tag -> KnowledgeModelEntities -> KnowledgeModelEntities insertTag tag entities = { entities | tags = Dict.insert tag.uuid tag entities.tags } @@ -343,6 +373,16 @@ updateReference reference entities = { entities | references = Dict.insert (Reference.getUuid reference) reference entities.references } +updateResourceCollection : ResourceCollection -> KnowledgeModelEntities -> KnowledgeModelEntities +updateResourceCollection resourceCollection entities = + { entities | resourceCollections = Dict.insert resourceCollection.uuid resourceCollection entities.resourceCollections } + + +updateResourcePage : ResourcePage -> KnowledgeModelEntities -> KnowledgeModelEntities +updateResourcePage resourcePage entities = + { entities | resourcePages = Dict.insert resourcePage.uuid resourcePage entities.resourcePages } + + -- diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm index 7543999d6..50ad8abb3 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm @@ -4,7 +4,7 @@ module Shared.Data.KnowledgeModel.Reference exposing , getAnnotations , getDescription , getLabel - , getShortUuid + , getResourcePageUuid , getTargetUuid , getTypeString , getUrl @@ -20,6 +20,7 @@ import Shared.Data.KnowledgeModel.Reference.CrossReferenceData as CrossReference import Shared.Data.KnowledgeModel.Reference.ReferenceType as ReferenceType exposing (ReferenceType(..)) import Shared.Data.KnowledgeModel.Reference.ResourcePageReferenceData as ResourcePageReferenceData exposing (ResourcePageReferenceData) import Shared.Data.KnowledgeModel.Reference.URLReferenceData as URLReferenceData exposing (URLReferenceData) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) type Reference @@ -83,9 +84,9 @@ getUuid = map .uuid .uuid .uuid -getVisibleName : Reference -> String -getVisibleName = - map .shortUuid URLReferenceData.toLabel .targetUuid +getVisibleName : List ResourcePage -> Reference -> String +getVisibleName resourcePages = + map (ResourcePageReferenceData.toLabel resourcePages) URLReferenceData.toLabel .targetUuid getAnnotations : Reference -> List Annotation @@ -93,9 +94,9 @@ getAnnotations = map .annotations .annotations .annotations -getShortUuid : Reference -> Maybe String -getShortUuid = - map (Just << .shortUuid) (always Nothing) (always Nothing) +getResourcePageUuid : Reference -> Maybe String +getResourcePageUuid = + map (Just << .resourcePageUuid) (always Nothing) (always Nothing) getUrl : Reference -> Maybe String diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm index 1c12c4a73..1930caff4 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm @@ -1,16 +1,18 @@ module Shared.Data.KnowledgeModel.Reference.ResourcePageReferenceData exposing ( ResourcePageReferenceData , decoder + , toLabel ) import Json.Decode as D exposing (Decoder) import Json.Decode.Pipeline as D import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) type alias ResourcePageReferenceData = { uuid : String - , shortUuid : String + , resourcePageUuid : String , annotations : List Annotation } @@ -19,5 +21,15 @@ decoder : Decoder ResourcePageReferenceData decoder = D.succeed ResourcePageReferenceData |> D.required "uuid" D.string - |> D.required "shortUuid" D.string + |> D.required "resourcePageUuid" D.string |> D.required "annotations" (D.list Annotation.decoder) + + +toLabel : List ResourcePage -> ResourcePageReferenceData -> String +toLabel resourcePages data = + case List.head <| List.filter (\rp -> rp.uuid == data.resourcePageUuid) resourcePages of + Just resourcePage -> + resourcePage.title + + Nothing -> + "" diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/ResourceCollection.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/ResourceCollection.elm new file mode 100644 index 000000000..7f2e13390 --- /dev/null +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/ResourceCollection.elm @@ -0,0 +1,31 @@ +module Shared.Data.KnowledgeModel.ResourceCollection exposing + ( ResourceCollection + , addResourcePageUuid + , decoder + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) + + +type alias ResourceCollection = + { uuid : String + , title : String + , resourcePageUuids : List String + , annotations : List Annotation + } + + +decoder : Decoder ResourceCollection +decoder = + D.succeed ResourceCollection + |> D.required "uuid" D.string + |> D.required "title" D.string + |> D.required "resourcePageUuids" (D.list D.string) + |> D.required "annotations" (D.list Annotation.decoder) + + +addResourcePageUuid : String -> ResourceCollection -> ResourceCollection +addResourcePageUuid resourcePageUuid resourceCollection = + { resourceCollection | resourcePageUuids = resourceCollection.resourcePageUuids ++ [ resourcePageUuid ] } diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/ResourcePage.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/ResourcePage.elm new file mode 100644 index 000000000..32bdd62c6 --- /dev/null +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/ResourcePage.elm @@ -0,0 +1,25 @@ +module Shared.Data.KnowledgeModel.ResourcePage exposing + ( ResourcePage + , decoder + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) + + +type alias ResourcePage = + { uuid : String + , title : String + , content : String + , annotations : List Annotation + } + + +decoder : Decoder ResourcePage +decoder = + D.succeed ResourcePage + |> D.required "uuid" D.string + |> D.required "title" D.string + |> D.required "content" D.string + |> D.required "annotations" (D.list Annotation.decoder) diff --git a/engine-shared/elm/Shared/Setters.elm b/engine-shared/elm/Shared/Setters.elm index ed6a1094b..8b03076b2 100644 --- a/engine-shared/elm/Shared/Setters.elm +++ b/engine-shared/elm/Shared/Setters.elm @@ -3,7 +3,6 @@ module Shared.Setters exposing , setApiKeys , setAppKeys , setAssets - , setBookReference , setCommentThreads , setDebouncer , setDropdownState @@ -50,11 +49,6 @@ setAssets value record = { record | assets = value } -setBookReference : a -> { b | bookReference : a } -> { b | bookReference : a } -setBookReference value record = - { record | bookReference = value } - - setCommentThreads : a -> { b | commentThreads : a } -> { b | commentThreads : a } setCommentThreads value record = { record | commentThreads = value } diff --git a/engine-wizard/elm/Wizard.elm b/engine-wizard/elm/Wizard.elm index 0c3503015..d2c3550a9 100644 --- a/engine-wizard/elm/Wizard.elm +++ b/engine-wizard/elm/Wizard.elm @@ -13,7 +13,6 @@ import Wizard.Models exposing (Model, initLocalModel, initialModel, userLoggedIn import Wizard.Msgs exposing (Msg) import Wizard.Ports as Ports import Wizard.Projects.Routes as PlansRoutes -import Wizard.Public.Routes import Wizard.Routes as Routes import Wizard.Routing as Routing exposing (cmdNavigate, routeIfAllowed, toUrl) import Wizard.Subscriptions exposing (subscriptions) @@ -69,9 +68,6 @@ decideInitialRoute model location route originalRoute = case route of Routes.PublicRoute subroute -> case ( userLoggedIn model, subroute ) of - ( True, Wizard.Public.Routes.BookReferenceRoute _ ) -> - dispatchUrlChange - ( True, _ ) -> cmdNavigate model.appState Routes.DashboardRoute @@ -87,6 +83,9 @@ decideInitialRoute model location route originalRoute = Routes.KnowledgeModelsRoute (KnowledgeModelsRoute.PreviewRoute _ _) -> dispatchUrlChange + Routes.KnowledgeModelsRoute (KnowledgeModelsRoute.ResourcePageRoute _ _) -> + dispatchUrlChange + _ -> if userLoggedIn model then dispatchUrlChange diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm index 8dd04e2a6..bbaba46aa 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm @@ -1,5 +1,7 @@ -module Wizard.Common.Components.Questionnaire.DefaultQuestionnaireRenderer exposing (create) +module Wizard.Common.Components.Questionnaire.DefaultQuestionnaireRenderer exposing (create, defaultResourcePageToRoute) +import Dict +import Dict.Extra as Dict import Gettext exposing (gettext) import Html exposing (Html, a, div, p, span, text) import Html.Attributes exposing (class, href, target) @@ -18,15 +20,18 @@ import Shared.Data.KnowledgeModel.Reference.ResourcePageReferenceData exposing ( import Shared.Data.KnowledgeModel.Reference.URLReferenceData exposing (URLReferenceData) import Shared.Html exposing (emptyNode, faSet) import Shared.Markdown as Markdown +import Shared.Utils exposing (flip) import Wizard.Common.AppState exposing (AppState) import Wizard.Common.Components.Questionnaire exposing (QuestionnaireRenderer) import Wizard.Common.Components.Questionnaire.QuestionnaireViewSettings exposing (QuestionnaireViewSettings) +import Wizard.Common.Html exposing (linkTo) +import Wizard.Routes -create : AppState -> KnowledgeModel -> QuestionnaireRenderer msg -create appState km = +create : AppState -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> QuestionnaireRenderer msg +create appState km resourcePageToRoute = { renderQuestionLabel = renderQuestionLabel - , renderQuestionDescription = renderQuestionDescription appState km + , renderQuestionDescription = renderQuestionDescription appState km resourcePageToRoute , getQuestionExtraClass = always Nothing , renderAnswerLabel = renderAnswerLabel , renderAnswerBadges = renderAnswerBadges (KnowledgeModel.getMetrics km) @@ -35,13 +40,18 @@ create appState km = } +defaultResourcePageToRoute : String -> String -> Wizard.Routes.Route +defaultResourcePageToRoute packageId = + Wizard.Routes.knowledgeModelsResourcePage packageId + + renderQuestionLabel : Question -> Html msg renderQuestionLabel question = text <| Question.getTitle question -renderQuestionDescription : AppState -> KnowledgeModel -> QuestionnaireViewSettings -> Question -> Html msg -renderQuestionDescription appState km qvs question = +renderQuestionDescription : AppState -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> QuestionnaireViewSettings -> Question -> Html msg +renderQuestionDescription appState km resourcePageToRoute qvs question = let description = Question.getText question @@ -52,7 +62,7 @@ renderQuestionDescription appState km qvs question = KnowledgeModel.getPhases km extraData = - viewExtraData appState qvs phases <| createQuestionExtraData km question + viewExtraData appState qvs km resourcePageToRoute phases <| createQuestionExtraData km question in div [ class "description" ] [ description @@ -155,8 +165,8 @@ createQuestionExtraData km question = |> List.foldl foldReferences newExtraData -viewExtraData : AppState -> QuestionnaireViewSettings -> List Phase -> FormExtraData -> Html msg -viewExtraData appState qvs phases data = +viewExtraData : AppState -> QuestionnaireViewSettings -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> List Phase -> FormExtraData -> Html msg +viewExtraData appState qvs km resourcePageToRoute phases data = let isEmpty = List.isEmpty data.resourcePageReferences @@ -169,11 +179,12 @@ viewExtraData appState qvs phases data = else p [ class "extra-data" ] - [ viewRequiredLevel appState qvs phases data.requiredPhaseUuid - , viewResourcePageReferences appState data.resourcePageReferences - , viewUrlReferences appState data.urlReferences - , viewExperts appState data.experts - ] + (viewRequiredLevel appState qvs phases data.requiredPhaseUuid + :: viewResourcePageReferences appState km resourcePageToRoute data.resourcePageReferences + ++ [ viewUrlReferences appState data.urlReferences + , viewExperts appState data.experts + ] + ) viewRequiredLevel : AppState -> QuestionnaireViewSettings -> List Phase -> Maybe String -> Html msg @@ -215,19 +226,51 @@ viewExtraItems cfg list = (span [ class "caption" ] [ cfg.icon, text (cfg.label ++ ": ") ] :: items) -viewResourcePageReferences : AppState -> List ResourcePageReferenceData -> Html msg -viewResourcePageReferences appState = - viewExtraItems - { icon = faSet "questionnaire.resourcePageReferences" appState - , label = "Data Stewardship for Open Science" - , viewItem = viewResourcePageReference - } - - -viewResourcePageReference : ResourcePageReferenceData -> Html msg -viewResourcePageReference data = - a [ href <| "/wizard/book-references/" ++ data.shortUuid, target "_blank" ] - [ text data.shortUuid ] +viewResourcePageReferences : AppState -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> List ResourcePageReferenceData -> List (Html msg) +viewResourcePageReferences appState km resourcePageToRoute resourcePageReferences = + let + resources = + Dict.filterGroupBy + (flip KnowledgeModel.getResourceCollectionUuidByResourcePageUuid km << .resourcePageUuid) + resourcePageReferences + + viewResourceCollection ( resourceCollectionUuid, collectionResourcePageReferences ) = + let + resourceCollection = + KnowledgeModel.getResourceCollection resourceCollectionUuid km + in + case resourceCollection of + Just rc -> + Just <| + ( rc.title + , viewExtraItems + { icon = faSet "questionnaire.resourcePageReferences" appState + , label = rc.title + , viewItem = viewResourcePageReference appState km resourcePageToRoute + } + collectionResourcePageReferences + ) + + Nothing -> + Nothing + in + Dict.toList resources + |> List.filterMap viewResourceCollection + |> List.sortBy Tuple.first + |> List.map Tuple.second + + +viewResourcePageReference : AppState -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> ResourcePageReferenceData -> Html msg +viewResourcePageReference appState km resourcePageToRoute data = + case KnowledgeModel.getResourcePage data.resourcePageUuid km of + Just resourcePage -> + linkTo appState + (resourcePageToRoute resourcePage.uuid) + [ target "_blank" ] + [ text resourcePage.title ] + + Nothing -> + emptyNode viewUrlReferences : AppState -> List URLReferenceData -> Html msg diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DiffQuestionnaireRenderer.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DiffQuestionnaireRenderer.elm index caf19874f..796830495 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DiffQuestionnaireRenderer.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DiffQuestionnaireRenderer.elm @@ -24,7 +24,7 @@ create : AppState -> QuestionnaireMigration -> QuestionnaireChanges -> Knowledge create appState migration changes km mbSelectedChange = let defaultRenderer = - DefaultQuestionnaireRenderer.create appState km + DefaultQuestionnaireRenderer.create appState km (DefaultQuestionnaireRenderer.defaultResourcePageToRoute migration.newQuestionnaire.packageId) getExtraQuestionClass question = if Just (Question.getUuid question) == Maybe.map QuestionChange.getQuestionUuid mbSelectedChange then diff --git a/engine-wizard/elm/Wizard/Common/Provisioning/DefaultIconSet.elm b/engine-wizard/elm/Wizard/Common/Provisioning/DefaultIconSet.elm index 66378eacf..314ae6fe1 100644 --- a/engine-wizard/elm/Wizard/Common/Provisioning/DefaultIconSet.elm +++ b/engine-wizard/elm/Wizard/Common/Provisioning/DefaultIconSet.elm @@ -78,6 +78,8 @@ iconSet = , ( "km.phase", "far fa-clock" ) , ( "km.question", "far fa-comment" ) , ( "km.reference", "far fa-bookmark" ) + , ( "km.resourceCollection", "fas fa-book" ) + , ( "km.resourcePage", "far fa-file-lines" ) , ( "km.tag", "fas fa-tag" ) , ( "kmEditor.collapseAll", "fas fa-angle-double-up" ) , ( "kmEditor.copyUuid", "fas fa-paste" ) diff --git a/engine-wizard/elm/Wizard/Common/Provisioning/DefaultLocale.elm b/engine-wizard/elm/Wizard/Common/Provisioning/DefaultLocale.elm index bfddba28b..34f9e1402 100644 --- a/engine-wizard/elm/Wizard/Common/Provisioning/DefaultLocale.elm +++ b/engine-wizard/elm/Wizard/Common/Provisioning/DefaultLocale.elm @@ -37,7 +37,6 @@ locale = , ( "__routing.projects.createMigration", "create-migration" ) , ( "__routing.projects.migration", "migration" ) , ( "__routing.projects.migration", "migration" ) - , ( "__routing.public.bookReferences", "book-references" ) , ( "__routing.public.forgottenPassword", "forgotten-password" ) , ( "__routing.public.signup", "signup" ) , ( "__routing.registry", "registry" ) diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm index d7ece69f3..b96ec2277 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm @@ -43,6 +43,8 @@ import Shared.Data.Event.AddMetricEventData as AddMetricEventData import Shared.Data.Event.AddPhaseEventData as AddPhaseEventData import Shared.Data.Event.AddQuestionEventData as AddQuestionEventData import Shared.Data.Event.AddReferenceEventData as AddReferenceEventData +import Shared.Data.Event.AddResourceCollectionEventData as AddResourceCollectionEventData +import Shared.Data.Event.AddResourcePageEventData as AddResourcePageEventData import Shared.Data.Event.AddTagEventData as AddTagEventData import Shared.Data.Event.CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditAnswerEventData as EditAnswerEventData @@ -55,6 +57,8 @@ import Shared.Data.Event.EditMetricEventData as EditMetricEventData import Shared.Data.Event.EditPhaseEventData as EditPhaseEventData import Shared.Data.Event.EditQuestionEventData as EditQuestionEventData import Shared.Data.Event.EditReferenceEventData as EditReferenceEventData +import Shared.Data.Event.EditResourceCollectionEventData as EditResourceCollectionEventData +import Shared.Data.Event.EditResourcePageEventData as EditResourcePageEventData import Shared.Data.Event.EditTagEventData as EditTagEventData import Shared.Data.Event.MoveEventData exposing (MoveEventData) import Shared.Data.KnowledgeModel as KnowledgeModel exposing (KnowledgeModel) @@ -67,6 +71,8 @@ import Shared.Data.KnowledgeModel.Metric exposing (Metric) import Shared.Data.KnowledgeModel.Phase exposing (Phase) import Shared.Data.KnowledgeModel.Question as Question exposing (Question(..)) import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.RegexPatterns as RegexPatterns import Shared.Utils exposing (flip) @@ -313,7 +319,7 @@ getEditorName appState uuid editorBranch = getEditorName_ (String.withDefault (gettext "Untitled choice" appState.locale) << .label) KnowledgeModel.getChoice getReferenceName = - getEditorName_ (String.withDefault (gettext "Untitled reference" appState.locale) << Reference.getVisibleName) KnowledgeModel.getReference + getEditorName_ (String.withDefault (gettext "Untitled reference" appState.locale) << Reference.getVisibleName (KnowledgeModel.getAllResourcePages editorBranch.branch.knowledgeModel)) KnowledgeModel.getReference getExpertName = getEditorName_ (String.withDefault (gettext "Untitled expert" appState.locale) << Expert.getVisibleName) KnowledgeModel.getExpert @@ -470,6 +476,7 @@ getAllUuids editorBranch = ++ Dict.keys editorBranch.branch.knowledgeModel.entities.experts ++ Dict.keys editorBranch.branch.knowledgeModel.entities.references ++ Dict.keys editorBranch.branch.knowledgeModel.entities.integrations + ++ Dict.keys editorBranch.branch.knowledgeModel.entities.resourceCollections ++ Dict.keys editorBranch.branch.knowledgeModel.entities.tags ++ Dict.keys editorBranch.branch.knowledgeModel.entities.metrics ++ Dict.keys editorBranch.branch.knowledgeModel.entities.phases @@ -622,6 +629,20 @@ applyEvent appState local event originalEditorBranch = in applyAdd local KnowledgeModel.insertReference reference commonData editorBranch + AddResourceCollectionEvent eventData commonData -> + let + resourceCollection = + AddResourceCollectionEventData.toResourceCollection commonData.entityUuid eventData + in + applyAdd local KnowledgeModel.insertResourceCollection resourceCollection commonData editorBranch + + AddResourcePageEvent eventData commonData -> + let + resourcePage = + AddResourcePageEventData.toResourcePage commonData.entityUuid eventData + in + applyAdd local KnowledgeModel.insertResourcePage resourcePage commonData editorBranch + AddTagEvent eventData commonData -> let tag = @@ -712,6 +733,22 @@ applyEvent appState local event originalEditorBranch = in applyEdit KnowledgeModel.updateReference mbReference commonData editorBranch + EditResourceCollectionEvent eventData commonData -> + let + mbResourceCollection = + KnowledgeModel.getResourceCollection commonData.entityUuid knowledgeModel + |> Maybe.map (EditResourceCollectionEventData.apply eventData) + in + applyEdit KnowledgeModel.updateResourceCollection mbResourceCollection commonData editorBranch + + EditResourcePageEvent eventData commonData -> + let + mbResourcePage = + KnowledgeModel.getResourcePage commonData.entityUuid knowledgeModel + |> Maybe.map (EditResourcePageEventData.apply eventData) + in + applyEdit KnowledgeModel.updateResourcePage mbResourcePage commonData editorBranch + EditTagEvent eventData commonData -> let mbTag = @@ -744,6 +781,12 @@ applyEvent appState local event originalEditorBranch = DeleteReferenceEvent commonData -> applyDelete commonData editorBranch + DeleteResourceCollectionEvent commonData -> + applyDelete commonData editorBranch + + DeleteResourcePageEvent commonData -> + applyDelete commonData editorBranch + DeleteQuestionEvent commonData -> applyDelete commonData editorBranch @@ -832,6 +875,7 @@ computeWarnings appState editorBranch = |> flip (++) (List.concatMap (computePhaseWarnings appState) (KnowledgeModel.getPhases filteredKM)) |> flip (++) (List.concatMap (computeTagWarnings appState) (KnowledgeModel.getTags filteredKM)) |> flip (++) (List.concatMap (computeIntegrationWarnings appState) (KnowledgeModel.getIntegrations filteredKM)) + |> flip (++) (List.concatMap (computeResourceCollectionWarnings appState filteredKM) (KnowledgeModel.getResourceCollections filteredKM)) in { editorBranch | warnings = warnings } @@ -972,8 +1016,8 @@ computeReferenceWarnings appState reference = in case reference of Reference.ResourcePageReference data -> - if String.isEmpty data.shortUuid then - createError (gettext "Empty short UUID for page reference" appState.locale) + if String.isEmpty data.resourcePageUuid || data.resourcePageUuid == Uuid.toString Uuid.nil then + createError (gettext "No resource page selected for resource page reference" appState.locale) else [] @@ -1098,3 +1142,50 @@ computeIntegrationWarnings appState integration = [] in idWarning ++ typeWarnings + + +computeResourceCollectionWarnings : AppState -> KnowledgeModel -> ResourceCollection -> List EditorBranchWarning +computeResourceCollectionWarnings appState km resourceCollection = + let + titleWarning = + if String.isEmpty resourceCollection.title then + [ { editorUuid = resourceCollection.uuid + , message = gettext "Empty title for resource collection" appState.locale + } + ] + + else + [] + + resourcePagesWarnings = + List.concatMap + (computeResourcePageWarnings appState) + (KnowledgeModel.getResourceCollectionResourcePages resourceCollection.uuid km) + in + titleWarning ++ resourcePagesWarnings + + +computeResourcePageWarnings : AppState -> ResourcePage -> List EditorBranchWarning +computeResourcePageWarnings appState resourcePage = + let + titleWarning = + if String.isEmpty resourcePage.title then + [ { editorUuid = resourcePage.uuid + , message = gettext "Empty title for resource page" appState.locale + } + ] + + else + [] + + contentWarning = + if String.isEmpty resourcePage.content then + [ { editorUuid = resourcePage.uuid + , message = gettext "Empty content for resource page" appState.locale + } + ] + + else + [] + in + titleWarning ++ contentWarning diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm index 82b1375de..28d59e095 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm @@ -33,12 +33,14 @@ import Shared.Data.Event.AddMetricEventData as AddMetricEventData import Shared.Data.Event.AddPhaseEventData as AddPhaseEventData import Shared.Data.Event.AddQuestionEventData as AddQuestionEventData import Shared.Data.Event.AddReferenceEventData as AddReferenceEventData +import Shared.Data.Event.AddResourceCollectionEventData as AddResourceCollectionEventData +import Shared.Data.Event.AddResourcePageEventData as AddResourcePageEventData import Shared.Data.Event.AddTagEventData as AddTagEventData import Shared.Data.Event.CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditAnswerEventData as EditAnswerEventData import Shared.Data.Event.EditChapterEventData as EditChapterEventData import Shared.Data.Event.EditChoiceEventData as EditChoiceEventData -import Shared.Data.Event.EditEventSetters exposing (setAbbreviation, setAdvice, setAnnotations, setAnswerUuids, setChapterUuids, setChoiceUuids, setColor, setDescription, setEmail, setExpertUuids, setFollowUpUuids, setId, setIntegrationUuid, setIntegrationUuids, setItemTemplateQuestionUuids, setItemUrl, setLabel, setLogo, setMetricMeasures, setMetricUuids, setName, setPhaseUuids, setProps, setQuestionUuids, setReferenceUuids, setRequestBody, setRequestEmptySearch, setRequestHeaders, setRequestMethod, setRequestUrl, setRequiredPhaseUuid, setResponseItemId, setResponseItemTemplate, setResponseListField, setShortUuid, setTagUuids, setText, setTitle, setUrl, setValueType, setWidgetUrl) +import Shared.Data.Event.EditEventSetters exposing (setAbbreviation, setAdvice, setAnnotations, setAnswerUuids, setChapterUuids, setChoiceUuids, setColor, setContent, setDescription, setEmail, setExpertUuids, setFollowUpUuids, setId, setIntegrationUuid, setIntegrationUuids, setItemTemplateQuestionUuids, setItemUrl, setLabel, setLogo, setMetricMeasures, setMetricUuids, setName, setPhaseUuids, setProps, setQuestionUuids, setReferenceUuids, setRequestBody, setRequestEmptySearch, setRequestHeaders, setRequestMethod, setRequestUrl, setRequiredPhaseUuid, setResourceCollectionUuids, setResourcePageUuid, setResourcePageUuids, setResponseItemId, setResponseItemTemplate, setResponseListField, setTagUuids, setText, setTitle, setUrl, setValueType, setWidgetUrl) import Shared.Data.Event.EditExpertEventData as EditExpertEventData import Shared.Data.Event.EditIntegrationApiEventData as EditIntegrationApiEventData import Shared.Data.Event.EditIntegrationEventData exposing (EditIntegrationEventData(..)) @@ -55,6 +57,8 @@ import Shared.Data.Event.EditQuestionValueEventData as EditQuestionValueEventDat import Shared.Data.Event.EditReferenceEventData exposing (EditReferenceEventData(..)) import Shared.Data.Event.EditReferenceResourcePageEventData as EditReferenceResourcePageEventData import Shared.Data.Event.EditReferenceURLEventData as EditReferenceURLEventData +import Shared.Data.Event.EditResourceCollectionEventData as EditResourceCollectionEventData +import Shared.Data.Event.EditResourcePageEventData as EditResourcePageEventData import Shared.Data.Event.EditTagEventData as EditTagEventData import Shared.Data.Event.EventField as EventField import Shared.Data.KnowledgeModel as KnowledgeModel exposing (KnowledgeModel) @@ -68,6 +72,8 @@ import Shared.Data.KnowledgeModel.Phase exposing (Phase) import Shared.Data.KnowledgeModel.Question as Question exposing (Question(..)) import Shared.Data.KnowledgeModel.Question.QuestionValueType as QuestionValueType import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference(..)) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.Html exposing (emptyNode, faSet) import Shared.Markdown as Markdown @@ -111,6 +117,8 @@ type DeleteModalState | PhaseState String | TagState String | IntegrationState String + | ResourceCollectionState String + | ResourcePageState String | QuestionState String | AnswerState String | ChoiceState String @@ -296,6 +304,8 @@ view appState wrapMsg eventMsg model integrationPrefabs editorBranch = , createChoice = \questionUuid -> eventMsg questionUuid Nothing (AddChoiceEvent AddChoiceEventData.init) , createExpert = \questionUuid -> eventMsg questionUuid Nothing (AddExpertEvent AddExpertEventData.init) , createReference = \questionUuid -> eventMsg questionUuid Nothing (AddReferenceEvent AddReferenceEventData.init) + , createResourceCollection = \questionUuid -> eventMsg questionUuid Nothing (AddResourceCollectionEvent AddResourceCollectionEventData.init) + , createResourcePage = \referenceUuid -> eventMsg referenceUuid Nothing (AddResourcePageEvent AddResourcePageEventData.init) , createIntegration = \kmUuid -> eventMsg kmUuid Nothing (AddIntegrationEvent AddIntegrationEventData.init) , createTag = \kmUuid -> eventMsg kmUuid Nothing (AddTagEvent AddTagEventData.init) , createMetric = \kmUuid -> eventMsg kmUuid Nothing (AddMetricEvent AddMetricEventData.init) @@ -425,6 +435,12 @@ viewEditor appState wrapMsg eventMsg model integrationPrefabs editorBranch = integrationEditor = createEditor viewIntegrationEditor KnowledgeModel.getIntegration + resourceCollectionEditor = + createEditor viewResourceCollectionEditor KnowledgeModel.getResourceCollection + + resourcePageEditor = + createEditor viewResourcePageEditor KnowledgeModel.getResourcePage + answerEditor = createEditor viewAnswerEditor KnowledgeModel.getAnswer @@ -448,6 +464,8 @@ viewEditor appState wrapMsg eventMsg model integrationPrefabs editorBranch = |> Maybe.orElse phaseEditor |> Maybe.orElse tagEditor |> Maybe.orElse integrationEditor + |> Maybe.orElse resourceCollectionEditor + |> Maybe.orElse resourcePageEditor |> Maybe.orElse answerEditor |> Maybe.orElse choiceEditor |> Maybe.orElse referenceEditor @@ -502,6 +520,10 @@ viewKnowledgeModelEditor { appState, wrapMsg, eventMsg, model, editorBranch } km AddIntegrationEvent AddIntegrationEventData.init |> eventMsg kmUuid Nothing + addResourceCollectionEvent = + AddResourceCollectionEvent AddResourceCollectionEventData.init + |> eventMsg kmUuid Nothing + chaptersInput = Input.reorderable appState { name = "chapters" @@ -587,6 +609,23 @@ viewKnowledgeModelEditor { appState, wrapMsg, eventMsg, model, editorBranch } km , addChildDataCy = "integration" } + resourceCollectionsInput = + Input.reorderable appState + { name = "resourceCollections" + , label = gettext "Resource Collections" appState.locale + , items = EditorBranch.filterDeleted editorBranch km.resourceCollectionUuids + , entityUuid = kmUuid + , getReorderableState = flip Dict.get model.reorderableStates + , toMsg = compose2 wrapMsg ReorderableMsg + , updateList = createEditEvent setResourceCollectionUuids + , getRoute = editorRoute editorBranch + , getName = KnowledgeModel.getResourceCollectionName km + , untitledLabel = gettext "Untitled resource collection" appState.locale + , addChildLabel = gettext "Add resource collection" appState.locale + , addChildMsg = addResourceCollectionEvent + , addChildDataCy = "resource-collection" + } + annotationsInput = Input.annotations appState { annotations = km.annotations @@ -600,6 +639,7 @@ viewKnowledgeModelEditor { appState, wrapMsg, eventMsg, model, editorBranch } km , phasesInput , tagsInput , integrationsInput + , resourceCollectionsInput , annotationsInput ] @@ -1856,12 +1896,23 @@ viewReferenceEditor { appState, wrapMsg, eventMsg, editorBranch } reference = |> (EditReferenceEvent << EditReferenceResourcePageEvent) |> eventMsg parentUuid (Just referenceUuid) - shortUuidInput = - Input.string - { name = "shortUuid" - , label = gettext "Short UUID" appState.locale - , value = data.shortUuid - , onInput = createTypeEditEvent setShortUuid + resourcePageOption resourcePageUuid = + KnowledgeModel.getResourcePage resourcePageUuid editorBranch.branch.knowledgeModel + |> Maybe.map (\rp -> ( rp.uuid, rp.title )) + + resourcePageUuidOptions = + KnowledgeModel.getResourceCollections editorBranch.branch.knowledgeModel + |> EditorBranch.filterDeletedWith .uuid editorBranch + |> List.map (\rc -> ( rc.title, List.filterMap resourcePageOption rc.resourcePageUuids )) + + resourcePageUuidSelect = + Input.selectWithGroups + { name = "resourcePageUuid" + , label = gettext "Resource Page" appState.locale + , value = data.resourcePageUuid + , defaultOption = ( Uuid.toString Uuid.nil, gettext "- select resource page -" appState.locale ) + , options = resourcePageUuidOptions + , onChange = createTypeEditEvent setResourcePageUuid } annotationsInput = @@ -1870,7 +1921,7 @@ viewReferenceEditor { appState, wrapMsg, eventMsg, editorBranch } reference = , onEdit = createTypeEditEvent setAnnotations } in - [ shortUuidInput, annotationsInput ] + [ resourcePageUuidSelect, annotationsInput ] URLReference data -> let @@ -1970,6 +2021,168 @@ viewExpertEditor { appState, wrapMsg, eventMsg, editorBranch } expert = ] +viewResourceCollectionEditor : EditorConfig msg -> ResourceCollection -> Html msg +viewResourceCollectionEditor { appState, wrapMsg, eventMsg, model, editorBranch } resourceCollection = + let + parentUuid = + EditorBranch.getParentUuid resourceCollection.uuid editorBranch + + createEditEvent map value = + EditResourceCollectionEventData.init + |> map value + |> EditResourceCollectionEvent + |> eventMsg parentUuid (Just resourceCollection.uuid) + + resourcePageAddEvent = + AddResourcePageEventData.init + |> AddResourcePageEvent + |> eventMsg resourceCollection.uuid Nothing + + resourceCollectionEditorTitle = + editorTitle appState + { title = gettext "Resource Collection" appState.locale + , uuid = resourceCollection.uuid + , wrapMsg = wrapMsg + , copyUuidButton = True + , mbDeleteModalState = Just ResourceCollectionState + , mbMovingEntity = Nothing + } + + titleInput = + Input.string + { name = "title" + , label = gettext "Title" appState.locale + , value = resourceCollection.title + , onInput = createEditEvent setTitle + } + + resourcePagesInput = + Input.reorderable appState + { name = "resourcePages" + , label = gettext "Resource Pages" appState.locale + , items = EditorBranch.filterDeleted editorBranch resourceCollection.resourcePageUuids + , entityUuid = resourceCollection.uuid + , getReorderableState = flip Dict.get model.reorderableStates + , toMsg = compose2 wrapMsg ReorderableMsg + , updateList = createEditEvent setResourcePageUuids + , getRoute = editorRoute editorBranch + , getName = KnowledgeModel.getResourcePageName editorBranch.branch.knowledgeModel + , untitledLabel = gettext "Untitled resource page" appState.locale + , addChildLabel = gettext "Add resource page" appState.locale + , addChildMsg = resourcePageAddEvent + , addChildDataCy = "resource-page" + } + + annotationsInput = + Input.annotations appState + { annotations = resourceCollection.annotations + , onEdit = createEditEvent setAnnotations + } + in + editor ("resource-collection-" ++ resourceCollection.uuid) + [ resourceCollectionEditorTitle + , titleInput + , resourcePagesInput + , annotationsInput + ] + + +viewResourcePageEditor : EditorConfig msg -> ResourcePage -> Html msg +viewResourcePageEditor { appState, wrapMsg, eventMsg, model, editorBranch } resourcePage = + let + parentUuid = + EditorBranch.getParentUuid resourcePage.uuid editorBranch + + createEditEvent map value = + EditResourcePageEventData.init + |> map value + |> EditResourcePageEvent + |> eventMsg parentUuid (Just resourcePage.uuid) + + resourcePageEditorTitle = + editorTitle appState + { title = gettext "Resource Page" appState.locale + , uuid = resourcePage.uuid + , wrapMsg = wrapMsg + , copyUuidButton = True + , mbDeleteModalState = Just ResourcePageState + , mbMovingEntity = Nothing + } + + titleInput = + Input.string + { name = "title" + , label = gettext "Title" appState.locale + , value = resourcePage.title + , onInput = createEditEvent setTitle + } + + contentInput = + Input.markdown appState + { name = "content" + , label = gettext "Content" appState.locale + , value = resourcePage.content + , onInput = createEditEvent setContent + , previewMsg = compose2 wrapMsg ShowHideMarkdownPreview + , entityUuid = resourcePage.uuid + , markdownPreviews = model.markdownPreviews + } + + annotationsInput = + Input.annotations appState + { annotations = resourcePage.annotations + , onEdit = createEditEvent setAnnotations + } + + viewQuestionLink question = + let + questionTitle = + Question.getTitle question + + questionTitleNode = + if String.isEmpty questionTitle then + i [] [ text (gettext "Untitled question" appState.locale) ] + + else + text questionTitle + in + li [] + [ linkTo appState + (editorRoute editorBranch (Question.getUuid question)) + [] + [ questionTitleNode ] + ] + + wrapQuestionsWithIntegration questions = + if List.isEmpty questions then + div [] [ i [] [ text (gettext "No questions" appState.locale) ] ] + + else + ul [] questions + + filterQuestionByResourcePageUuid questionUuid = + KnowledgeModel.getQuestionReferences questionUuid editorBranch.branch.knowledgeModel + |> List.filterMap Reference.getResourcePageUuid + |> List.member resourcePage.uuid + + questionsWithResourcePage = + KnowledgeModel.getAllQuestions editorBranch.branch.knowledgeModel + |> EditorBranch.filterDeletedWith Question.getUuid editorBranch + |> List.filter (filterQuestionByResourcePageUuid << Question.getUuid) + |> List.filter (EditorBranch.isReachable editorBranch << Question.getUuid) + |> List.sortBy Question.getTitle + |> List.map viewQuestionLink + |> wrapQuestionsWithIntegration + in + editor ("resource-page-" ++ resourcePage.uuid) + [ resourcePageEditorTitle + , titleInput + , contentInput + , annotationsInput + , FormGroup.plainGroup questionsWithResourcePage (gettext "Questions using this resource page" appState.locale) + ] + + viewEmptyEditor : AppState -> Html msg viewEmptyEditor appState = editor "empty" @@ -2109,6 +2322,20 @@ deleteModal appState wrapMsg eventMsg editorBranch deleteModalState = (createEvent DeleteIntegrationEvent uuid) ) + ResourceCollectionState uuid -> + ( True + , getContent + (gettext "Are you sure you want to delete this resource collection?" appState.locale) + (createEvent DeleteResourceCollectionEvent uuid) + ) + + ResourcePageState uuid -> + ( True + , getContent + (gettext "Are you sure you want to delete this resource page?" appState.locale) + (createEvent DeleteResourcePageEvent uuid) + ) + AnswerState uuid -> ( True , getContent diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm index 1cb7fb87e..4de6ccaee 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm @@ -9,6 +9,7 @@ module Wizard.KMEditor.Editor.Components.KMEditor.Input exposing , PropsInputConfig , ReorderableInputConfig , SelectInputConfig + , SelectWithGroupsInputConfig , TagsInputConfig , annotations , checkbox @@ -19,13 +20,14 @@ module Wizard.KMEditor.Editor.Components.KMEditor.Input exposing , props , reorderable , select + , selectWithGroups , string , tags , textarea ) import Gettext exposing (gettext) -import Html exposing (Html, a, div, input, label, li, option, span, text, ul) +import Html exposing (Html, a, div, input, label, li, optgroup, option, span, text, ul) import Html.Attributes exposing (attribute, checked, class, classList, for, id, name, placeholder, rows, selected, step, style, type_, value) import Html.Events exposing (onCheck, onClick, onInput) import Html.Keyed @@ -154,6 +156,39 @@ select config = ] +type alias SelectWithGroupsInputConfig msg = + { name : String + , label : String + , value : String + , defaultOption : ( String, String ) + , options : List ( String, List ( String, String ) ) + , onChange : String -> msg + } + + +selectWithGroups : SelectWithGroupsInputConfig msg -> Html msg +selectWithGroups config = + let + viewGroup ( groupTitle, groupOptions ) = + optgroup [ attribute "label" groupTitle ] + (List.map viewOption groupOptions) + + viewOption ( optionValue, optionLabel ) = + option [ value optionValue, selected (optionValue == config.value) ] + [ text optionLabel ] + in + div [ class "form-group" ] + [ label [ for config.name ] [ text config.label ] + , Html.select + [ class "form-control" + , id config.name + , name config.name + , onInput config.onChange + ] + (viewOption config.defaultOption :: List.map viewGroup config.options) + ] + + -- Markdown Input diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Tree.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Tree.elm index 55d27777c..32ac8f8cd 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Tree.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Tree.elm @@ -18,6 +18,8 @@ import Shared.Data.KnowledgeModel.Metric exposing (Metric) import Shared.Data.KnowledgeModel.Phase exposing (Phase) import Shared.Data.KnowledgeModel.Question as Question exposing (Question) import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.Html exposing (emptyNode, fa, faKeyClass, faSet) import Shared.Utils exposing (flip) @@ -44,6 +46,8 @@ type alias CreateEvents msg = , createChoice : String -> msg , createExpert : String -> msg , createReference : String -> msg + , createResourceCollection : String -> msg + , createResourcePage : String -> msg , createIntegration : String -> msg , createTag : String -> msg , createMetric : String -> msg @@ -141,6 +145,18 @@ treeNodeKM props appState editorBranch = (props.createEvents.createIntegration uuid) (gettext "Add integration" appState.locale) + resourceCollections = + KnowledgeModel.getResourceCollections knowledgeModel + |> EditorBranch.sortDeleted .uuid editorBranch + + resourceCollectionNodes = + List.map (treeNodeResourceCollection props appState editorBranch) resourceCollections + + addResourceCollection = + treeNodeAdd (anyEntityActive editorBranch (List.map .uuid resourceCollections)) + (props.createEvents.createResourceCollection uuid) + (gettext "Add resource collection" appState.locale) + config = { uuid = uuid , icon = faSet "km.knowledgeModel" appState @@ -157,6 +173,8 @@ treeNodeKM props appState editorBranch = , addTag , integrationNodes , addIntegration + , resourceCollectionNodes + , addResourceCollection ] , untitledLabel = "" } @@ -381,7 +399,7 @@ treeNodeReference props appState editorBranch reference = config = { uuid = Reference.getUuid reference , icon = faSet "km.reference" appState - , label = Reference.getVisibleName reference + , label = Reference.getVisibleName (KnowledgeModel.getAllResourcePages editorBranch.branch.knowledgeModel) reference , children = [] , untitledLabel = gettext "Untitled reference" appState.locale } @@ -403,6 +421,46 @@ treeNodeExperts props appState editorBranch expert = treeNode props appState editorBranch config +treeNodeResourceCollection : ViewProps msg -> AppState -> EditorBranch -> ResourceCollection -> Html msg +treeNodeResourceCollection props appState editorBranch resourceCollection = + let + resourcePages = + KnowledgeModel.getResourceCollectionResourcePages resourceCollection.uuid editorBranch.branch.knowledgeModel + |> EditorBranch.sortDeleted .uuid editorBranch + + resourcePageNodes = + List.map (treeNodeResourcePage props appState editorBranch) resourcePages + + addResourcePage = + treeNodeAdd (anyEntityActive editorBranch (List.map .uuid resourcePages)) + (props.createEvents.createResourcePage resourceCollection.uuid) + (gettext "Add resource page" appState.locale) + + config = + { uuid = resourceCollection.uuid + , icon = faSet "km.resourceCollection" appState + , label = resourceCollection.title + , children = resourcePageNodes ++ addResourcePage + , untitledLabel = gettext "Untitled resource collection" appState.locale + } + in + treeNode props appState editorBranch config + + +treeNodeResourcePage : ViewProps msg -> AppState -> EditorBranch -> ResourcePage -> Html msg +treeNodeResourcePage props appState editorBranch resourcePage = + let + config = + { uuid = resourcePage.uuid + , icon = faSet "km.resourcePage" appState + , label = resourcePage.title + , children = [] + , untitledLabel = gettext "Untitled resource page" appState.locale + } + in + treeNode props appState editorBranch config + + type alias TreeNodeConfig msg = { uuid : String , icon : Html msg diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/TreeInput.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/TreeInput.elm index 4bec6ab5c..1aa10e9c6 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/TreeInput.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/TreeInput.elm @@ -291,7 +291,7 @@ treeNodeReference appState props reference = config = { uuid = Reference.getUuid reference , icon = faSet "km.reference" appState - , label = Reference.getVisibleName reference + , label = Reference.getVisibleName (KnowledgeModel.getAllResourcePages props.editorBranch.branch.knowledgeModel) reference , children = [] , untitledLabel = gettext "Untitled reference" appState.locale , allowed = False diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/Preview.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/Preview.elm index 648afd9cd..9d1c65c7a 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/Preview.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/Preview.elm @@ -29,6 +29,7 @@ import Wizard.Common.Components.Questionnaire.DefaultQuestionnaireRenderer as De import Wizard.Common.Html.Attribute exposing (dataCy) import Wizard.Common.View.Tag as Tag import Wizard.KMEditor.Editor.Common.EditorBranch as EditorBranch exposing (EditorBranch) +import Wizard.Routes type alias Model = @@ -186,7 +187,10 @@ view appState editorBranch model = , toolbarEnabled = False , questionLinksEnabled = False } - , renderer = DefaultQuestionnaireRenderer.create appState knowledgeModel + , renderer = + DefaultQuestionnaireRenderer.create appState + knowledgeModel + (Wizard.Routes.kmEditorEditor editorBranch.branch.uuid << Just << Uuid.fromUuidString) , wrapMsg = QuestionnaireMsg , previewQuestionnaireEventMsg = Nothing , revertQuestionnaireMsg = Nothing diff --git a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm index 56ff7af43..bcad5ac44 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm @@ -20,6 +20,8 @@ import Shared.Data.Event.AddReferenceCrossEventData exposing (AddReferenceCrossE import Shared.Data.Event.AddReferenceEventData as AddReferenceEventData exposing (AddReferenceEventData) import Shared.Data.Event.AddReferenceResourcePageEventData exposing (AddReferenceResourcePageEventData) import Shared.Data.Event.AddReferenceURLEventData exposing (AddReferenceURLEventData) +import Shared.Data.Event.AddResourceCollectionEventData exposing (AddResourceCollectionEventData) +import Shared.Data.Event.AddResourcePageEventData exposing (AddResourcePageEventData) import Shared.Data.Event.AddTagEventData exposing (AddTagEventData) import Shared.Data.Event.EditAnswerEventData exposing (EditAnswerEventData) import Shared.Data.Event.EditChapterEventData exposing (EditChapterEventData) @@ -34,6 +36,8 @@ import Shared.Data.Event.EditReferenceCrossEventData exposing (EditReferenceCros import Shared.Data.Event.EditReferenceEventData as EditReferenceEventData exposing (EditReferenceEventData(..)) import Shared.Data.Event.EditReferenceResourcePageEventData exposing (EditReferenceResourcePageEventData) import Shared.Data.Event.EditReferenceURLEventData exposing (EditReferenceURLEventData) +import Shared.Data.Event.EditResourceCollectionEventData exposing (EditResourceCollectionEventData) +import Shared.Data.Event.EditResourcePageEventData exposing (EditResourcePageEventData) import Shared.Data.Event.EditTagEventData exposing (EditTagEventData) import Shared.Data.Event.EventField as EventField import Shared.Data.KnowledgeModel as KnowledgeModel exposing (KnowledgeModel) @@ -52,6 +56,8 @@ import Shared.Data.KnowledgeModel.Reference as Reference exposing (Reference(..) import Shared.Data.KnowledgeModel.Reference.CrossReferenceData exposing (CrossReferenceData) import Shared.Data.KnowledgeModel.Reference.ResourcePageReferenceData exposing (ResourcePageReferenceData) import Shared.Data.KnowledgeModel.Reference.URLReferenceData exposing (URLReferenceData) +import Shared.Data.KnowledgeModel.ResourceCollection exposing (ResourceCollection) +import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.Data.Migration exposing (Migration) import Shared.Data.Migration.MigrationState.MigrationStateType exposing (MigrationStateType(..)) @@ -310,6 +316,38 @@ getEventView appState model migration event = |> Maybe.map (viewEvent appState model event (gettext "Delete expert" appState.locale)) |> Maybe.withDefault errorMessage + AddResourceCollectionEvent eventData _ -> + viewAddResourceCollectionDiff appState eventData + |> viewEvent appState model event (gettext "Add resource collection" appState.locale) + + EditResourceCollectionEvent eventData commonData -> + KnowledgeModel.getResourceCollection commonData.entityUuid migration.currentKnowledgeModel + |> Maybe.map (viewEditResourceCollectionDiff appState migration.currentKnowledgeModel eventData) + |> Maybe.map (viewEvent appState model event (gettext "Edit resource collection" appState.locale)) + |> Maybe.withDefault errorMessage + + DeleteResourceCollectionEvent commonData -> + KnowledgeModel.getResourceCollection commonData.entityUuid migration.currentKnowledgeModel + |> Maybe.map (viewDeleteResourceCollectionDiff appState migration.currentKnowledgeModel) + |> Maybe.map (viewEvent appState model event (gettext "Delete resource collection" appState.locale)) + |> Maybe.withDefault errorMessage + + AddResourcePageEvent eventData _ -> + viewAddResourcePageDiff appState eventData + |> viewEvent appState model event (gettext "Add resource page" appState.locale) + + EditResourcePageEvent eventData commonData -> + KnowledgeModel.getResourcePage commonData.entityUuid migration.currentKnowledgeModel + |> Maybe.map (viewEditResourcePageDiff appState eventData) + |> Maybe.map (viewEvent appState model event (gettext "Edit resource page" appState.locale)) + |> Maybe.withDefault errorMessage + + DeleteResourcePageEvent commonData -> + KnowledgeModel.getResourcePage commonData.entityUuid migration.currentKnowledgeModel + |> Maybe.map (viewDeleteResourcePageDiff appState) + |> Maybe.map (viewEvent appState model event (gettext "Delete resource page" appState.locale)) + |> Maybe.withDefault errorMessage + MoveQuestionEvent _ commonData -> KnowledgeModel.getQuestion commonData.entityUuid migration.currentKnowledgeModel |> Maybe.map (viewMoveQuestion appState migration.currentKnowledgeModel) @@ -1132,7 +1170,7 @@ viewEditQuestionDiff appState km event question = List.map Reference.getUuid references referenceNames = - Dict.fromList <| List.map (\r -> ( Reference.getUuid r, Reference.getVisibleName r )) references + Dict.fromList <| List.map (\r -> ( Reference.getUuid r, Reference.getVisibleName (KnowledgeModel.getAllResourcePages km) r )) references referencesDiff = viewDiffChildren (gettext "References" appState.locale) @@ -1252,7 +1290,7 @@ viewDeleteQuestionDiff appState km question = KnowledgeModel.getQuestionReferences questionUuid km referencesDiff = - viewDeletedChildren (gettext "References" appState.locale) <| List.map Reference.getVisibleName references + viewDeletedChildren (gettext "References" appState.locale) <| List.map (Reference.getVisibleName (KnowledgeModel.getAllResourcePages km)) references -- Experts experts = @@ -1349,7 +1387,7 @@ viewMoveQuestion appState km question = KnowledgeModel.getQuestionReferences questionUuid km referencesDiff = - viewPlainChildren (gettext "References" appState.locale) <| List.map Reference.getVisibleName references + viewPlainChildren (gettext "References" appState.locale) <| List.map (Reference.getVisibleName (KnowledgeModel.getAllResourcePages km)) references -- Experts experts = @@ -1630,10 +1668,10 @@ viewAddResourcePageReferenceDiff appState data = viewAdd <| List.map2 (\a b -> ( a, b )) [ gettext "Reference Type" appState.locale - , gettext "Short UUID" appState.locale + , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.shortUuid + , data.resourcePageUuid ] annotationsDiff = @@ -1693,13 +1731,13 @@ viewEditReferenceDiff appState event reference = viewDiff <| List.map3 (\a b c -> ( a, b, c )) [ gettext "Reference Type" appState.locale - , gettext "Short UUID" appState.locale + , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , referenceData.shortUuid + , referenceData.resourcePageUuid ] [ gettext "Resource Page" appState.locale - , EventField.getValueWithDefault eventData.shortUuid referenceData.shortUuid + , EventField.getValueWithDefault eventData.resourcePageUuid referenceData.resourcePageUuid ] annotationsDiff = @@ -1775,10 +1813,10 @@ viewEditResourcePageReferenceDiff appState data = viewAdd <| List.map2 (\a b -> ( a, b )) [ gettext "Reference Type" appState.locale - , gettext "Short UUID" appState.locale + , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , EventField.getValueWithDefault data.shortUuid "" + , EventField.getValueWithDefault data.resourcePageUuid "" ] annotationsDiff = @@ -1844,10 +1882,10 @@ viewDeleteResourcePageReferenceDiff appState data = viewDelete <| List.map2 (\a b -> ( a, b )) [ gettext "Reference Type" appState.locale - , gettext "Short UUID" appState.locale + , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.shortUuid + , data.resourcePageUuid ] annotationsDiff = @@ -1913,10 +1951,10 @@ viewMoveResourcePageReference appState data = viewPlain <| List.map2 (\a b -> ( a, b )) [ gettext "Reference Type" appState.locale - , gettext "Short UUID" appState.locale + , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.shortUuid + , data.resourcePageUuid ] annotationsDiff = @@ -2046,6 +2084,143 @@ viewMoveExpert appState expert = div [] (fieldDiff ++ [ annotationsDiff ]) +viewAddResourceCollectionDiff : AppState -> AddResourceCollectionEventData -> Html Msg +viewAddResourceCollectionDiff appState event = + let + fieldDiff = + viewAdd <| + List.map2 (\a b -> ( a, b )) + [ gettext "Title" appState.locale + ] + [ event.title + ] + + annotationsDiff = + viewAnnotationsDiff appState [] event.annotations + in + div [] (fieldDiff ++ [ annotationsDiff ]) + + +viewEditResourceCollectionDiff : AppState -> KnowledgeModel -> EditResourceCollectionEventData -> ResourceCollection -> Html Msg +viewEditResourceCollectionDiff appState km event resourceCollection = + let + fieldDiff = + viewDiff <| + List.map3 (\a b c -> ( a, b, c )) + [ gettext "Title" appState.locale + ] + [ resourceCollection.title + ] + [ EventField.getValueWithDefault event.title resourceCollection.title + ] + + resourcePages = + KnowledgeModel.getResourceCollectionResourcePages resourceCollection.uuid km + + originalResourcePages = + List.map .uuid resourcePages + + resourcePageNames = + Dict.fromList <| List.map (\r -> ( r.uuid, r.title )) resourcePages + + resourcePagesDiff = + viewDiffChildren (gettext "Resource Pages" appState.locale) + originalResourcePages + (EventField.getValueWithDefault event.resourcePageUuids originalResourcePages) + resourcePageNames + + annotationsDiff = + viewAnnotationsDiff appState resourceCollection.annotations (EventField.getValueWithDefault event.annotations resourceCollection.annotations) + in + div [] (fieldDiff ++ [ resourcePagesDiff, annotationsDiff ]) + + +viewDeleteResourceCollectionDiff : AppState -> KnowledgeModel -> ResourceCollection -> Html Msg +viewDeleteResourceCollectionDiff appState km resourceCollection = + let + fieldDiff = + viewDelete <| + List.map2 (\a b -> ( a, b )) + [ gettext "Title" appState.locale + ] + [ resourceCollection.title + ] + + resourcePages = + KnowledgeModel.getResourceCollectionResourcePages resourceCollection.uuid km + + resourcePageNames = + List.map .title resourcePages + + resourcePagesDiff = + viewDeletedChildren (gettext "Resource Pages" appState.locale) resourcePageNames + + annotationsDiff = + viewAnnotationsDiff appState resourceCollection.annotations [] + in + div [] (fieldDiff ++ [ resourcePagesDiff, annotationsDiff ]) + + +viewAddResourcePageDiff : AppState -> AddResourcePageEventData -> Html Msg +viewAddResourcePageDiff appState event = + let + fieldDiff = + viewAdd <| + List.map2 (\a b -> ( a, b )) + [ gettext "Title" appState.locale + , gettext "Content" appState.locale + ] + [ event.title + , event.content + ] + + annotationsDiff = + viewAnnotationsDiff appState [] event.annotations + in + div [] (fieldDiff ++ [ annotationsDiff ]) + + +viewEditResourcePageDiff : AppState -> EditResourcePageEventData -> ResourcePage -> Html Msg +viewEditResourcePageDiff appState event resourcePage = + let + fieldDiff = + viewDiff <| + List.map3 (\a b c -> ( a, b, c )) + [ gettext "Title" appState.locale + , gettext "Content" appState.locale + ] + [ resourcePage.title + , resourcePage.content + ] + [ EventField.getValueWithDefault event.title resourcePage.title + , EventField.getValueWithDefault event.content resourcePage.content + ] + + annotationsDiff = + viewAnnotationsDiff appState resourcePage.annotations (EventField.getValueWithDefault event.annotations resourcePage.annotations) + in + div [] (fieldDiff ++ [ annotationsDiff ]) + + +viewDeleteResourcePageDiff : AppState -> ResourcePage -> Html Msg +viewDeleteResourcePageDiff appState resourcePage = + let + fieldDiff = + viewDelete <| + List.map2 (\a b -> ( a, b )) + [ gettext "Title" appState.locale + , gettext "Content" appState.locale + ] + [ resourcePage.title + , resourcePage.content + ] + + annotationsDiff = + viewAnnotationsDiff appState resourcePage.annotations [] + in + div [] (fieldDiff ++ [ annotationsDiff ]) + + viewDiff : List ( String, String, String ) -> List (Html Msg) viewDiff changes = List.map diff --git a/engine-wizard/elm/Wizard/KMEditor/Migration/View/DiffTree.elm b/engine-wizard/elm/Wizard/KMEditor/Migration/View/DiffTree.elm index 936686420..366d1812a 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Migration/View/DiffTree.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Migration/View/DiffTree.elm @@ -60,11 +60,17 @@ viewEvent appState kmName km event = Maybe.map .label <| KnowledgeModel.getChoice commonData.entityUuid km getReferenceName commonData = - Maybe.map Reference.getVisibleName <| KnowledgeModel.getReference commonData.entityUuid km + Maybe.map (Reference.getVisibleName (KnowledgeModel.getAllResourcePages km)) <| KnowledgeModel.getReference commonData.entityUuid km getExpertName commonData = Maybe.map .name <| KnowledgeModel.getExpert commonData.entityUuid km + getResourceCollectionName commonData = + Maybe.map .title <| KnowledgeModel.getResourceCollection commonData.entityUuid km + + getResourcePageName commonData = + Maybe.map .title <| KnowledgeModel.getResourcePage commonData.entityUuid km + viewKnolwedgeModelNode_ = viewKnowledgeModelNode appState Nothing @@ -97,6 +103,12 @@ viewEvent appState kmName km event = viewExpertNode_ = viewExpertNode appState kmName km getParent + + viewResourceCollectionNode_ = + viewResourceCollectionNode appState kmName Nothing + + viewResourcePageNode_ = + viewResourcePageNode appState kmName km in case event of AddKnowledgeModelEvent _ _ -> @@ -195,6 +207,24 @@ viewEvent appState kmName km event = DeleteExpertEvent commonData -> viewExpertNode_ stateClass.deleted (getExpertName commonData) commonData.parentUuid + AddResourceCollectionEvent _ _ -> + viewResourceCollectionNode_ stateClass.added eventEntityName + + EditResourceCollectionEvent _ _ -> + viewResourceCollectionNode_ stateClass.edited eventEntityName + + DeleteResourceCollectionEvent commonData -> + viewResourceCollectionNode_ stateClass.deleted (getResourceCollectionName commonData) + + AddResourcePageEvent _ commonData -> + viewResourcePageNode_ stateClass.added eventEntityName commonData.parentUuid + + EditResourcePageEvent _ commonData -> + viewResourcePageNode_ stateClass.edited (eventEntityNameOrDefault (getResourcePageName commonData)) commonData.parentUuid + + DeleteResourcePageEvent commonData -> + viewResourcePageNode_ stateClass.deleted (getResourcePageName commonData) commonData.parentUuid + MoveQuestionEvent eventData commonData -> div [] [ viewQuestionNode_ stateClass.deleted (getQuestionTitle commonData) commonData.parentUuid @@ -426,6 +456,41 @@ viewExpertNode appState kmName km getParent cssClass mbTitle parentUuid = |> Maybe.withDefault expertNode +viewResourceCollectionNode : + AppState + -> String + -> Maybe (Html msg) + -> String + -> Maybe String + -> Html msg +viewResourceCollectionNode appState kmName mbChildNode cssClass mbTitle = + let + resourceCollectionNode = + viewNode (faSet "km.resourceCollection" appState) cssClass mbTitle mbChildNode + in + viewKnowledgeModelNode appState (Just resourceCollectionNode) stateClass.none (Just kmName) + + +viewResourcePageNode : + AppState + -> String + -> KnowledgeModel + -> String + -> Maybe String + -> String + -> Html msg +viewResourcePageNode appState kmName km cssClass mbTitle parentUuid = + let + resourcePageNode = + viewNode (faSet "km.resourcePage" appState) cssClass mbTitle Nothing + + parentQuestion = + getParentResourceCollectionNode appState kmName km parentUuid resourcePageNode + in + parentQuestion + |> Maybe.withDefault resourcePageNode + + viewNode : Html msg -> String -> Maybe String -> Maybe (Html msg) -> Html msg viewNode icon cssClass mbTitle mbChildNode = let @@ -522,3 +587,23 @@ getParentAnswerNode appState kmName km getParent answerUuid node = (Just answer.label) (getParent answerUuid) ) + + +getParentResourceCollectionNode : + AppState + -> String + -> KnowledgeModel + -> String + -> Html msg + -> Maybe (Html msg) +getParentResourceCollectionNode appState kmName km resourceCollectionUuid node = + KnowledgeModel.getResourceCollection resourceCollectionUuid km + |> Maybe.map + (\resourceCollection -> + viewResourceCollectionNode + appState + kmName + (Just node) + stateClass.none + (Just resourceCollection.title) + ) diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Models.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Models.elm index 06e852eab..14c296bfe 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Models.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Models.elm @@ -6,6 +6,7 @@ import Wizard.KnowledgeModels.Detail.Models import Wizard.KnowledgeModels.Import.Models import Wizard.KnowledgeModels.Index.Models import Wizard.KnowledgeModels.Preview.Models +import Wizard.KnowledgeModels.ResourcePage.Models import Wizard.KnowledgeModels.Routes exposing (Route(..)) @@ -14,6 +15,7 @@ type alias Model = , importModel : Wizard.KnowledgeModels.Import.Models.Model , indexModel : Wizard.KnowledgeModels.Index.Models.Model , previewModel : Wizard.KnowledgeModels.Preview.Models.Model + , resourcePageModel : Wizard.KnowledgeModels.ResourcePage.Models.Model } @@ -23,6 +25,7 @@ initialModel appState = , importModel = Wizard.KnowledgeModels.Import.Models.initialModel appState Nothing , indexModel = Wizard.KnowledgeModels.Index.Models.initialModel PaginationQueryString.empty , previewModel = Wizard.KnowledgeModels.Preview.Models.initialModel Nothing + , resourcePageModel = Wizard.KnowledgeModels.ResourcePage.Models.initialModel "" } @@ -40,3 +43,6 @@ initLocalModel route appState model = PreviewRoute _ mbQuestionUuid -> { model | previewModel = Wizard.KnowledgeModels.Preview.Models.initialModel mbQuestionUuid } + + ResourcePageRoute _ resourcePageUuid -> + { model | resourcePageModel = Wizard.KnowledgeModels.ResourcePage.Models.initialModel resourcePageUuid } diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Msgs.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Msgs.elm index e186173a3..2da4a796b 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Msgs.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Msgs.elm @@ -4,6 +4,7 @@ import Wizard.KnowledgeModels.Detail.Msgs import Wizard.KnowledgeModels.Import.Msgs import Wizard.KnowledgeModels.Index.Msgs import Wizard.KnowledgeModels.Preview.Msgs +import Wizard.KnowledgeModels.ResourcePage.Msgs type Msg @@ -11,3 +12,4 @@ type Msg | ImportMsg Wizard.KnowledgeModels.Import.Msgs.Msg | IndexMsg Wizard.KnowledgeModels.Index.Msgs.Msg | PreviewMsg Wizard.KnowledgeModels.Preview.Msgs.Msg + | ResourcePageMsg Wizard.KnowledgeModels.ResourcePage.Msgs.Msg diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Preview/View.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Preview/View.elm index 224c85536..4c39f79fc 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Preview/View.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Preview/View.elm @@ -40,7 +40,10 @@ viewProject appState model ( package, questionnaireModel ) = , toolbarEnabled = False , questionLinksEnabled = False } - , renderer = DefaultQuestionnaireRenderer.create appState questionnaireModel.questionnaire.knowledgeModel + , renderer = + DefaultQuestionnaireRenderer.create appState + questionnaireModel.questionnaire.knowledgeModel + (DefaultQuestionnaireRenderer.defaultResourcePageToRoute questionnaireModel.questionnaire.packageId) , wrapMsg = QuestionnaireMsg , previewQuestionnaireEventMsg = Nothing , revertQuestionnaireMsg = Nothing diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Models.elm b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Models.elm new file mode 100644 index 000000000..bd5444207 --- /dev/null +++ b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Models.elm @@ -0,0 +1,17 @@ +module Wizard.KnowledgeModels.ResourcePage.Models exposing (Model, initialModel) + +import ActionResult exposing (ActionResult) +import Shared.Data.KnowledgeModel exposing (KnowledgeModel) + + +type alias Model = + { knowledgeModel : ActionResult KnowledgeModel + , resourcePageUuid : String + } + + +initialModel : String -> Model +initialModel resourcePageUuid = + { knowledgeModel = ActionResult.Loading + , resourcePageUuid = resourcePageUuid + } diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Msgs.elm b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Msgs.elm new file mode 100644 index 000000000..4714cceb3 --- /dev/null +++ b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Msgs.elm @@ -0,0 +1,8 @@ +module Wizard.KnowledgeModels.ResourcePage.Msgs exposing (Msg(..)) + +import Shared.Data.KnowledgeModel exposing (KnowledgeModel) +import Shared.Error.ApiError exposing (ApiError) + + +type Msg + = FetchPreviewComplete (Result ApiError KnowledgeModel) diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Update.elm b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Update.elm new file mode 100644 index 000000000..886eb6a7c --- /dev/null +++ b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/Update.elm @@ -0,0 +1,31 @@ +module Wizard.KnowledgeModels.ResourcePage.Update exposing + ( fetchData + , update + ) + +import Gettext exposing (gettext) +import Shared.Api.KnowledgeModels as KnowlegeModelsApi +import Shared.Setters exposing (setKnowledgeModel) +import Wizard.Common.Api exposing (applyResult) +import Wizard.Common.AppState exposing (AppState) +import Wizard.KnowledgeModels.ResourcePage.Models exposing (Model) +import Wizard.KnowledgeModels.ResourcePage.Msgs exposing (Msg(..)) +import Wizard.Msgs + + +fetchData : AppState -> String -> Cmd Msg +fetchData appState kmId = + KnowlegeModelsApi.fetchPreview (Just kmId) [] [] appState FetchPreviewComplete + + +update : AppState -> Msg -> Model -> ( Model, Cmd Wizard.Msgs.Msg ) +update appState msg model = + case msg of + FetchPreviewComplete result -> + applyResult appState + { setResult = setKnowledgeModel + , defaultError = gettext "Unable to get resource page." appState.locale + , model = model + , result = result + , logoutMsg = Wizard.Msgs.logoutMsg + } diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/View.elm b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/View.elm new file mode 100644 index 000000000..8367e418c --- /dev/null +++ b/engine-wizard/elm/Wizard/KnowledgeModels/ResourcePage/View.elm @@ -0,0 +1,38 @@ +module Wizard.KnowledgeModels.ResourcePage.View exposing (view) + +import Html exposing (Html, div, h1, text) +import Html.Attributes exposing (class) +import Shared.Data.KnowledgeModel as KnowledgeModel exposing (KnowledgeModel) +import Shared.Html exposing (faSet) +import Shared.Markdown as Markdown +import Wizard.Common.AppState exposing (AppState) +import Wizard.Common.View.Page as Page +import Wizard.KnowledgeModels.ResourcePage.Models exposing (Model) +import Wizard.KnowledgeModels.ResourcePage.Msgs exposing (Msg) + + +view : AppState -> Model -> Html Msg +view appState model = + Page.actionResultView appState (viewResourcePage appState model) model.knowledgeModel + + +viewResourcePage : AppState -> Model -> KnowledgeModel -> Html Msg +viewResourcePage appState model km = + case KnowledgeModel.getResourcePage model.resourcePageUuid km of + Just resourcePage -> + case KnowledgeModel.getResourceCollectionByResourcePageUuid model.resourcePageUuid km of + Just resourceCollection -> + div [ class "KnowledgeModels__BookReference container-fluid container-max" ] + [ div [ class "bg-light rounded px-3 py-3 fs-5 my-3" ] + [ faSet "km.resourceCollection" appState + , text resourceCollection.title + ] + , h1 [] [ text resourcePage.title ] + , Markdown.toHtml [] resourcePage.content + ] + + Nothing -> + div [] [ text "Resource page does not exist" ] + + Nothing -> + div [] [ text "Resource page does not exist" ] diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Routes.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Routes.elm index 410f150d9..5c1f987ea 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Routes.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Routes.elm @@ -8,3 +8,4 @@ type Route | ImportRoute (Maybe String) | IndexRoute PaginationQueryString | PreviewRoute String (Maybe String) + | ResourcePageRoute String String diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Routing.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Routing.elm index d9b752ae6..764dad689 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Routing.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Routing.elm @@ -18,11 +18,15 @@ parsers appState wrapRoute = let moduleRoot = lr "knowledgeModels" appState + + wrapResourcePageRoute kmId resourcePageUuid = + wrapRoute <| ResourcePageRoute kmId resourcePageUuid in [ map (wrapRoute << ImportRoute) (s moduleRoot s (lr "knowledgeModels.import" appState) Query.string (lr "knowledgeModels.import.packageId" appState)) , map (detail wrapRoute) (s moduleRoot string) , map (PaginationQueryString.wrapRoute (wrapRoute << IndexRoute) (Just "name")) (PaginationQueryString.parser (s moduleRoot)) , map (project wrapRoute) (s moduleRoot string s (lr "knowledgeModels.preview" appState) Query.string (lr "knowledgeModels.preview.questionUuid" appState)) + , map wrapResourcePageRoute (s moduleRoot string s "resource-pages" string) ] @@ -65,6 +69,9 @@ toUrl appState route = Nothing -> [ moduleRoot, packageId, lr "knowledgeModels.preview" appState ] + ResourcePageRoute kmId resourcePageUuid -> + [ moduleRoot, kmId, "resource-pages", resourcePageUuid ] + isAllowed : Route -> AppState -> Bool isAllowed route appState = @@ -72,11 +79,14 @@ isAllowed route appState = DetailRoute _ -> True + ImportRoute _ -> + Feature.knowledgeModelsImport appState + PreviewRoute _ _ -> True - ImportRoute _ -> - Feature.knowledgeModelsImport appState + ResourcePageRoute _ _ -> + True _ -> Feature.knowledgeModelsView appState diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Subscriptions.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Subscriptions.elm index 0faec4acc..c146e7cf9 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Subscriptions.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Subscriptions.elm @@ -23,3 +23,6 @@ subscriptions route model = PreviewRoute _ _ -> Sub.map PreviewMsg <| Wizard.KnowledgeModels.Preview.Subscriptions.subscriptions model.previewModel + + _ -> + Sub.none diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/Update.elm b/engine-wizard/elm/Wizard/KnowledgeModels/Update.elm index 95402b5aa..892222929 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/Update.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/Update.elm @@ -8,6 +8,7 @@ import Wizard.KnowledgeModels.Index.Update import Wizard.KnowledgeModels.Models exposing (Model) import Wizard.KnowledgeModels.Msgs exposing (Msg(..)) import Wizard.KnowledgeModels.Preview.Update +import Wizard.KnowledgeModels.ResourcePage.Update import Wizard.KnowledgeModels.Routes exposing (Route(..)) import Wizard.Msgs @@ -27,6 +28,10 @@ fetchData route appState = Cmd.map PreviewMsg <| Wizard.KnowledgeModels.Preview.Update.fetchData appState packageId + ResourcePageRoute kmId _ -> + Cmd.map ResourcePageMsg <| + Wizard.KnowledgeModels.ResourcePage.Update.fetchData appState kmId + _ -> Cmd.none @@ -61,3 +66,10 @@ update msg wrapMsg appState model = Wizard.KnowledgeModels.Preview.Update.update pMsg (wrapMsg << PreviewMsg) appState model.previewModel in ( newSeed, { model | previewModel = projectModel }, cmd ) + + ResourcePageMsg rpMsg -> + let + ( resourcePageModel, cmd ) = + Wizard.KnowledgeModels.ResourcePage.Update.update appState rpMsg model.resourcePageModel + in + ( appState.seed, { model | resourcePageModel = resourcePageModel }, cmd ) diff --git a/engine-wizard/elm/Wizard/KnowledgeModels/View.elm b/engine-wizard/elm/Wizard/KnowledgeModels/View.elm index f81b8fd6d..0e2a2add9 100644 --- a/engine-wizard/elm/Wizard/KnowledgeModels/View.elm +++ b/engine-wizard/elm/Wizard/KnowledgeModels/View.elm @@ -8,6 +8,7 @@ import Wizard.KnowledgeModels.Index.View import Wizard.KnowledgeModels.Models exposing (Model) import Wizard.KnowledgeModels.Msgs exposing (Msg(..)) import Wizard.KnowledgeModels.Preview.View +import Wizard.KnowledgeModels.ResourcePage.View import Wizard.KnowledgeModels.Routes exposing (Route(..)) @@ -25,3 +26,6 @@ view route appState model = PreviewRoute _ _ -> Html.map PreviewMsg <| Wizard.KnowledgeModels.Preview.View.view appState model.previewModel + + ResourcePageRoute _ _ -> + Html.map ResourcePageMsg <| Wizard.KnowledgeModels.ResourcePage.View.view appState model.resourcePageModel diff --git a/engine-wizard/elm/Wizard/Projects/Detail/Components/QuestionnaireVersionViewModal.elm b/engine-wizard/elm/Wizard/Projects/Detail/Components/QuestionnaireVersionViewModal.elm index 122b59c4f..0d4f7f510 100644 --- a/engine-wizard/elm/Wizard/Projects/Detail/Components/QuestionnaireVersionViewModal.elm +++ b/engine-wizard/elm/Wizard/Projects/Detail/Components/QuestionnaireVersionViewModal.elm @@ -161,7 +161,7 @@ viewContent appState qm = , toolbarEnabled = False , questionLinksEnabled = False } - , renderer = DefaultQuestionnaireRenderer.create appState qm.questionnaire.knowledgeModel + , renderer = DefaultQuestionnaireRenderer.create appState qm.questionnaire.knowledgeModel (DefaultQuestionnaireRenderer.defaultResourcePageToRoute qm.questionnaire.packageId) , wrapMsg = QuestionnaireMsg , previewQuestionnaireEventMsg = Nothing , revertQuestionnaireMsg = Nothing diff --git a/engine-wizard/elm/Wizard/Projects/Detail/View.elm b/engine-wizard/elm/Wizard/Projects/Detail/View.elm index 2309fda40..b4c6732b4 100644 --- a/engine-wizard/elm/Wizard/Projects/Detail/View.elm +++ b/engine-wizard/elm/Wizard/Projects/Detail/View.elm @@ -364,7 +364,7 @@ viewProjectContent appState route model questionnaire = , toolbarEnabled = True , questionLinksEnabled = True } - , renderer = DefaultQuestionnaireRenderer.create appState qm.questionnaire.knowledgeModel + , renderer = DefaultQuestionnaireRenderer.create appState qm.questionnaire.knowledgeModel (DefaultQuestionnaireRenderer.defaultResourcePageToRoute qm.questionnaire.packageId) , wrapMsg = QuestionnaireMsg , previewQuestionnaireEventMsg = Just (OpenVersionPreview qm.questionnaire.uuid) , revertQuestionnaireMsg = Just OpenRevertModal diff --git a/engine-wizard/elm/Wizard/Projects/Import/View.elm b/engine-wizard/elm/Wizard/Projects/Import/View.elm index 56c5c42de..768b4fd85 100644 --- a/engine-wizard/elm/Wizard/Projects/Import/View.elm +++ b/engine-wizard/elm/Wizard/Projects/Import/View.elm @@ -156,7 +156,7 @@ viewQuestionnairePreview appState model questionnaire questionnaireModel importR , toolbarEnabled = False , questionLinksEnabled = False } - , renderer = DefaultQuestionnaireRenderer.create appState questionnaire.knowledgeModel + , renderer = DefaultQuestionnaireRenderer.create appState questionnaire.knowledgeModel (DefaultQuestionnaireRenderer.defaultResourcePageToRoute questionnaire.packageId) , wrapMsg = QuestionnaireMsg , previewQuestionnaireEventMsg = Nothing , revertQuestionnaireMsg = Nothing diff --git a/engine-wizard/elm/Wizard/Public/BookReference/Models.elm b/engine-wizard/elm/Wizard/Public/BookReference/Models.elm deleted file mode 100644 index b7186bf56..000000000 --- a/engine-wizard/elm/Wizard/Public/BookReference/Models.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Wizard.Public.BookReference.Models exposing - ( Model - , initialModel - ) - -import ActionResult exposing (ActionResult(..)) -import Shared.Data.BookReference exposing (BookReference) - - -type alias Model = - { bookReference : ActionResult BookReference - } - - -initialModel : Model -initialModel = - { bookReference = Loading - } diff --git a/engine-wizard/elm/Wizard/Public/BookReference/Msgs.elm b/engine-wizard/elm/Wizard/Public/BookReference/Msgs.elm deleted file mode 100644 index 3fec1de04..000000000 --- a/engine-wizard/elm/Wizard/Public/BookReference/Msgs.elm +++ /dev/null @@ -1,8 +0,0 @@ -module Wizard.Public.BookReference.Msgs exposing (Msg(..)) - -import Shared.Data.BookReference exposing (BookReference) -import Shared.Error.ApiError exposing (ApiError) - - -type Msg - = GetBookReferenceCompleted (Result ApiError BookReference) diff --git a/engine-wizard/elm/Wizard/Public/BookReference/Update.elm b/engine-wizard/elm/Wizard/Public/BookReference/Update.elm deleted file mode 100644 index 123c07198..000000000 --- a/engine-wizard/elm/Wizard/Public/BookReference/Update.elm +++ /dev/null @@ -1,31 +0,0 @@ -module Wizard.Public.BookReference.Update exposing - ( fetchData - , update - ) - -import Gettext exposing (gettext) -import Shared.Api.BookReferences as BookReferencesApi -import Shared.Setters exposing (setBookReference) -import Wizard.Common.Api exposing (applyResult) -import Wizard.Common.AppState exposing (AppState) -import Wizard.Msgs -import Wizard.Public.BookReference.Models exposing (Model) -import Wizard.Public.BookReference.Msgs exposing (Msg(..)) - - -fetchData : String -> AppState -> Cmd Msg -fetchData uuid appState = - BookReferencesApi.getBookReference uuid appState GetBookReferenceCompleted - - -update : Msg -> AppState -> Model -> ( Model, Cmd Wizard.Msgs.Msg ) -update msg appState model = - case msg of - GetBookReferenceCompleted result -> - applyResult appState - { setResult = setBookReference - , defaultError = gettext "Unable to get a book reference." appState.locale - , model = model - , result = result - , logoutMsg = Wizard.Msgs.logoutMsg - } diff --git a/engine-wizard/elm/Wizard/Public/BookReference/View.elm b/engine-wizard/elm/Wizard/Public/BookReference/View.elm deleted file mode 100644 index 2f7813e48..000000000 --- a/engine-wizard/elm/Wizard/Public/BookReference/View.elm +++ /dev/null @@ -1,49 +0,0 @@ -module Wizard.Public.BookReference.View exposing (view) - -import Gettext exposing (gettext) -import Html exposing (Html, a, div, img, text) -import Html.Attributes exposing (alt, class, href, src, target) -import Shared.Data.BookReference exposing (BookReference) -import Shared.Markdown as Markdown -import String.Format as String -import Wizard.Common.AppState exposing (AppState) -import Wizard.Common.View.Page as Page -import Wizard.Public.BookReference.Models exposing (Model) -import Wizard.Public.BookReference.Msgs exposing (Msg) - - -view : AppState -> Model -> Html Msg -view appState model = - Page.actionResultView appState (viewBookReference appState) model.bookReference - - -bookUrl : String -bookUrl = - "https://www.crcpress.com/Data-Stewardship-for-Discovery-A-Practical-Guide-for-Data-Experts/Mons/p/book/9781498753173" - - -crcUrl : String -crcUrl = - "https://taylorandfrancis.com" - - -viewBookReference : AppState -> BookReference -> Html Msg -viewBookReference appState bookReference = - div [ class "Public__BookReference" ] - [ div [ class "px-4 py-5 bg-light rounded-3 book-title" ] - [ div [ class "book-name" ] - [ a [ href bookUrl, target "_blank" ] - [ img [ src "/wizard/img/book-preview.png", alt "Data Stewardship for Open Science Book Cover" ] [] - , text (gettext "Data Stewardship for Open Science" appState.locale) - ] - , text <| ": " ++ String.format (gettext "Chapter %s" appState.locale) [ bookReference.bookChapter ] - ] - , div [ class "book-crc" ] - [ div [] [ text (gettext "With kind permission of" appState.locale) ] - , a [ href crcUrl, target "_blank" ] - [ img [ src "/wizard/img/crc-logo.png", alt "CRC Press" ] [] - ] - ] - ] - , Markdown.toHtml [] bookReference.content - ] diff --git a/engine-wizard/elm/Wizard/Public/Models.elm b/engine-wizard/elm/Wizard/Public/Models.elm index 969c8b298..71caacfa6 100644 --- a/engine-wizard/elm/Wizard/Public/Models.elm +++ b/engine-wizard/elm/Wizard/Public/Models.elm @@ -2,7 +2,6 @@ module Wizard.Public.Models exposing (Model, initLocalModel, initialModel) import Wizard.Common.AppState exposing (AppState) import Wizard.Public.Auth.Models -import Wizard.Public.BookReference.Models import Wizard.Public.ForgottenPassword.Models import Wizard.Public.ForgottenPasswordConfirmation.Models import Wizard.Public.Login.Models @@ -13,7 +12,6 @@ import Wizard.Public.SignupConfirmation.Models type alias Model = { authModel : Wizard.Public.Auth.Models.Model - , bookReferenceModel : Wizard.Public.BookReference.Models.Model , forgottenPasswordModel : Wizard.Public.ForgottenPassword.Models.Model , forgottenPasswordConfirmationModel : Wizard.Public.ForgottenPasswordConfirmation.Models.Model , loginModel : Wizard.Public.Login.Models.Model @@ -25,7 +23,6 @@ type alias Model = initialModel : AppState -> Model initialModel appState = { authModel = Wizard.Public.Auth.Models.initialModel "" Nothing - , bookReferenceModel = Wizard.Public.BookReference.Models.initialModel , forgottenPasswordModel = Wizard.Public.ForgottenPassword.Models.initialModel , forgottenPasswordConfirmationModel = Wizard.Public.ForgottenPasswordConfirmation.Models.initialModel appState "" "" , loginModel = Wizard.Public.Login.Models.initialModel Nothing @@ -40,9 +37,6 @@ initLocalModel appState route model = AuthCallback id _ _ mbSessionState -> { model | authModel = Wizard.Public.Auth.Models.initialModel id mbSessionState } - BookReferenceRoute _ -> - { model | bookReferenceModel = Wizard.Public.BookReference.Models.initialModel } - ForgottenPasswordRoute -> { model | forgottenPasswordModel = Wizard.Public.ForgottenPassword.Models.initialModel } diff --git a/engine-wizard/elm/Wizard/Public/Msgs.elm b/engine-wizard/elm/Wizard/Public/Msgs.elm index 75ff5aff7..f4c854819 100644 --- a/engine-wizard/elm/Wizard/Public/Msgs.elm +++ b/engine-wizard/elm/Wizard/Public/Msgs.elm @@ -1,7 +1,6 @@ module Wizard.Public.Msgs exposing (Msg(..)) import Wizard.Public.Auth.Msgs -import Wizard.Public.BookReference.Msgs import Wizard.Public.ForgottenPassword.Msgs import Wizard.Public.ForgottenPasswordConfirmation.Msgs import Wizard.Public.Login.Msgs @@ -11,7 +10,6 @@ import Wizard.Public.SignupConfirmation.Msgs type Msg = AuthMsg Wizard.Public.Auth.Msgs.Msg - | BookReferenceMsg Wizard.Public.BookReference.Msgs.Msg | ForgottenPasswordMsg Wizard.Public.ForgottenPassword.Msgs.Msg | ForgottenPasswordConfirmationMsg Wizard.Public.ForgottenPasswordConfirmation.Msgs.Msg | LoginMsg Wizard.Public.Login.Msgs.Msg diff --git a/engine-wizard/elm/Wizard/Public/Routes.elm b/engine-wizard/elm/Wizard/Public/Routes.elm index e9f123fc4..9e128664f 100644 --- a/engine-wizard/elm/Wizard/Public/Routes.elm +++ b/engine-wizard/elm/Wizard/Public/Routes.elm @@ -3,7 +3,6 @@ module Wizard.Public.Routes exposing (Route(..)) type Route = AuthCallback String (Maybe String) (Maybe String) (Maybe String) - | BookReferenceRoute String | ForgottenPasswordRoute | ForgottenPasswordConfirmationRoute String String | LoginRoute (Maybe String) diff --git a/engine-wizard/elm/Wizard/Public/Routing.elm b/engine-wizard/elm/Wizard/Public/Routing.elm index 67ddc623d..6ffe84139 100644 --- a/engine-wizard/elm/Wizard/Public/Routing.elm +++ b/engine-wizard/elm/Wizard/Public/Routing.elm @@ -24,7 +24,6 @@ parsers appState wrapRoute = [] in [ map (authCallback wrapRoute) (s "auth" string s "callback" Query.string "error" Query.string "code" Query.string "session_state") - , map (wrapRoute << BookReferenceRoute) (s (lr "public.bookReferences" appState) string) , map (wrapRoute ForgottenPasswordRoute) (s (lr "public.forgottenPassword" appState)) , map (forgottenPasswordConfirmation wrapRoute) (s (lr "public.forgottenPassword" appState) string string) , map (wrapRoute << LoginRoute) (top Query.string (lr "login.originalUrl" appState)) @@ -58,9 +57,6 @@ toUrl appState route = , "?error=" ++ Maybe.withDefault "" error ++ "&code=" ++ Maybe.withDefault "" code ++ "&session_state=" ++ Maybe.withDefault "" sessionState ] - BookReferenceRoute uuid -> - [ lr "public.bookReferences" appState, uuid ] - ForgottenPasswordRoute -> [ lr "public.forgottenPassword" appState ] diff --git a/engine-wizard/elm/Wizard/Public/Update.elm b/engine-wizard/elm/Wizard/Public/Update.elm index a2f2e6592..4c77c3be0 100644 --- a/engine-wizard/elm/Wizard/Public/Update.elm +++ b/engine-wizard/elm/Wizard/Public/Update.elm @@ -3,7 +3,6 @@ module Wizard.Public.Update exposing (fetchData, update) import Wizard.Common.AppState exposing (AppState) import Wizard.Msgs import Wizard.Public.Auth.Update -import Wizard.Public.BookReference.Update import Wizard.Public.ForgottenPassword.Update import Wizard.Public.ForgottenPasswordConfirmation.Update import Wizard.Public.Login.Update @@ -22,10 +21,6 @@ fetchData route appState = Cmd.map AuthMsg <| Wizard.Public.Auth.Update.fetchData id error code sessionState appState - BookReferenceRoute uuid -> - Cmd.map BookReferenceMsg <| - Wizard.Public.BookReference.Update.fetchData uuid appState - LoginRoute mbOriginalUrl -> Wizard.Public.Login.Update.fetchData appState mbOriginalUrl @@ -50,13 +45,6 @@ update msg wrapMsg appState model = in ( { model | authModel = authModel }, cmd ) - BookReferenceMsg brMsg -> - let - ( bookReferenceModel, cmd ) = - Wizard.Public.BookReference.Update.update brMsg appState model.bookReferenceModel - in - ( { model | bookReferenceModel = bookReferenceModel }, cmd ) - ForgottenPasswordMsg fpMsg -> let ( forgottenPasswordModel, cmd ) = diff --git a/engine-wizard/elm/Wizard/Public/View.elm b/engine-wizard/elm/Wizard/Public/View.elm index d2c88257d..d8058baec 100644 --- a/engine-wizard/elm/Wizard/Public/View.elm +++ b/engine-wizard/elm/Wizard/Public/View.elm @@ -3,7 +3,6 @@ module Wizard.Public.View exposing (view) import Html exposing (Html) import Wizard.Common.AppState exposing (AppState) import Wizard.Public.Auth.View -import Wizard.Public.BookReference.View import Wizard.Public.ForgottenPassword.View import Wizard.Public.ForgottenPasswordConfirmation.View import Wizard.Public.Login.View @@ -22,10 +21,6 @@ view route appState model = Html.map AuthMsg <| Wizard.Public.Auth.View.view appState model.authModel - BookReferenceRoute _ -> - Html.map BookReferenceMsg <| - Wizard.Public.BookReference.View.view appState model.bookReferenceModel - ForgottenPasswordRoute -> Html.map ForgottenPasswordMsg <| Wizard.Public.ForgottenPassword.View.view appState model.forgottenPasswordModel diff --git a/engine-wizard/elm/Wizard/Routes.elm b/engine-wizard/elm/Wizard/Routes.elm index c143b505c..9c282d1d1 100644 --- a/engine-wizard/elm/Wizard/Routes.elm +++ b/engine-wizard/elm/Wizard/Routes.elm @@ -58,6 +58,7 @@ module Wizard.Routes exposing , knowledgeModelsIndex , knowledgeModelsIndexWithFilters , knowledgeModelsPreview + , knowledgeModelsResourcePage , localesCreate , localesDetail , localesImport @@ -524,6 +525,11 @@ knowledgeModelsPreview packageId mbQuestionUuid = KnowledgeModelsRoute <| Wizard.KnowledgeModels.Routes.PreviewRoute packageId mbQuestionUuid +knowledgeModelsResourcePage : String -> String -> Route +knowledgeModelsResourcePage kmId resourcePageUuid = + KnowledgeModelsRoute <| Wizard.KnowledgeModels.Routes.ResourcePageRoute kmId resourcePageUuid + + isKnowledgeModelsSubroute : Route -> Bool isKnowledgeModelsSubroute route = case route of diff --git a/engine-wizard/img/book-preview.png b/engine-wizard/img/book-preview.png deleted file mode 100644 index 2773ada1a6261bf70aa7aae8a1e84b86f10e2cf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18008 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 O0000 div { + .fa { + margin-right: $spacer-2; + } + } + + h1 { + border-bottom: 1px solid $gray-300; + padding-bottom: 10px; + margin-bottom: 20px; + } +} \ No newline at end of file diff --git a/engine-wizard/scss/modules/Public/_BookReference.scss b/engine-wizard/scss/modules/Public/_BookReference.scss deleted file mode 100644 index 3fc4fb12f..000000000 --- a/engine-wizard/scss/modules/Public/_BookReference.scss +++ /dev/null @@ -1,43 +0,0 @@ -@import '../../variables'; - -.Public__BookReference { - h1 { - border-bottom: 1px solid $gray-300; - padding-bottom: 10px; - margin-bottom: 20px; - } - - p { - text-align: justify; - } - - .book-title { - padding: 2rem 1rem; - display: flex; - justify-content: space-between; - align-items: center; - - .book-name { - font-size: 1.5rem; - - a { - img { - box-shadow: $box-shadow-sm; - margin-right: 20px; - height: 75px; - } - - &:not(:hover) { - color: $body-color; - text-decoration: underline; - } - } - } - - .book-crc { - img { - width: 150px; - } - } - } -} \ No newline at end of file diff --git a/engine-wizard/scss/modules/_KnowledgeModels.scss b/engine-wizard/scss/modules/_KnowledgeModels.scss index 8d2038b1f..86ccb20c3 100644 --- a/engine-wizard/scss/modules/_KnowledgeModels.scss +++ b/engine-wizard/scss/modules/_KnowledgeModels.scss @@ -1 +1,2 @@ @import 'KnowledgeModels/Preview'; +@import 'KnowledgeModels/ResourcePage'; diff --git a/engine-wizard/scss/modules/_Public.scss b/engine-wizard/scss/modules/_Public.scss index 520320c60..7093da941 100644 --- a/engine-wizard/scss/modules/_Public.scss +++ b/engine-wizard/scss/modules/_Public.scss @@ -1,5 +1,4 @@ @import '../variables'; -@import './Public/BookReference'; @import './Public/Login'; @import './Public/Questionnaire'; @import './Public/Signup'; diff --git a/tests/Shared/Data/EventTest.elm b/tests/Shared/Data/EventTest.elm index 2e0027aff..3f406b766 100644 --- a/tests/Shared/Data/EventTest.elm +++ b/tests/Shared/Data/EventTest.elm @@ -8,6 +8,8 @@ module Shared.Data.EventTest exposing , addPhaseEventTest , addQuestionEventTest , addReferenceEventTest + , addResourceCollectionEventTest + , addResourcePageEventTest , addTagEventTest , deleteAnswerEventTest , deleteChapterEventTest @@ -18,6 +20,8 @@ module Shared.Data.EventTest exposing , deletePhaseEventTest , deleteQuestionEventTest , deleteReferenceEventTest + , deleteResourceCollectionEventTest + , deleteResourcePageEventTest , deleteTagEventTest , editAnswerEventTest , editChapterEventTest @@ -29,6 +33,8 @@ module Shared.Data.EventTest exposing , editPhaseEventTest , editQuestionEventTest , editReferenceEventTest + , editResourceCollectionEventTest + , editResourcePageEventTest , editTagEventTest , moveAnswerEventTest , moveChoiceEventTest @@ -80,6 +86,10 @@ editKnowledgeModelEvent = { changed = False , value = Nothing } + , resourceCollectionUuids = + { changed = False + , value = Nothing + } , annotations = { changed = False , value = Nothing @@ -1372,7 +1382,7 @@ addResourcePageReferenceEvent : Event addResourcePageReferenceEvent = AddReferenceEvent (AddReferenceResourcePageEvent - { shortUuid = "atq" + { resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } ) @@ -1431,7 +1441,7 @@ addReferenceEventTest = \event -> Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid event) , parametrized - [ ( addResourcePageReferenceEvent, "atq" ) + [ ( addResourcePageReferenceEvent, "ba931b74-6254-403e-a10e-ba14bd55e384" ) , ( addURLReferenceEvent, "Example" ) , ( addCrossReferenceEvent, "072af95a-2dfd-11e9-b210-d663bd873d93" ) ] @@ -1446,9 +1456,9 @@ editResourcePageReferenceEvent : Event editResourcePageReferenceEvent = EditReferenceEvent (EditReferenceResourcePageEvent - { shortUuid = + { resourcePageUuid = { changed = True - , value = Just "atq" + , value = Just "ba931b74-6254-403e-a10e-ba14bd55e384" } , annotations = { changed = False @@ -1529,7 +1539,7 @@ editReferenceEventTest = \event -> Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid event) , parametrized - [ ( editResourcePageReferenceEvent, Just "atq" ) + [ ( editResourcePageReferenceEvent, Just "ba931b74-6254-403e-a10e-ba14bd55e384" ) , ( editURLReferenceEvent, Just "Example" ) , ( editCrossReferenceEvent, Nothing ) ] @@ -1735,6 +1745,243 @@ moveExpertEventTest = +{- resource collection events -} + + +addResourceCollectionEvent : Event +addResourceCollectionEvent = + AddResourceCollectionEvent + { title = "Collection" + , annotations = [] + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +addResourceCollectionEventTest : Test +addResourceCollectionEventTest = + describe "AddResourceCollectionEvent" + [ test "should encode and decode" <| + \_ -> expectEventEncodeDecode addResourceCollectionEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid addResourceCollectionEvent) + , test "get event entity visible name" <| + \_ -> + Expect.equal (Just "Collection") (Event.getEntityVisibleName addResourceCollectionEvent) + ] + + +editResourceCollectionEvent : Event +editResourceCollectionEvent = + EditResourceCollectionEvent + { title = + { changed = True + , value = Just "New Collection" + } + , resourcePageUuids = + { changed = True + , value = Just [ "ba931b74-6254-403e-a10e-ba14bd55e384", "bf2e14ca-67b9-4c00-b02e-b75213952992" ] + } + , annotations = + { changed = False + , value = Nothing + } + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +editResourceCollectionEventTest : Test +editResourceCollectionEventTest = + describe "EditResourceCollectionEvent" + [ test "should encode and decode" <| + \_ -> expectEventEncodeDecode editResourceCollectionEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid editResourceCollectionEvent) + , test "get event entity visible name when not changed" <| + \_ -> + let + event = + EditResourceCollectionEvent + { title = + { changed = False + , value = Nothing + } + , resourcePageUuids = + { changed = True + , value = Just [ "ba931b74-6254-403e-a10e-ba14bd55e384", "bf2e14ca-67b9-4c00-b02e-b75213952992" ] + } + , annotations = + { changed = False + , value = Nothing + } + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + in + Expect.equal Nothing (Event.getEntityVisibleName event) + , test "get event entity visible name when changed" <| + \_ -> + Expect.equal (Just "New Collection") (Event.getEntityVisibleName editResourceCollectionEvent) + ] + + +deleteResourceCollectionEvent : Event +deleteResourceCollectionEvent = + DeleteResourceCollectionEvent + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +deleteResourceCollectionEventTest : Test +deleteResourceCollectionEventTest = + describe "DeleteResourceCollectionEvent" + [ test "should encode and decode" <| + \_ -> + expectEventEncodeDecode deleteResourceCollectionEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid deleteResourceCollectionEvent) + , test "get entity visible name" <| + \_ -> + Expect.equal Nothing (Event.getEntityVisibleName deleteResourceCollectionEvent) + ] + + + +{- resource page events -} + + +addResourcePageEvent : Event +addResourcePageEvent = + AddResourcePageEvent + { title = "Page" + , content = "Content" + , annotations = [] + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +addResourcePageEventTest : Test +addResourcePageEventTest = + describe "AddResourcePageEvent" + [ test "should encode and decode" <| + \_ -> expectEventEncodeDecode addResourcePageEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid addResourcePageEvent) + , test "get event entity visible name" <| + \_ -> + Expect.equal (Just "Page") (Event.getEntityVisibleName addResourcePageEvent) + ] + + +editResourcePageEvent : Event +editResourcePageEvent = + EditResourcePageEvent + { title = + { changed = True + , value = Just "New Page" + } + , content = + { changed = True + , value = Just "New Content" + } + , annotations = + { changed = False + , value = Nothing + } + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +editResourcePageEventTest : Test +editResourcePageEventTest = + describe "EditResourcePageEvent" + [ test "should encode and decode" <| + \_ -> expectEventEncodeDecode editResourcePageEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid editResourcePageEvent) + , test "get event entity visible name when not changed" <| + \_ -> + let + event = + EditResourcePageEvent + { title = + { changed = False + , value = Nothing + } + , content = + { changed = True + , value = Just "New Content" + } + , annotations = + { changed = False + , value = Nothing + } + } + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + in + Expect.equal Nothing (Event.getEntityVisibleName event) + , test "get event entity visible name when changed" <| + \_ -> + Expect.equal (Just "New Page") (Event.getEntityVisibleName editResourcePageEvent) + ] + + +deleteResourcePageEvent : Event +deleteResourcePageEvent = + DeleteResourcePageEvent + { uuid = "349624f6-2dfc-11e9-b210-d663bd873d93" + , entityUuid = "bad22d1c-2e01-11e9-b210-d663bd873d93" + , parentUuid = "2f73c924-2dfc-11e9-b210-d663bd873d93" + , createdAt = Time.millisToPosix 1642607898 + } + + +deleteResourcePageEventTest : Test +deleteResourcePageEventTest = + describe "DeleteResourcePageEvent" + [ test "should encode and decode" <| + \_ -> + expectEventEncodeDecode deleteResourcePageEvent + , test "get event uuid" <| + \_ -> + Expect.equal "349624f6-2dfc-11e9-b210-d663bd873d93" (Event.getUuid deleteResourcePageEvent) + , test "get entity visible name" <| + \_ -> + Expect.equal Nothing (Event.getEntityVisibleName deleteResourcePageEvent) + ] + + + {- test utils -} diff --git a/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm b/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm index 4598f12af..edab3430d 100644 --- a/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm +++ b/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm @@ -27,7 +27,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -42,6 +44,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -68,7 +72,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -93,6 +99,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -127,7 +135,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -159,6 +169,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -186,7 +198,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -212,6 +226,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -236,7 +252,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -259,6 +277,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -284,7 +304,9 @@ knowledgeModelEntitiesDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -308,6 +330,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -326,14 +350,16 @@ knowledgeModelEntitiesDecoderTest = "8a703cfa-450f-421a-8819-875619ccb54d": { "referenceType": "ResourcePageReference", "uuid": "8a703cfa-450f-421a-8819-875619ccb54d", - "shortUuid": "atq", + "resourcePageUuid": "ba931b74-6254-403e-a10e-ba14bd55e384", "annotations": [] } }, "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -348,7 +374,7 @@ knowledgeModelEntitiesDecoderTest = [ ( "8a703cfa-450f-421a-8819-875619ccb54d" , ResourcePageReference { uuid = "8a703cfa-450f-421a-8819-875619ccb54d" - , shortUuid = "atq" + , resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } ) @@ -357,6 +383,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -394,7 +422,9 @@ knowledgeModelEntitiesDecoderTest = }, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -431,6 +461,8 @@ knowledgeModelEntitiesDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected @@ -457,7 +489,9 @@ knowledgeModelEntitiesDecoderTest = } }, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } """ @@ -482,6 +516,8 @@ knowledgeModelEntitiesDecoderTest = ] , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } in expectDecoder KnowledgeModelEntities.decoder raw expected diff --git a/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm b/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm index fa41ab9de..986ea31f1 100644 --- a/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm +++ b/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm @@ -16,7 +16,7 @@ referenceDecoderTest = { "referenceType": "ResourcePageReference", "uuid": "8a703cfa-450f-421a-8819-875619ccb54d", - "shortUuid": "atq", + "resourcePageUuid": "ba931b74-6254-403e-a10e-ba14bd55e384", "annotations": [] } """ @@ -24,7 +24,7 @@ referenceDecoderTest = expected = ResourcePageReference { uuid = "8a703cfa-450f-421a-8819-875619ccb54d" - , shortUuid = "atq" + , resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } in diff --git a/tests/Shared/Data/KnowledgeModelTest.elm b/tests/Shared/Data/KnowledgeModelTest.elm index 622faba58..eaa35f234 100644 --- a/tests/Shared/Data/KnowledgeModelTest.elm +++ b/tests/Shared/Data/KnowledgeModelTest.elm @@ -23,6 +23,7 @@ knowledgeModelDecoderTest = "integrationUuids": [], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": {}, @@ -34,7 +35,9 @@ knowledgeModelDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -46,6 +49,7 @@ knowledgeModelDecoderTest = , integrationUuids = [] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = Dict.empty @@ -58,6 +62,8 @@ knowledgeModelDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in @@ -74,6 +80,7 @@ knowledgeModelDecoderTest = "integrationUuids": [], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": { @@ -93,7 +100,9 @@ knowledgeModelDecoderTest = "integrations": {}, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -105,6 +114,7 @@ knowledgeModelDecoderTest = , integrationUuids = [] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = @@ -127,6 +137,8 @@ knowledgeModelDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in @@ -143,6 +155,7 @@ knowledgeModelDecoderTest = "integrationUuids": [], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": {}, @@ -162,7 +175,9 @@ knowledgeModelDecoderTest = } }, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -174,6 +189,7 @@ knowledgeModelDecoderTest = , integrationUuids = [] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = Dict.empty @@ -196,6 +212,8 @@ knowledgeModelDecoderTest = ] , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in @@ -212,6 +230,7 @@ knowledgeModelDecoderTest = "integrationUuids": ["aae37504-aec6-4be8-b703-5bcb3502f3e6"], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": {}, @@ -242,7 +261,9 @@ knowledgeModelDecoderTest = }, "tags": {}, "metrics": {}, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -254,6 +275,7 @@ knowledgeModelDecoderTest = , integrationUuids = [ "aae37504-aec6-4be8-b703-5bcb3502f3e6" ] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = Dict.empty @@ -288,6 +310,8 @@ knowledgeModelDecoderTest = , tags = Dict.empty , metrics = Dict.empty , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in @@ -304,6 +328,7 @@ knowledgeModelDecoderTest = "integrationUuids": ["aae37504-aec6-4be8-b703-5bcb3502f3e6"], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": {}, @@ -323,7 +348,9 @@ knowledgeModelDecoderTest = "annotations": [] } }, - "phases": {} + "phases": {}, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -335,6 +362,7 @@ knowledgeModelDecoderTest = , integrationUuids = [ "aae37504-aec6-4be8-b703-5bcb3502f3e6" ] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = Dict.empty @@ -357,6 +385,8 @@ knowledgeModelDecoderTest = ) ] , phases = Dict.empty + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in @@ -373,6 +403,7 @@ knowledgeModelDecoderTest = "integrationUuids": ["aae37504-aec6-4be8-b703-5bcb3502f3e6"], "metricUuids": [], "phaseUuids": [], + "resourceCollectionUuids": [], "annotations": [], "entities": { "chapters": {}, @@ -391,7 +422,9 @@ knowledgeModelDecoderTest = "description": "This is a phase", "annotations": [] } - } + }, + "resourceCollections": {}, + "resourcePages": {} } } """ @@ -403,6 +436,7 @@ knowledgeModelDecoderTest = , integrationUuids = [ "aae37504-aec6-4be8-b703-5bcb3502f3e6" ] , metricUuids = [] , phaseUuids = [] + , resourceCollectionUuids = [] , annotations = [] , entities = { chapters = Dict.empty @@ -424,6 +458,8 @@ knowledgeModelDecoderTest = } ) ] + , resourceCollections = Dict.empty + , resourcePages = Dict.empty } } in From 99503dcbc7e9c885b28965bb5268e93acea8b1e9 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Thu, 22 Aug 2024 10:10:55 +0200 Subject: [PATCH 11/24] Change resourcePageUuid to Maybe for ResourcePageReferenceData --- .../Data/Event/AddReferenceEventData.elm | 2 +- .../AddReferenceResourcePageEventData.elm | 7 ++++--- .../Data/Event/EditReferenceEventData.elm | 14 +++++++++++--- .../EditReferenceResourcePageEventData.elm | 7 ++++--- .../Shared/Data/KnowledgeModel/Reference.elm | 2 +- .../Reference/ResourcePageReferenceData.elm | 6 +++--- .../DefaultQuestionnaireRenderer.elm | 4 ++-- .../KMEditor/Editor/Common/EditorBranch.elm | 2 +- .../KMEditor/Editor/Components/KMEditor.elm | 6 +++--- .../elm/Wizard/KMEditor/Migration/View.elm | 19 ++++++++++--------- tests/Shared/Data/EventTest.elm | 4 ++-- .../KnowledgeModelEntitiesTest.elm | 2 +- .../Data/KnowledgeModel/ReferenceTest.elm | 2 +- 13 files changed, 44 insertions(+), 33 deletions(-) diff --git a/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm b/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm index 32850859c..cdb2ebfd0 100644 --- a/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/AddReferenceEventData.elm @@ -75,7 +75,7 @@ toReference referenceUuid data = getEntityVisibleName : AddReferenceEventData -> Maybe String getEntityVisibleName = - Just << map .resourcePageUuid .label .targetUuid + map .resourcePageUuid (Just << .label) (Just << .targetUuid) map : diff --git a/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm index 1f24beb27..e8e3ea743 100644 --- a/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/AddReferenceResourcePageEventData.elm @@ -8,12 +8,13 @@ module Shared.Data.Event.AddReferenceResourcePageEventData exposing import Json.Decode as D exposing (Decoder) import Json.Decode.Pipeline as D import Json.Encode as E +import Json.Encode.Extra as E import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) import Shared.Data.KnowledgeModel.Reference exposing (Reference(..)) type alias AddReferenceResourcePageEventData = - { resourcePageUuid : String + { resourcePageUuid : Maybe String , annotations : List Annotation } @@ -21,14 +22,14 @@ type alias AddReferenceResourcePageEventData = decoder : Decoder AddReferenceResourcePageEventData decoder = D.succeed AddReferenceResourcePageEventData - |> D.required "resourcePageUuid" D.string + |> D.required "resourcePageUuid" (D.nullable D.string) |> D.required "annotations" (D.list Annotation.decoder) encode : AddReferenceResourcePageEventData -> List ( String, E.Value ) encode data = [ ( "referenceType", E.string "ResourcePageReference" ) - , ( "resourcePageUuid", E.string data.resourcePageUuid ) + , ( "resourcePageUuid", E.maybe E.string data.resourcePageUuid ) , ( "annotations", E.list Annotation.encode data.annotations ) ] diff --git a/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm b/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm index 7a2b4e06d..3c07c28a6 100644 --- a/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditReferenceEventData.elm @@ -61,7 +61,7 @@ apply event reference = EditReferenceResourcePageEvent eventData -> ResourcePageReference { uuid = Reference.getUuid reference - , resourcePageUuid = EventField.getValueWithDefault eventData.resourcePageUuid (Maybe.withDefault "" (Reference.getResourcePageUuid reference)) + , resourcePageUuid = EventField.getValueWithDefault eventData.resourcePageUuid (Reference.getResourcePageUuid reference) , annotations = EventField.getValueWithDefault eventData.annotations (Reference.getAnnotations reference) } @@ -83,8 +83,16 @@ apply event reference = getEntityVisibleName : EditReferenceEventData -> Maybe String -getEntityVisibleName = - EventField.getValue << map .resourcePageUuid .label .targetUuid +getEntityVisibleName reference = + case reference of + EditReferenceResourcePageEvent data -> + Maybe.withDefault Nothing (EventField.getValue data.resourcePageUuid) + + EditReferenceURLEvent data -> + EventField.getValue data.label + + EditReferenceCrossEvent data -> + EventField.getValue data.targetUuid map : diff --git a/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm b/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm index 6cc259c95..079c63f9a 100644 --- a/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditReferenceResourcePageEventData.elm @@ -8,12 +8,13 @@ module Shared.Data.Event.EditReferenceResourcePageEventData exposing import Json.Decode as D exposing (Decoder) import Json.Decode.Pipeline as D import Json.Encode as E +import Json.Encode.Extra as E import Shared.Data.Event.EventField as EventField exposing (EventField) import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) type alias EditReferenceResourcePageEventData = - { resourcePageUuid : EventField String + { resourcePageUuid : EventField (Maybe String) , annotations : EventField (List Annotation) } @@ -21,14 +22,14 @@ type alias EditReferenceResourcePageEventData = decoder : Decoder EditReferenceResourcePageEventData decoder = D.succeed EditReferenceResourcePageEventData - |> D.required "resourcePageUuid" (EventField.decoder D.string) + |> D.required "resourcePageUuid" (EventField.decoder (D.nullable D.string)) |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) encode : EditReferenceResourcePageEventData -> List ( String, E.Value ) encode data = [ ( "referenceType", E.string "ResourcePageReference" ) - , ( "resourcePageUuid", EventField.encode E.string data.resourcePageUuid ) + , ( "resourcePageUuid", EventField.encode (E.maybe E.string) data.resourcePageUuid ) , ( "annotations", EventField.encode (E.list Annotation.encode) data.annotations ) ] diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm index 50ad8abb3..f4cab75e0 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference.elm @@ -96,7 +96,7 @@ getAnnotations = getResourcePageUuid : Reference -> Maybe String getResourcePageUuid = - map (Just << .resourcePageUuid) (always Nothing) (always Nothing) + map .resourcePageUuid (always Nothing) (always Nothing) getUrl : Reference -> Maybe String diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm index 1930caff4..91c2897eb 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Reference/ResourcePageReferenceData.elm @@ -12,7 +12,7 @@ import Shared.Data.KnowledgeModel.ResourcePage exposing (ResourcePage) type alias ResourcePageReferenceData = { uuid : String - , resourcePageUuid : String + , resourcePageUuid : Maybe String , annotations : List Annotation } @@ -21,13 +21,13 @@ decoder : Decoder ResourcePageReferenceData decoder = D.succeed ResourcePageReferenceData |> D.required "uuid" D.string - |> D.required "resourcePageUuid" D.string + |> D.required "resourcePageUuid" (D.maybe D.string) |> D.required "annotations" (D.list Annotation.decoder) toLabel : List ResourcePage -> ResourcePageReferenceData -> String toLabel resourcePages data = - case List.head <| List.filter (\rp -> rp.uuid == data.resourcePageUuid) resourcePages of + case List.head <| List.filter (\rp -> Just rp.uuid == data.resourcePageUuid) resourcePages of Just resourcePage -> resourcePage.title diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm index bbaba46aa..1aca9dcfc 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/DefaultQuestionnaireRenderer.elm @@ -231,7 +231,7 @@ viewResourcePageReferences appState km resourcePageToRoute resourcePageReference let resources = Dict.filterGroupBy - (flip KnowledgeModel.getResourceCollectionUuidByResourcePageUuid km << .resourcePageUuid) + (Maybe.andThen (flip KnowledgeModel.getResourceCollectionUuidByResourcePageUuid km) << .resourcePageUuid) resourcePageReferences viewResourceCollection ( resourceCollectionUuid, collectionResourcePageReferences ) = @@ -262,7 +262,7 @@ viewResourcePageReferences appState km resourcePageToRoute resourcePageReference viewResourcePageReference : AppState -> KnowledgeModel -> (String -> Wizard.Routes.Route) -> ResourcePageReferenceData -> Html msg viewResourcePageReference appState km resourcePageToRoute data = - case KnowledgeModel.getResourcePage data.resourcePageUuid km of + case Maybe.andThen (flip KnowledgeModel.getResourcePage km) data.resourcePageUuid of Just resourcePage -> linkTo appState (resourcePageToRoute resourcePage.uuid) diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm index b96ec2277..ba1618025 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm @@ -1016,7 +1016,7 @@ computeReferenceWarnings appState reference = in case reference of Reference.ResourcePageReference data -> - if String.isEmpty data.resourcePageUuid || data.resourcePageUuid == Uuid.toString Uuid.nil then + if Maybe.isNothing data.resourcePageUuid then createError (gettext "No resource page selected for resource page reference" appState.locale) else diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm index 28d59e095..b66110ec5 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm @@ -1909,10 +1909,10 @@ viewReferenceEditor { appState, wrapMsg, eventMsg, editorBranch } reference = Input.selectWithGroups { name = "resourcePageUuid" , label = gettext "Resource Page" appState.locale - , value = data.resourcePageUuid - , defaultOption = ( Uuid.toString Uuid.nil, gettext "- select resource page -" appState.locale ) + , value = Maybe.withDefault "" data.resourcePageUuid + , defaultOption = ( "", gettext "- select resource page -" appState.locale ) , options = resourcePageUuidOptions - , onChange = createTypeEditEvent setResourcePageUuid + , onChange = createTypeEditEvent setResourcePageUuid << String.toMaybe } annotationsInput = diff --git a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm index bcad5ac44..8a56a7735 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm @@ -62,6 +62,7 @@ import Shared.Data.KnowledgeModel.Tag exposing (Tag) import Shared.Data.Migration exposing (Migration) import Shared.Data.Migration.MigrationState.MigrationStateType exposing (MigrationStateType(..)) import Shared.Html exposing (emptyNode, faSet) +import Shared.Utils exposing (flip) import String.Format as String exposing (format) import Wizard.Common.AppState exposing (AppState) import Wizard.Common.Html exposing (linkTo) @@ -1671,7 +1672,7 @@ viewAddResourcePageReferenceDiff appState data = , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.resourcePageUuid + , Maybe.withDefault "" data.resourcePageUuid ] annotationsDiff = @@ -1734,10 +1735,10 @@ viewEditReferenceDiff appState event reference = , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , referenceData.resourcePageUuid + , Maybe.withDefault "" referenceData.resourcePageUuid ] [ gettext "Resource Page" appState.locale - , EventField.getValueWithDefault eventData.resourcePageUuid referenceData.resourcePageUuid + , Maybe.withDefault "" <| EventField.getValueWithDefault eventData.resourcePageUuid referenceData.resourcePageUuid ] annotationsDiff = @@ -1798,7 +1799,7 @@ viewEditReferenceDiff appState event reference = addReference = EditReferenceEventData.map - (viewEditResourcePageReferenceDiff appState) + (flip (viewEditResourcePageReferenceDiff appState) Nothing) (viewEditURLReferenceDiff appState) (viewEditCrossReferenceDiff appState) otherEvent @@ -1806,8 +1807,8 @@ viewEditReferenceDiff appState event reference = div [] [ deleteReference, addReference ] -viewEditResourcePageReferenceDiff : AppState -> EditReferenceResourcePageEventData -> Html Msg -viewEditResourcePageReferenceDiff appState data = +viewEditResourcePageReferenceDiff : AppState -> EditReferenceResourcePageEventData -> Maybe ResourcePageReferenceData -> Html Msg +viewEditResourcePageReferenceDiff appState data mbResourcePageReference = let fieldDiff = viewAdd <| @@ -1816,7 +1817,7 @@ viewEditResourcePageReferenceDiff appState data = , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , EventField.getValueWithDefault data.resourcePageUuid "" + , Maybe.withDefault "" <| EventField.getValueWithDefault data.resourcePageUuid (Maybe.andThen .resourcePageUuid mbResourcePageReference) ] annotationsDiff = @@ -1885,7 +1886,7 @@ viewDeleteResourcePageReferenceDiff appState data = , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.resourcePageUuid + , Maybe.withDefault "" data.resourcePageUuid ] annotationsDiff = @@ -1954,7 +1955,7 @@ viewMoveResourcePageReference appState data = , gettext "Resource Page UUID" appState.locale ] [ gettext "Resource Page" appState.locale - , data.resourcePageUuid + , Maybe.withDefault "" data.resourcePageUuid ] annotationsDiff = diff --git a/tests/Shared/Data/EventTest.elm b/tests/Shared/Data/EventTest.elm index 3f406b766..c0ab92ec3 100644 --- a/tests/Shared/Data/EventTest.elm +++ b/tests/Shared/Data/EventTest.elm @@ -1382,7 +1382,7 @@ addResourcePageReferenceEvent : Event addResourcePageReferenceEvent = AddReferenceEvent (AddReferenceResourcePageEvent - { resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" + { resourcePageUuid = Just "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } ) @@ -1458,7 +1458,7 @@ editResourcePageReferenceEvent = (EditReferenceResourcePageEvent { resourcePageUuid = { changed = True - , value = Just "ba931b74-6254-403e-a10e-ba14bd55e384" + , value = Just (Just "ba931b74-6254-403e-a10e-ba14bd55e384") } , annotations = { changed = False diff --git a/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm b/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm index edab3430d..c481bd68d 100644 --- a/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm +++ b/tests/Shared/Data/KnowledgeModel/KnowledgeModelEntitiesTest.elm @@ -374,7 +374,7 @@ knowledgeModelEntitiesDecoderTest = [ ( "8a703cfa-450f-421a-8819-875619ccb54d" , ResourcePageReference { uuid = "8a703cfa-450f-421a-8819-875619ccb54d" - , resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" + , resourcePageUuid = Just "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } ) diff --git a/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm b/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm index 986ea31f1..f7200463b 100644 --- a/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm +++ b/tests/Shared/Data/KnowledgeModel/ReferenceTest.elm @@ -24,7 +24,7 @@ referenceDecoderTest = expected = ResourcePageReference { uuid = "8a703cfa-450f-421a-8819-875619ccb54d" - , resourcePageUuid = "ba931b74-6254-403e-a10e-ba14bd55e384" + , resourcePageUuid = Just "ba931b74-6254-403e-a10e-ba14bd55e384" , annotations = [] } in From 064f604ebdd97fd4ab804ec94341ef1130691d34 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Thu, 22 Aug 2024 10:22:24 +0200 Subject: [PATCH 12/24] Fix deleted resource collection and pages in KM editor --- .../KMEditor/Editor/Common/EditorBranch.elm | 5 +++++ .../KMEditor/Editor/Components/KMEditor.elm | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm index ba1618025..320645f3d 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm @@ -194,11 +194,15 @@ getFilteredKM editorBranch = filterAnswer _ answer = { answer | followUpUuids = filterDeleted editorBranch answer.followUpUuids } + filterResourceCollection _ resourceCollection = + { resourceCollection | resourcePageUuids = filterDeleted editorBranch resourceCollection.resourcePageUuids } + entities = { knowledgeModelEntities | chapters = Dict.map filterChapter knowledgeModelEntities.chapters , questions = Dict.map filterQuestion knowledgeModelEntities.questions , answers = Dict.map filterAnswer knowledgeModelEntities.answers + , resourceCollections = Dict.map filterResourceCollection knowledgeModelEntities.resourceCollections } in { knowledgeModel @@ -207,6 +211,7 @@ getFilteredKM editorBranch = , integrationUuids = filterDeleted editorBranch knowledgeModel.integrationUuids , metricUuids = filterDeleted editorBranch knowledgeModel.metricUuids , phaseUuids = filterDeleted editorBranch knowledgeModel.phaseUuids + , resourceCollectionUuids = filterDeleted editorBranch knowledgeModel.resourceCollectionUuids , entities = entities } diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm index b66110ec5..f5786a70d 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm @@ -1898,11 +1898,21 @@ viewReferenceEditor { appState, wrapMsg, eventMsg, editorBranch } reference = resourcePageOption resourcePageUuid = KnowledgeModel.getResourcePage resourcePageUuid editorBranch.branch.knowledgeModel - |> Maybe.map (\rp -> ( rp.uuid, rp.title )) + |> Maybe.map + (\rp -> + let + title = + if String.isEmpty rp.title then + gettext "Untitled resource page" appState.locale + + else + rp.title + in + ( rp.uuid, title ) + ) resourcePageUuidOptions = - KnowledgeModel.getResourceCollections editorBranch.branch.knowledgeModel - |> EditorBranch.filterDeletedWith .uuid editorBranch + KnowledgeModel.getResourceCollections (EditorBranch.getFilteredKM editorBranch) |> List.map (\rc -> ( rc.title, List.filterMap resourcePageOption rc.resourcePageUuids )) resourcePageUuidSelect = From da649d3186a92c0d74e3dafbb40c442eb90f3354 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 17 Aug 2024 06:45:13 +0000 Subject: [PATCH 13/24] Bump @codemirror/legacy-modes from 6.4.0 to 6.4.1 Bumps [@codemirror/legacy-modes](https://github.com/codemirror/legacy-modes) from 6.4.0 to 6.4.1. - [Changelog](https://github.com/codemirror/legacy-modes/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/legacy-modes/compare/6.4.0...6.4.1) --- updated-dependencies: - dependency-name: "@codemirror/legacy-modes" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index ebf7e9c79..eb6c7dd29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-xml": "^6.1.0", "@codemirror/language": "^6.10.2", - "@codemirror/legacy-modes": "^6.4.0", + "@codemirror/legacy-modes": "^6.4.1", "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", @@ -216,9 +216,9 @@ } }, "node_modules/@codemirror/legacy-modes": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.0.tgz", - "integrity": "sha512-5m/K+1A6gYR0e+h/dEde7LoGimMjRtWXZFg4Lo70cc8HzjSdHe3fLwjWMR0VRl5KFT1SxalSap7uMgPKF28wBA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.1.tgz", + "integrity": "sha512-vdg3XY7OAs5uLDx2Iw+cGfnwtd7kM+Et/eMsqAGTfT/JKiVBQZXosTzjEbWAi/FrY6DcQIz8mQjBozFHZEUWQA==", "dependencies": { "@codemirror/language": "^6.0.0" } @@ -10689,9 +10689,9 @@ } }, "@codemirror/legacy-modes": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.0.tgz", - "integrity": "sha512-5m/K+1A6gYR0e+h/dEde7LoGimMjRtWXZFg4Lo70cc8HzjSdHe3fLwjWMR0VRl5KFT1SxalSap7uMgPKF28wBA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.1.tgz", + "integrity": "sha512-vdg3XY7OAs5uLDx2Iw+cGfnwtd7kM+Et/eMsqAGTfT/JKiVBQZXosTzjEbWAi/FrY6DcQIz8mQjBozFHZEUWQA==", "requires": { "@codemirror/language": "^6.0.0" } diff --git a/package.json b/package.json index 400445174..1ea66cde2 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-xml": "^6.1.0", "@codemirror/language": "^6.10.2", - "@codemirror/legacy-modes": "^6.4.0", + "@codemirror/legacy-modes": "^6.4.1", "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", From 980dddfe65281cc7be0326b64a534d4772de8b91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 17 Aug 2024 06:45:31 +0000 Subject: [PATCH 14/24] Bump uglify-js from 3.19.1 to 3.19.2 Bumps [uglify-js](https://github.com/mishoo/UglifyJS) from 3.19.1 to 3.19.2. - [Release notes](https://github.com/mishoo/UglifyJS/releases) - [Commits](https://github.com/mishoo/UglifyJS/compare/v3.19.1...v3.19.2) --- updated-dependencies: - dependency-name: uglify-js dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index eb6c7dd29..54bfc4cc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "sass": "^1.77.8", "sass-loader": "^16.0.0", "terser-webpack-plugin": "^5.3.10", - "uglify-js": "^3.19.1", + "uglify-js": "^3.19.2", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" @@ -9654,9 +9654,9 @@ } }, "node_modules/uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.2.tgz", + "integrity": "sha512-S8KA6DDI47nQXJSi2ctQ629YzwOVs+bQML6DAtvy0wgNdpi+0ySpQK0g2pxBq2xfF2z3YCscu7NNA8nXT9PlIQ==", "dev": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -17727,9 +17727,9 @@ } }, "uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.2.tgz", + "integrity": "sha512-S8KA6DDI47nQXJSi2ctQ629YzwOVs+bQML6DAtvy0wgNdpi+0ySpQK0g2pxBq2xfF2z3YCscu7NNA8nXT9PlIQ==", "dev": true }, "unicorn-magic": { diff --git a/package.json b/package.json index 1ea66cde2..846576bc8 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "sass": "^1.77.8", "sass-loader": "^16.0.0", "terser-webpack-plugin": "^5.3.10", - "uglify-js": "^3.19.1", + "uglify-js": "^3.19.2", "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" From 81b8b283e47954c5c451b1485408d8451e3edc0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Aug 2024 06:49:18 +0000 Subject: [PATCH 15/24] Bump axios from 1.7.3 to 1.7.5 Bumps [axios](https://github.com/axios/axios) from 1.7.3 to 1.7.5. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.3...v1.7.5) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54bfc4cc3..f2e9743f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", - "axios": "1.7.3", + "axios": "1.7.5", "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", @@ -1680,9 +1680,9 @@ } }, "node_modules/axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -11894,9 +11894,9 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index 846576bc8..e6fb77649 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.29.1", "@fortawesome/fontawesome-free": "^6.6.0", - "axios": "1.7.3", + "axios": "1.7.5", "axios-retry": "^4.5.0", "bootstrap": "^5.3.3", "chart.js": "^3.9.1", From 253266c58e1f0cd3e7413bac3ce6fddaa24cb8fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 07:03:59 +0000 Subject: [PATCH 16/24] Bump @codemirror/view from 6.29.1 to 6.33.0 Bumps [@codemirror/view](https://github.com/codemirror/view) from 6.29.1 to 6.33.0. - [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md) - [Commits](https://github.com/codemirror/view/compare/6.29.1...6.33.0) --- updated-dependencies: - dependency-name: "@codemirror/view" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2e9743f1..6c01b85ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@codemirror/language": "^6.10.2", "@codemirror/legacy-modes": "^6.4.1", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.29.1", + "@codemirror/view": "^6.33.0", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.5", "axios-retry": "^4.5.0", @@ -249,9 +249,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "node_modules/@codemirror/view": { - "version": "6.29.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", - "integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.33.0.tgz", + "integrity": "sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -10722,9 +10722,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "@codemirror/view": { - "version": "6.29.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", - "integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", + "version": "6.33.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.33.0.tgz", + "integrity": "sha512-AroaR3BvnjRW8fiZBalAaK+ZzB5usGgI014YKElYZvQdNH5ZIidHlO+cyf/2rWzyBFRkvG6VhiXeAEbC53P2YQ==", "requires": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", diff --git a/package.json b/package.json index e6fb77649..92f26f861 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@codemirror/language": "^6.10.2", "@codemirror/legacy-modes": "^6.4.1", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.29.1", + "@codemirror/view": "^6.33.0", "@fortawesome/fontawesome-free": "^6.6.0", "axios": "1.7.5", "axios-retry": "^4.5.0", From db09cfb4eb7fab71e2daf07044da676ce6637eff Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Mon, 26 Aug 2024 09:23:37 +0200 Subject: [PATCH 17/24] Fix npm dependencies --- package-lock.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c01b85ec..5d43fe846 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6031,13 +6031,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -7322,9 +7322,9 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -15058,13 +15058,13 @@ "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.3", + "picomatch": "^2.3.1" } }, "mime": { @@ -16035,9 +16035,9 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pkg-dir": { From 25ab14c84df2478a328a47f042aa0c47f9f7d44b Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Tue, 27 Aug 2024 14:52:13 +0200 Subject: [PATCH 18/24] Add ItemSelect question --- .../Data/Event/AddQuestionEventData.elm | 28 ++- .../Event/AddQuestionItemSelectEventData.elm | 62 +++++++ .../Shared/Data/Event/EditEventSetters.elm | 6 + .../Data/Event/EditQuestionEventData.elm | 31 +++- .../Data/Event/EditQuestionItemSelectData.elm | 65 +++++++ .../Shared/Data/KnowledgeModel/Question.elm | 28 +++ .../Question/ItemSelectQuestionData.elm | 18 ++ .../KnowledgeModel/Question/QuestionType.elm | 4 + .../QuestionnaireDetail/Reply/ReplyValue.elm | 25 +++ .../Data/QuestionnaireQuestionnaire.elm | 118 +++++++++++++ .../Common/Components/Questionnaire.elm | 140 ++++++++++++++- .../Components/Questionnaire/History.elm | 7 + .../KMEditor/Editor/Common/EditorBranch.elm | 41 +++-- .../KMEditor/Editor/Components/KMEditor.elm | 164 ++++++++++++------ .../Editor/Components/KMEditor/Input.elm | 4 +- .../Editor/Components/PhaseEditor.elm | 9 + .../KMEditor/Editor/Components/TagEditor.elm | 9 + .../elm/Wizard/KMEditor/Migration/View.elm | 20 +-- .../elm/Wizard/Projects/Import/View.elm | 4 + .../Common/Components/_Questionnaire.scss | 17 ++ .../Data/KnowledgeModel/QuestionTest.elm | 34 ++++ 21 files changed, 746 insertions(+), 88 deletions(-) create mode 100644 engine-shared/elm/Shared/Data/Event/AddQuestionItemSelectEventData.elm create mode 100644 engine-shared/elm/Shared/Data/Event/EditQuestionItemSelectData.elm create mode 100644 engine-shared/elm/Shared/Data/KnowledgeModel/Question/ItemSelectQuestionData.elm diff --git a/engine-shared/elm/Shared/Data/Event/AddQuestionEventData.elm b/engine-shared/elm/Shared/Data/Event/AddQuestionEventData.elm index 1ec5e4f36..cb3f8e7b0 100644 --- a/engine-shared/elm/Shared/Data/Event/AddQuestionEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/AddQuestionEventData.elm @@ -12,6 +12,7 @@ module Shared.Data.Event.AddQuestionEventData exposing import Json.Decode as D exposing (Decoder) import Json.Encode as E import Shared.Data.Event.AddQuestionIntegrationEventData as AddQuestionIntegrationEventData exposing (AddQuestionIntegrationEventData) +import Shared.Data.Event.AddQuestionItemSelectEventData as AddQuestionItemSelectEventData exposing (AddQuestionItemSelectEventData) import Shared.Data.Event.AddQuestionListEventData as AddQuestionListEventData exposing (AddQuestionListEventData) import Shared.Data.Event.AddQuestionMultiChoiceEventData as AddQuestionMultiChoiceEventData exposing (AddQuestionMultiChoiceEventData) import Shared.Data.Event.AddQuestionOptionsEventData as AddQuestionOptionsEventData exposing (AddQuestionOptionsEventData) @@ -25,6 +26,7 @@ type AddQuestionEventData | AddQuestionValueEvent AddQuestionValueEventData | AddQuestionIntegrationEvent AddQuestionIntegrationEventData | AddQuestionMultiChoiceEvent AddQuestionMultiChoiceEventData + | AddQuestionItemSelectEvent AddQuestionItemSelectEventData decoder : Decoder AddQuestionEventData @@ -48,6 +50,9 @@ decoder = "MultiChoiceQuestion" -> D.map AddQuestionMultiChoiceEvent AddQuestionMultiChoiceEventData.decoder + "ItemSelectQuestion" -> + D.map AddQuestionItemSelectEvent AddQuestionItemSelectEventData.decoder + _ -> D.fail <| "Unknown question type: " ++ questionType ) @@ -63,6 +68,7 @@ encode data = AddQuestionValueEventData.encode AddQuestionIntegrationEventData.encode AddQuestionMultiChoiceEventData.encode + AddQuestionItemSelectEventData.encode data in ( "eventType", E.string "AddQuestionEvent" ) :: eventData @@ -91,20 +97,24 @@ toQuestion questionUuid data = AddQuestionMultiChoiceEvent eventData -> AddQuestionMultiChoiceEventData.toQuestion questionUuid eventData + AddQuestionItemSelectEvent eventData -> + AddQuestionItemSelectEventData.toQuestion questionUuid eventData + getTypeString : AddQuestionEventData -> String getTypeString = map - (\_ -> "Options") - (\_ -> "List") - (\_ -> "Value") - (\_ -> "Integration") - (\_ -> "MultiChoice") + (always "Options") + (always "List") + (always "Value") + (always "Integration") + (always "MultiChoice") + (always "ItemSelect") getEntityVisibleName : AddQuestionEventData -> Maybe String getEntityVisibleName = - Just << map .title .title .title .title .title + Just << map .title .title .title .title .title .title map : @@ -113,9 +123,10 @@ map : -> (AddQuestionValueEventData -> a) -> (AddQuestionIntegrationEventData -> a) -> (AddQuestionMultiChoiceEventData -> a) + -> (AddQuestionItemSelectEventData -> a) -> AddQuestionEventData -> a -map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQuestion question = +map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQuestion itemSelectQuestion question = case question of AddQuestionOptionsEvent data -> optionsQuestion data @@ -131,3 +142,6 @@ map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQu AddQuestionMultiChoiceEvent data -> multiChoiceQuestion data + + AddQuestionItemSelectEvent data -> + itemSelectQuestion data diff --git a/engine-shared/elm/Shared/Data/Event/AddQuestionItemSelectEventData.elm b/engine-shared/elm/Shared/Data/Event/AddQuestionItemSelectEventData.elm new file mode 100644 index 000000000..d7f24f7bf --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/AddQuestionItemSelectEventData.elm @@ -0,0 +1,62 @@ +module Shared.Data.Event.AddQuestionItemSelectEventData exposing + ( AddQuestionItemSelectEventData + , decoder + , encode + , toQuestion + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Json.Encode.Extra as E +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) +import Shared.Data.KnowledgeModel.Question exposing (Question(..)) + + +type alias AddQuestionItemSelectEventData = + { title : String + , text : Maybe String + , requiredPhaseUuid : Maybe String + , tagUuids : List String + , listQuestionUuid : Maybe String + , annotations : List Annotation + } + + +decoder : Decoder AddQuestionItemSelectEventData +decoder = + D.succeed AddQuestionItemSelectEventData + |> D.required "title" D.string + |> D.required "text" (D.nullable D.string) + |> D.required "requiredPhaseUuid" (D.nullable D.string) + |> D.required "tagUuids" (D.list D.string) + |> D.required "listQuestionUuid" (D.nullable D.string) + |> D.required "annotations" (D.list Annotation.decoder) + + +encode : AddQuestionItemSelectEventData -> List ( String, E.Value ) +encode data = + [ ( "questionType", E.string "IntegrationQuestion" ) + , ( "title", E.string data.title ) + , ( "text", E.maybe E.string data.text ) + , ( "requiredPhaseUuid", E.maybe E.string data.requiredPhaseUuid ) + , ( "tagUuids", E.list E.string data.tagUuids ) + , ( "listQuestionUuid", E.maybe E.string data.listQuestionUuid ) + , ( "annotations", E.list Annotation.encode data.annotations ) + ] + + +toQuestion : String -> AddQuestionItemSelectEventData -> Question +toQuestion uuid data = + ItemSelectQuestion + { uuid = uuid + , title = data.title + , text = data.text + , requiredPhaseUuid = data.requiredPhaseUuid + , tagUuids = data.tagUuids + , referenceUuids = [] + , expertUuids = [] + , annotations = data.annotations + } + { listQuestionUuid = Nothing + } diff --git a/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm b/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm index f09e68bbb..c7202d28f 100644 --- a/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm +++ b/engine-shared/elm/Shared/Data/Event/EditEventSetters.elm @@ -17,6 +17,7 @@ module Shared.Data.Event.EditEventSetters exposing , setItemTemplateQuestionUuids , setItemUrl , setLabel + , setListQuestionUuid , setLogo , setMetricMeasures , setMetricUuids @@ -138,6 +139,11 @@ setLabel value data = { data | label = EventField.create value True } +setListQuestionUuid : a -> { b | listQuestionUuid : EventField a } -> { b | listQuestionUuid : EventField a } +setListQuestionUuid value data = + { data | listQuestionUuid = EventField.create value True } + + setLogo : a -> { b | logo : EventField a } -> { b | logo : EventField a } setLogo value data = { data | logo = EventField.create value True } diff --git a/engine-shared/elm/Shared/Data/Event/EditQuestionEventData.elm b/engine-shared/elm/Shared/Data/Event/EditQuestionEventData.elm index 9ba8632a3..5d3911edd 100644 --- a/engine-shared/elm/Shared/Data/Event/EditQuestionEventData.elm +++ b/engine-shared/elm/Shared/Data/Event/EditQuestionEventData.elm @@ -12,6 +12,7 @@ import Dict import Json.Decode as D exposing (Decoder) import Json.Encode as E import Shared.Data.Event.EditQuestionIntegrationEventData as EditQuestionIntegrationEventData exposing (EditQuestionIntegrationEventData) +import Shared.Data.Event.EditQuestionItemSelectData as EditQuestionItemSelectEventData exposing (EditQuestionItemSelectEventData) import Shared.Data.Event.EditQuestionListEventData as EditQuestionListEventData exposing (EditQuestionListEventData) import Shared.Data.Event.EditQuestionMultiChoiceEventData as EditQuestionMultiChoiceEventData exposing (EditQuestionMultiChoiceEventData) import Shared.Data.Event.EditQuestionOptionsEventData as EditQuestionOptionsEventData exposing (EditQuestionOptionsEventData) @@ -27,6 +28,7 @@ type EditQuestionEventData | EditQuestionValueEvent EditQuestionValueEventData | EditQuestionIntegrationEvent EditQuestionIntegrationEventData | EditQuestionMultiChoiceEvent EditQuestionMultiChoiceEventData + | EditQuestionItemSelectEvent EditQuestionItemSelectEventData decoder : Decoder EditQuestionEventData @@ -50,6 +52,9 @@ decoder = "MultiChoiceQuestion" -> D.map EditQuestionMultiChoiceEvent EditQuestionMultiChoiceEventData.decoder + "ItemSelectQuestion" -> + D.map EditQuestionItemSelectEvent EditQuestionItemSelectEventData.decoder + _ -> D.fail <| "Unknown question type: " ++ questionType ) @@ -65,6 +70,7 @@ encode data = EditQuestionValueEventData.encode EditQuestionIntegrationEventData.encode EditQuestionMultiChoiceEventData.encode + EditQuestionItemSelectEventData.encode data in ( "eventType", E.string "EditQuestionEvent" ) :: eventData @@ -116,20 +122,27 @@ apply event question = { choiceUuids = EventField.applyChildren eventData.choiceUuids (Question.getChoiceUuids question) } + EditQuestionItemSelectEvent eventData -> + ItemSelectQuestion + (applyCommonData eventData) + { listQuestionUuid = EventField.getValueWithDefault eventData.listQuestionUuid (Question.getListQuestionUuid question) + } + getTypeString : EditQuestionEventData -> String getTypeString = map - (\_ -> "Options") - (\_ -> "List") - (\_ -> "Value") - (\_ -> "Integration") - (\_ -> "MultiChoice") + (always "Options") + (always "List") + (always "Value") + (always "Integration") + (always "MultiChoice") + (always "ItemSelect") getEntityVisibleName : EditQuestionEventData -> Maybe String getEntityVisibleName = - EventField.getValue << map .title .title .title .title .title + EventField.getValue << map .title .title .title .title .title .title map : @@ -138,9 +151,10 @@ map : -> (EditQuestionValueEventData -> a) -> (EditQuestionIntegrationEventData -> a) -> (EditQuestionMultiChoiceEventData -> a) + -> (EditQuestionItemSelectEventData -> a) -> EditQuestionEventData -> a -map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQuestion question = +map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQuestion itemSelectQuestion question = case question of EditQuestionOptionsEvent data -> optionsQuestion data @@ -156,3 +170,6 @@ map optionsQuestion listQuestion valueQuestion integrationQuestion multiChoiceQu EditQuestionMultiChoiceEvent data -> multiChoiceQuestion data + + EditQuestionItemSelectEvent data -> + itemSelectQuestion data diff --git a/engine-shared/elm/Shared/Data/Event/EditQuestionItemSelectData.elm b/engine-shared/elm/Shared/Data/Event/EditQuestionItemSelectData.elm new file mode 100644 index 000000000..8ad402972 --- /dev/null +++ b/engine-shared/elm/Shared/Data/Event/EditQuestionItemSelectData.elm @@ -0,0 +1,65 @@ +module Shared.Data.Event.EditQuestionItemSelectData exposing + ( EditQuestionItemSelectEventData + , decoder + , encode + , init + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D +import Json.Encode as E +import Json.Encode.Extra as E +import Shared.Data.Event.EventField as EventField exposing (EventField) +import Shared.Data.KnowledgeModel.Annotation as Annotation exposing (Annotation) + + +type alias EditQuestionItemSelectEventData = + { title : EventField String + , text : EventField (Maybe String) + , requiredPhaseUuid : EventField (Maybe String) + , tagUuids : EventField (List String) + , referenceUuids : EventField (List String) + , expertUuids : EventField (List String) + , listQuestionUuid : EventField (Maybe String) + , annotations : EventField (List Annotation) + } + + +decoder : Decoder EditQuestionItemSelectEventData +decoder = + D.succeed EditQuestionItemSelectEventData + |> D.required "title" (EventField.decoder D.string) + |> D.required "text" (EventField.decoder (D.nullable D.string)) + |> D.required "requiredPhaseUuid" (EventField.decoder (D.nullable D.string)) + |> D.required "tagUuids" (EventField.decoder (D.list D.string)) + |> D.required "referenceUuids" (EventField.decoder (D.list D.string)) + |> D.required "expertUuids" (EventField.decoder (D.list D.string)) + |> D.required "listQuestionUuid" (EventField.decoder (D.nullable D.string)) + |> D.required "annotations" (EventField.decoder (D.list Annotation.decoder)) + + +encode : EditQuestionItemSelectEventData -> List ( String, E.Value ) +encode data = + [ ( "questionType", E.string "ItemSelectQuestion" ) + , ( "title", EventField.encode E.string data.title ) + , ( "text", EventField.encode (E.maybe E.string) data.text ) + , ( "requiredPhaseUuid", EventField.encode (E.maybe E.string) data.requiredPhaseUuid ) + , ( "tagUuids", EventField.encode (E.list E.string) data.tagUuids ) + , ( "referenceUuids", EventField.encode (E.list E.string) data.referenceUuids ) + , ( "expertUuids", EventField.encode (E.list E.string) data.expertUuids ) + , ( "listQuestionUuid", EventField.encode (E.maybe E.string) data.listQuestionUuid ) + , ( "annotations", EventField.encode (E.list Annotation.encode) data.annotations ) + ] + + +init : EditQuestionItemSelectEventData +init = + { title = EventField.empty + , text = EventField.empty + , requiredPhaseUuid = EventField.empty + , tagUuids = EventField.empty + , referenceUuids = EventField.empty + , expertUuids = EventField.empty + , listQuestionUuid = EventField.empty + , annotations = EventField.empty + } diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Question.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Question.elm index a14b3eca0..f85a85a0a 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Question.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Question.elm @@ -12,6 +12,7 @@ module Shared.Data.KnowledgeModel.Question exposing , getExpertUuids , getIntegrationUuid , getItemTemplateQuestionUuids + , getListQuestionUuid , getPropValue , getProps , getReferenceUuids @@ -40,6 +41,7 @@ import List.Extra as List import Shared.Data.KnowledgeModel.Annotation exposing (Annotation) import Shared.Data.KnowledgeModel.Question.CommonQuestionData as CommonQuestionData exposing (CommonQuestionData) import Shared.Data.KnowledgeModel.Question.IntegrationQuestionData as IntegrationQuestionData exposing (IntegrationQuestionData) +import Shared.Data.KnowledgeModel.Question.ItemSelectQuestionData as ItemSelectQuestionData exposing (ItemSelectQuestionData) import Shared.Data.KnowledgeModel.Question.ListQuestionData as ListQuestionData exposing (ListQuestionData) import Shared.Data.KnowledgeModel.Question.MultiChoiceQuestionData as MultiChoiceQuestionData exposing (MultiChoiceQuestionData) import Shared.Data.KnowledgeModel.Question.OptionsQuestionData as OptionsQuestionData exposing (OptionsQuestionData) @@ -54,6 +56,7 @@ type Question | ValueQuestion CommonQuestionData ValueQuestionData | IntegrationQuestion CommonQuestionData IntegrationQuestionData | MultiChoiceQuestion CommonQuestionData MultiChoiceQuestionData + | ItemSelectQuestion CommonQuestionData ItemSelectQuestionData @@ -68,6 +71,7 @@ decoder = , D.when QuestionType.decoder ((==) ValueQuestionType) valueQuestionDecoder , D.when QuestionType.decoder ((==) IntegrationQuestionType) integrationQuestionDecoder , D.when QuestionType.decoder ((==) MultiChoiceQuestionType) multiChoiceQuestionDecoder + , D.when QuestionType.decoder ((==) ItemSelectQuestionType) itemSelectQuestionDecoder ] @@ -96,6 +100,11 @@ multiChoiceQuestionDecoder = D.map2 MultiChoiceQuestion CommonQuestionData.decoder MultiChoiceQuestionData.decoder +itemSelectQuestionDecoder : Decoder Question +itemSelectQuestionDecoder = + D.map2 ItemSelectQuestion CommonQuestionData.decoder ItemSelectQuestionData.decoder + + -- Helpers @@ -204,6 +213,9 @@ mapCommonQuestionData map question = MultiChoiceQuestion commonData questionData -> MultiChoiceQuestion (map commonData) questionData + ItemSelectQuestion commonData questionData -> + ItemSelectQuestion (map commonData) questionData + getCommonQuestionData : Question -> CommonQuestionData getCommonQuestionData question = @@ -223,6 +235,9 @@ getCommonQuestionData question = MultiChoiceQuestion data _ -> data + ItemSelectQuestion data _ -> + data + getUuid : Question -> String getUuid = @@ -257,6 +272,9 @@ getTypeString question = MultiChoiceQuestion _ _ -> "MultiChoice" + ItemSelectQuestion _ _ -> + "ItemSelect" + getRequiredPhaseUuid : Question -> Maybe String getRequiredPhaseUuid = @@ -353,6 +371,16 @@ getPropValue prop question = Nothing +getListQuestionUuid : Question -> Maybe String +getListQuestionUuid question = + case question of + ItemSelectQuestion _ data -> + data.listQuestionUuid + + _ -> + Nothing + + isOptions : Question -> Bool isOptions question = case question of diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Question/ItemSelectQuestionData.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Question/ItemSelectQuestionData.elm new file mode 100644 index 000000000..507f3005c --- /dev/null +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Question/ItemSelectQuestionData.elm @@ -0,0 +1,18 @@ +module Shared.Data.KnowledgeModel.Question.ItemSelectQuestionData exposing + ( ItemSelectQuestionData + , decoder + ) + +import Json.Decode as D exposing (Decoder) +import Json.Decode.Pipeline as D + + +type alias ItemSelectQuestionData = + { listQuestionUuid : Maybe String + } + + +decoder : Decoder ItemSelectQuestionData +decoder = + D.succeed ItemSelectQuestionData + |> D.required "listQuestionUuid" (D.maybe D.string) diff --git a/engine-shared/elm/Shared/Data/KnowledgeModel/Question/QuestionType.elm b/engine-shared/elm/Shared/Data/KnowledgeModel/Question/QuestionType.elm index 3b89f712a..f609bb00e 100644 --- a/engine-shared/elm/Shared/Data/KnowledgeModel/Question/QuestionType.elm +++ b/engine-shared/elm/Shared/Data/KnowledgeModel/Question/QuestionType.elm @@ -12,6 +12,7 @@ type QuestionType | ValueQuestionType | IntegrationQuestionType | MultiChoiceQuestionType + | ItemSelectQuestionType decoder : Decoder QuestionType @@ -35,6 +36,9 @@ decoder = "MultiChoiceQuestion" -> D.succeed MultiChoiceQuestionType + "ItemSelectQuestion" -> + D.succeed ItemSelectQuestionType + valueType -> D.fail <| "Unknown question type: " ++ valueType ) diff --git a/engine-shared/elm/Shared/Data/QuestionnaireDetail/Reply/ReplyValue.elm b/engine-shared/elm/Shared/Data/QuestionnaireDetail/Reply/ReplyValue.elm index a07df8c13..398c7fcbc 100644 --- a/engine-shared/elm/Shared/Data/QuestionnaireDetail/Reply/ReplyValue.elm +++ b/engine-shared/elm/Shared/Data/QuestionnaireDetail/Reply/ReplyValue.elm @@ -5,6 +5,7 @@ module Shared.Data.QuestionnaireDetail.Reply.ReplyValue exposing , getAnswerUuid , getChoiceUuid , getItemUuids + , getSelectedItemUuid , getStringReply , isEmpty ) @@ -22,6 +23,7 @@ type ReplyValue | MultiChoiceReply (List String) | ItemListReply (List String) | IntegrationReply IntegrationReplyType + | ItemSelectReply String decoder : Decoder ReplyValue @@ -32,6 +34,7 @@ decoder = , D.when replyValueType ((==) "MultiChoiceReply") decodeMultiChoiceReply , D.when replyValueType ((==) "ItemListReply") decodeItemListReply , D.when replyValueType ((==) "IntegrationReply") decodeIntegrationReply + , D.when replyValueType ((==) "ItemSelectReply") decodeItemSelectReply ] @@ -70,6 +73,12 @@ decodeIntegrationReply = |> D.required "value" IntegrationReplyValue.decoder +decodeItemSelectReply : Decoder ReplyValue +decodeItemSelectReply = + D.succeed ItemSelectReply + |> D.required "value" D.string + + encode : ReplyValue -> E.Value encode replyValue = case replyValue of @@ -100,6 +109,12 @@ encode replyValue = IntegrationReply integrationReplyValue -> IntegrationReplyValue.encode integrationReplyValue + ItemSelectReply itemUuid -> + E.object + [ ( "type", E.string "ItemSelectReply" ) + , ( "value", E.string itemUuid ) + ] + getItemUuids : ReplyValue -> List String getItemUuids replyValue = @@ -149,6 +164,16 @@ getStringReply replyValue = "" +getSelectedItemUuid : ReplyValue -> String +getSelectedItemUuid replyValue = + case replyValue of + ItemSelectReply itemUuid -> + itemUuid + + _ -> + "" + + isEmpty : ReplyValue -> Bool isEmpty replyValue = case replyValue of diff --git a/engine-shared/elm/Shared/Data/QuestionnaireQuestionnaire.elm b/engine-shared/elm/Shared/Data/QuestionnaireQuestionnaire.elm index 8caf21c09..e89c2f893 100644 --- a/engine-shared/elm/Shared/Data/QuestionnaireQuestionnaire.elm +++ b/engine-shared/elm/Shared/Data/QuestionnaireQuestionnaire.elm @@ -12,12 +12,15 @@ module Shared.Data.QuestionnaireQuestionnaire exposing , decoder , generateReplies , getComments + , getItemSelectQuestionValueLabel , getItemTitle , getTodos , getUnresolvedCommentCount , getWarnings , hasReply , isCurrentVersion + , itemSelectQuestionItemMissing + , itemSelectQuestionItemPath , removeCommentThreadFromCount , setLabels , setPhaseUuid @@ -32,6 +35,7 @@ module Shared.Data.QuestionnaireQuestionnaire exposing import Dict exposing (Dict) import Dict.Extra as Dict +import Gettext exposing (gettext) import Json.Decode as D exposing (Decoder) import Json.Decode.Pipeline as D import List.Extra as List @@ -56,9 +60,11 @@ import Shared.Markdown as Markdown import Shared.RegexPatterns as RegexPatterns import Shared.Utils exposing (boolToInt, getUuidString) import String.Extra as String +import String.Format as String import Time import Tuple.Extra as Tuple import Uuid exposing (Uuid) +import Wizard.Common.AppState exposing (AppState) type alias QuestionnaireQuestionnaire = @@ -331,6 +337,111 @@ getComments questionnaire = concatMapVisibleQuestions fn questionnaire +itemSelectQuestionItemMissing : QuestionnaireQuestionnaire -> Maybe String -> String -> Bool +itemSelectQuestionItemMissing questionnaire itemSelectQuestionListQuestionUuid path = + case getReplyValue questionnaire path of + Just replyValue -> + case replyValue of + ItemSelectReply itemUuid -> + let + mbItemQuestionUuid = + itemSelectQuestionListQuestionUuid + |> Maybe.andThen (\uuid -> KnowledgeModel.getQuestion uuid questionnaire.knowledgeModel) + |> Maybe.map Question.getUuid + in + case mbItemQuestionUuid of + Just itemQuestionUuid -> + let + itemExists = + questionnaire.replies + |> Dict.filter (\key _ -> String.endsWith itemQuestionUuid key) + |> Dict.values + |> List.any (List.member itemUuid << ReplyValue.getItemUuids << .value) + in + not itemExists + + Nothing -> + False + + _ -> + False + + Nothing -> + False + + +itemSelectQuestionItemPath : QuestionnaireQuestionnaire -> Maybe String -> String -> Maybe String +itemSelectQuestionItemPath questionnaire itemSelectQuestionListQuestionUuid path = + case getReplyValue questionnaire path of + Just replyValue -> + case replyValue of + ItemSelectReply itemUuid -> + let + mbItemQuestionUuid = + itemSelectQuestionListQuestionUuid + |> Maybe.andThen (\uuid -> KnowledgeModel.getQuestion uuid questionnaire.knowledgeModel) + |> Maybe.map Question.getUuid + in + case mbItemQuestionUuid of + Just itemQuestionUuid -> + questionnaire.replies + |> Dict.filter (\key _ -> String.endsWith itemQuestionUuid key) + |> Dict.toList + |> List.find (List.member itemUuid << ReplyValue.getItemUuids << .value << Tuple.second) + |> Maybe.map (\( key, _ ) -> key ++ "." ++ itemUuid) + + Nothing -> + Nothing + + _ -> + Nothing + + Nothing -> + Nothing + + +getItemSelectQuestionValueLabel : AppState -> QuestionnaireQuestionnaire -> String -> String -> String +getItemSelectQuestionValueLabel appState questionnaire itemSelectQuestionUuid itemUuid = + let + mbItemSelectQuestion = + KnowledgeModel.getQuestion itemSelectQuestionUuid questionnaire.knowledgeModel + + mbListQuestionUuid = + mbItemSelectQuestion + |> Maybe.andThen Question.getListQuestionUuid + |> Maybe.andThen (\uuid -> KnowledgeModel.getQuestion uuid questionnaire.knowledgeModel) + |> Maybe.map Question.getUuid + + fallbackItemName = + gettext "Item" appState.locale + in + case mbListQuestionUuid of + Just listQuestionUuid -> + let + itemTemplateQuestions = + KnowledgeModel.getQuestionItemTemplateQuestions listQuestionUuid questionnaire.knowledgeModel + + itemsToOptions ( itemQuestionPath, reply ) = + ReplyValue.getItemUuids reply.value + |> List.indexedMap + (\i iUuid -> + ( iUuid + , getItemTitle questionnaire (String.split "." itemQuestionPath ++ [ itemUuid ]) itemTemplateQuestions + |> Maybe.withDefault (String.format (gettext "Item %s" appState.locale) [ String.fromInt (i + 1) ]) + ) + ) + in + questionnaire.replies + |> Dict.filter (\key _ -> String.endsWith listQuestionUuid key) + |> Dict.find (\_ value -> List.member itemUuid (ReplyValue.getItemUuids value.value)) + |> Maybe.unwrap [] itemsToOptions + |> List.find (\( iUuid, _ ) -> iUuid == itemUuid) + |> Maybe.unwrap fallbackItemName Tuple.second + + Nothing -> + fallbackItemName + + type alias QuestionnaireWarning = { chapter : Chapter , question : Question @@ -396,6 +507,13 @@ getWarnings questionnaire = Nothing -> [] + ItemSelectQuestion _ questionData -> + if itemSelectQuestionItemMissing questionnaire questionData.listQuestionUuid (pathToString currentPath) then + [ questionnaireWarning ] + + else + [] + _ -> [] in diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm index 79a6a121c..9ae79d6b6 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm @@ -36,9 +36,10 @@ import CharIdentifier import Debounce exposing (Debounce) import Dict exposing (Dict) import Gettext exposing (gettext, ngettext) -import Html exposing (Html, a, button, div, h2, h5, i, img, input, label, li, p, span, strong, text, ul) -import Html.Attributes exposing (attribute, checked, class, classList, disabled, href, id, name, placeholder, src, target, type_, value) +import Html exposing (Html, a, button, div, h2, h5, i, img, input, label, li, option, p, select, span, strong, text, ul) +import Html.Attributes exposing (attribute, checked, class, classList, disabled, href, id, name, placeholder, selected, src, target, type_, value) import Html.Events exposing (onBlur, onCheck, onClick, onFocus, onInput, onMouseDown, onMouseOut) +import Html.Events.Extra exposing (onChange) import Json.Decode as D exposing (Decoder, decodeValue) import Json.Decode.Extra as D import Json.Encode as E @@ -1369,6 +1370,7 @@ handleScrollToPath model immediate path = in ( { model | activePage = PageChapter chapterUuid + , removeItem = Nothing , collapsedItems = newCollapsedItems } , Cmd.batch @@ -2913,6 +2915,9 @@ viewQuestion appState cfg ctx model path humanIdentifiers order question = MultiChoiceQuestion _ _ -> ( viewQuestionMultiChoice appState cfg model newPath question, [] ) + ItemSelectQuestion _ _ -> + ( viewQuestionItemSelect appState cfg model newPath question, [] ) + ( questionClass, questionState ) = case ( QuestionnaireQuestionnaire.hasReply (pathToString newPath) model.questionnaire @@ -3604,6 +3609,92 @@ viewQuestionIntegrationLink integration mbId = emptyNode +viewQuestionItemSelect : AppState -> Config msg -> Model -> List String -> Question -> Html Msg +viewQuestionItemSelect appState cfg model path question = + let + mbSelectedItem = + Dict.get (pathToString path) model.questionnaire.replies + |> Maybe.map (.value >> ReplyValue.getSelectedItemUuid) + + extraAttrs = + if cfg.features.readonly then + [ disabled True ] + + else + [ onChange (SetReply (pathToString path) << createReply appState << ItemSelectReply) ] + + mbListQuestionUuid = + Question.getListQuestionUuid question + + mbItemQuestionUuid = + mbListQuestionUuid + |> Maybe.andThen (\uuid -> KnowledgeModel.getQuestion uuid model.questionnaire.knowledgeModel) + |> Maybe.map Question.getUuid + + items = + case mbItemQuestionUuid of + Just itemQuestionUuid -> + let + itemTemplateQuestions = + KnowledgeModel.getQuestionItemTemplateQuestions itemQuestionUuid model.questionnaire.knowledgeModel + + itemsToOptions ( itemQuestionPath, reply ) = + ReplyValue.getItemUuids reply.value + |> List.indexedMap + (\i itemUuid -> + ( itemUuid + , QuestionnaireQuestionnaire.getItemTitle model.questionnaire (String.split "." itemQuestionPath ++ [ itemUuid ]) itemTemplateQuestions + |> Maybe.withDefault (String.format (gettext "Item %s" appState.locale) [ String.fromInt (i + 1) ]) + ) + ) + in + model.questionnaire.replies + |> Dict.filter (\key _ -> String.endsWith itemQuestionUuid key) + |> Dict.toList + |> List.concatMap itemsToOptions + + Nothing -> + [] + + itemToOption ( optionValue, optionLabel ) = + option [ value optionValue, selected (Just optionValue == mbSelectedItem) ] + [ text optionLabel ] + + options = + itemToOption ( "", gettext "- select -" appState.locale ) + :: List.map itemToOption items + + itemLink = + case QuestionnaireQuestionnaire.itemSelectQuestionItemPath model.questionnaire mbListQuestionUuid (pathToString path) of + Just itemPath -> + div [ class "question-item-select-link" ] + [ a [ onClick (ScrollToPath itemPath) ] + [ text (gettext "Go to item" appState.locale) + , fa "fas fa-arrow-right ms-1" + ] + ] + + Nothing -> + emptyNode + + clearReplyButton = + viewQuestionClearButton appState cfg path (Maybe.isJust mbSelectedItem) + + missingItemWarning = + if QuestionnaireQuestionnaire.itemSelectQuestionItemMissing model.questionnaire mbListQuestionUuid (pathToString path) then + Flash.warning appState (gettext "The selected item was deleted." appState.locale) + + else + emptyNode + in + div [ class "question-item-select" ] + [ select (class "form-control" :: extraAttrs) options + , itemLink + , clearReplyButton + , missingItemWarning + ] + + viewChoice : AppState -> Config msg -> List String -> List String -> Int -> Choice -> Html Msg viewChoice appState cfg path selectedChoicesUuids order choice = let @@ -3817,9 +3908,52 @@ viewCopyLinkAction appState cfg model path = viewRemoveItemModal : AppState -> Model -> Html Msg viewRemoveItemModal appState model = let + removeItemUuid = + Maybe.map Tuple.second model.removeItem + + mapItem ( path, reply ) = + case String.toMaybe (ReplyValue.getSelectedItemUuid reply.value) of + Just selectedItemUuid -> + if Just selectedItemUuid == removeItemUuid then + String.split "." path + |> List.last + |> Maybe.andThen (flip KnowledgeModel.getQuestion model.questionnaire.knowledgeModel) + |> Maybe.map (\q -> ( path, Question.getTitle q )) + + else + Nothing + + Nothing -> + Nothing + + viewLink ( path, label ) = + li [] + [ a [ onClick (ScrollToPath path) ] + [ text label ] + ] + + wrapItemLinks links = + if List.isEmpty links then + emptyNode + + else + p [ class "mt-3" ] + [ text (gettext "There are some item select questions using this item:" appState.locale) + , ul [] links + ] + + items = + Dict.toList model.questionnaire.replies + |> List.filterMap mapItem + |> List.map viewLink + |> wrapItemLinks + cfg = { modalTitle = gettext "Remove Item" appState.locale - , modalContent = [ text (gettext "Are you sure you want to remove this item?" appState.locale) ] + , modalContent = + [ text (gettext "Are you sure you want to remove this item?" appState.locale) + , items + ] , visible = Maybe.isJust model.removeItem , actionResult = Unset , actionName = gettext "Remove" appState.locale diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/History.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/History.elm index 7a63c3d11..55aa98f01 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/History.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/History.elm @@ -476,6 +476,13 @@ viewEventDetailSetReply appState cfg data question = IntegrationType _ reply -> eventView [ ( fa "fas fa-link", Markdown.toString reply ) ] + ItemSelectReply itemUuid -> + let + itemLabel = + QuestionnaireQuestionnaire.getItemSelectQuestionValueLabel appState cfg.questionnaire (Question.getUuid question) itemUuid + in + eventView [ ( fa "far fa-square-caret-down", itemLabel ) ] + _ -> emptyNode diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm index 320645f3d..74978cf98 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm @@ -191,6 +191,9 @@ getFilteredKM editorBranch = MultiChoiceQuestion (filterCommonData commonData) { multichoiceData | choiceUuids = filterDeleted editorBranch multichoiceData.choiceUuids } + ItemSelectQuestion commonData itemSelectData -> + ItemSelectQuestion (filterCommonData commonData) itemSelectData + filterAnswer _ answer = { answer | followUpUuids = filterDeleted editorBranch answer.followUpUuids } @@ -875,7 +878,7 @@ computeWarnings appState editorBranch = getFilteredKM editorBranch warnings = - List.concatMap (computeChapterWarnings appState filteredKM) (KnowledgeModel.getChapters filteredKM) + List.concatMap (computeChapterWarnings appState editorBranch filteredKM) (KnowledgeModel.getChapters filteredKM) |> flip (++) (List.concatMap (computeMetricWarnings appState) (KnowledgeModel.getMetrics filteredKM)) |> flip (++) (List.concatMap (computePhaseWarnings appState) (KnowledgeModel.getPhases filteredKM)) |> flip (++) (List.concatMap (computeTagWarnings appState) (KnowledgeModel.getTags filteredKM)) @@ -885,8 +888,8 @@ computeWarnings appState editorBranch = { editorBranch | warnings = warnings } -computeChapterWarnings : AppState -> KnowledgeModel -> Chapter -> List EditorBranchWarning -computeChapterWarnings appState km chapter = +computeChapterWarnings : AppState -> EditorBranch -> KnowledgeModel -> Chapter -> List EditorBranchWarning +computeChapterWarnings appState editorBranch km chapter = let titleWarning = if String.isEmpty chapter.title then @@ -900,14 +903,14 @@ computeChapterWarnings appState km chapter = questionWarnings = List.concatMap - (computeQuestionWarnings appState km) + (computeQuestionWarnings appState editorBranch km) (KnowledgeModel.getChapterQuestions chapter.uuid km) in titleWarning ++ questionWarnings -computeQuestionWarnings : AppState -> KnowledgeModel -> Question -> List EditorBranchWarning -computeQuestionWarnings appState km question = +computeQuestionWarnings : AppState -> EditorBranch -> KnowledgeModel -> Question -> List EditorBranchWarning +computeQuestionWarnings appState editorBranch km question = let questionUuid = Question.getUuid question @@ -933,7 +936,7 @@ computeQuestionWarnings appState km question = else List.concatMap - (computeAnswerWarnings appState km) + (computeAnswerWarnings appState editorBranch km) (KnowledgeModel.getQuestionAnswers questionUuid km) Question.ListQuestion _ data -> @@ -942,7 +945,7 @@ computeQuestionWarnings appState km question = else List.concatMap - (computeQuestionWarnings appState km) + (computeQuestionWarnings appState editorBranch km) (KnowledgeModel.getQuestionItemTemplateQuestions questionUuid km) Question.IntegrationQuestion _ data -> @@ -961,6 +964,22 @@ computeQuestionWarnings appState km question = (computeChoiceWarnings appState) (KnowledgeModel.getQuestionChoices questionUuid km) + Question.ItemSelectQuestion _ data -> + let + listQuestionNotSelected = + case data.listQuestionUuid of + Just listQuestionUuid -> + isDeleted listQuestionUuid editorBranch + + Nothing -> + True + in + if listQuestionNotSelected then + createError (gettext "No list question selected for item select question" appState.locale) + + else + [] + _ -> [] @@ -977,8 +996,8 @@ computeQuestionWarnings appState km question = titleWarning ++ typeWarnings ++ referencesWarnings ++ expertWarnings -computeAnswerWarnings : AppState -> KnowledgeModel -> Answer -> List EditorBranchWarning -computeAnswerWarnings appState km answer = +computeAnswerWarnings : AppState -> EditorBranch -> KnowledgeModel -> Answer -> List EditorBranchWarning +computeAnswerWarnings appState editorBranch km answer = let labelWarning = if String.isEmpty answer.label then @@ -992,7 +1011,7 @@ computeAnswerWarnings appState km answer = followUpQuestionsWarnings = List.concatMap - (computeQuestionWarnings appState km) + (computeQuestionWarnings appState editorBranch km) (KnowledgeModel.getAnswerFollowupQuestions answer.uuid km) in labelWarning ++ followUpQuestionsWarnings diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm index f5786a70d..bede4ee2a 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor.elm @@ -40,7 +40,7 @@ import Shared.Data.Event.CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditAnswerEventData as EditAnswerEventData import Shared.Data.Event.EditChapterEventData as EditChapterEventData import Shared.Data.Event.EditChoiceEventData as EditChoiceEventData -import Shared.Data.Event.EditEventSetters exposing (setAbbreviation, setAdvice, setAnnotations, setAnswerUuids, setChapterUuids, setChoiceUuids, setColor, setContent, setDescription, setEmail, setExpertUuids, setFollowUpUuids, setId, setIntegrationUuid, setIntegrationUuids, setItemTemplateQuestionUuids, setItemUrl, setLabel, setLogo, setMetricMeasures, setMetricUuids, setName, setPhaseUuids, setProps, setQuestionUuids, setReferenceUuids, setRequestBody, setRequestEmptySearch, setRequestHeaders, setRequestMethod, setRequestUrl, setRequiredPhaseUuid, setResourceCollectionUuids, setResourcePageUuid, setResourcePageUuids, setResponseItemId, setResponseItemTemplate, setResponseListField, setTagUuids, setText, setTitle, setUrl, setValueType, setWidgetUrl) +import Shared.Data.Event.EditEventSetters exposing (setAbbreviation, setAdvice, setAnnotations, setAnswerUuids, setChapterUuids, setChoiceUuids, setColor, setContent, setDescription, setEmail, setExpertUuids, setFollowUpUuids, setId, setIntegrationUuid, setIntegrationUuids, setItemTemplateQuestionUuids, setItemUrl, setLabel, setListQuestionUuid, setLogo, setMetricMeasures, setMetricUuids, setName, setPhaseUuids, setProps, setQuestionUuids, setReferenceUuids, setRequestBody, setRequestEmptySearch, setRequestHeaders, setRequestMethod, setRequestUrl, setRequiredPhaseUuid, setResourceCollectionUuids, setResourcePageUuid, setResourcePageUuids, setResponseItemId, setResponseItemTemplate, setResponseListField, setTagUuids, setText, setTitle, setUrl, setValueType, setWidgetUrl) import Shared.Data.Event.EditExpertEventData as EditExpertEventData import Shared.Data.Event.EditIntegrationApiEventData as EditIntegrationApiEventData import Shared.Data.Event.EditIntegrationEventData exposing (EditIntegrationEventData(..)) @@ -50,6 +50,7 @@ import Shared.Data.Event.EditMetricEventData as EditMetricEventData import Shared.Data.Event.EditPhaseEventData as EditPhaseEventData import Shared.Data.Event.EditQuestionEventData exposing (EditQuestionEventData(..)) import Shared.Data.Event.EditQuestionIntegrationEventData as EditQuestionIntegrationEventData +import Shared.Data.Event.EditQuestionItemSelectData as EditQuestionItemSelectEvent import Shared.Data.Event.EditQuestionListEventData as EditQuestionListEventData import Shared.Data.Event.EditQuestionMultiChoiceEventData as EditQuestionMultiChoiceEventData import Shared.Data.Event.EditQuestionOptionsEventData as EditQuestionOptionsEventData @@ -731,7 +732,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question parentUuid = EditorBranch.getParentUuid questionUuid editorBranch - createEditEvent setOptions setList setValue setIntegration setMultiChoice value = + createEditEvent setOptions setList setValue setIntegration setMultiChoice setItemSelect value = eventMsg parentUuid (Just questionUuid) <| EditQuestionEvent <| case question of @@ -760,6 +761,11 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question |> setMultiChoice value |> EditQuestionMultiChoiceEvent + ItemSelectQuestion _ _ -> + EditQuestionItemSelectEvent.init + |> setItemSelect value + |> EditQuestionItemSelectEvent + onTypeChange value = eventMsg parentUuid (Just questionUuid) <| case value of @@ -785,6 +791,11 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question |> EditQuestionMultiChoiceEvent |> EditQuestionEvent + "ItemSelect" -> + EditQuestionItemSelectEvent.init + |> EditQuestionItemSelectEvent + |> EditQuestionEvent + _ -> EditQuestionOptionsEventData.init |> EditQuestionOptionsEvent @@ -806,6 +817,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , ( "Value", gettext "Value" appState.locale ) , ( "Integration", gettext "Integration" appState.locale ) , ( "MultiChoice", gettext "Multi-Choice" appState.locale ) + , ( "ItemSelect", gettext "Item Select" appState.locale ) ] requiredPhaseUuidOptions = @@ -831,6 +843,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , value = Question.getTypeString question , options = questionTypeOptions , onChange = onTypeChange + , extra = Nothing } typeWarning = @@ -873,7 +886,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question { name = "title" , label = gettext "Title" appState.locale , value = Question.getTitle question - , onInput = createEditEvent setTitle setTitle setTitle setTitle setTitle + , onInput = createEditEvent setTitle setTitle setTitle setTitle setTitle setTitle } textInput = @@ -881,7 +894,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question { name = "text" , label = gettext "Text" appState.locale , value = Maybe.withDefault "" (Question.getText question) - , onInput = createEditEvent setText setText setText setText setText << String.toMaybe + , onInput = createEditEvent setText setText setText setText setText setText << String.toMaybe , previewMsg = compose2 wrapMsg ShowHideMarkdownPreview , entityUuid = questionUuid , markdownPreviews = model.markdownPreviews @@ -893,7 +906,8 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , label = gettext "When does this question become desirable?" appState.locale , value = String.fromMaybe <| Question.getRequiredPhaseUuid question , options = requiredPhaseUuidOptions - , onChange = createEditEvent setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid << String.toMaybe + , onChange = createEditEvent setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid setRequiredPhaseUuid << String.toMaybe + , extra = Nothing } tagUuidsInput = @@ -901,7 +915,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question { label = gettext "Question Tags" appState.locale , tags = EditorBranch.filterDeletedWith .uuid editorBranch <| KnowledgeModel.getTags editorBranch.branch.knowledgeModel , selected = Question.getTagUuids question - , onChange = createEditEvent setTagUuids setTagUuids setTagUuids setTagUuids setTagUuids + , onChange = createEditEvent setTagUuids setTagUuids setTagUuids setTagUuids setTagUuids setTagUuids } referencesInput = @@ -912,7 +926,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , entityUuid = questionUuid , getReorderableState = flip Dict.get model.reorderableStates , toMsg = compose2 wrapMsg ReorderableMsg - , updateList = createEditEvent setReferenceUuids setReferenceUuids setReferenceUuids setReferenceUuids setReferenceUuids + , updateList = createEditEvent setReferenceUuids setReferenceUuids setReferenceUuids setReferenceUuids setReferenceUuids setReferenceUuids , getRoute = editorRoute editorBranch , getName = KnowledgeModel.getReferenceName editorBranch.branch.knowledgeModel , untitledLabel = gettext "Untitled reference" appState.locale @@ -929,7 +943,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , entityUuid = questionUuid , getReorderableState = flip Dict.get model.reorderableStates , toMsg = compose2 wrapMsg ReorderableMsg - , updateList = createEditEvent setExpertUuids setExpertUuids setExpertUuids setExpertUuids setExpertUuids + , updateList = createEditEvent setExpertUuids setExpertUuids setExpertUuids setExpertUuids setExpertUuids setExpertUuids , getRoute = editorRoute editorBranch , getName = KnowledgeModel.getExpertName editorBranch.branch.knowledgeModel , untitledLabel = gettext "Untitled expert" appState.locale @@ -941,7 +955,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question annotationsInput = Input.annotations appState { annotations = Question.getAnnotations question - , onEdit = createEditEvent setAnnotations setAnnotations setAnnotations setAnnotations setAnnotations + , onEdit = createEditEvent setAnnotations setAnnotations setAnnotations setAnnotations setAnnotations setAnnotations } questionTypeInputs = @@ -1045,6 +1059,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , value = QuestionValueType.toString <| Maybe.withDefault QuestionValueType.default <| Question.getValueType question , options = questionValueTypeOptions , onChange = createTypeEditEvent setValueType << Maybe.withDefault QuestionValueType.default << QuestionValueType.fromString + , extra = Nothing } in [ valueTypeInput ] @@ -1105,6 +1120,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question , value = String.fromMaybe <| Question.getIntegrationUuid question , options = integrationUuidOptions , onChange = createTypeEditEvent setIntegrationUuid + , extra = Nothing } in [ integrationUuidInput @@ -1142,6 +1158,69 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question } in [ choicesInput ] + + ItemSelectQuestion _ _ -> + let + createTypeEditEvent map value = + EditQuestionItemSelectEvent.init + |> map value + |> (EditQuestionEvent << EditQuestionItemSelectEvent) + |> eventMsg parentUuid (Just questionUuid) + + listQuestionUuidOptions = + KnowledgeModel.getAllQuestions editorBranch.branch.knowledgeModel + |> EditorBranch.filterDeletedWith Question.getUuid editorBranch + |> List.filter Question.isList + |> List.sortBy Question.getTitle + |> List.map (\q -> ( Question.getUuid q, Question.getTitle q )) + |> (::) ( "", gettext "- select list question -" appState.locale ) + + listQuestionUuidInput = + Input.select + { name = "listQuestionUuid" + , label = gettext "List Question" appState.locale + , value = String.fromMaybe <| Question.getListQuestionUuid question + , options = listQuestionUuidOptions + , onChange = createTypeEditEvent setListQuestionUuid << String.toMaybe + , extra = + case Question.getListQuestionUuid question of + Just listQuestionUuid -> + if EditorBranch.isDeleted listQuestionUuid editorBranch then + Nothing + + else + Just <| + div [ class "mt-1" ] + [ linkTo appState (editorRoute editorBranch listQuestionUuid) [] [ text (gettext "Go to list question" appState.locale) ] + ] + + Nothing -> + Nothing + } + in + [ listQuestionUuidInput + ] + + wrapQuestionsWithIntegration questions = + if List.isEmpty questions then + emptyNode + + else + FormGroup.plainGroup (ul [] questions) (gettext "Item select questions using this list question" appState.locale) + + itemSelectQuestionsWithListQuestion = + case question of + ListQuestion _ _ -> + KnowledgeModel.getAllQuestions editorBranch.branch.knowledgeModel + |> EditorBranch.filterDeletedWith Question.getUuid editorBranch + |> List.filter ((==) (Just questionUuid) << Question.getListQuestionUuid) + |> List.filter (EditorBranch.isReachable editorBranch << Question.getUuid) + |> List.sortBy Question.getTitle + |> List.map (viewQuestionLink appState editorBranch) + |> wrapQuestionsWithIntegration + + _ -> + emptyNode in editor ("question-" ++ questionUuid) ([ questionEditorTitle @@ -1156,6 +1235,7 @@ viewQuestionEditor { appState, wrapMsg, eventMsg, model, editorBranch } question ++ [ referencesInput , expertsInput , annotationsInput + , itemSelectQuestionsWithListQuestion ] ) @@ -1428,6 +1508,7 @@ viewIntegrationEditor { appState, wrapMsg, eventMsg, integrationPrefabs, editorB , value = Integration.getTypeString integration , options = integrationTypeOptions , onChange = onTypeChange + , extra = Nothing } idInput = @@ -1500,6 +1581,7 @@ viewIntegrationEditor { appState, wrapMsg, eventMsg, integrationPrefabs, editorB , value = data.requestMethod , options = httpMethodOptions , onChange = createTypeEditEvent setRequestMethod + , extra = Nothing } requestHeadersInput = @@ -1600,25 +1682,6 @@ viewIntegrationEditor { appState, wrapMsg, eventMsg, integrationPrefabs, editorB , FormExtra.mdAfter (gettext "Defines the URL to the selected item. Use `${id}` value returned from the widget, for example `https://example.com/${id}`." appState.locale) ] - viewQuestionLink question = - let - questionTitle = - Question.getTitle question - - questionTitleNode = - if String.isEmpty questionTitle then - i [] [ text (gettext "Untitled question" appState.locale) ] - - else - text questionTitle - in - li [] - [ linkTo appState - (editorRoute editorBranch (Question.getUuid question)) - [] - [ questionTitleNode ] - ] - wrapQuestionsWithIntegration questions = if List.isEmpty questions then div [] [ i [] [ text (gettext "No questions" appState.locale) ] ] @@ -1632,7 +1695,7 @@ viewIntegrationEditor { appState, wrapMsg, eventMsg, integrationPrefabs, editorB |> List.filter ((==) (Just integrationUuid) << Question.getIntegrationUuid) |> List.filter (EditorBranch.isReachable editorBranch << Question.getUuid) |> List.sortBy Question.getTitle - |> List.map viewQuestionLink + |> List.map (viewQuestionLink appState editorBranch) |> wrapQuestionsWithIntegration prefabsView = @@ -1884,6 +1947,7 @@ viewReferenceEditor { appState, wrapMsg, eventMsg, editorBranch } reference = , value = Reference.getTypeString reference , options = referenceTypeOptions , onChange = onTypeChange + , extra = Nothing } referenceTypeInputs = @@ -2144,25 +2208,6 @@ viewResourcePageEditor { appState, wrapMsg, eventMsg, model, editorBranch } reso , onEdit = createEditEvent setAnnotations } - viewQuestionLink question = - let - questionTitle = - Question.getTitle question - - questionTitleNode = - if String.isEmpty questionTitle then - i [] [ text (gettext "Untitled question" appState.locale) ] - - else - text questionTitle - in - li [] - [ linkTo appState - (editorRoute editorBranch (Question.getUuid question)) - [] - [ questionTitleNode ] - ] - wrapQuestionsWithIntegration questions = if List.isEmpty questions then div [] [ i [] [ text (gettext "No questions" appState.locale) ] ] @@ -2181,7 +2226,7 @@ viewResourcePageEditor { appState, wrapMsg, eventMsg, model, editorBranch } reso |> List.filter (filterQuestionByResourcePageUuid << Question.getUuid) |> List.filter (EditorBranch.isReachable editorBranch << Question.getUuid) |> List.sortBy Question.getTitle - |> List.map viewQuestionLink + |> List.map (viewQuestionLink appState editorBranch) |> wrapQuestionsWithIntegration in editor ("resource-page-" ++ resourcePage.uuid) @@ -2278,6 +2323,27 @@ editorTitle appState config = ] +viewQuestionLink : AppState -> EditorBranch -> Question -> Html msg +viewQuestionLink appState editorBranch question = + let + questionTitle = + Question.getTitle question + + questionTitleNode = + if String.isEmpty questionTitle then + i [] [ text (gettext "Untitled question" appState.locale) ] + + else + text questionTitle + in + li [] + [ linkTo appState + (editorRoute editorBranch (Question.getUuid question)) + [] + [ questionTitleNode ] + ] + + -- DELETE MODAL diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm index 4de6ccaee..c15e9c801 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/KMEditor/Input.elm @@ -39,7 +39,7 @@ import Shared.Data.KnowledgeModel.Integration.RequestHeader as RequestHeader exp import Shared.Data.KnowledgeModel.Metric exposing (Metric) import Shared.Data.KnowledgeModel.MetricMeasure as MetricMeasure exposing (MetricMeasure) import Shared.Data.KnowledgeModel.Tag exposing (Tag) -import Shared.Html exposing (faSet) +import Shared.Html exposing (emptyNode, faSet) import Shared.Markdown as Markdown import Wizard.Common.AppState exposing (AppState) import Wizard.Common.Html exposing (linkTo) @@ -134,6 +134,7 @@ type alias SelectInputConfig msg = , value : String , options : List ( String, String ) , onChange : String -> msg + , extra : Maybe (Html msg) } @@ -153,6 +154,7 @@ select config = , onInput config.onChange ] (List.map viewOption config.options) + , Maybe.withDefault emptyNode config.extra ] diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/PhaseEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/PhaseEditor.elm index 7c36d7b3c..7579419b4 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/PhaseEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/PhaseEditor.elm @@ -10,6 +10,7 @@ import Shared.Data.Event.CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditEventSetters exposing (setRequiredPhaseUuid) import Shared.Data.Event.EditQuestionEventData exposing (EditQuestionEventData(..)) import Shared.Data.Event.EditQuestionIntegrationEventData as EditQuestionIntegrationEventData +import Shared.Data.Event.EditQuestionItemSelectData as EditQuestionItemSelectEventData import Shared.Data.Event.EditQuestionListEventData as EditQuestionListEventData import Shared.Data.Event.EditQuestionMultiChoiceEventData as EditQuestionMultiChoiceEventData import Shared.Data.Event.EditQuestionOptionsEventData as EditQuestionOptionsEventData @@ -91,6 +92,11 @@ view appState wrapMsg eventMsg editorBranch model = |> setRequiredPhaseUuid mbPhaseUuid |> EditQuestionMultiChoiceEvent + ItemSelectQuestion _ _ -> + EditQuestionItemSelectEventData.init + |> setRequiredPhaseUuid mbPhaseUuid + |> EditQuestionItemSelectEvent + content = if List.isEmpty editorBranch.branch.knowledgeModel.phaseUuids then Flash.info appState (gettext "There are no phases, create them first." appState.locale) @@ -218,6 +224,9 @@ foldQuestion appState props model indent phase question = MultiChoiceQuestion _ _ -> questionRow + ItemSelectQuestion _ _ -> + questionRow + foldAnswer : AppState -> Props msg -> Model -> Int -> List Phase -> Answer -> List (Html msg) foldAnswer appState props model indent phases answer = diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/TagEditor.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/TagEditor.elm index 909f4e6a8..ff0a40e2d 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Components/TagEditor.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Components/TagEditor.elm @@ -17,6 +17,7 @@ import Shared.Data.Event.CommonEventData exposing (CommonEventData) import Shared.Data.Event.EditEventSetters exposing (setTagUuids) import Shared.Data.Event.EditQuestionEventData exposing (EditQuestionEventData(..)) import Shared.Data.Event.EditQuestionIntegrationEventData as EditQuestionIntegrationEventData +import Shared.Data.Event.EditQuestionItemSelectData as EditQuestionItemSelectEventData import Shared.Data.Event.EditQuestionListEventData as EditQuestionListEventData import Shared.Data.Event.EditQuestionMultiChoiceEventData as EditQuestionMultiChoiceEventData import Shared.Data.Event.EditQuestionOptionsEventData as EditQuestionOptionsEventData @@ -99,6 +100,11 @@ view appState wrapMsg eventMsg editorBranch model = |> setTagUuids tagUuids |> EditQuestionMultiChoiceEvent + ItemSelectQuestion _ _ -> + EditQuestionItemSelectEventData.init + |> setTagUuids tagUuids + |> EditQuestionItemSelectEvent + content = if List.isEmpty editorBranch.branch.knowledgeModel.tagUuids then Flash.info appState (gettext "There are no question tags, create them first." appState.locale) @@ -228,6 +234,9 @@ foldQuestion appState props model indent tags question = MultiChoiceQuestion _ _ -> questionRow + ItemSelectQuestion _ _ -> + questionRow + foldAnswer : AppState -> Props msg -> Model -> Int -> List Tag -> Answer -> List (Html msg) foldAnswer appState props model indent tags answer = diff --git a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm index 8a56a7735..9f305b41d 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Migration/View.elm @@ -947,8 +947,8 @@ viewAddQuestionDiff appState km event = , gettext "Text" appState.locale ] [ AddQuestionEventQuestionData.getTypeString event - , AddQuestionEventQuestionData.map .title .title .title .title .title event - , AddQuestionEventQuestionData.map .text .text .text .text .text event |> Maybe.withDefault "" + , AddQuestionEventQuestionData.map .title .title .title .title .title .title event + , AddQuestionEventQuestionData.map .text .text .text .text .text .text event |> Maybe.withDefault "" ] extraFields = @@ -981,7 +981,7 @@ viewAddQuestionDiff appState km event = KnowledgeModel.getTags km tagUuids = - AddQuestionEventQuestionData.map .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids event + AddQuestionEventQuestionData.map .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids event tagNames = Dict.fromList <| List.map (\t -> ( t.uuid, t.name )) tags @@ -993,7 +993,7 @@ viewAddQuestionDiff appState km event = viewDiffChildren (gettext "Question Tags" appState.locale) originalTags tagUuids tagNames annotations = - AddQuestionEventQuestionData.map .annotations .annotations .annotations .annotations .annotations event + AddQuestionEventQuestionData.map .annotations .annotations .annotations .annotations .annotations .annotations event annotationsDiff = viewAnnotationsDiff appState [] annotations @@ -1010,10 +1010,10 @@ viewEditQuestionDiff appState km event question = Question.getUuid question title = - EditQuestionEventData.map .title .title .title .title .title event + EditQuestionEventData.map .title .title .title .title .title .title event questionText = - EditQuestionEventData.map .text .text .text .text .text event + EditQuestionEventData.map .text .text .text .text .text .text event fields = List.map3 (\a b c -> ( a, b, c )) @@ -1105,7 +1105,7 @@ viewEditQuestionDiff appState km event question = tagsDiff = viewDiffChildren (gettext "Question Tags" appState.locale) originalTags - (EventField.getValueWithDefault (EditQuestionEventData.map .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids event) originalTags) + (EventField.getValueWithDefault (EditQuestionEventData.map .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids .tagUuids event) originalTags) tagNames -- Answers @@ -1176,7 +1176,7 @@ viewEditQuestionDiff appState km event question = referencesDiff = viewDiffChildren (gettext "References" appState.locale) originalReferences - (EventField.getValueWithDefault (EditQuestionEventData.map .referenceUuids .referenceUuids .referenceUuids .referenceUuids .referenceUuids event) originalReferences) + (EventField.getValueWithDefault (EditQuestionEventData.map .referenceUuids .referenceUuids .referenceUuids .referenceUuids .referenceUuids .referenceUuids event) originalReferences) referenceNames -- Experts @@ -1192,7 +1192,7 @@ viewEditQuestionDiff appState km event question = expertsDiff = viewDiffChildren (gettext "Experts" appState.locale) originalExperts - (EventField.getValueWithDefault (EditQuestionEventData.map .expertUuids .expertUuids .expertUuids .expertUuids .expertUuids event) originalExperts) + (EventField.getValueWithDefault (EditQuestionEventData.map .expertUuids .expertUuids .expertUuids .expertUuids .expertUuids .expertUuids event) originalExperts) expertNames -- Annotations @@ -1201,7 +1201,7 @@ viewEditQuestionDiff appState km event question = annotations = EventField.getValueWithDefault - (EditQuestionEventData.map .annotations .annotations .annotations .annotations .annotations event) + (EditQuestionEventData.map .annotations .annotations .annotations .annotations .annotations .annotations event) originalAnnotations annotationsDiff = diff --git a/engine-wizard/elm/Wizard/Projects/Import/View.elm b/engine-wizard/elm/Wizard/Projects/Import/View.elm index 768b4fd85..e745dcbb4 100644 --- a/engine-wizard/elm/Wizard/Projects/Import/View.elm +++ b/engine-wizard/elm/Wizard/Projects/Import/View.elm @@ -275,3 +275,7 @@ viewReply appState questionnaire question data = IntegrationReplyType.IntegrationType _ reply -> eventView [ ( fa "fas fa-link", text (Markdown.toString reply) ) ] + + ReplyValue.ItemSelectReply _ -> + -- TODO + eventView [ ( fa "fas fa-plus", text (gettext "Added item" appState.locale) ) ] diff --git a/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss b/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss index 26f457f60..e274f73f7 100644 --- a/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss +++ b/engine-wizard/scss/modules/Common/Components/_Questionnaire.scss @@ -1066,6 +1066,23 @@ $toolbar-height: 40px; } } + .question-item-select { + select { + position: relative; + z-index: 1; + } + + &-link { + @include border-radius-bottom($border-radius); + margin-top: -$border-radius; + border: 1px solid $border-color; + position: relative; + background: $light; + padding: ($input-padding-y + $border-radius) $input-padding-x $input-padding-y; + + } + } + &-disabled { .radio { label { diff --git a/tests/Shared/Data/KnowledgeModel/QuestionTest.elm b/tests/Shared/Data/KnowledgeModel/QuestionTest.elm index 0ff59d398..ac45f6a94 100644 --- a/tests/Shared/Data/KnowledgeModel/QuestionTest.elm +++ b/tests/Shared/Data/KnowledgeModel/QuestionTest.elm @@ -260,4 +260,38 @@ questionDecoderTest = } in expectDecoder Question.decoder raw expected + , test "should decode item select question type" <| + \_ -> + let + raw = + """ + { + "uuid": "8a703cfa-450f-421a-8819-875619ccb54d", + "questionType": "ItemSelectQuestion", + "title": "Can you answer this question?", + "text": "Please answer the question", + "requiredPhaseUuid": null, + "tagUuids": [], + "referenceUuids": [], + "expertUuids": [], + "listQuestionUuid": "b50bf5ce-2fc3-4779-9756-5f176c233374", + "annotations": [] + } + """ + + expected = + ItemSelectQuestion + { uuid = "8a703cfa-450f-421a-8819-875619ccb54d" + , title = "Can you answer this question?" + , text = Just "Please answer the question" + , requiredPhaseUuid = Nothing + , tagUuids = [] + , referenceUuids = [] + , expertUuids = [] + , annotations = [] + } + { listQuestionUuid = Just "b50bf5ce-2fc3-4779-9756-5f176c233374" + } + in + expectDecoder Question.decoder raw expected ] From 841b8f5d6cbbc69f461acbe0a344c7e192e5c17f Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 28 Aug 2024 11:33:53 +0200 Subject: [PATCH 19/24] Add ItemSelect question support to integration SDK --- .../Components/Questionnaire/Importer.elm | 13 +++++++++++++ .../Questionnaire/Importer/ImporterEvent.elm | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer.elm index 6d123de72..d8907bd50 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer.elm @@ -136,6 +136,19 @@ createEvent appState questionnaire importerEvent ( seed, items, importerResult ) Nothing -> questionNotFound data.path + ReplyItemSelect data -> + case getQuestionFromPath data.path of + Just question -> + case question of + ItemSelectQuestion _ _ -> + wrap <| setReply data.path <| ItemSelectReply data.value + + _ -> + replyTypeUnexpected data.path + + Nothing -> + questionNotFound data.path + AddItem data -> case getQuestionFromPath data.path of Just question -> diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer/ImporterEvent.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer/ImporterEvent.elm index 68460379c..34cb8f246 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer/ImporterEvent.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire/Importer/ImporterEvent.elm @@ -2,6 +2,7 @@ module Wizard.Common.Components.Questionnaire.Importer.ImporterEvent exposing ( AddItemData , ImporterEvent(..) , ReplyIntegrationData + , ReplyItemSelectData , ReplyListData , ReplyStringData , decoder @@ -15,6 +16,7 @@ type ImporterEvent = ReplyString ReplyStringData | ReplyList ReplyListData | ReplyIntegration ReplyIntegrationData + | ReplyItemSelect ReplyItemSelectData | AddItem AddItemData @@ -36,6 +38,9 @@ decoderByType eventType = "ReplyIntegration" -> D.map ReplyIntegration decodeReplyIntegrationData + "ReplyItemSelect" -> + D.map ReplyItemSelect decodeReplyItemSelectData + "AddItem" -> D.map AddItem decodeAddItemData @@ -84,6 +89,19 @@ decodeReplyIntegrationData = |> D.required "id" (D.maybe D.string) +type alias ReplyItemSelectData = + { path : String + , value : String + } + + +decodeReplyItemSelectData : Decoder ReplyItemSelectData +decodeReplyItemSelectData = + D.succeed ReplyItemSelectData + |> D.required "path" D.string + |> D.required "value" D.string + + type alias AddItemData = { path : String , uuid : String From ea667835093197ae672508b5b69be47d3cc512cc Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 28 Aug 2024 11:38:13 +0200 Subject: [PATCH 20/24] Fix item select question so that empty option is not selectable --- .../elm/Wizard/Common/Components/Questionnaire.elm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm index 9ae79d6b6..0b61042c2 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm @@ -3661,8 +3661,14 @@ viewQuestionItemSelect appState cfg model path question = [ text optionLabel ] options = - itemToOption ( "", gettext "- select -" appState.locale ) - :: List.map itemToOption items + List.map itemToOption items + + optionsWithSelect = + if Maybe.isJust mbSelectedItem then + options + + else + itemToOption ( "", gettext "- select -" appState.locale ) :: options itemLink = case QuestionnaireQuestionnaire.itemSelectQuestionItemPath model.questionnaire mbListQuestionUuid (pathToString path) of @@ -3688,7 +3694,7 @@ viewQuestionItemSelect appState cfg model path question = emptyNode in div [ class "question-item-select" ] - [ select (class "form-control" :: extraAttrs) options + [ select (class "form-control" :: extraAttrs) optionsWithSelect , itemLink , clearReplyButton , missingItemWarning From a8c2751f49be0abf9d037a0bae4b7bf43a9c4411 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 28 Aug 2024 11:43:32 +0200 Subject: [PATCH 21/24] Fix breadcrumbs for resource collection and page in KM Editor --- .../elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm index 74978cf98..d4bad9287 100644 --- a/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm +++ b/engine-wizard/elm/Wizard/KMEditor/Editor/Common/EditorBranch.elm @@ -331,6 +331,12 @@ getEditorName appState uuid editorBranch = getExpertName = getEditorName_ (String.withDefault (gettext "Untitled expert" appState.locale) << Expert.getVisibleName) KnowledgeModel.getExpert + + getResourceCollectionName = + getEditorName_ (String.withDefault (gettext "Untitled resource collection" appState.locale) << .title) KnowledgeModel.getResourceCollection + + getResourcePageName = + getEditorName_ (String.withDefault (gettext "Untitled resource page" appState.locale) << .title) KnowledgeModel.getResourcePage in getKnowledgeModelName |> Maybe.orElse getChapterName @@ -343,6 +349,8 @@ getEditorName appState uuid editorBranch = |> Maybe.orElse getChoiceName |> Maybe.orElse getReferenceName |> Maybe.orElse getExpertName + |> Maybe.orElse getResourceCollectionName + |> Maybe.orElse getResourcePageName |> Maybe.withDefault "" From 9f72b0945ebfc21d1ca20fe7ad3f1dc92bd99386 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Wed, 28 Aug 2024 11:55:10 +0200 Subject: [PATCH 22/24] Change default sort for tenants on the Tenants Page --- engine-wizard/elm/Wizard/Tenants/Routing.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine-wizard/elm/Wizard/Tenants/Routing.elm b/engine-wizard/elm/Wizard/Tenants/Routing.elm index 98e1daa19..bb6d9bf56 100644 --- a/engine-wizard/elm/Wizard/Tenants/Routing.elm +++ b/engine-wizard/elm/Wizard/Tenants/Routing.elm @@ -27,7 +27,7 @@ parsers wrapRoute = wrappedIndexRoute pqs q = wrapRoute <| IndexRoute pqs q in - [ map (PaginationQueryString.wrapRoute1 wrappedIndexRoute (Just "name")) (PaginationQueryString.parser1 (s moduleRoot) (Query.string indexRouteEnabledFilterId)) + [ map (PaginationQueryString.wrapRoute1 wrappedIndexRoute (Just "createdAt,desc")) (PaginationQueryString.parser1 (s moduleRoot) (Query.string indexRouteEnabledFilterId)) , map (wrapRoute <| CreateRoute) (s moduleRoot s "create") , map (wrapRoute << DetailRoute) (s moduleRoot uuid) ] From 048a40627b140d00e78614cb6e697a748714abef Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Thu, 29 Aug 2024 16:22:31 +0200 Subject: [PATCH 23/24] Fix deleted item in questionnaire in item select question --- .../elm/Wizard/Common/Components/Questionnaire.elm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm index 0b61042c2..05e7f69f3 100644 --- a/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm +++ b/engine-wizard/elm/Wizard/Common/Components/Questionnaire.elm @@ -3660,11 +3660,14 @@ viewQuestionItemSelect appState cfg model path question = option [ value optionValue, selected (Just optionValue == mbSelectedItem) ] [ text optionLabel ] + itemMissing = + QuestionnaireQuestionnaire.itemSelectQuestionItemMissing model.questionnaire mbListQuestionUuid (pathToString path) + options = List.map itemToOption items optionsWithSelect = - if Maybe.isJust mbSelectedItem then + if Maybe.isJust mbSelectedItem && not itemMissing then options else @@ -3687,7 +3690,7 @@ viewQuestionItemSelect appState cfg model path question = viewQuestionClearButton appState cfg path (Maybe.isJust mbSelectedItem) missingItemWarning = - if QuestionnaireQuestionnaire.itemSelectQuestionItemMissing model.questionnaire mbListQuestionUuid (pathToString path) then + if itemMissing then Flash.warning appState (gettext "The selected item was deleted." appState.locale) else From cf5613ce546e11dd7c161d5a52d68570034c5cd1 Mon Sep 17 00:00:00 2001 From: Jan Slifka Date: Tue, 3 Sep 2024 08:00:29 +0200 Subject: [PATCH 24/24] 4.10.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d43fe846..d0a6e9489 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "engine-frontend", - "version": "4.9.0", + "version": "4.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "engine-frontend", - "version": "4.9.0", + "version": "4.10", "license": "Apache-2.0", "dependencies": { "@codemirror/commands": "^6.6.0", diff --git a/package.json b/package.json index 92f26f861..138b9b1a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "engine-frontend", - "version": "4.9.0", + "version": "4.10", "repository": "https://github.com/ds-wizard/engine-frontend", "license": "Apache-2.0", "scripts": {