@3cr/viewer-browser 0.0.161 → 0.0.194

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/.circleci/config.yml +53 -0
  2. package/__tests__/index.spec.ts +31 -24
  3. package/components.d.ts +32 -0
  4. package/dist/Viewer3CR.js +44 -27
  5. package/dist/Viewer3CR.mjs +22734 -17747
  6. package/dist/Viewer3CR.umd.js +44 -27
  7. package/index.html +6 -8
  8. package/index.ts +9 -5
  9. package/package.json +5 -2
  10. package/playground/index.html +7 -10
  11. package/src/App.vue +12 -4
  12. package/src/__tests__/app.spec.ts +2 -13
  13. package/src/assets/magic_wand.svg +24 -0
  14. package/src/components/WebGL3DR.vue +53 -92
  15. package/src/components/__tests__/webgl3dr.spec.ts +29 -48
  16. package/src/{demo → components/demo}/DemoModal.vue +1 -1
  17. package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
  18. package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
  19. package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
  20. package/src/components/demo/__tests__/options.spec.ts +25 -0
  21. package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
  22. package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
  23. package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
  24. package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
  25. package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
  26. package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
  27. package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
  28. package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
  29. package/src/{demo → components/demo}/options.ts +18 -20
  30. package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
  31. package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
  32. package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
  33. package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
  34. package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
  35. package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
  36. package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
  37. package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
  38. package/src/components/loading/LoadingSpinner.vue +1 -1
  39. package/src/components/modal/ActionRail.vue +96 -0
  40. package/src/components/modal/AskAI.vue +250 -0
  41. package/src/components/modal/CloseViewerModal.vue +104 -0
  42. package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
  43. package/src/components/modal/ViewerActionRail.vue +123 -0
  44. package/src/components/modal/ViewerAnnotationModal.vue +115 -0
  45. package/src/components/modal/ViewerAnnotations.vue +283 -0
  46. package/src/components/modal/ViewerDisplaySettings.vue +102 -0
  47. package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
  48. package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
  49. package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
  50. package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
  51. package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
  52. package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
  53. package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
  54. package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
  55. package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
  56. package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
  57. package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
  58. package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
  59. package/src/components/modal/actions/Action.vue +40 -0
  60. package/src/components/modal/actions/Flip3dAction.vue +79 -0
  61. package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
  62. package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
  63. package/src/components/modal/actions/FullscreenAction.vue +47 -0
  64. package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
  65. package/src/components/modal/actions/PanAction.vue +78 -0
  66. package/src/components/modal/actions/ResetViewAction.vue +29 -0
  67. package/src/components/modal/actions/Rotate2dAction.vue +78 -0
  68. package/src/components/modal/actions/Slice3dAction.vue +71 -0
  69. package/src/components/modal/actions/ZoomAction.vue +70 -0
  70. package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
  71. package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
  72. package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
  73. package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
  74. package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
  75. package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
  76. package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
  77. package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
  78. package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
  79. package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
  80. package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
  81. package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
  82. package/src/components/modal/composables/useEventListener.ts +22 -0
  83. package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
  84. package/src/components/selectors/ValueSelector.vue +30 -33
  85. package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
  86. package/src/components/sliders/DoubleSliderSelector.vue +79 -71
  87. package/src/components/sliders/VerticalSliderSelector.vue +12 -17
  88. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
  89. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
  90. package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
  91. package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
  92. package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
  93. package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
  94. package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
  95. package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
  96. package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
  97. package/src/dataLayer/clamp.ts +9 -0
  98. package/src/dataLayer/eventHandlers.ts +26 -0
  99. package/src/dataLayer/patchDataOverlay.ts +101 -0
  100. package/src/dataLayer/scanState.ts +105 -26
  101. package/src/dataLayer/useViewer3cr.ts +7 -0
  102. package/src/dataLayer/viewer3cr.ts +410 -0
  103. package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
  104. package/src/helpers/__tests__/model-helper.spec.ts +44 -13
  105. package/src/helpers/layoutOverlayStyle.ts +16 -27
  106. package/src/helpers/modelHelper.ts +62 -10
  107. package/src/models/Callbacks.ts +2 -2
  108. package/src/models/LoadViewerOptions.ts +2 -0
  109. package/src/models/LoadViewerPayload.ts +1 -0
  110. package/src/notifications/notification.ts +3 -4
  111. package/src/plugins/vuetify.ts +5 -0
  112. package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
  113. package/src/services/gpt/gpt.service.ts +27 -0
  114. package/static/3cr-types-browser/index.ts +74 -0
  115. package/static/3cr-types-browser/types/Action.ts +6 -0
  116. package/static/3cr-types-browser/types/ActionData.ts +4 -0
  117. package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
  118. package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
  119. package/static/3cr-types-browser/types/CallToAction.ts +5 -0
  120. package/static/3cr-types-browser/types/ColourData.ts +7 -0
  121. package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
  122. package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
  123. package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
  124. package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
  125. package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
  126. package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
  127. package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
  128. package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
  129. package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
  130. package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
  131. package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
  132. package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
  133. package/static/3cr-types-browser/types/FlipValue.ts +7 -0
  134. package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
  135. package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
  136. package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
  137. package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
  138. package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
  139. package/static/3cr-types-browser/types/InteractionType.ts +8 -0
  140. package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
  141. package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
  142. package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
  143. package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
  144. package/static/3cr-types-browser/types/LayoutData.ts +7 -0
  145. package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
  146. package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
  147. package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
  148. package/static/3cr-types-browser/types/MovementData.ts +7 -0
  149. package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
  150. package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
  151. package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
  152. package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
  153. package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
  154. package/static/3cr-types-browser/types/Object.ts +1 -0
  155. package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
  156. package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
  157. package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
  158. package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
  159. package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
  160. package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
  161. package/static/3cr-types-browser/types/PositionData.ts +14 -0
  162. package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
  163. package/static/3cr-types-browser/types/RotationValue.ts +7 -0
  164. package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
  165. package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
  166. package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
  167. package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
  168. package/static/3cr-types-browser/types/ScanView.ts +6 -0
  169. package/static/3cr-types-browser/types/SettingsData.ts +12 -0
  170. package/static/3cr-types-browser/types/SlicerData.ts +9 -0
  171. package/static/3cr-types-browser/types/SliderValue.ts +4 -0
  172. package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
  173. package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
  174. package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
  175. package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
  176. package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
  177. package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
  178. package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
  179. package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
  180. package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
  181. package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
  182. package/test/helper.ts +10 -1
  183. package/test/setup.ts +13 -0
  184. package/tsconfig.json +1 -0
  185. package/vite.config.mts +1 -0
  186. package/coverage/3cr-viewer-browser/index.html +0 -116
  187. package/coverage/3cr-viewer-browser/index.ts.html +0 -199
  188. package/coverage/3cr-viewer-browser/src/App.vue.html +0 -316
  189. package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +0 -442
  190. package/coverage/3cr-viewer-browser/src/components/icons/index.html +0 -116
  191. package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +0 -148
  192. package/coverage/3cr-viewer-browser/src/components/index.html +0 -116
  193. package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +0 -622
  194. package/coverage/3cr-viewer-browser/src/components/loading/index.html +0 -116
  195. package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +0 -3118
  196. package/coverage/3cr-viewer-browser/src/components/modal/index.html +0 -116
  197. package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +0 -358
  198. package/coverage/3cr-viewer-browser/src/components/selectors/index.html +0 -116
  199. package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +0 -487
  200. package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +0 -358
  201. package/coverage/3cr-viewer-browser/src/components/sliders/index.html +0 -131
  202. package/coverage/3cr-viewer-browser/src/dataLayer/iconData.ts.html +0 -118
  203. package/coverage/3cr-viewer-browser/src/dataLayer/index.html +0 -146
  204. package/coverage/3cr-viewer-browser/src/dataLayer/payloadHandler.ts.html +0 -463
  205. package/coverage/3cr-viewer-browser/src/dataLayer/scanState.ts.html +0 -598
  206. package/coverage/3cr-viewer-browser/src/helpers/index.html +0 -146
  207. package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +0 -406
  208. package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +0 -412
  209. package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +0 -133
  210. package/coverage/3cr-viewer-browser/src/index.html +0 -131
  211. package/coverage/3cr-viewer-browser/src/main.ts.html +0 -124
  212. package/coverage/3cr-viewer-browser/src/models/LoadViewerOptions.ts.html +0 -166
  213. package/coverage/3cr-viewer-browser/src/models/index.html +0 -116
  214. package/coverage/3cr-viewer-browser/src/notifications/index.html +0 -116
  215. package/coverage/3cr-viewer-browser/src/notifications/notification.ts.html +0 -238
  216. package/coverage/3cr-viewer-browser/src/plugins/index.html +0 -131
  217. package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +0 -136
  218. package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +0 -220
  219. package/coverage/base.css +0 -224
  220. package/coverage/block-navigation.js +0 -87
  221. package/coverage/favicon.png +0 -0
  222. package/coverage/index.html +0 -296
  223. package/coverage/prettify.css +0 -1
  224. package/coverage/prettify.js +0 -2
  225. package/coverage/sort-arrow-sprite.png +0 -0
  226. package/coverage/sorter.js +0 -196
  227. package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
  228. package/src/dataLayer/payloadHandler.ts +0 -138
  229. /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <v-dialog width="600" v-model:model-value="modalState">
