├── deltares.env ├── public ├── app-config.json ├── images │ ├── logo.png │ ├── blue_sky.jpg │ ├── favicon.ico │ ├── map-marker.png │ ├── deltares_logo.png │ ├── D111-103_getijdenland.jpg │ ├── D111-103_getijdenland_small.jpg │ └── logo_iwp.svg ├── fonts │ ├── RO │ │ ├── ROserifwebbold.woff │ │ ├── ROsanswebtextbold.woff │ │ ├── ROserifwebitalic.woff │ │ ├── ROserifwebregular.woff │ │ ├── ROsanswebtextitalic.woff │ │ ├── ROsanswebtextregular.woff │ │ └── fonts.css │ └── exo-2 │ │ ├── exo-2-v21-latin-600.woff2 │ │ ├── exo-2-v21-latin-700.woff2 │ │ ├── exo-2-v21-latin-italic.woff2 │ │ ├── exo-2-v21-latin-regular.woff2 │ │ ├── exo-2-v21-latin-600italic.woff2 │ │ ├── exo-2-v21-latin-700italic.woff2 │ │ └── fonts.css ├── sprites │ ├── mdi-overlay-sdf.png │ ├── mdi-overlay-sdf@2x.png │ └── mdi-overlay-sdf@3x.png ├── weboc-default-style.css ├── rwsos-style.css ├── css │ └── login.css └── vuetify-overrule.css ├── src ├── lib │ ├── css │ │ └── index.ts │ ├── whatif │ │ └── index.ts │ ├── location-icons │ │ └── index.ts │ ├── router │ │ ├── index.ts │ │ └── types.ts │ ├── search │ │ ├── index.ts │ │ ├── types.ts │ │ └── substring.ts │ ├── thresholds │ │ ├── index.ts │ │ └── types.ts │ ├── download │ │ ├── index.ts │ │ └── types │ │ │ ├── index.ts │ │ │ ├── ParameterQualifiersHeader.ts │ │ │ └── DataDownloadFilter.ts │ ├── streamlines │ │ ├── index.ts │ │ └── types.ts │ ├── svg │ │ └── index.ts │ ├── map │ │ └── index.ts │ ├── period │ │ ├── index.ts │ │ ├── types.ts │ │ └── convert.ts │ ├── display │ │ ├── index.ts │ │ └── DisplayConfig.ts │ ├── products │ │ ├── index.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── log │ │ ├── index.ts │ │ ├── actions.ts │ │ └── types.ts │ ├── charts │ │ ├── domain.ts │ │ ├── types │ │ │ ├── ThresholdLine.ts │ │ │ ├── ChartSeriesOptions.ts │ │ │ ├── ChartConfig.ts │ │ │ └── ChartSeries.ts │ │ ├── getUniqueSeriesIds.ts │ │ ├── horizontalColorCode.ts │ │ └── thresholds.ts │ ├── taskruns │ │ ├── index.ts │ │ ├── utils.ts │ │ ├── types.ts │ │ └── convert.ts │ ├── workflows │ │ ├── index.ts │ │ ├── types.ts │ │ ├── tasks.ts │ │ ├── coordinates.ts │ │ └── form.ts │ ├── timeseries │ │ └── types │ │ │ ├── index.ts │ │ │ ├── SeriesResourceType.ts │ │ │ ├── SeriesHeader.ts │ │ │ ├── SeriesOperationType.ts │ │ │ └── SeriesData.ts │ ├── analysis │ │ ├── index.ts │ │ ├── resampling.ts │ │ ├── serializer.ts │ │ ├── collection.ts │ │ └── utils.ts │ ├── utils │ │ ├── toSnakeCase.ts │ │ ├── absoluteUrl.ts │ │ ├── uid.ts │ │ ├── resolveCSSVariable.ts │ │ ├── types.ts │ │ ├── absoluteUrl.test.ts │ │ └── math.ts │ ├── topology │ │ ├── componentSettings │ │ │ ├── index.ts │ │ │ ├── reportSettings.ts │ │ │ ├── schematicStatusDisplaySettings.ts │ │ │ ├── mapSettings.ts │ │ │ └── settings.ts │ │ ├── dashboardActions.ts │ │ ├── getTopologyNodes.ts │ │ └── createTopologyHashMaps.ts │ ├── table │ │ ├── types │ │ │ └── TableHeaders.ts │ │ └── createTableHeaders.ts │ ├── requests │ │ ├── transformRequest.ts │ │ └── fetchBlob.ts │ ├── date │ │ └── convertFewsPiDateTimeToJsDate.test.ts │ ├── icons │ │ └── index.ts │ ├── legend │ │ └── index.ts │ ├── selection │ │ └── index.ts │ ├── basemap │ │ └── index.ts │ ├── filters │ │ └── index.ts │ └── fews-properties │ │ └── fewsProperties.ts ├── styles │ ├── main.scss │ └── settings.scss ├── views │ ├── auth │ │ ├── Callback.vue │ │ ├── Silent.vue │ │ └── Logout.vue │ ├── Empty.vue │ ├── SystemMonitorDisplayView.vue │ ├── DataDownloadDisplayView.vue │ ├── InformationDisplayView.vue │ ├── HtmlDisplayView.vue │ ├── DashboardView.vue │ └── WhatIfDisplayView.vue ├── services │ ├── application-config │ │ ├── index.ts │ │ └── ApplicationConfig.ts │ ├── useMap │ │ └── index.ts │ ├── useProducts │ │ └── types.ts │ ├── useSelectedElevation │ │ └── index.ts │ ├── authentication │ │ └── oidcSettings.ts │ ├── useBaseMap │ │ └── index.ts │ ├── useFocusAwareInterval │ │ └── index.ts │ ├── useOverlays │ │ └── index.ts │ ├── useChartHandlers │ │ └── index.ts │ ├── useCurrentUser │ │ └── index.ts │ ├── useMenuItemsStack │ │ ├── useMenuItemStack.test.ts │ │ └── index.ts │ ├── useSelectedDate │ │ └── index.ts │ ├── useDynamicCss │ │ └── index.ts │ ├── useCorrelation │ │ └── index.ts │ ├── useHorizontalScroll │ │ └── index.ts │ ├── useReports │ │ └── index.ts │ ├── useDataAnalysisDisplay │ │ └── index.ts │ └── useSeriesUpdateChartData │ │ └── index.ts ├── vite-env.d.ts ├── assets │ ├── attributeNames.json │ ├── fews-flags.css │ ├── browserLayout.json │ ├── DefaultBaseMaps.json │ └── JsonFormsConfig.json ├── components │ ├── map │ │ ├── SyncMap.vue │ │ ├── BoundingBoxControl.vue │ │ └── CoordinateSelectorControl.vue │ ├── general │ │ ├── ColumnItem.ts │ │ ├── HighlightMatch.vue │ │ ├── LoadingSpinner.vue │ │ ├── HtmlDisplay.vue │ │ ├── ColumnItemIcon.vue │ │ ├── HierarchicalMenu.vue │ │ ├── BtnGroup.vue │ │ ├── TreeMenu.vue │ │ ├── WindowComponent.vue │ │ ├── DateTimeTextField.vue │ │ └── ShadowFrame.vue │ ├── tasks │ │ ├── TaskRunsOverview.vue │ │ ├── VisualizeDataControl.vue │ │ └── WorkflowFilterControl.vue │ ├── workflows │ │ └── WorkflowsControl.vue │ ├── reports │ │ └── tiptap │ │ │ ├── CustomKeybindings.ts │ │ │ ├── TableMenu.vue │ │ │ ├── data.css │ │ │ ├── CustomImage.ts │ │ │ ├── Data.ts │ │ │ ├── time.css │ │ │ ├── CustomDivision.ts │ │ │ ├── Head.ts │ │ │ └── CustomHeader.ts │ ├── analysis │ │ ├── AnalysisUnsupportedChart.vue │ │ ├── AnalysisAddButton.vue │ │ ├── AnalysisMenu.vue │ │ ├── DataAnalysisDisplay.vue │ │ ├── AnalysisLineStyleEdit.vue │ │ ├── AnalysisProductChart.vue │ │ ├── AnalysisMap.vue │ │ └── workflows │ │ │ └── AnalysisWorkflow.vue │ ├── wms │ │ ├── panel │ │ │ └── OverlayInformationPanel.vue │ │ ├── ColourLegendTable.vue │ │ ├── locations │ │ │ ├── LocationsCircleLayer.vue │ │ │ ├── LocationsDataAvailabilityLayer.vue │ │ │ └── LocationsTextLayer.vue │ │ ├── ColourLegend.vue │ │ ├── OverlayLayer.vue │ │ ├── ControlChip.vue │ │ ├── ColourStrip.vue │ │ └── CoordinateSelectorLayer.vue │ ├── webdisplay │ │ └── WebDisplay.vue │ ├── thresholds │ │ ├── ThresholdSummaryChip.vue │ │ └── ThresholdsButton.vue │ ├── logdisplay │ │ ├── LogDisplay.vue │ │ └── DateSeparator.vue │ ├── table │ │ └── TableTooltip.vue │ ├── tasksdisplay │ │ ├── AvailableWorkflowServers.vue │ │ ├── ExpectedWorkflowRuntime.vue │ │ └── WhatIfScenarioSelect.vue │ ├── charts │ │ └── LoadingOverlay.vue │ ├── products │ │ └── ProductDetails.vue │ ├── user-settings │ │ └── UserSettingsDialog.vue │ ├── dialog │ │ ├── StartupDialog.vue │ │ └── SplashScreenDialog.vue │ └── sidepanel │ │ └── SidePanelControl.vue ├── environment.d.ts ├── stores │ ├── globalSearch.ts │ ├── alerts.ts │ ├── locationNames.ts │ ├── sidePanel.ts │ ├── nodes.ts │ ├── taskRuns.ts │ ├── baseMaps.ts │ ├── availableTimeSteps.ts │ ├── availableWhatIfTemplates.ts │ └── nodes.test.ts ├── plugins │ └── vuetify.ts ├── layouts │ └── EmptyLayout.vue └── locales │ ├── en.json │ └── de.json ├── .prettierrc.json ├── tests ├── docker │ └── zap │ │ ├── report │ │ └── README.md │ │ ├── weboc │ │ └── README.md │ │ ├── weboc-zap.conf │ │ ├── docker-compose.yaml │ │ └── README.md └── e2e │ ├── chart │ └── threshold.spec.ts │ ├── login │ └── login.spec.ts │ └── ssd │ └── schematicStatusDisplay.spec.ts ├── .prettierignore ├── docs └── public │ ├── deployments │ ├── azure │ │ ├── staticwebapp.config.json │ │ ├── azure-pipelines-archive.yml │ │ └── azure-pipelines-secured-api.yml │ └── delftfews-sa │ │ └── Modules │ │ └── weboc │ │ └── WEB-INF │ │ └── web.xml │ ├── oidc │ ├── keycloak │ │ ├── create-user.png │ │ ├── realm-weboc.png │ │ ├── weboc-login.png │ │ ├── access_token.png │ │ ├── create-client.png │ │ ├── roles-mapper.png │ │ ├── web-oc-groups.png │ │ ├── weboc-profile.png │ │ ├── create-password.png │ │ ├── dedicated-scope.png │ │ ├── keycloack-group.png │ │ ├── keycload-roles.png │ │ ├── keycloak-login.png │ │ ├── login-settings.png │ │ ├── access_token_roles.png │ │ ├── assign-roles-to-user.png │ │ ├── client-capabilities.png │ │ ├── custom-mapper-groups.png │ │ ├── keycloak-join-group.png │ │ └── group-membership-mapper.png │ ├── entra-id │ │ ├── expose-an-api.png │ │ ├── app-registration-web-oc.png │ │ ├── request-api-permissions.png │ │ ├── access_token_optional_claim_email.png │ │ ├── app-registration-web-services-api.png │ │ ├── delft-fews-web-oc-api-permissions.png │ │ └── optional-claim-graph-email-permission.png │ └── README.md │ └── architecture │ └── web_oc_diagram.png ├── .editorconfig ├── README.md ├── .vscode └── extensions.json ├── tsconfig.node.json ├── .env.e2e ├── playwright ├── index.ts └── index.html ├── .eslintrc.cjs ├── index.html ├── .gitignore ├── .github ├── release.yml ├── workflows │ ├── playwright.yml │ ├── npm-test.yml │ ├── npm-build.yml │ ├── release.yml │ └── github-pages.yml └── pull_request_template.md ├── CONTRIBUTING.md └── tsconfig.json /deltares.env: -------------------------------------------------------------------------------- 1 | BASE_URL=/weboc-next/ -------------------------------------------------------------------------------- /public/app-config.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /src/lib/css/index.ts: -------------------------------------------------------------------------------- 1 | export * from './inline' 2 | -------------------------------------------------------------------------------- /src/lib/whatif/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | -------------------------------------------------------------------------------- /src/lib/location-icons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './load' 2 | -------------------------------------------------------------------------------- /src/lib/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types.ts' 2 | -------------------------------------------------------------------------------- /src/lib/search/index.ts: -------------------------------------------------------------------------------- 1 | export * from './substring' 2 | -------------------------------------------------------------------------------- /src/lib/thresholds/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types' 2 | -------------------------------------------------------------------------------- /src/lib/download/index.ts: -------------------------------------------------------------------------------- 1 | export * from './downloadFiles' 2 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @use './settings'; 2 | @use 'vuetify'; 3 | -------------------------------------------------------------------------------- /src/lib/streamlines/index.ts: -------------------------------------------------------------------------------- 1 | export { LayerKind } from './types' 2 | -------------------------------------------------------------------------------- /src/lib/svg/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './export' 3 | -------------------------------------------------------------------------------- /src/lib/map/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './locations' 3 | -------------------------------------------------------------------------------- /src/lib/period/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types' 2 | export * from './convert' 3 | -------------------------------------------------------------------------------- /src/lib/display/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './DisplayConfig' 3 | -------------------------------------------------------------------------------- /src/lib/products/index.ts: -------------------------------------------------------------------------------- 1 | export * from './documentDisplay' 2 | export * from './utils' 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "endOfLine": "auto" 5 | } 6 | -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/logo.png -------------------------------------------------------------------------------- /public/images/blue_sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/blue_sky.jpg -------------------------------------------------------------------------------- /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/favicon.ico -------------------------------------------------------------------------------- /src/lib/log/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types' 2 | export * from './utils' 3 | export * from './actions' 4 | -------------------------------------------------------------------------------- /src/styles/settings.scss: -------------------------------------------------------------------------------- 1 | @use 'vuetify/settings' with ( 2 | $data-table-footer-padding: 0px 4px 3 | ); 4 | -------------------------------------------------------------------------------- /public/images/map-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/map-marker.png -------------------------------------------------------------------------------- /public/images/deltares_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/deltares_logo.png -------------------------------------------------------------------------------- /src/lib/charts/domain.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateDomainEmits { 2 | 'update:x-domain': [new: [Date, Date]] 3 | } 4 | -------------------------------------------------------------------------------- /tests/docker/zap/report/README.md: -------------------------------------------------------------------------------- 1 | In this folder the ZAP report is generate if you use the docker-compose file. 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore all Markdown files: 2 | **/*.md 3 | # Ignore all files in the public directory 4 | public 5 | -------------------------------------------------------------------------------- /src/lib/streamlines/types.ts: -------------------------------------------------------------------------------- 1 | export enum LayerKind { 2 | Streamline = 'streamline', 3 | Static = 'static', 4 | } 5 | -------------------------------------------------------------------------------- /src/views/auth/Callback.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/fonts/RO/ROserifwebbold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROserifwebbold.woff -------------------------------------------------------------------------------- /public/sprites/mdi-overlay-sdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/sprites/mdi-overlay-sdf.png -------------------------------------------------------------------------------- /src/lib/download/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ParameterQualifiersHeader.ts' 2 | export * from './DataDownloadFilter.ts' 3 | -------------------------------------------------------------------------------- /src/lib/search/types.ts: -------------------------------------------------------------------------------- 1 | export interface SearchMatch { 2 | before: string 3 | match: string 4 | after: string 5 | } 6 | -------------------------------------------------------------------------------- /public/fonts/RO/ROsanswebtextbold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROsanswebtextbold.woff -------------------------------------------------------------------------------- /public/fonts/RO/ROserifwebitalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROserifwebitalic.woff -------------------------------------------------------------------------------- /public/fonts/RO/ROserifwebregular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROserifwebregular.woff -------------------------------------------------------------------------------- /public/sprites/mdi-overlay-sdf@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/sprites/mdi-overlay-sdf@2x.png -------------------------------------------------------------------------------- /public/sprites/mdi-overlay-sdf@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/sprites/mdi-overlay-sdf@3x.png -------------------------------------------------------------------------------- /docs/public/deployments/azure/staticwebapp.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationFallback": { 3 | "rewrite": "index.html" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/create-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/create-user.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/realm-weboc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/realm-weboc.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/weboc-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/weboc-login.png -------------------------------------------------------------------------------- /public/fonts/RO/ROsanswebtextitalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROsanswebtextitalic.woff -------------------------------------------------------------------------------- /public/fonts/RO/ROsanswebtextregular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/RO/ROsanswebtextregular.woff -------------------------------------------------------------------------------- /public/images/D111-103_getijdenland.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/D111-103_getijdenland.jpg -------------------------------------------------------------------------------- /docs/public/architecture/web_oc_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/architecture/web_oc_diagram.png -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/expose-an-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/expose-an-api.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/access_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/access_token.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/create-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/create-client.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/roles-mapper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/roles-mapper.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/web-oc-groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/web-oc-groups.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/weboc-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/weboc-profile.png -------------------------------------------------------------------------------- /src/lib/taskruns/index.ts: -------------------------------------------------------------------------------- 1 | export * from './convert' 2 | export * from './status' 3 | export * from './types' 4 | export * from './utils' 5 | -------------------------------------------------------------------------------- /src/lib/workflows/index.ts: -------------------------------------------------------------------------------- 1 | export * from './coordinates' 2 | export * from './fetch' 3 | export * from './form' 4 | export * from './types' 5 | -------------------------------------------------------------------------------- /tests/docker/zap/weboc/README.md: -------------------------------------------------------------------------------- 1 | In this folder the web oc distribution is expected. 2 | Make sure the app-config.json is setup correctly. 3 | -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/create-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/create-password.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/dedicated-scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/dedicated-scope.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/keycloack-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/keycloack-group.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/keycload-roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/keycload-roles.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/keycloak-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/keycloak-login.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/login-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/login-settings.png -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-600.woff2 -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-700.woff2 -------------------------------------------------------------------------------- /public/images/D111-103_getijdenland_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/images/D111-103_getijdenland_small.jpg -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/access_token_roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/access_token_roles.png -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-italic.woff2 -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-regular.woff2 -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/assign-roles-to-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/assign-roles-to-user.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/client-capabilities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/client-capabilities.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/custom-mapper-groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/custom-mapper-groups.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/keycloak-join-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/keycloak-join-group.png -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-600italic.woff2 -------------------------------------------------------------------------------- /public/fonts/exo-2/exo-2-v21-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/public/fonts/exo-2/exo-2-v21-latin-700italic.woff2 -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/app-registration-web-oc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/app-registration-web-oc.png -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/request-api-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/request-api-permissions.png -------------------------------------------------------------------------------- /docs/public/oidc/keycloak/group-membership-mapper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/keycloak/group-membership-mapper.png -------------------------------------------------------------------------------- /src/lib/timeseries/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SeriesHeader.js' 2 | export * from './SeriesOperationType.js' 3 | export * from './SeriesResourceType.js' 4 | -------------------------------------------------------------------------------- /src/lib/thresholds/types.ts: -------------------------------------------------------------------------------- 1 | export interface WarningLevel { 2 | id: string 3 | name: string 4 | severity: number 5 | icon?: string 6 | count: number 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | # EditorConfig is awesome: https://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | [*.ts, *.js, *.vue] 8 | quote_type = single -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delft Fews Web Operator Client 2 | 3 | For general documentation about the Web OC, please see: 4 | 5 | [Documentation](https://deltares.github.io/fews-web-oc/) 6 | -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/access_token_optional_claim_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/access_token_optional_claim_email.png -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/app-registration-web-services-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/app-registration-web-services-api.png -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/delft-fews-web-oc-api-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/delft-fews-web-oc-api-permissions.png -------------------------------------------------------------------------------- /src/lib/download/types/ParameterQualifiersHeader.ts: -------------------------------------------------------------------------------- 1 | export interface ParameterQualifiersHeader { 2 | parameterId: string | undefined 3 | qualifiers: string[] | undefined 4 | } 5 | -------------------------------------------------------------------------------- /src/services/application-config/index.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfigManager } from './ApplicationConfigManager' 2 | 3 | export const configManager = new ApplicationConfigManager() 4 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare const __GIT_HASH__: string 4 | declare const __GIT_TAG__: string 5 | declare const __BUILD_DATE__: string 6 | -------------------------------------------------------------------------------- /src/assets/attributeNames.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Author", 3 | "dataSetCreationTime": "Date modified", 4 | "name": "Name", 5 | "status": "Status", 6 | "timeZero": "Date" 7 | } 8 | -------------------------------------------------------------------------------- /docs/public/oidc/entra-id/optional-claim-graph-email-permission.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Deltares/fews-web-oc/main/docs/public/oidc/entra-id/optional-claim-graph-email-permission.png -------------------------------------------------------------------------------- /src/lib/workflows/types.ts: -------------------------------------------------------------------------------- 1 | import { Workflow } from '@deltares/fews-pi-requests' 2 | 3 | export interface WorkflowItem extends Workflow { 4 | expectedRuntimeSeconds: number | null 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/charts/types/ThresholdLine.ts: -------------------------------------------------------------------------------- 1 | import { AlertLineOptions } from '@deltares/fews-web-oc-charts' 2 | 3 | export interface ThresholdLine extends AlertLineOptions { 4 | id: string 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/analysis/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types' 2 | export * from './resampling' 3 | export * from './serializer' 4 | export * from './collection' 5 | export * from './utils' 6 | export * from './filter' 7 | -------------------------------------------------------------------------------- /src/lib/router/types.ts: -------------------------------------------------------------------------------- 1 | import type { RouteLocationNormalized } from 'vue-router' 2 | 3 | export type NavigateRoute = Pick & 4 | Partial> 5 | -------------------------------------------------------------------------------- /src/lib/utils/toSnakeCase.ts: -------------------------------------------------------------------------------- 1 | export function toSnakeCase(str: string): string { 2 | return str 3 | .replace(/([a-z])([A-Z])/g, '$1_$2') 4 | .replace(/[\s,./\\?%*:|"<>]+/g, '_') 5 | .toLowerCase() 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/timeseries/types/SeriesResourceType.ts: -------------------------------------------------------------------------------- 1 | export enum SeriesResourceType { 2 | DdApiRequest = 'DdApiRequest', 3 | FewsPiRequest = 'FewsPiRequest', 4 | UrlRequest = 'UrlRequest', 5 | Derived = 'Derived', 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "vue.volar", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode", 6 | "ms-playwright.playwright", 7 | "vitest.explorer" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/topology/componentSettings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './settings' 2 | export * from './chartSettings' 3 | export * from './schematicStatusDisplaySettings' 4 | export * from './reportSettings' 5 | export * from './mapSettings' 6 | -------------------------------------------------------------------------------- /src/lib/period/types.ts: -------------------------------------------------------------------------------- 1 | export interface AbsolutePeriod { 2 | startTimestamp: number 3 | endTimestamp: number 4 | } 5 | 6 | export interface RelativePeriod { 7 | startOffsetSeconds: number 8 | endOffsetSeconds: number 9 | } 10 | -------------------------------------------------------------------------------- /src/views/Empty.vue: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/services/useMap/index.ts: -------------------------------------------------------------------------------- 1 | import { mapSymbol } from '@indoorequal/vue-maplibre-gl' 2 | import { inject } from 'vue' 3 | 4 | export function useMap() { 5 | const map = inject(mapSymbol)?.value 6 | return { 7 | map, 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/map/SyncMap.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /src/lib/charts/types/ChartSeriesOptions.ts: -------------------------------------------------------------------------------- 1 | export interface ChartSeriesOptions { 2 | x: { 3 | key: string 4 | axisIndex: number 5 | } 6 | y: { 7 | key: string 8 | axisIndex: number 9 | } 10 | color?: { 11 | key: string 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/download/types/DataDownloadFilter.ts: -------------------------------------------------------------------------------- 1 | export interface DataDownloadFilter { 2 | filterId: string | undefined 3 | parameterIds: string | undefined 4 | locationIds: string | undefined 5 | qualifierIds: string | undefined 6 | onlyHeaders: boolean | undefined 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.env.e2e: -------------------------------------------------------------------------------- 1 | VITE_FEWS_WEBSERVICES_URL="http://localhost:8080/FewsWebServices/" 2 | VITE_APP_NAME="WebOC E2E Tests" 3 | VITE_LOGIN_STYLESHEET_URL="css/login.css" 4 | VITE_LOGIN_BUTTON_PROPS='{"prependIcon": "mdi-virus", "text": "Login Button Text", "appendIcon": "mdi-account" , "color": "purple"}' 5 | -------------------------------------------------------------------------------- /src/views/auth/Silent.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /playwright/index.ts: -------------------------------------------------------------------------------- 1 | // Import styles, initialize component theme here. 2 | // import '../src/common.css'; 3 | import { beforeMount } from '@playwright/experimental-ct-vue/hooks' 4 | import vuetify from '../src/plugins/vuetify.js' 5 | 6 | beforeMount(async ({ app }) => { 7 | app.use(vuetify) 8 | }) 9 | -------------------------------------------------------------------------------- /src/environment.d.ts: -------------------------------------------------------------------------------- 1 | import type { ApplicationConfig } from './services/application-config/ApplicationConfig.ts' 2 | 3 | declare global { 4 | namespace NodeJS { 5 | interface ProcessEnv extends ApplicationConfig { 6 | NODE_ENV: 'development' | 'production' 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/utils/absoluteUrl.ts: -------------------------------------------------------------------------------- 1 | export function absoluteUrl(urlString: string): URL { 2 | let url!: URL 3 | try { 4 | url = new URL(urlString) 5 | } catch (error) { 6 | if (error instanceof TypeError) { 7 | url = new URL(urlString, document.baseURI) 8 | } 9 | } 10 | return url 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/timeseries/types/SeriesHeader.ts: -------------------------------------------------------------------------------- 1 | import { TimeStep } from '@deltares/fews-pi-requests' 2 | 3 | export interface SeriesHeader { 4 | name?: string 5 | location?: string 6 | parameter?: string 7 | source?: string 8 | unit?: string 9 | timeStep?: TimeStep 10 | version?: string 11 | timeZone?: string 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/topology/dashboardActions.ts: -------------------------------------------------------------------------------- 1 | import { SsdActionResult } from '@deltares/fews-ssd-requests' 2 | 3 | export type DashboardActionParams = Pick 4 | export interface DashboardActionEventBus { 5 | trigger: number 6 | payload: { 7 | actionId?: string 8 | } & DashboardActionParams 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/fews-flags.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --flag-reliable-color: none; 3 | --flag-unreliable-color: #ffff00; 4 | --flag-doubtful-color: #ffc800; 5 | /* Unused? */ 6 | --flag-modified-color: #ff0000; 7 | --flag-accumulation-reset-color: Beige; 8 | --flag-missing-color: #ffffff; 9 | --flag-persistent-unreliable-color: #e18b6b; 10 | } 11 | -------------------------------------------------------------------------------- /playwright/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Testing Page 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/utils/uid.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates a unique identifier string based on the current timestamp and a random value. 3 | * 4 | * @returns {string} A unique identifier composed of the current time in base-36 and a random base-36 string. 5 | */ 6 | export function uid(): string { 7 | return Date.now().toString(36) + Math.random().toString(36).slice(2) 8 | } 9 | -------------------------------------------------------------------------------- /src/components/general/ColumnItem.ts: -------------------------------------------------------------------------------- 1 | import { RouteLocationRaw } from 'vue-router' 2 | 3 | export interface ColumnItem { 4 | id: string 5 | name: string 6 | children?: ColumnItem[] 7 | to?: RouteLocationRaw 8 | href?: string 9 | target?: string 10 | icon?: string 11 | appendIcon?: string 12 | thresholdIcon?: string 13 | thresholdCount?: number 14 | } 15 | -------------------------------------------------------------------------------- /src/components/tasks/TaskRunsOverview.vue: -------------------------------------------------------------------------------- 1 | 4 | 14 | -------------------------------------------------------------------------------- /src/lib/charts/types/ChartConfig.ts: -------------------------------------------------------------------------------- 1 | import type { ChartSeries } from './ChartSeries' 2 | import type { AxisOptions } from '@deltares/fews-web-oc-charts' 3 | 4 | export interface ChartConfig { 5 | id: string 6 | title: string 7 | xAxis?: AxisOptions[] 8 | yAxis?: AxisOptions[] 9 | radialAxis?: AxisOptions[] 10 | angularAxis?: AxisOptions[] 11 | series: ChartSeries[] 12 | } 13 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:vue/vue3-recommended', 8 | '@vue/eslint-config-typescript/recommended', 9 | '@vue/eslint-config-prettier', 10 | ], 11 | rules: { 12 | // override/add rules settings here, such as: 13 | // 'vue/no-unused-vars': 'error' 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/timeseries/types/SeriesOperationType.ts: -------------------------------------------------------------------------------- 1 | export enum SeriesOperationType { 2 | FactorOffset = 'factor-offset', 3 | Add = 'add', 4 | Subtract = 'subtract', 5 | Multiply = 'multiply', 6 | Divide = 'divide', 7 | WindDirection = 'wind-direction', 8 | WaterDirection = 'water-direction', 9 | Amplitude = 'amplitude', 10 | VerticalClearance = 'vertical-clearance', 11 | } 12 | -------------------------------------------------------------------------------- /src/services/useProducts/types.ts: -------------------------------------------------------------------------------- 1 | import { type ArchiveProductsMetadataEntry } from '@deltares/fews-pi-requests' 2 | 3 | export type ProductMetaDataWithoutAttributes = Omit< 4 | ArchiveProductsMetadataEntry, 5 | 'attributes' 6 | > 7 | 8 | export type ProductMetaDataType = Required & { 9 | key: string 10 | attributes: Record 11 | } 12 | -------------------------------------------------------------------------------- /src/views/SystemMonitorDisplayView.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /tests/docker/zap/weboc-zap.conf: -------------------------------------------------------------------------------- 1 | 10106 IGNORE (HTTP Only Site - Active/beta) 2 | 10055 IGNORE (CSP - Passive/release) 3 | 10055 IGNORE (CSP: Wildcard Directive) 4 | 10055 IGNORE (CSP: style-src unsafe-inline) 5 | 40035 IGNORE (Hidden File Finder - Active/release) 6 | 10096 IGNORE (Timestamp Disclosure - Passive/release) 7 | 90004 IGNORE (Insufficient Site Isolation Against Spectre Vulnerability - Passive/beta) -------------------------------------------------------------------------------- /public/fonts/exo-2/fonts.css: -------------------------------------------------------------------------------- 1 | /* exo-2-regular - latin */ 2 | @font-face { 3 | font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ 4 | font-family: 'Exo 2'; 5 | font-style: normal; 6 | font-weight: 400; 7 | src: url('./exo-2-v21-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/workflows/tasks.ts: -------------------------------------------------------------------------------- 1 | import { TopologyNode } from '@deltares/fews-pi-requests' 2 | 3 | export function getWorkflowIdsForNode(node: TopologyNode): string[] { 4 | return [ 5 | node.workflowId, 6 | node.secondaryWorkflows?.map((wf) => wf.secondaryWorkflowId), 7 | ] 8 | .flat() 9 | .filter((id) => id !== undefined) 10 | .filter((id, index, self) => id && self.indexOf(id) === index) 11 | } 12 | -------------------------------------------------------------------------------- /src/components/workflows/WorkflowsControl.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /src/lib/products/types.ts: -------------------------------------------------------------------------------- 1 | interface KeyValueType { 2 | key: string 3 | value: string 4 | } 5 | 6 | export interface PostResponse { 7 | areaId: string 8 | attributes: Array 9 | dataSetCreationTime: string 10 | fileSize: number 11 | relativePathMetaDataFile: string 12 | relativePathProducts: Array 13 | sourceId: string 14 | version: number 15 | timeZero: string 16 | } 17 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Delft-FEWS WebOC 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/tasks/VisualizeDataControl.vue: -------------------------------------------------------------------------------- 1 | 4 | 14 | -------------------------------------------------------------------------------- /src/lib/utils/resolveCSSVariable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Resolves a CSS variable to its value 3 | * @param {string} cssVariable - The CSS variable to resolve. 4 | * @returns {string} The value of the CSS variable. 5 | */ 6 | export function resolveCSSVariable(cssVariable: string): string { 7 | const property = cssVariable.replace('var(', '').replace(')', '') 8 | return getComputedStyle(document.documentElement).getPropertyValue(property) 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/table/types/TableHeaders.ts: -------------------------------------------------------------------------------- 1 | import type { VDataTable } from 'vuetify/components' 2 | 3 | type ReadonlyHeaders = VDataTable['$props']['headers'] 4 | type UnwrapReadonlyArrayType = A extends Readonly> ? I : never 5 | export type ReadonlyDataTableHeader = UnwrapReadonlyArrayType 6 | 7 | export interface TableHeaders extends ReadonlyDataTableHeader { 8 | color?: string 9 | editable: boolean 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/topology/componentSettings/reportSettings.ts: -------------------------------------------------------------------------------- 1 | import type { DeepRequired } from '@/lib/utils/types' 2 | import type { ReportSettings as PiReportSettings } from '@deltares/fews-pi-requests' 3 | 4 | export type ReportSettings = DeepRequired 5 | 6 | export const defaultReportSettings: ReportSettings = { 7 | downloadReport: true, 8 | nonCurrentReports: true, 9 | reportName: true, 10 | analysisTimes: true, 11 | } 12 | -------------------------------------------------------------------------------- /src/services/useSelectedElevation/index.ts: -------------------------------------------------------------------------------- 1 | import { inject, provide, ref } from 'vue' 2 | 3 | const ELEVATION_KEY = Symbol('elevation') 4 | 5 | export function provideSelectedElevation() { 6 | const sharedElevation = ref(0) 7 | provide(ELEVATION_KEY, sharedElevation) 8 | } 9 | 10 | export function useSelectedElevation() { 11 | const fallbackElevation = ref(0) 12 | return inject(ELEVATION_KEY, fallbackElevation) 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/log/actions.ts: -------------------------------------------------------------------------------- 1 | import { LogDisplayDisseminationAction } from '@deltares/fews-pi-requests' 2 | import { LogMessage } from './types' 3 | 4 | export interface LogActionEmit { 5 | disseminateLog: [ 6 | log: LogMessage, 7 | dissemination: LogDisplayDisseminationAction, 8 | ] 9 | deleteLog: [log: LogMessage] 10 | editLog: [log: LogMessage] 11 | acknowledgeLog: [log: LogMessage] 12 | unacknowledgeLog: [log: LogMessage] 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/workflows/coordinates.ts: -------------------------------------------------------------------------------- 1 | import { LngLat } from 'maplibre-gl' 2 | 3 | export function coordinateToString(coordinate: LngLat | null) { 4 | return coordinate 5 | ? `${coordinate.lat.toFixed(5)}°N ${coordinate.lng.toFixed(5)}°E` 6 | : '—' 7 | } 8 | 9 | export function coordinateToStringParts(coordinate: LngLat | null) { 10 | return coordinate 11 | ? [`${coordinate.lat.toFixed(5)}°N`, `${coordinate.lng.toFixed(5)}°E`] 12 | : ['—°N', '—°E'] 13 | } 14 | -------------------------------------------------------------------------------- /public/weboc-default-style.css: -------------------------------------------------------------------------------- 1 | @import './fonts/exo-2/fonts.css'; 2 | @import './default-styles.css'; 3 | @import './vuetify-overrule.css'; 4 | 5 | :root { 6 | --font-family: 'Exo 2', sans-serif; 7 | --theme-color: white; 8 | --contrast-color: black; 9 | --canvas-fill: rgb(223, 223, 223); 10 | --weboc-app-bar-bg-color: #080c80; 11 | } 12 | 13 | .dark { 14 | --contrast-color: rgb(255, 255, 255); 15 | --theme-color: black; 16 | --canvas-fill: rgb(54, 54, 54); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/reports/tiptap/CustomKeybindings.ts: -------------------------------------------------------------------------------- 1 | import { Extension } from '@tiptap/core' 2 | 3 | export const CustomKeybindings = Extension.create({ 4 | addKeyboardShortcuts() { 5 | return { 6 | Enter: () => { 7 | this.editor 8 | .chain() 9 | .selectParentNode() 10 | .createParagraphNear() 11 | .focus() 12 | .run() 13 | return true 14 | }, 15 | } 16 | }, 17 | }) 18 | 19 | export default CustomKeybindings 20 | -------------------------------------------------------------------------------- /src/components/reports/tiptap/TableMenu.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | -------------------------------------------------------------------------------- /src/assets/browserLayout.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": { 3 | "preview": true, 4 | "headers": [ 5 | { 6 | "attribute": "name", 7 | "title": "Name" 8 | }, 9 | { 10 | "attribute": "author", 11 | "title": "Author" 12 | }, 13 | { 14 | "property": "timeZero", 15 | "title": "Date" 16 | }, 17 | { 18 | "attribute": "status", 19 | "title": "Status" 20 | } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/rwsos-style.css: -------------------------------------------------------------------------------- 1 | @import 'fonts/RO/fonts.css'; 2 | @import './default-styles.css'; 3 | @import './vuetify-overrule.css'; 4 | 5 | :root { 6 | --font-family: 'RO-Sans', sans-serif; 7 | --theme-color: white; 8 | --contrast-color: black; 9 | --canvas-fill: rgb(223, 223, 223); 10 | --weboc-app-bar-bg-color: rgb(249, 225, 30); 11 | } 12 | 13 | .dark { 14 | --contrast-color: rgb(255, 255, 255); 15 | --theme-color: black; 16 | --canvas-fill: rgb(54, 54, 54); 17 | --weboc-app-bar-bg-color: none; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/topology/componentSettings/schematicStatusDisplaySettings.ts: -------------------------------------------------------------------------------- 1 | import type { DeepRequired } from '@/lib/utils/types' 2 | import type { SSDSettings as PiSSDSettings } from '@deltares/fews-pi-requests' 3 | 4 | export type SSDSettings = DeepRequired 5 | 6 | export const defaultSchematicStatusDisplaySettings: SSDSettings = { 7 | zoomEnabled: false, 8 | singleClickAction: true, // TODO: Implement 9 | doubleClickAction: true, // TODO: Implement 10 | useBrowserStyle: false, // TODO: Implement 11 | } 12 | -------------------------------------------------------------------------------- /docs/public/oidc/README.md: -------------------------------------------------------------------------------- 1 | # Delft-FEWS Web OC Open ID Connect 2 | 3 | The Delft-FEWS Web OC is distributed as a single page web application. 4 | It is possible to enable OIDC as authentication solution and pass access tokens to the Delft-FEWS Web Service API to apply role based access. 5 | 6 | For instructions on how to configure the Delft-FEWS Web OC to use OIDC, see the following sections for the different OIDC providers: 7 | 8 | - [Microsoft Entra ID Configuration](entra-id/README.md) 9 | - [Keycloak](keycloak/README.md) 10 | -------------------------------------------------------------------------------- /src/lib/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type ComponentProps = T extends new (...angs: any) => { 2 | $props: infer P 3 | } 4 | ? NonNullable

