@fdm-monster/client-next 0.0.1

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 (205) hide show
  1. package/.all-contributorsrc +57 -0
  2. package/.browserslistrc +4 -0
  3. package/.editorconfig +5 -0
  4. package/.env +1 -0
  5. package/.eslintrc-auto-import.json +73 -0
  6. package/.eslintrc.js +126 -0
  7. package/.github/FUNDING.yml +3 -0
  8. package/.github/workflows/release-client.yml +94 -0
  9. package/.github/workflows/vue-publish.yml +26 -0
  10. package/.prettierignore +15 -0
  11. package/.prettierrc.cjs +7 -0
  12. package/.whitesource +12 -0
  13. package/.yarn/releases/yarn-4.5.1.cjs +934 -0
  14. package/.yarnrc.yml +3 -0
  15. package/CODE_OF_CONDUCT.md +46 -0
  16. package/README.md +93 -0
  17. package/RELEASE_NOTES.MD +11 -0
  18. package/index.html +16 -0
  19. package/package.default.json +42 -0
  20. package/package.json +26 -0
  21. package/public/favicon.ico +0 -0
  22. package/public/img/DavidZwart.jpg +0 -0
  23. package/public/img/OIG.JYDC2RaWdz7g9.jpg +0 -0
  24. package/public/img/OIG.jpg +0 -0
  25. package/public/img/icons/android-chrome-192x192.png +0 -0
  26. package/public/img/icons/android-chrome-256x256.png +0 -0
  27. package/public/img/icons/android-chrome-384x384.png +0 -0
  28. package/public/img/icons/android-chrome-512x512.png +0 -0
  29. package/public/img/icons/favicon.svg +1 -0
  30. package/public/img/logo.png +0 -0
  31. package/public/img/logo.svg +1 -0
  32. package/public/img/manifest.webmanifest +33 -0
  33. package/public/img/octoprint-tentacle.svg +144 -0
  34. package/public/img/thumbail_unknown.jpg +0 -0
  35. package/public/img/vbanner.jpg +0 -0
  36. package/public/index.html +17 -0
  37. package/public/robots.txt +2 -0
  38. package/renovate.json +30 -0
  39. package/src/App.vue +60 -0
  40. package/src/AppLoader.vue +383 -0
  41. package/src/assets/adjectives.json +1468 -0
  42. package/src/assets/android-chrome-192x192.png +0 -0
  43. package/src/assets/logo.png +0 -0
  44. package/src/assets/logo.svg +6 -0
  45. package/src/assets/nouns.json +4309 -0
  46. package/src/auto-imports.d.ts +139 -0
  47. package/src/backend/app.service.ts +39 -0
  48. package/src/backend/auth.service.ts +56 -0
  49. package/src/backend/base.service.ts +57 -0
  50. package/src/backend/batch.service.ts +37 -0
  51. package/src/backend/camera-stream.service.ts +33 -0
  52. package/src/backend/custom-gcode.service.ts +11 -0
  53. package/src/backend/dto/octoprint-settings.dto.ts +168 -0
  54. package/src/backend/first-time-setup.service.ts +17 -0
  55. package/src/backend/floor.service.ts +84 -0
  56. package/src/backend/index.ts +4 -0
  57. package/src/backend/print-completions.service.ts +11 -0
  58. package/src/backend/printer-file.service.ts +91 -0
  59. package/src/backend/printer-group.service.ts +62 -0
  60. package/src/backend/printer-job.service.ts +20 -0
  61. package/src/backend/printer-settings.service.ts +28 -0
  62. package/src/backend/printers.service.ts +136 -0
  63. package/src/backend/server-private.service.ts +55 -0
  64. package/src/backend/server.api.ts +132 -0
  65. package/src/backend/settings.service.ts +85 -0
  66. package/src/backend/user.service.ts +51 -0
  67. package/src/components/AboutHelp/AboutView.vue +164 -0
  68. package/src/components/CameraGrid/CameraGridView.vue +111 -0
  69. package/src/components/FirstTimeSetup/FirstTimeSetupView.vue +354 -0
  70. package/src/components/Generic/Actions/PrinterConnectionAction.vue +56 -0
  71. package/src/components/Generic/Actions/PrinterCreateAction.vue +22 -0
  72. package/src/components/Generic/Actions/PrinterDeleteAction.vue +29 -0
  73. package/src/components/Generic/Actions/PrinterQuickStopAction.vue +35 -0
  74. package/src/components/Generic/Actions/PrinterSettingsAction.vue +35 -0
  75. package/src/components/Generic/Actions/PrinterUrlAction.vue +24 -0
  76. package/src/components/Generic/Actions/RefreshFilesAction.vue +50 -0
  77. package/src/components/Generic/Actions/SyncPrinterNameAction.vue +36 -0
  78. package/src/components/Generic/Dialogs/AddOrUpdateCameraStreamDialog.vue +131 -0
  79. package/src/components/Generic/Dialogs/AddOrUpdateFloorDialog.vue +141 -0
  80. package/src/components/Generic/Dialogs/AddOrUpdatePrinterDialog.vue +303 -0
  81. package/src/components/Generic/Dialogs/BaseDialog.vue +81 -0
  82. package/src/components/Generic/Dialogs/BatchJsonCreateDialog.vue +109 -0
  83. package/src/components/Generic/Dialogs/BatchReprintDialog.vue +190 -0
  84. package/src/components/Generic/Dialogs/PrinterChecksPanel.vue +37 -0
  85. package/src/components/Generic/Dialogs/PrinterControlDialog.vue +202 -0
  86. package/src/components/Generic/Dialogs/PrinterMaintenanceDialog.vue +130 -0
  87. package/src/components/Generic/Dialogs/YamlImportExportDialog.vue +186 -0
  88. package/src/components/Generic/Dialogs/dialog.constants.ts +19 -0
  89. package/src/components/Generic/FileExplorerSideNav.vue +734 -0
  90. package/src/components/Generic/Loaders/GridLoader.vue +68 -0
  91. package/src/components/Generic/NavigationDrawer.vue +69 -0
  92. package/src/components/Generic/PrintJobsMenu.vue +148 -0
  93. package/src/components/Generic/Snackbars/AppErrorSnackbar.vue +64 -0
  94. package/src/components/Generic/Snackbars/AppInfoSnackbar.vue +63 -0
  95. package/src/components/Generic/Snackbars/AppProgressSnackbar.vue +158 -0
  96. package/src/components/Generic/Vuetify/TooltipButton.vue +47 -0
  97. package/src/components/HelpOverlay/HelpOverlay.vue +57 -0
  98. package/src/components/Login/LoginForm.vue +206 -0
  99. package/src/components/Login/LoginView.spec.ts +64 -0
  100. package/src/components/Login/LoginView.vue +65 -0
  101. package/src/components/Login/Logo.vue +13 -0
  102. package/src/components/Login/PermissionDenied.vue +109 -0
  103. package/src/components/Login/RegistrationForm.vue +207 -0
  104. package/src/components/Login/RegistrationView.vue +17 -0
  105. package/src/components/Login/__snapshots__/LoginView.spec.ts.snap +1051 -0
  106. package/src/components/NotFound/NotFoundView.vue +39 -0
  107. package/src/components/PrintStatistics/PrintStatistics.vue +168 -0
  108. package/src/components/PrintStatistics/PrintStatisticsView.vue +15 -0
  109. package/src/components/PrinterGrid/HomeToolbar.vue +90 -0
  110. package/src/components/PrinterGrid/PrinterGrid.vue +164 -0
  111. package/src/components/PrinterGrid/PrinterGridTile.vue +438 -0
  112. package/src/components/PrinterGrid/PrinterGridView.vue +210 -0
  113. package/src/components/PrinterList/FileControlList.vue +40 -0
  114. package/src/components/PrinterList/PrinterDetails.vue +91 -0
  115. package/src/components/PrinterList/PrintersView.vue +492 -0
  116. package/src/components/Settings/AccountSettings.vue +163 -0
  117. package/src/components/Settings/DiagnosticsSettings.vue +137 -0
  118. package/src/components/Settings/EmergencyCommands.vue +265 -0
  119. package/src/components/Settings/FloorSettings.vue +276 -0
  120. package/src/components/Settings/GridSettings.vue +127 -0
  121. package/src/components/Settings/OctoPrintSettings.vue +188 -0
  122. package/src/components/Settings/ServerProtectionSettings.vue +370 -0
  123. package/src/components/Settings/SettingsView.vue +73 -0
  124. package/src/components/Settings/SoftwareUpgradeSettings.vue +297 -0
  125. package/src/components/Settings/UserManagementSettings.vue +257 -0
  126. package/src/components/TopBar.vue +147 -0
  127. package/src/components.d.ts +70 -0
  128. package/src/directives/file-upload.directive.ts +117 -0
  129. package/src/directives/printer-drop-position.directive.ts +92 -0
  130. package/src/env.d.ts +6 -0
  131. package/src/main.ts +76 -0
  132. package/src/models/batch/reprint.dto.ts +79 -0
  133. package/src/models/batch.model.ts +11 -0
  134. package/src/models/camera-streams/camera-stream.ts +19 -0
  135. package/src/models/floors/floor.model.ts +30 -0
  136. package/src/models/octoprint/connection-options.model.ts +8 -0
  137. package/src/models/plugins/firmware-updates/prusa-firmware-release.model.ts +57 -0
  138. package/src/models/print-completions/print-completions.model.ts +49 -0
  139. package/src/models/printers/crud/create-printer.model.ts +26 -0
  140. package/src/models/printers/file-upload-commands.model.ts +4 -0
  141. package/src/models/printers/gcode/gcode-analysis.model.ts +30 -0
  142. package/src/models/printers/printer-current-job.model.ts +90 -0
  143. package/src/models/printers/printer-file.model.ts +48 -0
  144. package/src/models/printers/printer.model.ts +18 -0
  145. package/src/models/server/client-releases.model.ts +27 -0
  146. package/src/models/server/export-yaml.model.ts +11 -0
  147. package/src/models/server/features.model.ts +37 -0
  148. package/src/models/server/github-rate-limit.model.ts +21 -0
  149. package/src/models/server/version.model.ts +14 -0
  150. package/src/models/settings/printer-file-clean-settings.model.ts +5 -0
  151. package/src/models/settings/server-settings.dto.ts +19 -0
  152. package/src/models/settings/settings.model.ts +57 -0
  153. package/src/models/socketio-messages/socketio-message.model.ts +53 -0
  154. package/src/models/uploads/queued-upload.model.ts +12 -0
  155. package/src/models/user.model.ts +15 -0
  156. package/src/plugins/README.md +3 -0
  157. package/src/plugins/index.ts +17 -0
  158. package/src/plugins/vuetify.ts +53 -0
  159. package/src/router/index.ts +192 -0
  160. package/src/router/route-names.ts +14 -0
  161. package/src/router/utils.ts +23 -0
  162. package/src/shared/alert.events.ts +14 -0
  163. package/src/shared/app.constants.ts +23 -0
  164. package/src/shared/auth.constants.ts +34 -0
  165. package/src/shared/dialog.composable.ts +41 -0
  166. package/src/shared/drag.constants.ts +19 -0
  167. package/src/shared/experimental.constants.ts +1 -0
  168. package/src/shared/http-client.ts +162 -0
  169. package/src/shared/noun-adjectives.data.ts +24 -0
  170. package/src/shared/printer-grid.constants.ts +5 -0
  171. package/src/shared/printer-state.constants.ts +194 -0
  172. package/src/shared/snackbar.composable.ts +66 -0
  173. package/src/shared/socketio.service.ts +104 -0
  174. package/src/store/auth.store.ts +255 -0
  175. package/src/store/connection.store.ts +66 -0
  176. package/src/store/dialog.store.ts +114 -0
  177. package/src/store/features.store.ts +57 -0
  178. package/src/store/floor.store.ts +173 -0
  179. package/src/store/grid.store.ts +10 -0
  180. package/src/store/index.ts +4 -0
  181. package/src/store/printer-state.store.ts +246 -0
  182. package/src/store/printer.store.ts +236 -0
  183. package/src/store/profile.store.ts +25 -0
  184. package/src/store/settings.store.ts +64 -0
  185. package/src/store/test-printer.store.ts +70 -0
  186. package/src/store/uploads.store.ts +75 -0
  187. package/src/styles/README.md +3 -0
  188. package/src/styles/settings.scss +10 -0
  189. package/src/types/global.d.ts +15 -0
  190. package/src/utils/array.utils.ts +15 -0
  191. package/src/utils/date.utils.ts +5 -0
  192. package/src/utils/download-file.util.ts +25 -0
  193. package/src/utils/error.utils.ts +3 -0
  194. package/src/utils/file-size.util.ts +11 -0
  195. package/src/utils/id.type.ts +1 -0
  196. package/src/utils/sentry.util.ts +8 -0
  197. package/src/utils/test.util.ts +30 -0
  198. package/src/utils/time.utils.ts +2 -0
  199. package/src/utils/uploads-state.utils.ts +58 -0
  200. package/src/utils/validation.utils.ts +14 -0
  201. package/src/vite-env.d.ts +7 -0
  202. package/test/setup-axios-mock.ts +15 -0
  203. package/tsconfig.json +47 -0
  204. package/tsconfig.node.json +9 -0
  205. package/vite.config.mts +106 -0