3
+ <v-card theme="dark" class="motif-background">
4
+ <v-card-title class="text-center font-weight-bold">
5
+ Enable Cloud Storage
6
+ </v-card-title>
7
+ <v-card-text>
8
+ <p style="text-align: center;">Enable annual storage for just <b>5 credits</b>.</p>
9
+ <br />
10
+ <div>
11
+ <v-row no-gutters v-for="(benefit, idx) in benefits" :key="idx">
12
+ <v-col class="d-flex justify-end" cols="2">
13
+ <v-icon class="mr-2" color="success" icon="check" />
14
+ </v-col>
15
+ <v-col>
16
+ {{ benefit }}
17
+ </v-col>
18
+ </v-row>
19
+ </div>
20
+ </v-card-text>
21
+ <v-card-actions>
22
+ <v-btn data-testid="close" color="red" @click="modalState = false">
23
+ Continue with Demo
24
+ </v-btn>
25
+ <v-spacer />
26
+ <v-btn data-testid="confirm" variant="flat" color="success" @click="openUrl(PRICING_URL)">
27
+ Enable Cloud Storage
28
+ </v-btn>
29
+ </v-card-actions>
30
+ </v-card>
31
+ </v-dialog>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { computed } from 'vue';
36
+ import { PRICING_URL, openUrl } from '@/components/demo/options';
37
+
38
+ interface Props {
39
+ modal?: boolean;
40
+ }
41
+
42
+ type Emits = {
43
+ 'update:modal': [value: boolean];
44
+ };
45
+
46
+ const props = defineProps<Props>();
47
+
48
+ const emit = defineEmits<Emits>();
49
+
50
+ const benefits = [
51
+ 'Secure storage',
52
+ 'Access your scan from any device',
53
+ 'Keep your medical imaging records at your fingertips'
54
+ ];
55
+
56
+ const modalState = computed({
57
+ get(): boolean {
58
+ return props.modal;
59
+ },
60
+ set(value: boolean): void {
61
+ emit('update:modal', value);
62
+ }
63
+ });
64
+ </script>
@@ -3,8 +3,8 @@ import { computed } from "vue";
3
3
  import {
4
4
  demoLicenceSubtitles,
5
5
  demoLicenceTitle,
6
- openUrl,
7
- } from "@/demo/options";
6
+ openUrl, SUPPORT_URL,
7
+ } from "@/components/demo/options";
8
8
 
