From 2a88e5c9131d25eec13e371df85dd4fe15766c54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Jakab?=
<61331998+jakdan99@users.noreply.github.com>
Date: Mon, 23 Sep 2024 16:19:49 +0200
Subject: [PATCH] Study overview page rework (#11)
* Device deployment card
* Inactive Deployments card
* DeploymentsInProgress card
* Fix navigation url on cards
* Cleanup
* Remove kotlinx value datetime
---
.prettierrc.js => .prettierrc.mjs | 0
package.json | 1 +
pnpm-lock.yaml | 381 +++++++++++++++++-
src/components/PieCenterLabel/index.tsx | 17 +
src/components/PieCenterLabel/styles.ts | 21 +
src/pages/Studies/StudyCard/index.tsx | 4 +-
.../DeploymentStatusLegend/index.tsx | 23 ++
.../DeploymentStatusLegend/styles.ts | 12 +
.../DeploymentStatus/TooltipContent/index.tsx | 39 ++
.../Overview/DeploymentStatus/index.tsx | 147 +++++++
.../styles.ts | 23 +-
.../TooltipContent/index.tsx | 38 ++
.../TooltipContent/styles.ts | 14 +
.../Overview/DeploymentsInProgress/index.tsx | 149 +++++++
.../styles.ts | 20 +-
.../Overview/DeviceDeploymentStatus/index.tsx | 169 --------
.../Overview/InactiveDeployments/index.tsx | 141 +++++++
.../styles.ts | 17 +-
.../Overview/InactiveParticipants/index.tsx | 161 --------
.../Overview/Participants/index.tsx | 169 --------
.../StudyOverview/Overview/Status/index.tsx | 11 +
.../StudyOverview/Overview/Status/styles.ts | 3 +-
.../Overview/StudyData/index.tsx | 15 -
.../Overview/StudyData/styles.ts | 20 -
.../Overview/StudyDataTypes/index.tsx | 14 -
.../Overview/StudyDataTypes/styles.ts | 19 -
src/pages/StudyOverview/Overview/index.tsx | 14 +-
src/pages/StudyOverview/Overview/styles.ts | 2 +-
src/pages/StudySettings/StudyData/index.tsx | 2 +-
src/utils/queries/participants.ts | 9 +
src/utils/queries/studies.ts | 10 +-
src/utils/theme.tsx | 1 +
src/utils/utility.tsx | 2 +
33 files changed, 1055 insertions(+), 613 deletions(-)
rename .prettierrc.js => .prettierrc.mjs (100%)
create mode 100644 src/components/PieCenterLabel/index.tsx
create mode 100644 src/components/PieCenterLabel/styles.ts
create mode 100644 src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/index.tsx
create mode 100644 src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/styles.ts
create mode 100644 src/pages/StudyOverview/Overview/DeploymentStatus/TooltipContent/index.tsx
create mode 100644 src/pages/StudyOverview/Overview/DeploymentStatus/index.tsx
rename src/pages/StudyOverview/Overview/{Participants => DeploymentStatus}/styles.ts (74%)
create mode 100644 src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/index.tsx
create mode 100644 src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/styles.ts
create mode 100644 src/pages/StudyOverview/Overview/DeploymentsInProgress/index.tsx
rename src/pages/StudyOverview/Overview/{DeviceDeploymentStatus => DeploymentsInProgress}/styles.ts (82%)
delete mode 100644 src/pages/StudyOverview/Overview/DeviceDeploymentStatus/index.tsx
create mode 100644 src/pages/StudyOverview/Overview/InactiveDeployments/index.tsx
rename src/pages/StudyOverview/Overview/{InactiveParticipants => InactiveDeployments}/styles.ts (78%)
delete mode 100644 src/pages/StudyOverview/Overview/InactiveParticipants/index.tsx
delete mode 100644 src/pages/StudyOverview/Overview/Participants/index.tsx
delete mode 100644 src/pages/StudyOverview/Overview/StudyData/index.tsx
delete mode 100644 src/pages/StudyOverview/Overview/StudyData/styles.ts
delete mode 100644 src/pages/StudyOverview/Overview/StudyDataTypes/index.tsx
delete mode 100644 src/pages/StudyOverview/Overview/StudyDataTypes/styles.ts
diff --git a/.prettierrc.js b/.prettierrc.mjs
similarity index 100%
rename from .prettierrc.js
rename to .prettierrc.mjs
diff --git a/package.json b/package.json
index 8f7aeb3..18c0609 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"@mui/material": "^5.15.20",
"@mui/styles": "^5.15.20",
"@mui/system": "^5.15.20",
+ "@mui/x-charts": "^7.7.0",
"@mui/x-date-pickers": "^7.6.2",
"@tanstack/react-query": "^5.45.0",
"@tanstack/react-query-devtools": "^5.45.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index adae092..a25725f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -18,8 +18,8 @@ importers:
specifier: ^1.0.1
version: 1.0.1(axios@1.7.2)(oidc-client-ts@3.0.1)(react-oidc-context@3.1.0(oidc-client-ts@3.0.1)(react@18.3.1))(react@18.3.1)
'@carp-dk/client':
- specifier: 1.1.0
- version: 1.1.0
+ specifier: 1.2.0
+ version: 1.2.0
'@emotion/react':
specifier: ^11.11.4
version: 11.11.4(@types/react@18.3.3)(react@18.3.1)
@@ -47,6 +47,9 @@ importers:
'@mui/system':
specifier: ^5.15.20
version: 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+ '@mui/x-charts':
+ specifier: ^7.7.0
+ version: 7.12.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mui/x-date-pickers':
specifier: ^7.6.2
version: 7.7.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -281,6 +284,10 @@ packages:
resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
engines: {node: '>=6.9.0'}
+ '@babel/runtime@7.25.0':
+ resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/template@7.24.7':
resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
engines: {node: '>=6.9.0'}
@@ -301,8 +308,8 @@ packages:
react: '>=16.8.0'
react-oidc-context: ^2.3.1
- '@carp-dk/client@1.1.0':
- resolution: {integrity: sha512-m1FO/2dMO4fhfSN7fzGOynBTfRahgBy8uk2NeO/v5qyMFzp2+v3HXVOssdheMGAK5Bl9656BOQwbKBsYV2w5ZQ==}
+ '@carp-dk/client@1.2.0':
+ resolution: {integrity: sha512-b48+yhXWFbAfkMaImlq8ibJgt75idjLdQvwXJUPduFn96Xj6E8fEYNNcHG0iTtDovGmymyxGEIl+ikePlD4hQg==}
'@carp-dk/eslint-config@1.1.0':
resolution: {integrity: sha512-vIz9PJZBaSadbe3rO9nTJQoa01c8h9judJv1uVDYjlApienWU8W22Yi8aBJLHil3wI9dFXTPcDvx1yqMpkOo+w==}
@@ -671,6 +678,16 @@ packages:
'@types/react':
optional: true
+ '@mui/private-theming@5.16.6':
+ resolution: {integrity: sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@mui/styled-engine@5.15.14':
resolution: {integrity: sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==}
engines: {node: '>=12.0.0'}
@@ -684,6 +701,19 @@ packages:
'@emotion/styled':
optional: true
+ '@mui/styled-engine@5.16.6':
+ resolution: {integrity: sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.4.1
+ '@emotion/styled': ^11.3.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+
'@mui/styles@5.15.20':
resolution: {integrity: sha512-zpXYhNxQ9A4zxF3IRQRZRUg7fXYj6Wfa3nB+7yOLVecokhjCAr1zY2VC5Uznf5qs2cfgBRfmDkBYqvQjHWf2uA==}
engines: {node: '>=12.0.0'}
@@ -710,6 +740,22 @@ packages:
'@types/react':
optional: true
+ '@mui/system@5.16.6':
+ resolution: {integrity: sha512-5xgyJjBIMPw8HIaZpfbGAaFYPwImQn7Nyh+wwKWhvkoIeDosQ1ZMVrbTclefi7G8hNmqhip04duYwYpbBFnBgw==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.5.0
+ '@emotion/styled': ^11.3.0
+ '@types/react': ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+ '@types/react':
+ optional: true
+
'@mui/types@7.2.14':
resolution: {integrity: sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==}
peerDependencies:
@@ -718,6 +764,14 @@ packages:
'@types/react':
optional: true
+ '@mui/types@7.2.15':
+ resolution: {integrity: sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@mui/utils@5.15.20':
resolution: {integrity: sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==}
engines: {node: '>=12.0.0'}
@@ -728,6 +782,34 @@ packages:
'@types/react':
optional: true
+ '@mui/utils@5.16.6':
+ resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@mui/x-charts-vendor@7.12.0':
+ resolution: {integrity: sha512-05J1o0T3/7iNd0I4LnXLjGwxybzJN8hF2qr/n8XduJYWxYIXRbGsD/Y0nVnHh/EjIGe3aHqCYbt1Ob9E/RQUtQ==}
+
+ '@mui/x-charts@7.12.0':
+ resolution: {integrity: sha512-N0Q83vXinNsdVJ4l8f1WcPdbsSxO7iOIvwn6URW/iCvDx6ZxZOxw2AKAerM5l2lPr9N3G8dYWMz5aG3DrVpNjg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@emotion/react': ^11.9.0
+ '@emotion/styled': ^11.8.1
+ '@mui/material': ^5.15.14
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@emotion/react':
+ optional: true
+ '@emotion/styled':
+ optional: true
+
'@mui/x-date-pickers@7.7.1':
resolution: {integrity: sha512-p7/TY8QcdQd6RelNqzW5q89GeUFctvZnDHTfQVEC0l0nAy7ArE6u21uNF8QWGrijZoJXCM+OlIRzlZADaUPpWA==}
engines: {node: '>=14.0.0'}
@@ -787,6 +869,33 @@ packages:
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
+ '@react-spring/animated@9.7.4':
+ resolution: {integrity: sha512-7As+8Pty2QlemJ9O5ecsuPKjmO0NKvmVkRR1n6mEotFgWar8FKuQt2xgxz3RTgxcccghpx1YdS1FCdElQNexmQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/core@9.7.4':
+ resolution: {integrity: sha512-GzjA44niEJBFUe9jN3zubRDDDP2E4tBlhNlSIkTChiNf9p4ZQlgXBg50qbXfSXHQPHak/ExYxwhipKVsQ/sUTw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/rafz@9.7.4':
+ resolution: {integrity: sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA==}
+
+ '@react-spring/shared@9.7.4':
+ resolution: {integrity: sha512-bEPI7cQp94dOtCFSEYpxvLxj0+xQfB5r9Ru1h8OMycsIq7zFZon1G0sHrBLaLQIWeMCllc4tVDYRTLIRv70C8w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ '@react-spring/types@9.7.4':
+ resolution: {integrity: sha512-iQVztO09ZVfsletMiY+DpT/JRiBntdsdJ4uqk3UJFhrhS8mIC9ZOZbmfGSRs/kdbNPQkVyzucceDicQ/3Mlj9g==}
+
+ '@react-spring/web@9.7.4':
+ resolution: {integrity: sha512-UMvCZp7I5HCVIleSa4BwbNxynqvj+mJjG2m20VO2yPoi2pnCYANy58flvz9v/YcXTAvsmL655FV3pm5fbr6akA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
'@remix-run/router@1.17.0':
resolution: {integrity: sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==}
engines: {node: '>=14.0.0'}
@@ -937,6 +1046,27 @@ packages:
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.0':
+ resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==}
+
+ '@types/d3-scale@4.0.8':
+ resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
+
+ '@types/d3-shape@3.1.6':
+ resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==}
+
+ '@types/d3-time@3.0.3':
+ resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==}
+
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
@@ -1370,6 +1500,46 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.0:
+ resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -1432,6 +1602,9 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
+ delaunator@5.0.1:
+ resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -1984,6 +2157,10 @@ packages:
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
engines: {node: '>= 0.4'}
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@@ -2719,6 +2896,9 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
+ robust-predicates@3.0.2:
+ resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
+
rollup@4.18.0:
resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -3301,6 +3481,10 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
+ '@babel/runtime@7.25.0':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
'@babel/template@7.24.7':
dependencies:
'@babel/code-frame': 7.24.7
@@ -3335,7 +3519,7 @@ snapshots:
react: 18.3.1
react-oidc-context: 3.1.0(oidc-client-ts@3.0.1)(react@18.3.1)
- '@carp-dk/client@1.1.0': {}
+ '@carp-dk/client@1.2.0': {}
? '@carp-dk/eslint-config@1.1.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0))(eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.34.3(eslint@8.57.0))(eslint@8.57.0))(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0))(eslint-plugin-prettier@5.0.0-alpha.2(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.2))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react-refresh@0.4.7(eslint@8.57.0))(eslint-plugin-react@7.34.3(eslint@8.57.0))(eslint-plugin-unused-imports@4.0.0(@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0))(eslint@8.57.0)(prettier-plugin-organize-imports@3.2.4(prettier@3.3.2)(typescript@5.5.2))(prettier@3.3.2)'
: dependencies:
@@ -3680,6 +3864,15 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@mui/private-theming@5.16.6(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1)
+ prop-types: 15.8.1
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@mui/styled-engine@5.15.14(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -3691,6 +3884,17 @@ snapshots:
'@emotion/react': 11.11.4(@types/react@18.3.3)(react@18.3.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+ '@mui/styled-engine@5.16.6(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@emotion/cache': 11.11.0
+ csstype: 3.1.3
+ prop-types: 15.8.1
+ react: 18.3.1
+ optionalDependencies:
+ '@emotion/react': 11.11.4(@types/react@18.3.3)(react@18.3.1)
+ '@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+
'@mui/styles@5.15.20(@types/react@18.3.3)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -3730,10 +3934,30 @@ snapshots:
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
'@types/react': 18.3.3
+ '@mui/system@5.16.6(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@mui/private-theming': 5.16.6(@types/react@18.3.3)(react@18.3.1)
+ '@mui/styled-engine': 5.16.6(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)
+ '@mui/types': 7.2.15(@types/react@18.3.3)
+ '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1)
+ clsx: 2.1.1
+ csstype: 3.1.3
+ prop-types: 15.8.1
+ react: 18.3.1
+ optionalDependencies:
+ '@emotion/react': 11.11.4(@types/react@18.3.3)(react@18.3.1)
+ '@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+ '@types/react': 18.3.3
+
'@mui/types@7.2.14(@types/react@18.3.3)':
optionalDependencies:
'@types/react': 18.3.3
+ '@mui/types@7.2.15(@types/react@18.3.3)':
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@mui/utils@5.15.20(@types/react@18.3.3)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -3744,6 +3968,55 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@mui/utils@5.16.6(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@mui/types': 7.2.15(@types/react@18.3.3)
+ '@types/prop-types': 15.7.12
+ clsx: 2.1.1
+ prop-types: 15.8.1
+ react: 18.3.1
+ react-is: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
+ '@mui/x-charts-vendor@7.12.0':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@types/d3-color': 3.1.3
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.8
+ '@types/d3-shape': 3.1.6
+ '@types/d3-time': 3.0.3
+ d3-color: 3.1.0
+ d3-delaunay: 6.0.4
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ delaunator: 5.0.1
+ robust-predicates: 3.0.2
+
+ '@mui/x-charts@7.12.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.0
+ '@mui/material': 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mui/system': 5.16.6(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+ '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1)
+ '@mui/x-charts-vendor': 7.12.0
+ '@react-spring/rafz': 9.7.4
+ '@react-spring/web': 9.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ clsx: 2.1.1
+ prop-types: 15.8.1
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@emotion/react': 11.11.4(@types/react@18.3.3)(react@18.3.1)
+ '@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
+ transitivePeerDependencies:
+ - '@types/react'
+
'@mui/x-date-pickers@7.7.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -3783,6 +4056,38 @@ snapshots:
'@popperjs/core@2.11.8': {}
+ '@react-spring/animated@9.7.4(react@18.3.1)':
+ dependencies:
+ '@react-spring/shared': 9.7.4(react@18.3.1)
+ '@react-spring/types': 9.7.4
+ react: 18.3.1
+
+ '@react-spring/core@9.7.4(react@18.3.1)':
+ dependencies:
+ '@react-spring/animated': 9.7.4(react@18.3.1)
+ '@react-spring/shared': 9.7.4(react@18.3.1)
+ '@react-spring/types': 9.7.4
+ react: 18.3.1
+
+ '@react-spring/rafz@9.7.4': {}
+
+ '@react-spring/shared@9.7.4(react@18.3.1)':
+ dependencies:
+ '@react-spring/rafz': 9.7.4
+ '@react-spring/types': 9.7.4
+ react: 18.3.1
+
+ '@react-spring/types@9.7.4': {}
+
+ '@react-spring/web@9.7.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@react-spring/animated': 9.7.4(react@18.3.1)
+ '@react-spring/core': 9.7.4(react@18.3.1)
+ '@react-spring/shared': 9.7.4(react@18.3.1)
+ '@react-spring/types': 9.7.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
'@remix-run/router@1.17.0': {}
'@rollup/plugin-alias@5.1.0(rollup@4.18.0)':
@@ -3900,6 +4205,26 @@ snapshots:
dependencies:
'@babel/types': 7.24.7
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.0': {}
+
+ '@types/d3-scale@4.0.8':
+ dependencies:
+ '@types/d3-time': 3.0.3
+
+ '@types/d3-shape@3.1.6':
+ dependencies:
+ '@types/d3-path': 3.1.0
+
+ '@types/d3-time@3.0.3': {}
+
'@types/estree@1.0.5': {}
'@types/history@4.7.11': {}
@@ -4412,6 +4737,44 @@ snapshots:
csstype@3.1.3: {}
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-color@3.1.0: {}
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.0.1
+
+ d3-format@3.1.0: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@3.1.0: {}
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.0
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
damerau-levenshtein@1.0.8: {}
data-view-buffer@1.0.1:
@@ -4485,6 +4848,10 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
+ delaunator@5.0.1:
+ dependencies:
+ robust-predicates: 3.0.2
+
delayed-stream@1.0.0: {}
depd@2.0.0: {}
@@ -5223,6 +5590,8 @@ snapshots:
hasown: 2.0.2
side-channel: 1.0.6
+ internmap@2.0.3: {}
+
ipaddr.js@1.9.1: {}
is-arguments@1.1.1:
@@ -5928,6 +6297,8 @@ snapshots:
dependencies:
glob: 7.2.3
+ robust-predicates@3.0.2: {}
+
rollup@4.18.0:
dependencies:
'@types/estree': 1.0.5
diff --git a/src/components/PieCenterLabel/index.tsx b/src/components/PieCenterLabel/index.tsx
new file mode 100644
index 0000000..767ddf4
--- /dev/null
+++ b/src/components/PieCenterLabel/index.tsx
@@ -0,0 +1,17 @@
+import { useDrawingArea } from "@mui/x-charts";
+import { StyledNumberTSpan, StyledTSpan } from "./styles";
+
+const PieCenterLabel = ({ children }: { children: React.ReactNode }) => {
+ const { width, height, left, top } = useDrawingArea();
+ return (
+
+
+ {children}
+
+
+ Deployments
+
+
+ );
+};
+export default PieCenterLabel;
diff --git a/src/components/PieCenterLabel/styles.ts b/src/components/PieCenterLabel/styles.ts
new file mode 100644
index 0000000..4d848e9
--- /dev/null
+++ b/src/components/PieCenterLabel/styles.ts
@@ -0,0 +1,21 @@
+import { styled } from "@Utils/theme";
+
+export const StyledTSpan = styled("tspan")(({ theme }) => ({
+ fill: theme.palette.text.primary,
+ font: theme.typography.fontFamily,
+ fontWeight: 600,
+ lineHeight: "16px",
+ textAnchor: "middle",
+ dominantBaseline: "central",
+ fontSize: 12,
+}));
+
+export const StyledNumberTSpan = styled("tspan")(({ theme }) => ({
+ fill: theme.palette.text.primary,
+ font: theme.typography.fontFamily,
+ fontWeight: 600,
+ lineHeight: "32px",
+ textAnchor: "middle",
+ dominantBaseline: "central",
+ fontSize: 24,
+}));
diff --git a/src/pages/Studies/StudyCard/index.tsx b/src/pages/Studies/StudyCard/index.tsx
index b6aefca..220fbd4 100644
--- a/src/pages/Studies/StudyCard/index.tsx
+++ b/src/pages/Studies/StudyCard/index.tsx
@@ -33,9 +33,7 @@ const StudyCard = ({ onClick, study, status, description }: Props) => {
{description}
- {formatDateTime(
- study.createdOn.toString(),
- )}
+ {formatDateTime(study.createdOn.toString())}
{/* TODO: Add real owner name after backend supports it */}
{/* Jakob */}
diff --git a/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/index.tsx b/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/index.tsx
new file mode 100644
index 0000000..895baad
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/index.tsx
@@ -0,0 +1,23 @@
+import { Typography } from "@mui/material";
+import { Stack } from "@mui/system";
+import { PieValueType } from "@mui/x-charts";
+import ParticipantsRow from "./styles";
+
+const DeploymentStatusLegend = ({ data }: { data: PieValueType[] }) => {
+ return (
+
+ {data.map((entry) => (
+
+
+ {entry.value}
+
+
+ {`${entry.label}`}
+
+
+ ))}
+
+ );
+};
+
+export default DeploymentStatusLegend;
diff --git a/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/styles.ts b/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/styles.ts
new file mode 100644
index 0000000..a4f6677
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentStatus/DeploymentStatusLegend/styles.ts
@@ -0,0 +1,12 @@
+import { styled } from "@Utils/theme";
+
+const ParticipantsRow = styled("div")({
+ paddingBottom: 10,
+ marginBottom: 0,
+ display: "grid",
+ gridTemplateColumns: "80px 1fr",
+ gap: 8,
+ alignItems: "end",
+});
+
+export default ParticipantsRow;
diff --git a/src/pages/StudyOverview/Overview/DeploymentStatus/TooltipContent/index.tsx b/src/pages/StudyOverview/Overview/DeploymentStatus/TooltipContent/index.tsx
new file mode 100644
index 0000000..a322cb7
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentStatus/TooltipContent/index.tsx
@@ -0,0 +1,39 @@
+import { getDeploymentStatusColor } from "@Utils/utility";
+import { Typography } from "@mui/material";
+import { Stack } from "@mui/system";
+
+const TooltipContent = () => {
+ return (
+
+
+
+ Invited
+
+ : Indicates that the invited participants have not yet acted on the
+ invitation.
+
+
+
+ Deploying
+
+ : Participants have started registering devices, but are remaining
+ devices.
+
+
+
+ Running
+
+ : All the devices have been deployed and started the data collection.
+
+
+
+ Stopped
+
+ : The study deployment has been stopped and no more data will be
+ collected.
+
+
+ );
+};
+
+export default TooltipContent;
diff --git a/src/pages/StudyOverview/Overview/DeploymentStatus/index.tsx b/src/pages/StudyOverview/Overview/DeploymentStatus/index.tsx
new file mode 100644
index 0000000..8838e84
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentStatus/index.tsx
@@ -0,0 +1,147 @@
+import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
+import PieCenterLabel from "@Components/PieCenterLabel";
+import { useParticipantsStatus } from "@Utils/queries/participants";
+import { getDeploymentStatusColor } from "@Utils/utility";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
+import { Typography } from "@mui/material";
+import { Stack } from "@mui/system";
+import { PieValueType } from "@mui/x-charts";
+import { PieChart } from "@mui/x-charts/PieChart";
+import { useEffect, useState } from "react";
+import { useNavigate, useParams } from "react-router";
+import LoadingSkeleton from "../LoadingSkeleton";
+import DeploymentStatusLegend from "./DeploymentStatusLegend";
+import TooltipContent from "./TooltipContent";
+import {
+ StyledButton,
+ StyledCard,
+ StyledTitle,
+ StyledTooltip,
+ Top,
+} from "./styles";
+
+const DeploymentStatus = () => {
+ const navigate = useNavigate();
+ const { id: studyId } = useParams();
+ const {
+ data: participantStatus,
+ isLoading: participantStatusLoading,
+ error: participantStatusError,
+ } = useParticipantsStatus(studyId);
+
+ const [statuses, setStatuses] = useState([]);
+
+ useEffect(() => {
+ if (!participantStatus) return;
+ const data = participantStatus.reduce(
+ (acc, curr) => {
+ acc[
+ curr.constructor.name.toLocaleLowerCase().replace("_0", "")
+ ].value += 1;
+ return acc;
+ },
+ {
+ invited: {
+ id: 0,
+ value: 0,
+ label: "Invited",
+ color: getDeploymentStatusColor("Invited"),
+ },
+ inDeployment: {
+ id: 1,
+ value: 0,
+ label: "Deploying",
+ color: getDeploymentStatusColor("Deploying"),
+ },
+ running: {
+ id: 2,
+ value: 0,
+ label: "Running",
+ color: getDeploymentStatusColor("Running"),
+ },
+ stopped: {
+ id: 3,
+ value: 0,
+ label: "Stopped",
+ color: getDeploymentStatusColor("Stopped"),
+ },
+ },
+ );
+ setStatuses(Object.values(data));
+ }, [participantStatus]);
+
+ if (participantStatusLoading) return ;
+ if (participantStatusError)
+ return (
+
+ );
+
+ return (
+
+
+
+ Deployment Status
+
+
+
+
+
+ navigate(`/studies/${studyId}/participants/deployments`)
+ }
+ variant="outlined"
+ >
+
+ Manage
+
+
+
+
+
+ {participantStatus.length}
+
+
+
+
+
+ );
+};
+
+export default DeploymentStatus;
diff --git a/src/pages/StudyOverview/Overview/Participants/styles.ts b/src/pages/StudyOverview/Overview/DeploymentStatus/styles.ts
similarity index 74%
rename from src/pages/StudyOverview/Overview/Participants/styles.ts
rename to src/pages/StudyOverview/Overview/DeploymentStatus/styles.ts
index 45924ee..8fb9000 100644
--- a/src/pages/StudyOverview/Overview/Participants/styles.ts
+++ b/src/pages/StudyOverview/Overview/DeploymentStatus/styles.ts
@@ -1,14 +1,20 @@
-import { Button, Card, Typography } from "@mui/material";
+import { Button, Card, Tooltip, Typography } from "@mui/material";
import { styled } from "@Utils/theme";
export const StyledCard = styled(Card)({
- display: "flex",
flexDirection: "column",
padding: 24,
height: 288,
borderRadius: 16,
});
+export const StyledTooltip = styled(Tooltip)(({ theme }) => ({
+ color: theme.palette.grey[500],
+ "&:hover": {
+ color: theme.palette.primary.main,
+ },
+}));
+
export const Top = styled("div")({
display: "flex",
flexDirection: "row",
@@ -27,18 +33,9 @@ export const StyledButton = styled(Button)(({ theme }) => ({
export const StyledTitle = styled(Typography)(({ theme }) => ({
color: theme.palette.primary.main,
-}));
-
-export const ParticipantsRow = styled("div", {
- shouldForwardProp: (prop) => prop !== "isFirst",
-})<{ isFirst?: boolean }>(({ isFirst, theme }) => ({
- borderBottom: isFirst ? `1px solid ${theme.palette.grey[500]}` : "none",
- paddingBottom: isFirst ? 10 : 0,
- marginBottom: isFirst ? 4 : 0,
- display: "grid",
- gridTemplateColumns: "80px 1fr",
+ display: "flex",
+ alignItems: "center",
gap: 8,
- alignItems: "end",
}));
export const StyledNumber = styled(Typography, {
diff --git a/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/index.tsx b/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/index.tsx
new file mode 100644
index 0000000..ceb58cc
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/index.tsx
@@ -0,0 +1,38 @@
+import { Typography } from "@mui/material";
+import { Stack } from "@mui/system";
+import StyledStatusDot from "./styles";
+
+const TooltipContent = () => {
+ return (
+
+
+
+
+ Unregistered: The device has not been registered.
+
+
+
+
+
+ Registered: The device has been registered.
+
+
+
+
+
+ Deployed: The device was able to load all the necessary plugins to
+ execute the study.
+
+
+
+
+
+ Needs Redeployment: The device has previously been deployed correctly,
+ but due to changes in the device registration needs to be redeployed.
+
+
+
+ );
+};
+
+export default TooltipContent;
diff --git a/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/styles.ts b/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/styles.ts
new file mode 100644
index 0000000..251b17c
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentsInProgress/TooltipContent/styles.ts
@@ -0,0 +1,14 @@
+import { styled } from "@mui/system";
+import { getDeviceStatusColor } from "@Utils/utility";
+
+const StyledStatusDot = styled("div", {
+ shouldForwardProp: (prop) => prop !== "status",
+})<{ status?: string }>(({ status }) => ({
+ width: 8,
+ height: 8,
+ minWidth: 8,
+ borderRadius: "50%",
+ backgroundColor: getDeviceStatusColor(status),
+}));
+
+export default StyledStatusDot;
diff --git a/src/pages/StudyOverview/Overview/DeploymentsInProgress/index.tsx b/src/pages/StudyOverview/Overview/DeploymentsInProgress/index.tsx
new file mode 100644
index 0000000..b795272
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/DeploymentsInProgress/index.tsx
@@ -0,0 +1,149 @@
+/* eslint-disable no-underscore-dangle */
+import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
+import { useParticipantGroupsAccountsAndStatus } from "@Utils/queries/participants";
+import {
+ Table,
+ TableBody,
+ TableContainer,
+ TableHead,
+ Typography,
+} from "@mui/material";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import { useEffect, useState } from "react";
+import { useParams } from "react-router-dom";
+import { Stack } from "@mui/system";
+import LoadingSkeleton from "../LoadingSkeleton";
+import {
+ HeaderTableCell,
+ HeaderText,
+ SecondaryCellText,
+ StyledCard,
+ StyledDescription,
+ StyledStatusDot,
+ StyledTableCell,
+ StyledTableRow,
+ StyledTitle,
+ StyledTooltip,
+} from "./styles";
+import TooltipContent from "./TooltipContent";
+
+const DeploymentsInProgress = () => {
+ const { id: studyId } = useParams();
+ const {
+ data: deploymentsAccountAndStatus,
+ isLoading: isDeploymentsAccountAndStatusLoading,
+ error: deploymentsAccountAndStatusError,
+ } = useParticipantGroupsAccountsAndStatus(studyId);
+ const [deploymentProgress, setDeploymentProgress] = useState<
+ {
+ deploymentId: string;
+ devices: any[];
+ }[]
+ >([]);
+
+ useEffect(() => {
+ if (
+ deploymentsAccountAndStatus?.groups !== undefined &&
+ deploymentsAccountAndStatus?.groups.length !== 0
+ ) {
+ const deployments = deploymentsAccountAndStatus.groups
+ .filter((g) => !g.deploymentStatus.__type.includes("Stopped"))
+ .map((g) => {
+ const devices = g.deploymentStatus.deviceStatusList.filter(
+ (dl) => dl.device.isPrimaryDevice,
+ );
+ return { deploymentId: g.participantGroupId, devices };
+ })
+ .flat();
+ setDeploymentProgress(deployments);
+ }
+ }, [deploymentsAccountAndStatus]);
+
+ if (isDeploymentsAccountAndStatusLoading) {
+ return ;
+ }
+
+ if (deploymentsAccountAndStatusError) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+ Deployments in Progress
+
+
+
+
+
+ The status of the master devices, for each Deployment. Select the
+ Deployment ID for further information.
+
+
+
+
+
+
+ Deployment ID
+
+
+ Device Registration
+
+
+
+
+ {deploymentProgress.map((g) => (
+
+
+
+ {`... ${g.deploymentId.slice(-4)}`}
+
+
+
+
+ {g.devices.map((d) => (
+
+
+
+ {d.device.roleName}
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default DeploymentsInProgress;
diff --git a/src/pages/StudyOverview/Overview/DeviceDeploymentStatus/styles.ts b/src/pages/StudyOverview/Overview/DeploymentsInProgress/styles.ts
similarity index 82%
rename from src/pages/StudyOverview/Overview/DeviceDeploymentStatus/styles.ts
rename to src/pages/StudyOverview/Overview/DeploymentsInProgress/styles.ts
index c77ab37..3ea58fa 100644
--- a/src/pages/StudyOverview/Overview/DeviceDeploymentStatus/styles.ts
+++ b/src/pages/StudyOverview/Overview/DeploymentsInProgress/styles.ts
@@ -1,18 +1,27 @@
-import { Card, TableCell, TableRow, Typography } from "@mui/material";
+import { Card, TableCell, TableRow, Tooltip, Typography } from "@mui/material";
import { styled } from "@Utils/theme";
import { getDeviceStatusColor } from "@Utils/utility";
export const StyledCard = styled(Card)({
display: "flex",
flexDirection: "column",
- gridColumn: "span 2",
padding: 24,
- height: 288,
+ height: 580,
borderRadius: 16,
});
export const StyledTitle = styled(Typography)(({ theme }) => ({
color: theme.palette.primary.main,
+ display: "flex",
+ alignItems: "center",
+ gap: 8,
+}));
+
+export const StyledTooltip = styled(Tooltip)(({ theme }) => ({
+ color: theme.palette.grey[500],
+ "&:hover": {
+ color: theme.palette.primary.main,
+ },
}));
export const StyledDescription = styled(Typography)(({ theme }) => ({
@@ -25,7 +34,6 @@ export const HeaderTableCell = styled(TableCell)(({ theme }) => ({
backgroundColor: theme.palette.common.white,
borderBottomWidth: 1,
zIndex: 0,
- width: "30%",
paddingLeft: 0,
paddingBottom: 0,
}));
@@ -63,8 +71,8 @@ export const StatusContainer = styled("div")({
export const StyledStatusDot = styled("div", {
shouldForwardProp: (prop) => prop !== "status",
})<{ status?: string }>(({ status }) => ({
- width: 12,
- height: 12,
+ width: 8,
+ height: 8,
borderRadius: "50%",
backgroundColor: getDeviceStatusColor(status),
}));
diff --git a/src/pages/StudyOverview/Overview/DeviceDeploymentStatus/index.tsx b/src/pages/StudyOverview/Overview/DeviceDeploymentStatus/index.tsx
deleted file mode 100644
index 38cc9e9..0000000
--- a/src/pages/StudyOverview/Overview/DeviceDeploymentStatus/index.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
-import GeneratedAccountLabel from "@Components/GeneratedAccountLabel";
-import { useParticipantGroupsAccountsAndStatus } from "@Utils/queries/participants";
-import { DeviceStatus, ParticipantData } from "@carp-dk/client";
-import {
- Table,
- TableBody,
- TableContainer,
- TableHead,
- Typography,
-} from "@mui/material";
-import { useEffect, useState } from "react";
-import { useNavigate, useParams } from "react-router-dom";
-import LoadingSkeleton from "../LoadingSkeleton";
-import {
- HeaderTableCell,
- HeaderText,
- SecondaryCellText,
- StatusContainer,
- StyledCard,
- StyledDescription,
- StyledStatusDot,
- StyledTableCell,
- StyledTableRow,
- StyledTitle,
-} from "./styles";
-
-const DeviceDeploymentStatus = () => {
- const { id: studyId } = useParams();
- const navigate = useNavigate();
- const {
- data: deploymentsAccountAndStatus,
- isLoading: isDeploymentsAccountAndStatusLoading,
- error: deploymentsAccountAndStatusError,
- } = useParticipantGroupsAccountsAndStatus(studyId);
- const [devicesNeededForRedeployment, setDevicesNeededForRedeployment] =
- useState<
- {
- device: DeviceStatus;
- type: string;
- deploymentId: string;
- participant: ParticipantData;
- }[]
- >([]);
-
- useEffect(() => {
- if (
- deploymentsAccountAndStatus?.groups !== undefined &&
- deploymentsAccountAndStatus?.groups.length !== 0
- ) {
- const devicesForRedeployment = deploymentsAccountAndStatus.groups
- .map((g) => {
- const devices = g.deploymentStatus.deviceStatusList.filter(
- (dl) =>
- // eslint-disable-next-line no-underscore-dangle
- dl.__type.split(".").pop() === "NeedsRedeployment" &&
- dl.device.isPrimaryDevice,
- );
- const devicesWithParticipant = devices.map((d) => {
- const { participantId } =
- g.deploymentStatus.participantStatusList.find((psl) =>
- psl.assignedPrimaryDeviceRoleNames.includes(d.device.roleName),
- );
- const participant = g.participants.find(
- (p) => p.participantId === participantId,
- );
- return {
- device: d,
- // eslint-disable-next-line no-underscore-dangle
- type: d.__type.split(".").pop(),
- deploymentId: g.participantGroupId,
- participant,
- };
- });
- return devicesWithParticipant;
- })
- .flat();
- setDevicesNeededForRedeployment(devicesForRedeployment);
- }
- }, [deploymentsAccountAndStatus]);
-
- if (isDeploymentsAccountAndStatusLoading) {
- return ;
- }
-
- if (deploymentsAccountAndStatusError) {
- return (
-
- );
- }
-
- return (
-
- Device deployment status
-
- List of primary devices the ones that are not successfully deployed.
-
-
-
-
-
-
- Name
-
-
- Email
-
-
- Device Role
-
-
- Device Status
-
-
-
-
- {devicesNeededForRedeployment.map(
- ({ device, type, deploymentId, participant }) => (
-
- navigate(
- `/studies/${studyId}/participants/deployments/${deploymentId}/participants/${participant.participantId}`,
- )
- }
- key={participant.participantId}
- >
-
-
- {`${participant.firstName ?? ""} ${
- participant.lastName ?? ""
- }`}
-
-
-
-
- {participant.email ?? }
-
-
-
-
- {device.device.roleName}
-
-
-
-
-
-
- {type}
-
-
-
-
- ),
- )}
-
-
-
-
- );
-};
-
-export default DeviceDeploymentStatus;
diff --git a/src/pages/StudyOverview/Overview/InactiveDeployments/index.tsx b/src/pages/StudyOverview/Overview/InactiveDeployments/index.tsx
new file mode 100644
index 0000000..dd447c6
--- /dev/null
+++ b/src/pages/StudyOverview/Overview/InactiveDeployments/index.tsx
@@ -0,0 +1,141 @@
+import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
+import { useInactiveDeployments } from "@Utils/queries/participants";
+import {
+ MenuItem,
+ Table,
+ TableBody,
+ TableContainer,
+ TableHead,
+ Typography,
+} from "@mui/material";
+import { useState } from "react";
+import { useParams } from "react-router-dom";
+import { Stack } from "@mui/system";
+import { formatDateTime } from "@Utils/utility";
+import LoadingSkeleton from "../LoadingSkeleton";
+import {
+ HeaderTableCell,
+ HeaderText,
+ SecondaryCellText,
+ StyledCard,
+ StyledDescription,
+ StyledSelect,
+ StyledTableCell,
+ StyledTableRow,
+ StyledTitle,
+} from "./styles";
+
+const InactiveDeployments = () => {
+ const { id: studyId } = useParams();
+ const menuItems = [
+ { value: 24, label: "24 h" },
+ { value: 48, label: "48 h" },
+ { value: 168, label: "1 week" },
+ { value: 336, label: "2 weeks" },
+ { value: 730, label: "1 month" },
+ { value: 4380, label: "6 months" },
+ ];
+
+ const [lastUpdateTime, setLastUpdateTime] = useState(
+ menuItems[0].value,
+ );
+
+ const {
+ data: inactiveDeployments,
+ isLoading: isInactiveDeploymentsLoading,
+ error: inactiveDeploymentsError,
+ } = useInactiveDeployments(studyId, lastUpdateTime);
+
+ if (isInactiveDeploymentsLoading) {
+ return ;
+ }
+
+ if (inactiveDeploymentsError) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+ Inactive Deployments
+ {
+ const selectedItem = menuItems.find(
+ (item) => item.value === lastUpdateTime,
+ );
+ return {selectedItem.label};
+ }}
+ onChange={(e) => {
+ setLastUpdateTime(e.target.value as unknown as number);
+ }}
+ >
+ {menuItems.map((item) => (
+
+ ))}
+
+
+
+ The following Deployments have not uploaded any data in the timeframe
+ selected. Select the Deployments ID for further information or to send a
+ reminder.
+
+
+
+
+
+
+ Deployment ID
+
+
+ Last Data
+
+
+
+
+ {inactiveDeployments.map((participant) => (
+
+
+
+ {participant.deploymentId as unknown as string}
+
+
+
+
+ {formatDateTime(
+ participant.dateOfLastDataUpload.toString(),
+ { year: "numeric", month: "numeric", day: "numeric" },
+ )}
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default InactiveDeployments;
diff --git a/src/pages/StudyOverview/Overview/InactiveParticipants/styles.ts b/src/pages/StudyOverview/Overview/InactiveDeployments/styles.ts
similarity index 78%
rename from src/pages/StudyOverview/Overview/InactiveParticipants/styles.ts
rename to src/pages/StudyOverview/Overview/InactiveDeployments/styles.ts
index fd6bd6f..3d7abf7 100644
--- a/src/pages/StudyOverview/Overview/InactiveParticipants/styles.ts
+++ b/src/pages/StudyOverview/Overview/InactiveDeployments/styles.ts
@@ -1,11 +1,11 @@
-import { Card, TableCell, TableRow, Typography } from "@mui/material";
+import { Card, Select, TableCell, TableRow, Typography } from "@mui/material";
import { styled } from "@Utils/theme";
export const StyledCard = styled(Card)({
display: "flex",
flexDirection: "column",
padding: 24,
- height: 288,
+ height: 580,
borderRadius: 16,
});
@@ -23,7 +23,6 @@ export const HeaderTableCell = styled(TableCell)(({ theme }) => ({
backgroundColor: theme.palette.common.white,
borderBottomWidth: 1,
zIndex: 0,
- width: "30%",
paddingLeft: 0,
paddingBottom: 0,
}));
@@ -51,3 +50,15 @@ export const StyledTableCell = styled(TableCell)({
paddingTop: "8px",
border: "none",
});
+
+export const StyledSelect = styled(Select)({
+ height: "32px",
+ width: "116px",
+ borderRadius: "16px",
+ "& .MuiOutlinedInput-notchedOutline": {
+ borderRadius: "16px",
+ },
+ "& .MuiSelect-select": {
+ borderRadius: "16px",
+ },
+});
diff --git a/src/pages/StudyOverview/Overview/InactiveParticipants/index.tsx b/src/pages/StudyOverview/Overview/InactiveParticipants/index.tsx
deleted file mode 100644
index bf08f58..0000000
--- a/src/pages/StudyOverview/Overview/InactiveParticipants/index.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
-import GeneratedAccountLabel from "@Components/GeneratedAccountLabel";
-import { useParticipantGroupsAccountsAndStatus } from "@Utils/queries/participants";
-import { formatDateTime } from "@Utils/utility";
-import { ParticipantData } from "@carp-dk/client";
-import { Table, TableBody, TableContainer, TableHead } from "@mui/material";
-import { useEffect, useMemo, useState } from "react";
-import { useNavigate, useParams } from "react-router-dom";
-import LoadingSkeleton from "../LoadingSkeleton";
-import {
- HeaderTableCell,
- HeaderText,
- SecondaryCellText,
- StyledCard,
- StyledDescription,
- StyledTableCell,
- StyledTableRow,
- StyledTitle,
-} from "./styles";
-
-const InactiveParticipants = () => {
- const { id: studyId } = useParams();
- const navigate = useNavigate();
- const {
- data: deploymentsAccountAndStatus,
- isLoading: isDeploymentsAccountAndStatusLoading,
- error: deploymentsAccountAndStatusError,
- } = useParticipantGroupsAccountsAndStatus(studyId);
- const [inactiveParticipants, setInactiveParticipants] = useState<
- (ParticipantData & { deploymentId: string })[]
- >([]);
- const [lastDataUploads, setLastDataUploads] = useState<{
- [key: string]: string;
- }>({});
-
- useEffect(() => {
- if (
- deploymentsAccountAndStatus?.groups !== undefined &&
- deploymentsAccountAndStatus?.groups.length !== 0
- ) {
- const participants = deploymentsAccountAndStatus.groups
- .map((g) => {
- const participantsWithDate = g.participants.filter(
- (p) => p.dateOfLastDataUpload,
- );
- return participantsWithDate.map((p) => ({
- ...p,
- deploymentId: g.participantGroupId,
- }));
- })
- .flat()
- .sort(
- (a, b) =>
- new Date(b.dateOfLastDataUpload.value$kotlinx_datetime).getTime() -
- new Date(a.dateOfLastDataUpload.value$kotlinx_datetime).getTime(),
- );
- const uniqueParticipants = [...new Set(participants)].filter(
- (p) =>
- new Date(p.dateOfLastDataUpload.value$kotlinx_datetime).getTime() <=
- new Date().getTime() - 2 * 7 * 24 * 60 * 60 * 1000,
- );
- setInactiveParticipants(uniqueParticipants);
- }
- }, [deploymentsAccountAndStatus]);
-
- useMemo(() => {
- inactiveParticipants.forEach((p) => {
- const lastDataUpload = p.dateOfLastDataUpload
- ? formatDateTime(
- p.dateOfLastDataUpload.value$kotlinx_datetime.toString(),
- {
- year: "numeric",
- month: "numeric",
- day: "numeric",
- },
- )
- : "";
- setLastDataUploads((prevData) => ({
- ...prevData,
- [p.participantId]: lastDataUpload,
- }));
- });
- }, [inactiveParticipants]);
-
- if (isDeploymentsAccountAndStatusLoading) {
- return ;
- }
-
- if (deploymentsAccountAndStatusError) {
- return (
-
- );
- }
-
- return (
-
- Inactive Participants
-
- The following participants have not uploaded any data in the last 2
- weeks. Select the participant for further information or to send a
- reminder.
-
-
-
-
-
-
- Name
-
-
- Email
-
-
- Last Data
-
-
-
-
- {inactiveParticipants.map((participant) => (
-
- navigate(
- `/studies/${studyId}/participants/deployments/${participant.deploymentId}/participants/${participant.participantId}`,
- )
- }
- key={participant.participantId}
- >
-
-
- {`${participant.firstName ?? ""} ${
- participant.lastName ?? ""
- }`}
-
-
-
-
- {participant.email ?? }
-
-
-
-
- {lastDataUploads[participant.participantId]}
-
-
-
- ))}
-
-
-
-
- );
-};
-
-export default InactiveParticipants;
diff --git a/src/pages/StudyOverview/Overview/Participants/index.tsx b/src/pages/StudyOverview/Overview/Participants/index.tsx
deleted file mode 100644
index c9932b9..0000000
--- a/src/pages/StudyOverview/Overview/Participants/index.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import CarpErrorCardComponent from "@Components/CarpErrorCardComponent";
-import { useParticipantsStatus } from "@Utils/queries/participants";
-import carpDeployments from "@cachet/carp-deployments-core";
-import carpStudies from "@cachet/carp-studies-core";
-import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
-import { Typography } from "@mui/material";
-import { useMemo } from "react";
-import { useNavigate, useParams } from "react-router-dom";
-import LoadingSkeleton from "../LoadingSkeleton";
-import {
- ParticipantsRow,
- StyledButton,
- StyledCard,
- StyledNumber,
- StyledTitle,
- Top,
-} from "./styles";
-
-import ParticipantGroupStatus = carpStudies.dk.cachet.carp.studies.application.users.ParticipantGroupStatus;
-
-const { DeviceDeploymentStatus } =
- carpDeployments.dk.cachet.carp.deployments.application;
-
-const { PrimaryDeviceConfiguration } =
- carpDeployments.dk.cachet.carp.common.application.devices;
-
-const Participants = () => {
- const navigate = useNavigate();
- const { id: studyId } = useParams();
- const {
- data: participantsStatus,
- isLoading: participantsStatusLoading,
- error: participantsStatusError,
- } = useParticipantsStatus(studyId);
-
- const numberOfRegisteredDevices = useMemo(() => {
- if (!participantsStatus) return 0;
- return participantsStatus
- .filter((ps) => ps instanceof ParticipantGroupStatus.InDeployment)
- .map((ps: ParticipantGroupStatus.InDeployment) => {
- return ps.studyDeploymentStatus.deviceStatusList
- .toArray()
- .filter(
- (device) =>
- device instanceof DeviceDeploymentStatus.Registered &&
- device.device instanceof PrimaryDeviceConfiguration,
- ).length;
- })
- .reduce((a, b) => a + b, 0);
- }, [participantsStatus]);
-
- const numberOfDeployedDevices = useMemo(() => {
- if (!participantsStatus) return 0;
- return participantsStatus
- .filter((ps) => ps instanceof ParticipantGroupStatus.InDeployment)
- .map((ps: ParticipantGroupStatus.InDeployment) => {
- return ps.studyDeploymentStatus.deviceStatusList
- .toArray()
- .filter(
- (device) =>
- device instanceof DeviceDeploymentStatus.Deployed &&
- device.device instanceof PrimaryDeviceConfiguration,
- ).length;
- })
- .reduce((a, b) => a + b, 0);
- }, [participantsStatus]);
-
- const numberOfUnregisteredDevices = useMemo(() => {
- if (!participantsStatus) return 0;
- return participantsStatus
- .filter((ps) => ps instanceof ParticipantGroupStatus.InDeployment)
- .map((ps: ParticipantGroupStatus.InDeployment) => {
- return ps.studyDeploymentStatus.deviceStatusList
- .toArray()
- .filter(
- (device) =>
- device instanceof DeviceDeploymentStatus.Unregistered &&
- device.device instanceof PrimaryDeviceConfiguration,
- ).length;
- })
- .reduce((a, b) => a + b, 0);
- }, [participantsStatus]);
-
- const numberOfNeedsRedeploymentDevices = useMemo(() => {
- if (!participantsStatus) return 0;
- return participantsStatus
- .filter((ps) => ps instanceof ParticipantGroupStatus.InDeployment)
- .map((ps: ParticipantGroupStatus.InDeployment) => {
- return ps.studyDeploymentStatus.deviceStatusList
- .toArray()
- .filter(
- (device) =>
- device instanceof DeviceDeploymentStatus.NeedsRedeployment &&
- device.device instanceof PrimaryDeviceConfiguration,
- ).length;
- })
- .reduce((a, b) => a + b, 0);
- }, [participantsStatus]);
-
- const numberOfParticipants = useMemo(() => {
- if (!participantsStatus) return 0;
- return participantsStatus
- .filter((ps) => ps instanceof ParticipantGroupStatus.InDeployment)
- .map((ps: ParticipantGroupStatus.InDeployment) => {
- return ps.studyDeploymentStatus.deviceStatusList
- .toArray()
- .filter((g) => g.device instanceof PrimaryDeviceConfiguration).length;
- })
- .reduce((a, b) => a + b, 0);
- }, [participantsStatus]);
-
- if (participantsStatusLoading) return ;
- if (participantsStatusError)
- return (
-
- );
-
- return (
-
-
- Participants
-
- navigate(`/studies/${studyId}/participants/deployments`)
- }
- variant="outlined"
- >
-
- Manage
-
-
-
-
- {numberOfParticipants}
-
- Participants
-
-
-
- {numberOfDeployedDevices}
-
- Deployed
-
-
-
- {numberOfRegisteredDevices}
-
- Registered
-
-
-
- {numberOfUnregisteredDevices}
-
- Unregistered
-
-
-
- {numberOfNeedsRedeploymentDevices}
-
- Needs Redeployment
-
-
- );
-};
-
-export default Participants;
diff --git a/src/pages/StudyOverview/Overview/Status/index.tsx b/src/pages/StudyOverview/Overview/Status/index.tsx
index 8a83320..4775cbf 100644
--- a/src/pages/StudyOverview/Overview/Status/index.tsx
+++ b/src/pages/StudyOverview/Overview/Status/index.tsx
@@ -4,6 +4,8 @@ import { formatDateTime } from "@Utils/utility";
import kotlinx from "@cachet/carp-kotlinx-datetime";
import carpStudies from "@cachet/carp-studies-core";
import { useNavigate, useParams } from "react-router-dom";
+import { Typography } from "@mui/material";
+import LinkIcon from "@mui/icons-material/Link";
import LoadingSkeleton from "../LoadingSkeleton";
import {
ProtocolData,
@@ -76,6 +78,9 @@ const Status = () => {
Study Protocol: {studyDetails.protocolSnapshot.name}
+
+ {studyDetails.protocolSnapshot.description}
+
{
@@ -83,6 +88,12 @@ const Status = () => {
}}
>
See detailed information in Study Settings
+
)}
diff --git a/src/pages/StudyOverview/Overview/Status/styles.ts b/src/pages/StudyOverview/Overview/Status/styles.ts
index 038d167..36a862c 100644
--- a/src/pages/StudyOverview/Overview/Status/styles.ts
+++ b/src/pages/StudyOverview/Overview/Status/styles.ts
@@ -31,10 +31,11 @@ export const Top = styled("div")(({ theme }) => ({
export const ProtocolData = styled(Typography)(({ theme }) => ({
color: theme.palette.text.primary,
+ marginBottom: 8,
}));
export const StyledLink = styled(Typography)(({ theme }) => ({
- marginTop: 16,
+ marginTop: 24,
color: theme.palette.primary.main,
cursor: "pointer",
textDecoration: "underline",
diff --git a/src/pages/StudyOverview/Overview/StudyData/index.tsx b/src/pages/StudyOverview/Overview/StudyData/index.tsx
deleted file mode 100644
index 57c920c..0000000
--- a/src/pages/StudyOverview/Overview/StudyData/index.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { StyledCard, StyledDescription, StyledTitle } from "./styles";
-
-const StudyData = () => {
- return (
-
- Study Data
-
- See an overview of the collected and expected data in percentages, in
- the last 2 weeks.
-
-
- );
-};
-
-export default StudyData;
diff --git a/src/pages/StudyOverview/Overview/StudyData/styles.ts b/src/pages/StudyOverview/Overview/StudyData/styles.ts
deleted file mode 100644
index 478ccc7..0000000
--- a/src/pages/StudyOverview/Overview/StudyData/styles.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Card, Typography } from "@mui/material";
-import { styled } from "@Utils/theme";
-
-export const StyledCard = styled(Card)({
- display: "flex",
- flexDirection: "column",
- gridColumn: "span 2",
- padding: 24,
- height: 288,
- borderRadius: 16,
-});
-
-export const StyledTitle = styled(Typography)(({ theme }) => ({
- color: theme.palette.primary.main,
-}));
-
-export const StyledDescription = styled(Typography)(({ theme }) => ({
- color: theme.palette.text.secondary,
- marginTop: 8,
-}));
diff --git a/src/pages/StudyOverview/Overview/StudyDataTypes/index.tsx b/src/pages/StudyOverview/Overview/StudyDataTypes/index.tsx
deleted file mode 100644
index 0ed2731..0000000
--- a/src/pages/StudyOverview/Overview/StudyDataTypes/index.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { StyledCard, StyledDescription, StyledTitle } from "./styles";
-
-const StudyDataTypes = () => {
- return (
-
- Data types
-
- See an overview of the collected data in the last 2 weeks.
-
-
- );
-};
-
-export default StudyDataTypes;
diff --git a/src/pages/StudyOverview/Overview/StudyDataTypes/styles.ts b/src/pages/StudyOverview/Overview/StudyDataTypes/styles.ts
deleted file mode 100644
index ffdc68a..0000000
--- a/src/pages/StudyOverview/Overview/StudyDataTypes/styles.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Card, Typography } from "@mui/material";
-import { styled } from "@Utils/theme";
-
-export const StyledCard = styled(Card)({
- display: "flex",
- flexDirection: "column",
- padding: 24,
- height: 288,
- borderRadius: 16,
-});
-
-export const StyledTitle = styled(Typography)(({ theme }) => ({
- color: theme.palette.primary.main,
-}));
-
-export const StyledDescription = styled(Typography)(({ theme }) => ({
- color: theme.palette.text.secondary,
- marginTop: 8,
-}));
diff --git a/src/pages/StudyOverview/Overview/index.tsx b/src/pages/StudyOverview/Overview/index.tsx
index 95f1c16..53ff2ad 100644
--- a/src/pages/StudyOverview/Overview/index.tsx
+++ b/src/pages/StudyOverview/Overview/index.tsx
@@ -1,6 +1,6 @@
-import DeviceDeploymentStatus from "./DeviceDeploymentStatus";
-import InactiveParticipants from "./InactiveParticipants";
-import Participants from "./Participants";
+import DeploymentStatus from "./DeploymentStatus";
+import DeploymentsInProgress from "./DeploymentsInProgress";
+import InactiveDeployments from "./InactiveDeployments";
import Status from "./Status";
import StyledContainer from "./styles";
@@ -8,11 +8,9 @@ const Overview = () => {
return (
-
- {/* */}
-
-
- {/* */}
+
+
+
);
};
diff --git a/src/pages/StudyOverview/Overview/styles.ts b/src/pages/StudyOverview/Overview/styles.ts
index 0d4de86..2675953 100644
--- a/src/pages/StudyOverview/Overview/styles.ts
+++ b/src/pages/StudyOverview/Overview/styles.ts
@@ -2,7 +2,7 @@ import { styled } from "@Utils/theme";
const StyledContainer = styled("div")({
display: "grid",
- gridTemplateColumns: "repeat(auto-fit, minmax(350px, 1fr))",
+ gridTemplateColumns: "repeat(auto-fill, minmax(450px, 1fr))",
gap: 48,
});
diff --git a/src/pages/StudySettings/StudyData/index.tsx b/src/pages/StudySettings/StudyData/index.tsx
index a2deb85..c879aec 100644
--- a/src/pages/StudySettings/StudyData/index.tsx
+++ b/src/pages/StudySettings/StudyData/index.tsx
@@ -141,7 +141,7 @@ const StudyData = () => {
value={studyProtocolFormik.values.protocolId}
onChange={handleProtocolChange}
>
- {protocols.map((protocol) => (
+ {protocols?.map((protocol) => (