@@ -0,0 +1,438 @@
1
+ <template>
2
+ <div v-drop-printer-position="{ x, y, printerSet: printer }">
3
+ <v-card
4
+ v-drop-upload="{ printers: [printer] }"
5
+ :class="{
6
+ 'tile-large': largeTilesEnabled,
7
+ 'tile-selected': selected,
8
+ 'tile-unselected': unselected,
9
+ 'tile-setup': printer
10
+ }"
11
+ :disabled="!printer"
12
+ :style="{
13
+ 'background-color':
14
+ !gridStore.gridEditMode || !printer
15
+ ? printerStateColor
16
+ : 'rgba(1,1,1,0)'
17
+ }"
18
+ class="tile fill-height"
19
+ variant="outlined"
20
+ tile
21
+ @click="selectOrUnplacePrinter()"
22
+ >
23
+ <v-icon
24
+ v-if="printerState?.text.includes('API')"
25
+ color="primary"
26
+ size="70"
27
+ style="opacity: 0.2; position: absolute; top: 5%; right: 10%"
28
+ >
29
+ wifi_off
30
+ </v-icon>
31
+ <v-icon
32
+ v-if="printerState?.text.includes('USB')"
33
+ color="primary"
34
+ size="70"
35
+ style="opacity: 0.2; position: absolute; top: 5%; right: 10%"
36
+ >
37
+ usb_off
38
+ </v-icon>
39
+ <v-container
40
+ v-if="printer?.id"
41
+ class="tile-inner fill-height"
42
+ >
43
+ <small class="small-resized-font">
44
+ {{ printer?.name }}
45
+ </small>
46
+ <v-menu offset-y>
47
+ <template #activator="{ props }">
48
+ <v-btn
49
+ class="float-right d-inline d-xl-none"
50
+ v-bind="props"
51
+ >
52
+ <v-icon>more_vert</v-icon>
53
+ </v-btn>
54
+ </template>
55
+ <v-list>
56
+ <v-list-item
57
+ :close-on-click="true"
58
+ @click="clickInfo()"
59
+ >
60
+ <v-icon>info</v-icon>
61
+ &nbsp;Details
62
+ </v-list-item>
63
+ <v-list-item
64
+ v-if="hasPrinterControlFeature"
65
+ :close-on-click="true"
66
+ @click="clickOpenPrinterControlDialog()"
67
+ >
68
+ <v-icon>open_with</v-icon>
69
+ &nbsp;Control
70
+ </v-list-item>
71
+ <v-list-item
72
+ :close-on-click="true"
73
+ @click="clickOpenPrinterURL()"
74
+ >
75
+ <v-icon>directions</v-icon>
76
+ &nbsp;Visit OctoPrint
77
+ </v-list-item>
78
+ <v-list-item
79
+ :close-on-click="true"
80
+ @click="clickOpenSettings()"
81
+ >
82
+ <v-icon>settings</v-icon>
83
+ &nbsp;Edit Printer
84
+ </v-list-item>
85
+ <v-list-item
86
+ :close-on-click="true"
87
+ @click="clickEmergencyStop()"
88
+ >
89
+ <v-icon>stop</v-icon>
90
+ &nbsp;Quick stop
91
+ </v-list-item>
92
+ </v-list>
93
+ </v-menu>
94
+ <div
95
+ v-if="!gridStore.gridEditMode"
96
+ class="float-right d-none d-xl-inline"
97
+ >
98
+ <!-- Connect USB -->
99
+ <v-btn
100
+ v-if="
101
+ !printerStateStore.isPrinterOperational(printer?.id) &&
102
+ printerStateStore.isApiResponding(printer?.id)
103
+ "
104
+ @click.prevent.stop="clickConnectUsb()"
105
+ >
106
+ <v-icon>usb</v-icon>
107
+ </v-btn>
108
+
109
+ <!-- Quick stop button -->
110
+ <v-tooltip
111
+ v-if="
112
+ hasPrinterControlFeature &&
113
+ printerStateStore.isPrinterOperational(printer?.id)
114
+ "
115
+ location="bottom"
116
+ >
117
+ <template #activator="{ props }">
118
+ <v-btn
119
+ elevation="4"
120
+ size="36"
121
+ v-bind="props"
122
+ @click.prevent.stop="clickOpenPrinterControlDialog()"
123
+ >
124
+ <v-icon>open_with</v-icon>
125
+ </v-btn>
126
+ </template>
127
+ <template #default>
128
+ <span> Control your printer head or extruder. </span>
129
+ </template>
130
+ </v-tooltip>
131
+
132
+ <!-- Quick stop button -->
133
+ <v-tooltip
134
+ v-if="printerStateStore.isPrinterOperational(printer?.id)"
135
+ location="bottom"
136
+ >
137
+ <template #activator="{ props }">
138
+ <v-btn
139
+ elevation="4"
140
+ size="36"
141
+ v-bind="props"
142
+ @click.prevent.stop="clickEmergencyStop()"
143
+ >
144
+ <v-icon>dangerous</v-icon>
145
+ </v-btn>
146
+ </template>
147
+ <template #default>
148
+ <span> Send a quick stop, causing USB to be disconnected. </span>
149
+ </template>
150
+ </v-tooltip>
151
+
152
+ <!-- Refresh connectivity button -->
153
+ <v-tooltip
154
+ v-if="
155
+ printer.enabled &&
156
+ printerStateStore.isPrinterNotOnline(printer.id)
157
+ "
158
+ location="bottom"
159
+ >
160
+ <template #activator="{ props }">
161
+ <v-btn
162
+ elevation="4"
163
+ size="36"
164
+ v-bind="props"
165
+ @click.prevent.stop="clickRefreshSocket()"
166
+ >
167
+ <v-icon>autorenew</v-icon>
168
+ </v-btn>
169
+ </template>
170
+ <template #default>
171
+ <span> Retry connecting to OctoPrint API </span>
172
+ </template>
173
+ </v-tooltip>
174
+ <v-btn
175
+ elevation="5"
176
+ @click.prevent.stop="clickInfo()"
177
+ >
178
+ <v-icon>menu_open</v-icon>
179
+ </v-btn>
180
+ </div>
181
+ <div
182
+ v-else
183
+ class="float-end"
184
+ >
185
+ <strong
186
+ class="pl-5 pr-5"
187
+ color="primary"
188
+ >
189
+ <v-icon>disabled_visible</v-icon>
190
+ Click to clear
191
+ </strong>
192
+ </div>
193
+ <br />
194
+
195
+ <v-tooltip
196
+ :disabled="!printer?.disabledReason"
197
+ close-delay="100"
198
+ color="danger"
199
+ open-delay="0"
200
+ location="top"
201
+ >
202
+ <template #activator="{ props }">
203
+ <small
204
+ class="xsmall-resized-font text--secondary d-lg-inline d-none"
205
+ v-bind="props"
206
+ >
207
+ <span v-if="printer?.disabledReason">
208
+ <small> MAINTENANCE </small>
209
+ <v-icon
210
+ class="d-none d-xl-inline"
211
+ color="primary"
212
+ size="small"
213
+ >
214
+ info
215
+ </v-icon>
216
+ </span>
217
+ <span v-else>
218
+ <small>
219
+ {{ printerState?.text?.toUpperCase() }}
220
+ </small>
221
+ </span>
222
+ </small>
223
+ </template>
224
+ Maintenance reason: <br />
225
+ {{ printer.disabledReason }}
226
+ </v-tooltip>
227
+ <small v-if="largeTilesEnabled && currentPrintingFilePath">
228
+ <strong> File: </strong> {{ currentPrintingFilePath }}
229
+ </small>
230
+ </v-container>
231
+ <v-container v-else-if="gridStore.gridEditMode">
232
+ <v-icon size="48">add</v-icon>
233
+ Place printer
234
+ </v-container>
235
+ <v-progress-linear
236
+ v-if="currentJob?.progress"
237
+ :model-value="currentJob.progress.completion"
238
+ absolute
239
+ bottom
240
+ color="green"
241
+ height="13"
242
+ >
243
+ <span class="xsmall-resized-font">
244
+ {{
245
+ largeTilesEnabled
246
+ ? currentJob?.progress?.completion
247
+ ? currentJob?.progress?.completion?.toFixed(1) + '%'
248
+ : '-'
249
+ : currentPrintingFilePath
250
+ }}
251
+ </span>
252
+ </v-progress-linear>
253
+ </v-card>
254
+ </div>
255
+ </template>
256
+
257
+ <script lang="ts" setup>
258
+ import { computed, PropType } from 'vue'
259
+ import { CustomGcodeService } from '@/backend/custom-gcode.service'
260
+ import { PrintersService } from '@/backend'
261
+ import { usePrinterStore } from '@/store/printer.store'
262
+ import { DialogName } from '@/components/Generic/Dialogs/dialog.constants'
263
+ import { useGridStore } from '@/store/grid.store'
264
+ import { FloorService } from '@/backend/floor.service'
265
+ import { useSettingsStore } from '@/store/settings.store'
266
+ import { useFloorStore } from '@/store/floor.store'
267
+ import { interpretStates } from '@/shared/printer-state.constants'
268
+ import { usePrinterStateStore } from '@/store/printer-state.store'
269
+ import { PrinterDto } from '@/models/printers/printer.model'
270
+ import { useSnackbar } from '@/shared/snackbar.composable'
271
+ import { useDialog } from '@/shared/dialog.composable'
272
+ import { useFeatureStore } from '@/store/features.store'
273
+
274
+ const defaultColor = 'rgba(100,100,100,0.1)'
275
+
276
+ const props = defineProps({
277
+ printer: {
278
+ type: Object as PropType<PrinterDto | undefined>,
279
+ required: false
280
+ },
281
+ x: { type: Number, required: true },
282
+ y: { type: Number, required: true }
283
+ })
284
+
285
+ const printerStore = usePrinterStore()
286
+ const printerStateStore = usePrinterStateStore()
287
+ const floorStore = useFloorStore()
288
+ const featureStore = useFeatureStore()
289
+ const settingsStore = useSettingsStore()
290
+ const gridStore = useGridStore()
291
+ const controlDialog = useDialog(DialogName.PrinterControlDialog)
292
+ const addOrUpdateDialog = useDialog(DialogName.AddOrUpdatePrinterDialog)
293
+ const snackbar = useSnackbar()
294
+
295
+ const printerId = computed(() => props.printer?.id)
296
+
297
+ const selected = computed(() => {
298
+ if (!printerId.value) return false
299
+ return printerStore.isSelectedPrinter(printerId.value)
300
+ })
301
+
302
+ const unselected = computed(() => {
303
+ return printerStore.selectedPrinters?.length && !selected.value
304
+ })
305
+
306
+ const hasPrinterControlFeature = computed(() => {
307
+ return featureStore.hasFeature('printerControlApi')
308
+ })
309
+
310
+ const largeTilesEnabled = computed(() => {
311
+ return settingsStore.largeTiles
312
+ })
313
+
314
+ const printerState = computed(() => {
315
+ if (!printerId.value) return
316
+ const printer = printerStore.printer(printerId.value)
317
+ if (!printer) return
318
+
319
+ const printerEvents = printerStateStore.printerEventsById[printerId.value]
320
+ const socketState = printerStateStore.socketStatesById[printerId.value]
321
+ return interpretStates(printer, socketState, printerEvents)
322
+ })
323
+
324
+ const printerStateColor = computed(() => {
325
+ const states = printerState.value
326
+ if (!states) {
327
+ return defaultColor
328
+ }
329
+ return states.rgb || defaultColor
330
+ })
331
+
332
+ const currentJob = computed(() => {
333
+ if (!printerId.value) return
334
+ return printerStateStore.printerJobsById[printerId.value]
335
+ })
336
+
337
+ const currentPrintingFilePath = computed(() => {
338
+ if (!printerId.value) return
339
+ return printerStateStore.printingFilePathsByPrinterId[printerId.value]
340
+ })
341
+
342
+ const clickInfo = () => {
343
+ printerStore.setSideNavPrinter(props.printer)
344
+ }
345
+
346
+ const clickRefreshSocket = async () => {
347
+ if (!printerId.value) return
348
+ await PrintersService.refreshSocket(printerId.value)
349
+ snackbar.openInfoMessage({
350
+ title: 'Refreshing OctoPrint connection state'
351
+ })
352
+ }
353
+
354
+ const clickOpenPrinterURL = () => {
355
+ if (!props.printer) return
356
+ PrintersService.openPrinterURL(props.printer.printerURL)
357
+ }
358
+
359
+ const clickOpenSettings = () => {
360
+ printerStore.setUpdateDialogPrinter(props.printer)
361
+ addOrUpdateDialog.openDialog()
362
+ }
363
+
364
+ const clickOpenPrinterControlDialog = async () => {
365
+ if (!printerId.value) {
366
+ throw new Error('PrinterId not set, cant open dialog')
367
+ }
368
+
369
+ await controlDialog.openDialog({ printerId })
370
+ }
371
+
372
+ const clickEmergencyStop = async () => {
373
+ if (!printerId.value) return
374
+ if (
375
+ confirm(
376
+ 'Are you sure to abort the print in Quick Stop mode? Please reconnect after.'
377
+ )
378
+ ) {
379
+ await CustomGcodeService.postQuickStopM112Command(printerId.value)
380
+ }
381
+ }
382
+
383
+ const clickConnectUsb = async () => {
384
+ if (!printerId.value) return
385
+ await PrintersService.sendPrinterConnectCommand(printerId.value)
386
+ }
387
+
388
+ const selectOrUnplacePrinter = async () => {
389
+ if (!props.printer || !printerId.value) return
390
+ if (gridStore.gridEditMode) {
391
+ const floorId = floorStore.selectedFloor?.id
392
+ if (!floorId) throw new Error('Cant clear printer, floor not selected')
393
+ await FloorService.deletePrinterFromFloor(floorId, printerId.value)
394
+ return
395
+ }
396
+ printerStore.toggleSelectedPrinter(props.printer)
397
+ }
398
+ </script>
399
+
400
+ <style>
401
+ .tile {
402
+ min-height: 75px;
403
+ -webkit-user-select: none; /* Safari */
404
+ -moz-user-select: none; /* Firefox */
405
+ -ms-user-select: none; /* IE10+/Edge */
406
+ user-select: none; /* Standard */
407
+ }
408
+
409
+ .tile-large {
410
+ min-height: 200px;
411
+ }
412
+
413
+ .tile-inner {
414
+ opacity: 0.85;
415
+ }
416
+
417
+ .tile-selected {
418
+ outline: 2px solid rgb(2, 248, 23) !important;
419
+ opacity: 1;
420
+ }
421
+
422
+ .tile-unselected {
423
+ opacity: 0.65;
424
+ }
425
+
426
+ .tile-setup:hover {
427
+ outline: 2px solid #02b102 !important;
428
+ border-right-width: 8px;
429
+ }
430
+
431
+ .small-resized-font {
432
+ font-size: clamp(10px, 1vw, 18px);
433
+ }
434
+
435
+ .xsmall-resized-font {
436
+ font-size: clamp(8px, 1vw, 10px);
437
+ }
438
+ </style>
@@ -0,0 +1,210 @@
1
+ <template>
2
+ <div>
3
+ <HomeToolbar />
4
+
5
+ <v-banner
6
+ v-if="!gridStore.gridEditMode"
7
+ v-drop-upload="{ printers: selectedPrinters }"
8
+ >
9
+ <v-row style="margin-bottom: -5px">
10
+ <v-col style="padding: 5px 0 0 15px">
11
+ <v-chip-group class="d-inline-block">
12
+ <v-chip
13
+ v-if="selectedPrinters.length === 0"
14
+ size="small"
15
+ >
16
+ No selected printers
17
+ </v-chip>
18
+ <v-chip
19
+ v-for="selectedPrinter in selectedPrinters"
20
+ :key="selectedPrinter.id"
21
+ closable
22
+ color="primary"
23
+ size="small"
24
+ @click="openPrinter(selectedPrinter)"
25
+ @click:close="deselectPrinter(selectedPrinter)"
26
+ >
27
+ {{ selectedPrinter.name }}
28
+ </v-chip>
29
+ </v-chip-group>
30
+ </v-col>
31
+ <v-col
32
+ align="right"
33
+ style="padding: 0"
34
+ >
35
+ <v-chip-group
36
+ v-if="selectedFile"
37
+ class="float-end"
38
+ >
39
+ <v-chip
40
+ closable
41
+ @click:close="deselectFile()"
42
+ >
43
+ {{ selectedFile.name }}
44
+ <strong class="pl-1">
45
+ {{ formatBytes(selectedFile.size) }}
46
+ </strong>
47
+ </v-chip>
48
+ </v-chip-group>
49
+ <br />
50
+ <v-btn
51
+ v-if="isBatchReprintFeatureAvailable"
52
+ :disabled="!hasPrintersSelected"
53
+ color="primary"
54
+ size="x-small"
55
+ @click="batchReprintFiles()"
56
+ >
57
+ <v-icon
58
+ class="pr-2"
59
+ size="small"
60
+ >
61
+ refresh
62
+ </v-icon>
63
+ Batch reprint
64
+ </v-btn>
65
+ <v-btn
66
+ :color="hasPrintersSelected ? 'primary' : 'secondary'"
67
+ class="ml-2"
68
+ size="x-small"
69
+ @click="clearSelectedPrinters()"
70
+ >
71
+ <v-icon
72
+ class="pr-2"
73
+ size="small"
74
+ >
75
+ delete
76
+ </v-icon>
77
+ Clear all ({{ selectedPrinters.length }})
78
+ </v-btn>
79
+ <v-btn
80
+ class="ml-2"
81
+ color="primary"
82
+ size="x-small"
83
+ @click="$refs.fileUpload?.click()"
84
+ >
85
+ Select gcode file
86
+ </v-btn>
87
+ <v-btn
88
+ :disabled="!selectedFile"
89
+ class="ml-2 mr-5"
90
+ color="green"
91
+ size="x-small"
92
+ @click="uploadFile()"
93
+ >
94
+ Upload gcode file
95
+ </v-btn>
96
+ <input
97
+ ref="fileUpload"
98
+ :multiple="false"
99
+ accept=".gcode"
100
+ style="display: none"
101
+ type="file"
102
+ @change="filesSelected()"
103
+ />
104
+ </v-col>
105
+ </v-row>
106
+ </v-banner>
107
+
108
+ <PrinterGrid class="ma-2" />
109
+ </div>
110
+ </template>
111
+
112
+ <script lang="ts" setup>
113
+ import { computed, ref } from 'vue'
114
+ import PrinterGrid from '@/components/PrinterGrid/PrinterGrid.vue'
115
+ import { PrinterDto } from '@/models/printers/printer.model'
116
+ import { PrintersService } from '@/backend'
117
+ import { formatBytes } from '@/utils/file-size.util'
118
+ import { convertMultiPrinterFileToQueue } from '@/utils/uploads-state.utils'
119
+ import HomeToolbar from '@/components/PrinterGrid/HomeToolbar.vue'
120
+ import { usePrinterStore } from '@/store/printer.store'
121
+ import { useUploadsStore } from '@/store/uploads.store'
122
+ import { useFeatureStore } from '@/store/features.store'
123
+ import { usePrinterStateStore } from '@/store/printer-state.store'
124
+ import { useGridStore } from '@/store/grid.store'
125
+ import { useSnackbar } from '@/shared/snackbar.composable'
126
+ import { DialogName } from '@/components/Generic/Dialogs/dialog.constants'
127
+ import { useDialog } from '@/shared/dialog.composable'
128
+
129
+ const gridStore = useGridStore()
130
+ const printersStore = usePrinterStore()
131
+ const printerStateStore = usePrinterStateStore()
132
+ const uploadsStore = useUploadsStore()
133
+ const featureStore = useFeatureStore()
134
+ const snackbar = useSnackbar()
135
+
136
+ const selectedFile = ref<File | undefined>(undefined)
137
+ const isBatchReprintFeatureAvailable = computed(() =>
138
+ featureStore.hasFeature('batchReprintCalls')
139
+ )
140
+ const hasPrintersSelected = computed(
141
+ () => printersStore.selectedPrinters.length > 0
142
+ )
143
+ const selectedPrinters = computed(() => printersStore.selectedPrinters)
144
+ const fileUpload = ref<HTMLInputElement | null>(null)
145
+
146
+ const deselectFile = () => {
147
+ if (fileUpload.value) {
148
+ fileUpload.value.value = ''
149
+ selectedFile.value = undefined
150
+ }
151
+ }
152
+
153
+ const clearSelectedPrinters = () => {
154
+ printersStore.clearSelectedPrinters()
155
+ }
156
+
157
+ const batchReprintFiles = async () => {
158
+ const output = await useDialog(DialogName.BatchReprintDialog).handleAsync(
159
+ printersStore.selectedPrinters?.map((p) => p.id)
160
+ )
161
+ console.log('[PrinterGridView] Dialog completed', output)
162
+ // await printersStore.batchReprintFiles();
163
+ }
164
+
165
+ const uploadFile = () => {
166
+ const selectedPrintersValue = selectedPrinters.value
167
+ const accessiblePrinters = selectedPrintersValue.filter((p) =>
168
+ printerStateStore.isApiResponding(p.id)
169
+ )
170
+
171
+ if (!selectedFile.value) return
172
+
173
+ // Checking and informing user
174
+ const incompleteListCount =
175
+ selectedPrintersValue.length - accessiblePrinters.length
176
+ if (incompleteListCount > 0) {
177
+ snackbar.openInfoMessage({
178
+ title: `${incompleteListCount} printers inaccessible`,
179
+ subtitle: 'These were skipped from uploading.'
180
+ })
181
+ }
182
+
183
+ const uploads = convertMultiPrinterFileToQueue(
184
+ accessiblePrinters,
185
+ selectedFile.value
186
+ )
187
+ uploadsStore.queueUploads(uploads)
188
+
189
+ if (fileUpload.value) {
190
+ fileUpload.value.value = ''
191
+ }
192
+ clearSelectedPrinters()
193
+ }
194
+
195
+ const filesSelected = () => {
196
+ if (fileUpload.value && fileUpload.value.files) {
197
+ selectedFile.value = fileUpload.value.files[0]
198
+ } else {
199
+ selectedFile.value = undefined
200
+ }
201
+ }
202
+
203
+ const deselectPrinter = (printer: PrinterDto) => {
204
+ printersStore.toggleSelectedPrinter(printer)
205
+ }
206
+
207
+ const openPrinter = (printer: PrinterDto) => {
208
+ PrintersService.openPrinterURL(printer.printerURL)
209
+ }
210
+ </script>
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <div v-if="fileList && printerId">
3
+ <strong> Files: </strong>
4
+ <v-list color="primary">
5
+ <v-list-item
6
+ v-for="file in fileList"
7
+ :key="file.path"
8
+ >
9
+ {{ file.path }}
10
+ <small class="ml-4 mr-4">
11
+ {{ new Date(file.date).toUTCString() }}
12
+ </small>
13
+ <v-btn @click="deleteFile(file)">
14
+ <v-icon>delete</v-icon>
15
+ <v-spacer />
16
+ Delete
17
+ </v-btn>
18
+ </v-list-item>
19
+ </v-list>
20
+ </div>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import { FileDto } from '@/models/printers/printer-file.model'
25
+ import { usePrinterStore } from '@/store/printer.store'
26
+
27
+ interface Props {
28
+ fileList: FileDto[]
29
+ printerId: string
30
+ }
31
+
32
+ const props = defineProps<Props>()
33
+
34
+ const printersStore = usePrinterStore()
35
+
36
+ const deleteFile = async (file: FileDto) => {
37
+ if (!props.fileList || !props.printerId) return
38
+ await printersStore.deletePrinterFile(props.printerId, file.path)
39
+ }
40
+ </script>