9
9
  export interface Props {
10
10
  modal: boolean;
@@ -47,14 +47,15 @@ const modalState = computed({
47
47
  >{{ subtitle }}
48
48
  </v-card-text>
49
49
  <v-card-actions class="mt-4">
50
- <v-btn color="red" @click="modalState = false" :disabled="!isModalOpen">
50
+ <v-btn data-testid="close" color="red" @click="modalState = false" :disabled="!isModalOpen">
51
51
  Continue with Demo
52
52
  </v-btn>
53
53
  <v-spacer />
54
54
  <v-btn
55
+ data-testid="confirm"
55
56
  variant="flat"
56
57
  color="success"
57
- @click="openUrl('mailto:ecooper@singular.health')"
58
+ @click="openUrl(SUPPORT_URL)"
58
59
  >
59
60
  Enquire about technical integration
60
61
  </v-btn>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from "vue";
3
- import { openUrl } from "@/demo/options";
3
+ import {openUrl, PRICING_URL} from "@/components/demo/options";
4
4
 
5
5
  export interface Props {
6
6
  modal: boolean;
@@ -116,11 +116,12 @@ function isValid() {
116
116
  </v-alert>
117
117
  </v-card-text>
118
118
  <v-card-actions>
119
- <v-btn color="red" @click="modalState = false">
119
+ <v-btn data-testid="close" color="red" @click="modalState = false">
120
120
  Continue with Demo
121
121
  </v-btn>
122
122
  <v-spacer />
123
123
  <v-btn
124
+ data-testid="confirm"
124
125
  variant="flat"
125
126
  color="success"
126
127
  @click="
@@ -155,12 +156,13 @@ function isValid() {
155
156
  please select below
156
157
  </v-card-text>
157
158
  <v-card-actions>
158
- <v-btn color="red" @click="step2 = false"> Continue with Demo </v-btn>
159
+ <v-btn data-testid="close" color="red" @click="step2 = false"> Continue with Demo </v-btn>
159
160
  <v-spacer />
160
161
  <v-btn
162
+ data-testid="confirm"
161
163
  variant="flat"
162
164
  color="success"
163
- @click="openUrl('https://3dicomviewer.com/pricing')"
165
+ @click="openUrl(PRICING_URL)"
164
166
  >
165
167
  I want to view my own scan
166
168
  </v-btn>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from "vue";
3
- import { openUrl } from "@/demo/options";
3
+ import {openUrl, PRICING_URL} from "@/components/demo/options";
4
4
 
5
5
  export interface Props {
6
6
  modal: boolean;
@@ -37,24 +37,25 @@ const modalState = computed({
37
37
  </v-card-title>
38
38
  <v-card-text class="text-justify">
39
39
  <img
40
- src="../../assets/images/apple-qr.svg"
40
+ src="../../../assets/images/apple-qr.svg"
41
41
  alt="logo-home"
42
42
  width="47%"
43
43
  class="mr-auto rounded-lg"
44
44
  />
45
45
  <img
46
- src="../../assets/images/android-qr.svg"
46
+ src="../../../assets/images/android-qr.svg"
47
47
  alt="logo-home"
48
48
  width="47%"
49
49
  class="ml-auto rounded-lg float-right"
50
50
  />
51
51
  </v-card-text>
52
52
  <v-card-actions>
53
- <v-btn color="red" @click="modalState = false">
53
+ <v-btn data-testid="close-1" color="red" @click="modalState = false">
54
54
  Continue with Demo
55
55
  </v-btn>
56
56
  <v-spacer />
57
57
  <v-btn
58
+ data-testid="confirm-1"
58
59
  variant="flat"
59
60
  color="success"
60
61
  @click="
@@ -83,12 +84,13 @@ const modalState = computed({
83
84
  Tomography (PET) scans.
84
85
  </v-card-text>
85
86
  <v-card-actions>
86
- <v-btn color="red" @click="step2 = false"> Continue with Demo </v-btn>
87
+ <v-btn data-testid="close-2" color="red" @click="step2 = false"> Continue with Demo </v-btn>
87
88
  <v-spacer />
88
89
  <v-btn
90
+ data-testid="confirm-2"
89
91
  variant="flat"
90
92
  color="success"
91
- @click="openUrl('https://3dicomviewer.com/pricing')"
93
+ @click="openUrl(PRICING_URL)"
92
94
  >
93
95
  I want to view my own scan
94
96
  </v-btn>
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import DemoLicenceEnableCloudStorageModal from "@/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue";
4
+ import { PRICING_URL } from "../../options";
5
+
6
+ describe('DemoLicenceEnableCloudStorageModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoLicenceEnableCloudStorageModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoLicenceEnableCloudStorageModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should open pricing url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoLicenceEnableCloudStorageModal, props);
26
+ const button = wrapper.findComponent('[data-testid="confirm"]');
27
+ await button.trigger('click');
28
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
29
+ vi.restoreAllMocks();
30
+ });
31
+
32
+ it('should open via input', async () => {
33
+ const props = { modal: false };
34
+ const wrapper = mountVuetify(DemoLicenceEnableCloudStorageModal, props);
35
+ await openAllModals(wrapper);
36
+ });
37
+ });
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { SUPPORT_URL } from "@/components/demo/options";
4
+ import DemoLicenceInfoModal from "@/components/demo/licence/DemoLicenceInfoModal.vue";
5
+
6
+ describe('DemoLicenceInfoModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true, isModalOpen: true };
9
+ const wrapper = mountVuetify(DemoLicenceInfoModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true, isModalOpen: true };
15
+ const wrapper = mountVuetify(DemoLicenceInfoModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should open support url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoLicenceInfoModal, props);
26
+ const button = wrapper.findComponent('[data-testid="confirm"]');
27
+ await button.trigger('click');
28
+ expect(spy).toHaveBeenCalledWith(SUPPORT_URL, '_self');
29
+ vi.restoreAllMocks();
30
+ });
31
+
32
+ it('should open via input', async () => {
33
+ const props = { modal: false, isModalOpen: true };
34
+ const wrapper = mountVuetify(DemoLicenceInfoModal, props);
35
+ await openAllModals(wrapper);
36
+ });
37
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { VTextField } from "vuetify/components";
4
+ import DemoLicenceSendToPartyModal from "@/components/demo/licence/DemoLicenceSendToPartyModal.vue";
5
+
6
+ describe('DemoLicenceSendToPartyModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoLicenceSendToPartyModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should enter email', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoLicenceSendToPartyModal, props);
16
+ const input = wrapper.getComponent(VTextField);
17
+ await input.setValue('example@singular.health');
18
+ await input.trigger('keyup.enter');
19
+ const confirm = wrapper.findComponent('[data-testid="confirm"]');
20
+ await confirm.trigger('click');
21
+ });
22
+
23
+ it('should close modal', async () => {
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoLicenceSendToPartyModal, props);
26
+ const close = wrapper.findComponent('[data-testid="close"]');
27
+ await close.trigger('click');
28
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
29
+ });
30
+
31
+ it('should open via input', async () => {
32
+ const props = { modal: false };
33
+ const wrapper = mountVuetify(DemoLicenceSendToPartyModal, props);
34
+ await openAllModals(wrapper);
35
+ });
36
+ });
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { PRICING_URL } from "../../options";
4
+ import DemoLicenceShareToMobileModal from "@/components/demo/licence/DemoLicenceShareToMobileModal.vue";
5
+
6
+ describe('DemoLicenceShareToMobileModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoLicenceShareToMobileModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoLicenceShareToMobileModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close-1"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should go to next step and open url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoLicenceShareToMobileModal, props);
26
+ const confirm1 = wrapper.findComponent('[data-testid="confirm-1"]');
27
+ await confirm1.trigger('click');
28
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
29
+ const confirm2 = wrapper.findComponent('[data-testid="confirm-2"]');
30
+ await confirm2.trigger('click');
31
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
32
+ vi.restoreAllMocks();
33
+ });
34
+
35
+ it('should go to next step and close', async () => {
36
+ const props = { modal: true };
37
+ const wrapper = mountVuetify(DemoLicenceShareToMobileModal, props);
38
+ const confirm1 = wrapper.findComponent('[data-testid="confirm-1"]');
39
+ await confirm1.trigger('click');
40
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
41
+ const close2 = wrapper.findComponent('[data-testid="close-2"]');
42
+ await close2.trigger('click');
43
+ expect(wrapper.text()).toBe('');
44
+ });
45
+
46
+ it('should open via input', async () => {
47
+ const props = { modal: false };
48
+ const wrapper = mountVuetify(DemoLicenceShareToMobileModal, props);
49
+ await openAllModals(wrapper);
50
+ });
51
+ });
@@ -1,7 +1,9 @@
1
1
  import { computed, ref, unref, watch } from "vue";