5 | : T extends (props: infer P, ...args: any) => any 6 | ? P 7 | : {} 8 | 9 | export type DeepRequired = Required<{ 10 | [K in keyof T]: T[K] extends Required ? T[K] : DeepRequired 11 | }> 12 | 13 | export const arrayOfAll = 14 | () => 15 | ( 16 | array: U & ([T] extends [U[number]] ? unknown : 'Invalid') & { 0: T }, 17 | ) => 18 | array 19 | -------------------------------------------------------------------------------- /src/components/analysis/AnalysisUnsupportedChart.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env 26 | /test-results/ 27 | /playwright-report/ 28 | /blob-report/ 29 | /playwright/.cache/ 30 | /test-results/ 31 | /playwright-report/ 32 | /blob-report/ 33 | /playwright/.cache/ 34 | -------------------------------------------------------------------------------- /src/components/general/HighlightMatch.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /src/components/wms/panel/OverlayInformationPanel.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # .github/release.yml 2 | 3 | changelog: 4 | exclude: 5 | labels: 6 | - 'rel: ignore' 7 | categories: 8 | - title: New Features 9 | labels: 10 | - 'rel: new feature' 11 | 12 | - title: Improvements 13 | labels: 14 | - 'rel: improvement' 15 | 16 | - title: Fixes 17 | labels: 18 | - 'rel: fix' 19 | 20 | - title: Beta Features 21 | labels: 22 | - 'rel: beta' 23 | 24 | - title: Other Changes 25 | labels: 26 | - '*' 27 | -------------------------------------------------------------------------------- /src/lib/products/utils.ts: -------------------------------------------------------------------------------- 1 | export function getFileExtension(url: string): string { 2 | const urlParts = url.toLowerCase().split('.') 3 | return urlParts[urlParts.length - 1] 4 | } 5 | 6 | export type ViewMode = 'html' | 'iframe' | 'img' | 'pdf' 7 | export function getViewMode(extension: string): ViewMode { 8 | switch (extension) { 9 | case 'html': 10 | return 'html' 11 | case 'png': 12 | case 'jpg': 13 | case 'jpeg': 14 | return 'img' 15 | case 'pdf': 16 | return 'pdf' 17 | default: 18 | return 'iframe' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/views/DataDownloadDisplayView.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | -------------------------------------------------------------------------------- /src/lib/workflows/form.ts: -------------------------------------------------------------------------------- 1 | import { ScenarioData } from '@/lib/whatif' 2 | 3 | export function isBoundingBoxInFormData(data: ScenarioData): boolean { 4 | const properties = Object.keys(data) 5 | return ( 6 | properties.includes('xMin') && 7 | properties.includes('yMin') && 8 | properties.includes('xMax') && 9 | properties.includes('yMax') 10 | ) 11 | } 12 | 13 | export function isCoordinateInFormData(data: ScenarioData): boolean { 14 | const properties = Object.keys(data) 15 | return properties.includes('latitude') && properties.includes('longitude') 16 | } 17 | -------------------------------------------------------------------------------- /src/components/webdisplay/WebDisplay.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /src/components/analysis/AnalysisAddButton.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 27 | -------------------------------------------------------------------------------- /src/stores/globalSearch.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface GlobalSearchItem { 4 | id: string 5 | title: string 6 | children?: GlobalSearchItem[] 7 | } 8 | 9 | interface GlobalSearchState { 10 | active: boolean 11 | type: 'locations' | 'parameters' | 'nodes' 12 | items: GlobalSearchItem[] 13 | selectedItems: string[] 14 | } 15 | 16 | const useGlobalSearchState = defineStore('globalSearchState', { 17 | state: (): GlobalSearchState => ({ 18 | active: false, 19 | type: 'locations', 20 | items: [], 21 | selectedItems: [], 22 | }), 23 | }) 24 | 25 | export { useGlobalSearchState } 26 | -------------------------------------------------------------------------------- /src/views/auth/Logout.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /docs/public/deployments/delftfews-sa/Modules/weboc/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Delft-FEWS Web OC 8 | 9 | 10 | 404 11 | /index.html 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/DefaultBaseMaps.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "automatic", 4 | "name": "Automatic", 5 | "icon": "mdi-theme-light-dark", 6 | "style": "automatic" 7 | }, 8 | { 9 | "id": "light", 10 | "name": "CartoDB (light)", 11 | "icon": "mdi-weather-sunny", 12 | "style": "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json", 13 | "beforeId": "boundary_country_outline" 14 | }, 15 | { 16 | "id": "dark", 17 | "name": "CartoDB (dark)", 18 | "icon": "mdi-weather-night", 19 | "style": "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json", 20 | "beforeId": "boundary_country_outline" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /src/components/reports/tiptap/data.css: -------------------------------------------------------------------------------- 1 | .ProseMirror data { 2 | background-position: right; 3 | padding-right: 1.5em; 4 | outline: 2px solid #68cef8; 5 | outline-offset: 2px; 6 | border-radius: 2px; 7 | background-image: url('data:image/svg+xml,'); 8 | } 9 | -------------------------------------------------------------------------------- /src/components/wms/ColourLegendTable.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 21 | 22 | 33 | -------------------------------------------------------------------------------- /src/services/application-config/ApplicationConfig.ts: -------------------------------------------------------------------------------- 1 | import { VBtn } from 'vuetify/components' 2 | 3 | export enum RequestHeaderAuthorization { 4 | BEARER = 'Bearer', 5 | OFF = 'Off', 6 | } 7 | 8 | export type ApplicationConfig = { 9 | VITE_APP_NAME: string 10 | VITE_AUTH_AUTHORITY: string 11 | VITE_AUTH_ID: string 12 | VITE_AUTH_METADATA_URL: string 13 | VITE_AUTH_SCOPE: string 14 | VITE_FEWS_ARCHIVE_WEBSERVICES_URL: string 15 | VITE_FEWS_WEBSERVICES_URL: string 16 | VITE_LOGIN_BUTTON_PROPS: string | VBtn['$props'] 17 | VITE_LOGIN_STYLESHEET_URL: string 18 | VITE_REQUEST_HEADER_AUTHORIZATION: RequestHeaderAuthorization 19 | VITE_I18N_LOCALE: string 20 | } 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # The Delft-FEWS Web OC Developers Documentation 2 | 3 | ## Definition Of Done 4 | 5 | Before merging a feature into the main branch, the following requirements have to be met: 6 | 7 | - All feature branches have to be reviewed and merged by another developer than the feature developer. 8 | - All unit tests and e2e tests have to succeed: npm run test:unit and test:e2e. 9 | - No lint errors are allowed: npm run lint 10 | - Sonarqube (https://sonarqube.deltares.nl/projects/) reports all A's. 11 | - Code coverage of libraries at least 80%. 12 | - Typescript types have to be used from the libraries. 13 | - Reusable vue components to web components. 14 | - Documentation up to date 15 | -------------------------------------------------------------------------------- /src/components/reports/tiptap/CustomImage.ts: -------------------------------------------------------------------------------- 1 | import Image from '@tiptap/extension-image' 2 | 3 | export const CustomImage = Image.extend({ 4 | addAttributes() { 5 | return { 6 | ...this.parent?.(), 7 | 8 | alt: { 9 | parseHTML: (element) => element.getAttribute('alt'), 10 | }, 11 | 12 | src: { 13 | parseHTML: (element) => element.getAttribute('src'), 14 | }, 15 | 16 | width: { 17 | parseHTML: (element) => element.getAttribute('width'), 18 | }, 19 | 20 | height: { 21 | parseHTML: (element) => element.getAttribute('height'), 22 | }, 23 | } 24 | }, 25 | }) 26 | 27 | export default CustomImage 28 | -------------------------------------------------------------------------------- /src/lib/topology/componentSettings/mapSettings.ts: -------------------------------------------------------------------------------- 1 | import type { DeepRequired } from '@/lib/utils/types' 2 | import type { MapSettings as PiMapSettings } from '@deltares/fews-pi-requests' 3 | 4 | export type MapSettings = DeepRequired 5 | 6 | export const defaultMapSettings: MapSettings = { 7 | wmsLayer: { 8 | show: true, // TODO: Implement 9 | autoPlay: false, // TODO: Implement 10 | animateVectors: true, // TODO: Implement 11 | doubleClickAction: true, 12 | }, 13 | locationsLayer: { 14 | show: true, 15 | locationNames: true, // TODO: Implement 16 | singleClickAction: true, 17 | locationSearchEnabled: true, 18 | }, 19 | overlays: [], 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/timeseries/types/SeriesData.ts: -------------------------------------------------------------------------------- 1 | import type { TimeSeriesEvent } from '@deltares/fews-pi-requests' 2 | 3 | export interface SeriesData 4 | extends Pick { 5 | x: Date | number | null 6 | y: number | null 7 | } 8 | 9 | export interface SeriesArrayData 10 | extends Pick { 11 | x: Date | number | null 12 | y: (number | null)[] 13 | } 14 | 15 | export interface TimeSeriesData extends SeriesData { 16 | x: Date 17 | } 18 | 19 | export function isSeriesArrayData( 20 | data: SeriesData | SeriesArrayData, 21 | ): data is SeriesArrayData { 22 | return Array.isArray(data.y) 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/display/DisplayConfig.ts: -------------------------------------------------------------------------------- 1 | import { ChartConfig } from '../charts/types/ChartConfig.js' 2 | import { ActionPeriod, ActionRequest } from '@deltares/fews-pi-requests' 3 | 4 | export enum DisplayType { 5 | TimeSeriesChart = 'TimeSeriesChart', 6 | TimeSeriesTable = 'TimeSeriesTable', 7 | ElevationChart = 'ElevationChart', 8 | Information = 'Information', 9 | } 10 | 11 | export interface DisplayConfig { 12 | id: string 13 | nodeId: string | undefined 14 | plotId: string | undefined 15 | index: number | undefined 16 | title: string 17 | forecastLegend: string | undefined 18 | class: string 19 | requests: ActionRequest[] 20 | period: ActionPeriod | undefined 21 | subplots: ChartConfig[] 22 | } 23 | -------------------------------------------------------------------------------- /src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | // Vuetify 2 | import { toHumanReadableDate } from '@/lib/date' 3 | import '@/styles/main.scss' 4 | import '@mdi/font/css/materialdesignicons.css' 5 | import { createVuetify } from 'vuetify' 6 | import { createVueI18nAdapter } from 'vuetify/locale/adapters/vue-i18n' 7 | import { i18n } from './i18n' 8 | import { useI18n } from 'vue-i18n' 9 | 10 | const vuetify = createVuetify({ 11 | locale: { 12 | adapter: createVueI18nAdapter({ i18n, useI18n }), 13 | }, 14 | defaults: { 15 | VBtn: { 16 | variant: 'text', 17 | }, 18 | VDateInput: { 19 | displayFormat: toHumanReadableDate, 20 | placeholder: 'dd/mm/yyyy', 21 | }, 22 | }, 23 | }) 24 | 25 | export default vuetify 26 | -------------------------------------------------------------------------------- /src/lib/requests/transformRequest.ts: -------------------------------------------------------------------------------- 1 | import { authenticationManager } from '@/services/authentication/AuthenticationManager.ts' 2 | 3 | export function createTransformRequestFn(controller?: AbortController) { 4 | return async (request: Request): Promise => { 5 | return Promise.resolve( 6 | authenticationManager.transformRequestAuth(request, controller?.signal), 7 | ) 8 | } 9 | } 10 | 11 | export function mergeHeaders(headers1: Headers, headers2: Headers): Headers { 12 | const mergedHeaders = new Headers() 13 | headers1.forEach((value, key) => { 14 | mergedHeaders.set(key, value) 15 | }) 16 | headers2.forEach((value, key) => { 17 | mergedHeaders.set(key, value) 18 | }) 19 | return mergedHeaders 20 | } 21 | -------------------------------------------------------------------------------- /src/views/InformationDisplayView.vue: -------------------------------------------------------------------------------- 1 |