2
2
  import { LoadViewerOptions } from "@/models/LoadViewerOptions";
3
3
  import { LoadViewerPayload } from "@/models/LoadViewerPayload";
4
- import { ViewerAsyncCallback, ViewerCallback } from "@/models/Callbacks";
4
+
5
+ export const PRICING_URL = 'https://3dicomviewer.com/pricing';
6
+ export const SUPPORT_URL = 'https://3dicomviewer.com/contact-3dicom';
5
7
 
6
8
  export function checkIsDemo(payload: LoadViewerPayload) {
7
9
  isDemo.value =
@@ -13,13 +15,16 @@ export const isDemo = ref<boolean>(false);
13
15
  export const m_demo = ref<boolean>(false);
14
16
  export const m_demoLicenceShareToMobile = ref<boolean>(false);
15
17
  export const m_demoLicenceSendToParty = ref<boolean>(false);
18
+ export const m_demoLicenseEnableCloudStorage = ref<boolean>(false);
16
19
  export const m_demoPatient = ref<boolean>(false);
17
20
  export const m_demoPatientShareToMobile = ref<boolean>(false);
18
21
  export const m_demoPatientSendToParty = ref<boolean>(false);
22
+ export const m_demoPatientEnableCloudStorage = ref<boolean>(false);
19
23
  export const demoPatientTitle = ref<string>("");
20
24
  export const demoPatientSubtitles = ref<Array<string>>([]);
21
25
  export const demoLicenceTitle = ref<string>("");
22
26
  export const demoLicenceSubtitles = ref<Array<string>>([]);
27
+
23
28
  export const demoLicenceOptions: LoadViewerOptions = {
24
29
  OnClosePopup: () => Promise.resolve(),
25
30
  OnExitViewer: () => {
@@ -66,6 +71,9 @@ export const demoLicenceOptions: LoadViewerOptions = {
66
71
  OnShareToMobile: () => {
67
72
  m_demoLicenceShareToMobile.value = true;
68
73
  },
74
+ OnEnableCloudStorage: () => {
75
+ m_demoLicenseEnableCloudStorage.value = true;
76
+ },
69
77
  OnShare: () => {
70
78
  //TODO:
71
79
  // m_demo.value = true;
@@ -128,6 +136,9 @@ export const demoPatientOptions: LoadViewerOptions = {
128
136
  OnShareToMobile: () => {
129
137
  m_demoPatientShareToMobile.value = true;
130
138
  },
139
+ OnEnableCloudStorage: () => {
140
+ m_demoPatientEnableCloudStorage.value = true;
141
+ },
131
142
  OnShare: () => {
132
143
  //TODO:
133
144
  // m_demoPatient.value = true;
@@ -146,28 +157,15 @@ export const demoType = computed(() => {
146
157
  return optionalTypes.includes(type) ? type : optionalTypes[0];
147
158
  });
148
159
 
149
- export function executeDemoOption(key: keyof LoadViewerOptions) {
150
- if (unref(demoType) === "licence") {
151
- const functionToExecute = demoLicenceOptions[key];
152
- if (functionToExecute !== undefined) {
153
- functionToExecute();
154
- }
155
- } else if (unref(demoType) === "patient") {
156
- const functionToExecute = demoPatientOptions[key];
157
- if (functionToExecute !== undefined) {
158
- functionToExecute();
159
- }
160
- }
161
- }
162
- export function getDemoOption(
163
- key: keyof LoadViewerOptions
164
- ): ViewerCallback | ViewerAsyncCallback | undefined {
160
+ export const demoOptions = computed(() => {
165
161
  if (unref(demoType) === "licence") {
166
- return demoLicenceOptions[key];
162
+ return demoLicenceOptions;
167
163
  } else if (unref(demoType) === "patient") {
168
- return demoPatientOptions[key];
164
+ return demoPatientOptions;
165
+ } else {
166
+ throw new Error('Unknown demo type');
169
167
  }
170
- }
168
+ });
171
169
 
172
170
  export function openUrl(url: string, target: string = "_self") {
173
171
  window.open(url, target);
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <v-dialog width="600" v-model:model-value="modalState">
3
+ <v-card theme="dark" class="motif-background">
4
+ <v-card-title class="text-center font-weight-bold">
5
+ Enable Cloud Storage
6
+ </v-card-title>
7
+ <v-card-text>
8
+ <p style="text-align: center;">Enable annual storage for just <b>5 credits</b>.</p>
9
+ <br />
10
+ <div>
11
+ <v-row no-gutters v-for="(benefit, idx) in benefits" :key="idx">
12
+ <v-col class="d-flex justify-end" cols="2">
13
+ <v-icon class="mr-2" color="success" icon="check" />
14
+ </v-col>
15
+ <v-col>
16
+ {{ benefit }}
17
+ </v-col>
18
+ </v-row>
19
+ </div>
20
+ </v-card-text>
21
+ <v-card-actions>
22
+ <v-btn data-testid="close" color="red" @click="modalState = false">
23
+ Continue with Demo
24
+ </v-btn>
25
+ <v-spacer />
26
+ <v-btn data-testid="confirm" variant="flat" color="success" @click="openUrl(PRICING_URL)">
27
+ Enable Cloud Storage
28
+ </v-btn>
29
+ </v-card-actions>
30
+ </v-card>
31
+ </v-dialog>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { computed } from 'vue';
36
+ import { PRICING_URL, openUrl } from '@/components/demo/options';
37
+
38
+ interface Props {
39
+ modal?: boolean;
40
+ }
41
+
42
+ type Emits = {
43
+ 'update:modal': [value: boolean];
44
+ };
45
+
46
+ const props = defineProps<Props>();
47
+
48
+ const emit = defineEmits<Emits>();
49
+
50
+ const benefits = [
51
+ 'Secure storage',
52
+ 'Access your scan from any device',
53
+ 'Keep your medical imaging records at your fingertips'
54
+ ];
55
+
56
+ const modalState = computed({
57
+ get(): boolean {
58
+ return props.modal;
59
+ },
60
+ set(value: boolean): void {
61
+ emit('update:modal', value);
62
+ }
63
+ });
64
+ </script>
@@ -3,8 +3,8 @@ import { computed } from "vue";
3
3
  import {
4
4
  demoPatientSubtitles,
5
5
  demoPatientTitle,
6
- openUrl,
7
- } from "@/demo/options";
6
+ openUrl, PRICING_URL,
7
+ } from "@/components/demo/options";
8
8
 
9
9
  export interface Props {
10
10
  modal: boolean;
@@ -47,14 +47,15 @@ const modalState = computed({
47
47
  >{{ subtitle }}
48
48
  </v-card-text>
49
49
  <v-card-actions class="mt-4">
50
- <v-btn color="red" @click="modalState = false" :disabled="!isModalOpen">
50
+ <v-btn data-testid="close" color="red" @click="modalState = false" :disabled="!isModalOpen">
51
51
  Continue with Demo
52
52
  </v-btn>
53
53
  <v-spacer />
54
54
  <v-btn
55
+ data-testid="confirm"
55
56
  variant="flat"
56
57
  color="success"
57
- @click="openUrl('https://3dicomviewer.com/pricing')"
58
+ @click="openUrl(PRICING_URL)"
58
59
  >
59
60
  Purchase 3Dicom Patient
60
61
  </v-btn>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from "vue";
3
- import { openUrl } from "@/demo/options";
3
+ import {openUrl, PRICING_URL} from "@/components/demo/options";
4
4
 
5
5
  export interface Props {
6
6
  modal: boolean;
@@ -116,11 +116,12 @@ function isValid() {
116
116
  </v-alert>
117
117
  </v-card-text>
118
118
  <v-card-actions>
119
- <v-btn color="red" @click="modalState = false">
119
+ <v-btn data-testid="close" color="red" @click="modalState = false">
120
120
  Continue with Demo
121
121
  </v-btn>
122
122
  <v-spacer />
123
123
  <v-btn
124
+ data-testid="confirm"
124
125
  variant="flat"
125
126
  color="success"
126
127
  @click="
@@ -160,7 +161,7 @@ function isValid() {
160
161
  <v-btn
161
162
  variant="flat"
162
163
  color="success"
163
- @click="openUrl('https://3dicomviewer.com/pricing')"
164
+ @click="openUrl(PRICING_URL)"
164
165
  >
165
166
  I want to view my own scan
166
167
  </v-btn>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref } from "vue";
3
- import { openUrl } from "@/demo/options";
3
+ import {openUrl, PRICING_URL} from "@/components/demo/options";
4
4
 
5
5
  export interface Props {
6
6
  modal: boolean;
@@ -37,24 +37,25 @@ const modalState = computed({
37
37
  </v-card-title>
38
38
  <v-card-text class="text-justify">
39
39
  <img
40
- src="../../assets/images/apple-qr.svg"
40
+ src="../../../assets/images/apple-qr.svg"
41
41
  alt="logo-home"
42
42
  width="47%"
43
43
  class="mr-auto rounded-lg"
44
44
  />
45
45
  <img
46
- src="../../assets/images/android-qr.svg"
46
+ src="../../../assets/images/android-qr.svg"
47
47
  alt="logo-home"
48
48
  width="47%"
49
49
  class="ml-auto rounded-lg float-right"
50
50
  />
51
51
  </v-card-text>
52
52
  <v-card-actions>
53
- <v-btn color="red" @click="modalState = false">
53
+ <v-btn data-testid="close-1" color="red" @click="modalState = false">
54
54
  Continue with Demo
55
55
  </v-btn>
56
56
  <v-spacer />
57
57
  <v-btn
58
+ data-testid="confirm-1"
58
59
  variant="flat"
59
60
  color="success"
60
61
  @click="
@@ -83,12 +84,13 @@ const modalState = computed({
83
84
  Tomography (PET) scans.
84
85
  </v-card-text>
85
86
  <v-card-actions>
86
- <v-btn color="red" @click="step2 = false"> Continue with Demo </v-btn>
87
+ <v-btn data-testid="close-2" color="red" @click="step2 = false"> Continue with Demo </v-btn>
87
88
  <v-spacer />
88
89
  <v-btn
90
+ data-testid="confirm-2"
89
91
  variant="flat"
90
92
  color="success"
91
- @click="openUrl('https://3dicomviewer.com/pricing')"
93
+ @click="openUrl(PRICING_URL)"
92
94
  >
93
95
  I want to view my own scan
94
96
  </v-btn>
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import DemoPatientEnableCloudStorageModal from "@/components/demo/patient/DemoPatientEnableCloudStorageModal.vue";
4
+ import { PRICING_URL } from "../../options";
5
+
6
+ describe('DemoPatientEnableCloudStorageModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoPatientEnableCloudStorageModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoPatientEnableCloudStorageModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should open pricing url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoPatientEnableCloudStorageModal, props);
26
+ const button = wrapper.findComponent('[data-testid="confirm"]');
27
+ await button.trigger('click');
28
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
29
+ vi.restoreAllMocks();
30
+ });
31
+
32
+ it('should open via input', async () => {
33
+ const props = { modal: false };
34
+ const wrapper = mountVuetify(DemoPatientEnableCloudStorageModal, props);
35
+ await openAllModals(wrapper);
36
+ });
37
+ });