@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,236 @@
1
+ import { defineStore } from 'pinia'
2
+ import { PrinterDto } from '@/models/printers/printer.model'
3
+ import {
4
+ ClearedFilesResult,
5
+ FileDto
6
+ } from '@/models/printers/printer-file.model'
7
+ import { PrinterFileService, PrintersService } from '@/backend'
8
+ import { CreatePrinter } from '@/models/printers/crud/create-printer.model'
9
+ import { PrinterJobService } from '@/backend/printer-job.service'
10
+ import { usePrinterStateStore } from './printer-state.store'
11
+ import { IdType } from '@/utils/id.type'
12
+ import {
13
+ isPrinterDisabled,
14
+ isPrinterDisconnected,
15
+ isPrinterInMaintenance
16
+ } from '@/shared/printer-state.constants'
17
+
18
+ interface State {
19
+ printers: PrinterDto[]
20
+ printerFileCache: Record<IdType, FileDto[]>
21
+
22
+ sideNavPrinter?: PrinterDto
23
+ updateDialogPrinter?: PrinterDto
24
+ selectedPrinters: PrinterDto[]
25
+ maintenanceDialogPrinter?: PrinterDto
26
+ }
27
+
28
+ export const usePrinterStore = defineStore('Printers', {
29
+ state: (): State => ({
30
+ printers: [],
31
+ printerFileCache: {},
32
+
33
+ sideNavPrinter: undefined,
34
+ updateDialogPrinter: undefined,
35
+ selectedPrinters: [],
36
+ maintenanceDialogPrinter: undefined
37
+ }),
38
+ getters: {
39
+ printer() {
40
+ return (printerId?: IdType) => {
41
+ return this.printers.find((p) => p.id == printerId)
42
+ }
43
+ },
44
+ isSelectedPrinter(state) {
45
+ return (printerId?: IdType) =>
46
+ !!state.selectedPrinters.find((p: PrinterDto) => p.id === printerId)
47
+ },
48
+ printerFiles() {
49
+ return (printerId: IdType) => this.printerFileCache[printerId]
50
+ },
51
+ disabledCount(): number {
52
+ return this.printers.filter(isPrinterDisabled).length
53
+ },
54
+ disconnectedCount(): number {
55
+ const printerStateStore = usePrinterStateStore()
56
+ return this.printers.filter((p) =>
57
+ isPrinterDisconnected(p, printerStateStore.printerEventsById[p.id])
58
+ ).length
59
+ },
60
+ maintenanceCount(): number {
61
+ return this.printers.filter((p) => isPrinterInMaintenance(p)).length
62
+ }
63
+ },
64
+ actions: {
65
+ async createPrinter(newPrinter: CreatePrinter, forceSave: boolean) {
66
+ const data = await PrintersService.createPrinter(newPrinter, forceSave)
67
+ this.printers.push(data)
68
+ this.printers.sort((a: PrinterDto, b: PrinterDto) =>
69
+ a.name?.toLowerCase()?.localeCompare(b?.name?.toLowerCase()) ? 1 : -1
70
+ )
71
+ return data
72
+ },
73
+ toggleSelectedPrinter(printer: PrinterDto) {
74
+ const printerStateStore = usePrinterStateStore()
75
+ const selectedPrinterIndex = this.selectedPrinters.findIndex(
76
+ (sp) => sp.id == printer.id
77
+ )
78
+ if (selectedPrinterIndex === -1) {
79
+ if (printerStateStore.isApiResponding(printer.id)) {
80
+ this.selectedPrinters.push(printer)
81
+ }
82
+ } else {
83
+ this.selectedPrinters.splice(selectedPrinterIndex, 1)
84
+ }
85
+ },
86
+ clearSelectedPrinters() {
87
+ this.selectedPrinters = []
88
+ },
89
+ setSideNavPrinter(printer?: PrinterDto) {
90
+ this.sideNavPrinter = printer
91
+ },
92
+ setUpdateDialogPrinter(printer?: PrinterDto) {
93
+ this.updateDialogPrinter = printer
94
+ },
95
+ setMaintenanceDialogPrinter(printer?: PrinterDto) {
96
+ this.maintenanceDialogPrinter = printer
97
+ },
98
+ async updatePrinter(
99
+ {
100
+ printerId,
101
+ updatedPrinter
102
+ }: {
103
+ printerId: IdType
104
+ updatedPrinter: CreatePrinter
105
+ },
106
+ forceSave: boolean
107
+ ) {
108
+ const data = await PrintersService.updatePrinter(
109
+ printerId,
110
+ updatedPrinter,
111
+ forceSave
112
+ )
113
+ this._replacePrinter({ printerId, printer: data })
114
+ return data
115
+ },
116
+ async loadPrinters() {
117
+ const data = await PrintersService.getPrinters()
118
+ this.setPrinters(data)
119
+ return data
120
+ },
121
+ async deletePrinter(printerId: IdType) {
122
+ const data = await PrintersService.deletePrinter(printerId)
123
+ this._popPrinter(printerId)
124
+ return data
125
+ },
126
+ setPrinters(printers: PrinterDto[]) {
127
+ if (!printers?.length) {
128
+ this.printers = []
129
+ return
130
+ }
131
+ const viewedPrinterId = this.sideNavPrinter?.id
132
+ if (viewedPrinterId) {
133
+ this.sideNavPrinter = printers.find((p) => p.id === viewedPrinterId)
134
+ }
135
+ this.printers = printers.sort((a: PrinterDto, b: PrinterDto) =>
136
+ a.name?.toLowerCase()?.localeCompare(b?.name?.toLowerCase()) ? 1 : -1
137
+ )
138
+ },
139
+ _popPrinter(printerId: IdType) {
140
+ const printerIndex = this.printers.findIndex(
141
+ (p: PrinterDto) => p.id === printerId
142
+ )
143
+
144
+ if (printerIndex !== -1) {
145
+ this.printers.splice(printerIndex, 1)
146
+ } else {
147
+ console.warn(
148
+ 'Printer was not popped as it did not occur in state',
149
+ printerId
150
+ )
151
+ }
152
+ },
153
+ _replacePrinter({
154
+ printerId,
155
+ printer
156
+ }: {
157
+ printerId: IdType
158
+ printer: PrinterDto
159
+ }) {
160
+ const printerIndex = this.printers.findIndex(
161
+ (p: PrinterDto) => p.id === printerId
162
+ )
163
+
164
+ if (printerIndex !== -1) {
165
+ this.printers[printerIndex] = printer
166
+ } else {
167
+ console.warn(
168
+ 'Printer was not purged as it did not occur in state',
169
+ printerId
170
+ )
171
+ }
172
+ },
173
+ async clearPrinterFiles(printerId: IdType) {
174
+ if (!printerId) {
175
+ throw new Error('No printerId was provided')
176
+ }
177
+ const result = (await PrinterFileService.clearFiles(
178
+ printerId
179
+ )) as ClearedFilesResult
180
+ if (!result?.failedFiles) {
181
+ throw new Error('No failed files were returned')
182
+ }
183
+ const bucket = this.printerFileCache[printerId]
184
+ if (bucket) {
185
+ this.printerFileCache[printerId] = result.failedFiles
186
+ }
187
+ },
188
+ async loadPrinterFiles(printerId: IdType, recursive: boolean) {
189
+ const files = await PrinterFileService.getFiles(printerId, recursive)
190
+
191
+ files.sort((f1, f2) => {
192
+ return f1.date < f2.date ? 1 : -1
193
+ })
194
+
195
+ this.printerFileCache[printerId] = files
196
+ return files
197
+ },
198
+ async deletePrinterFile(printerId: IdType, fullPath: string) {
199
+ await PrinterFileService.deleteFileOrFolder(printerId, fullPath)
200
+
201
+ const fileBucket = this.printerFileCache[printerId]
202
+ if (!fileBucket?.length) {
203
+ console.warn('Printer file list was nonexistent', printerId)
204
+ return
205
+ }
206
+
207
+ const deletedFileIndex = fileBucket.findIndex((f) => f.path === fullPath)
208
+
209
+ if (deletedFileIndex !== -1) {
210
+ fileBucket.splice(deletedFileIndex, 1)
211
+ } else {
212
+ console.warn(
213
+ 'File was not purged as it did not occur in state',
214
+ fullPath
215
+ )
216
+ }
217
+
218
+ return this.printerFiles(printerId)
219
+ },
220
+ async sendStopJobCommand(printerId?: IdType) {
221
+ const printerStateStore = usePrinterStateStore()
222
+ if (!printerId) return
223
+ const printer = this.printer(printerId)
224
+ if (!printer) return
225
+
226
+ const question = !printerStateStore.isPrinterPrinting(printerId)
227
+ ? 'The printer is still printing - are you sure to stop it?'
228
+ : 'The printer seems idle - do you want to command it to stop anyway?'
229
+
230
+ const answer = confirm(question)
231
+ if (answer) {
232
+ await PrinterJobService.stopPrintJob(printer.id)
233
+ }
234
+ }
235
+ }
236
+ })
@@ -0,0 +1,25 @@
1
+ import { defineStore } from 'pinia'
2
+ import { UserService } from '@/backend/user.service'
3
+
4
+ interface State {
5
+ username: string | null
6
+ isDemoUser: boolean | null
7
+ userId: string | null
8
+ }
9
+
10
+ export const useProfileStore = defineStore('profile', {
11
+ state: (): State => ({
12
+ username: null,
13
+ isDemoUser: null,
14
+ userId: null
15
+ }),
16
+ actions: {
17
+ async getProfile() {
18
+ return await UserService.getProfile().then((response) => {
19
+ this.username = response.username
20
+ this.userId = response.id
21
+ this.isDemoUser = response.isDemoUser
22
+ })
23
+ }
24
+ }
25
+ })
@@ -0,0 +1,64 @@
1
+ import { defineStore } from 'pinia'
2
+ import {
3
+ FrontendSettings,
4
+ SettingsDto,
5
+ TimeoutSettings
6
+ } from '@/models/settings/settings.model'
7
+ import { SettingsService } from '@/backend'
8
+ import { ServerSettingsDto } from '@/models/settings/server-settings.dto'
9
+
10
+ export interface FrontendDebugSettings {
11
+ showPrinterStateUpdateSideNav: boolean
12
+ showInterpretedPrinterState: boolean
13
+ showJobsRendered: boolean
14
+ }
15
+
16
+ export interface SettingsState {
17
+ settings?: SettingsDto
18
+ frontendDebugSettings: FrontendDebugSettings
19
+ }
20
+
21
+ export const useSettingsStore = defineStore({
22
+ id: 'Settings',
23
+ state: (): SettingsState => ({
24
+ settings: undefined,
25
+ frontendDebugSettings: {
26
+ showPrinterStateUpdateSideNav: false,
27
+ showInterpretedPrinterState: false,
28
+ showJobsRendered: false
29
+ }
30
+ }),
31
+ actions: {
32
+ async loadSettings(): Promise<SettingsDto> {
33
+ const response = await SettingsService.getSettings()
34
+ this.settings = response
35
+ return response
36
+ },
37
+ async updateFrontendSettings(
38
+ update: FrontendSettings
39
+ ): Promise<SettingsDto> {
40
+ const response = await SettingsService.updateFrontendSettings(update)
41
+ this.settings = response
42
+ return response
43
+ },
44
+ async updateTimeoutSettings(update: TimeoutSettings): Promise<SettingsDto> {
45
+ const response = await SettingsService.updateTimeoutSettings(update)
46
+ this.settings = response
47
+ return response
48
+ }
49
+ },
50
+ getters: {
51
+ serverSettings(): ServerSettingsDto | undefined {
52
+ return this.settings?.server
53
+ },
54
+ largeTiles(): boolean {
55
+ return this.settings?.frontend?.largeTiles || false
56
+ },
57
+ gridCols(): number {
58
+ return this.settings?.frontend?.gridCols || 8
59
+ },
60
+ gridRows(): number {
61
+ return this.settings?.frontend?.gridRows || 8
62
+ }
63
+ }
64
+ })
@@ -0,0 +1,70 @@
1
+ import { defineStore } from 'pinia'
2
+ import { CreatePrinter } from '@/models/printers/crud/create-printer.model'
3
+ import { PrintersService } from '@/backend'
4
+
5
+ export interface TestEvent {
6
+ correlationToken: string
7
+ event: string
8
+ payload: string
9
+ }
10
+
11
+ interface State {
12
+ currentCorrelationToken?: string
13
+ testPrinter?: CreatePrinter
14
+ testPrinterEvents?: TestEvent[]
15
+ }
16
+
17
+ export const useTestPrinterStore = defineStore('TestPrinter', {
18
+ state: (): State => ({
19
+ currentCorrelationToken: undefined,
20
+ testPrinter: undefined,
21
+ testPrinterEvents: []
22
+ }),
23
+ getters: {
24
+ getEvents() {
25
+ return () =>
26
+ (
27
+ this.testPrinterEvents?.filter(
28
+ (e) => e.correlationToken === this.currentCorrelationToken
29
+ ) || []
30
+ )
31
+ .filter(
32
+ (e) =>
33
+ (e.event !== 'API_STATE_UPDATED' || e.payload !== 'unset') &&
34
+ (e.event !== 'WS_STATE_UPDATED' || e.payload !== 'unopened')
35
+ )
36
+ .map((e) => {
37
+ const event = e.event.startsWith('WS_') ? 'Socket' : 'API'
38
+ return {
39
+ event,
40
+ payload: e.payload
41
+ ?.toString()
42
+ ?.replace('noResponse', 'unreachable')
43
+ .replace('authFail', 'authentication failure'),
44
+ failure: [
45
+ 'authFail',
46
+ 'noResponse',
47
+ 'aborted',
48
+ 'globalKey',
49
+ 'error',
50
+ 'connection error',
51
+ 'closed',
52
+ 'connection closed'
53
+ ].includes(e.payload?.toString())
54
+ }
55
+ })
56
+ }
57
+ },
58
+ actions: {
59
+ clearEvents() {
60
+ this.testPrinterEvents = []
61
+ },
62
+ async createTestPrinter(newPrinter: CreatePrinter) {
63
+ this.testPrinter = newPrinter
64
+ return await PrintersService.testConnection(newPrinter)
65
+ },
66
+ saveEvent(event: TestEvent) {
67
+ this.testPrinterEvents?.push(event)
68
+ }
69
+ }
70
+ })
@@ -0,0 +1,75 @@
1
+ import { defineStore } from 'pinia'
2
+ import {
3
+ FailedQueuedUpload,
4
+ QueuedUpload
5
+ } from '@/models/uploads/queued-upload.model'
6
+ import { PrinterFileService } from '@/backend'
7
+ import { useSnackbar } from '@/shared/snackbar.composable'
8
+
9
+ export interface UploadsState {
10
+ queuedUploads: QueuedUpload[]
11
+ failedUploads: FailedQueuedUpload[]
12
+ uploadingNow: boolean
13
+ }
14
+
15
+ export const useUploadsStore = defineStore('Uploads', {
16
+ state: (): UploadsState => ({
17
+ queuedUploads: [],
18
+ failedUploads: [],
19
+ uploadingNow: false
20
+ }),
21
+ getters: {
22
+ hasPendingUploads(state) {
23
+ return state.queuedUploads?.length > 0
24
+ },
25
+ isUploadingNow(state) {
26
+ return state.uploadingNow
27
+ },
28
+ nextUpload(state) {
29
+ return state.queuedUploads[0]
30
+ }
31
+ },
32
+ actions: {
33
+ queueUploads(uploads: QueuedUpload[]) {
34
+ this.failedUploads = []
35
+ this.queuedUploads.push(...uploads)
36
+ },
37
+ cancelUploads() {
38
+ this.queuedUploads = []
39
+ this.failedUploads = []
40
+ },
41
+ async handleNextUpload() {
42
+ const snackbar = useSnackbar()
43
+ // Dont upload when queue empty
44
+ if (!this.queuedUploads?.length) return
45
+ this.uploadingNow = true
46
+ const { file, printer, commands } = this.nextUpload
47
+ // We'd rather fail fast and avoid the same upload failing many times
48
+ this.queuedUploads.splice(0, 1)
49
+
50
+ try {
51
+ await PrinterFileService.uploadFile(printer, file, commands)
52
+ } catch (e: any) {
53
+ if (e.isAxiosError) {
54
+ const failedUpload: FailedQueuedUpload = {
55
+ file,
56
+ printer,
57
+ commands,
58
+ error: e
59
+ }
60
+ this.failedUploads.push(failedUpload)
61
+ snackbar.openErrorMessage({
62
+ title: 'Upload failure',
63
+ subtitle: `File ${file.name}Upload failed for to printer ${printer.name}`
64
+ })
65
+ } else {
66
+ snackbar.openErrorMessage({
67
+ title: 'Upload failure',
68
+ subtitle: 'Unknown upload error occurred'
69
+ })
70
+ }
71
+ }
72
+ this.uploadingNow = false
73
+ }
74
+ }
75
+ })
@@ -0,0 +1,3 @@
1
+ # Styles
2
+
3
+ This directory is for configuring the styles of the application.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * src/styles/settings.scss
3
+ *
4
+ * Configures SASS variables and Vuetify overwrites
5
+ */
6
+
7
+ // https://vuetifyjs.com/features/sass-variables/`
8
+ // @use 'vuetify/settings' with (
9
+ // $color-pack: false
10
+ // );
@@ -0,0 +1,15 @@
1
+ /// <reference types="vite/client" />
2
+ interface ImportMetaEnv {
3
+ readonly BASE_URL: string
4
+ readonly PACKAGE_VERSION: string
5
+ readonly MODE: string
6
+ readonly BASE_URL: string
7
+ readonly PROD: boolean
8
+ readonly DEV: boolean
9
+ readonly SSR: boolean
10
+ // more env variables...
11
+ }
12
+
13
+ interface ImportMeta {
14
+ readonly env: ImportMetaEnv
15
+ }
@@ -0,0 +1,15 @@
1
+ export const groupArrayBy = <T>(
2
+ array: T[],
3
+ predicate: (value: T, index: number, array: T[]) => string
4
+ ) =>
5
+ array.reduce(
6
+ (acc, value, index, array) => {
7
+ ;(acc[predicate(value, index, array)] ||= []).push(value)
8
+ return acc
9
+ },
10
+ {} as { [key: string]: T[] }
11
+ )
12
+
13
+ export const distinct = (value: any, index: number, self: any) => {
14
+ return self.indexOf(value) === index
15
+ }
@@ -0,0 +1,5 @@
1
+ export function formatIntlDate(dateString: string | Date) {
2
+ const date = new Date(dateString)
3
+ // Then specify how you want your dates to be formatted
4
+ return new Intl.DateTimeFormat('default', { dateStyle: 'long' }).format(date)
5
+ }
@@ -0,0 +1,25 @@
1
+ import { getBaseUri } from '@/shared/http-client'
2
+
3
+ export function downloadFileByBlob(data: ArrayBuffer, fileName: string) {
4
+ if (!data) {
5
+ throw new Error('No data to download')
6
+ }
7
+ const blob = new Blob([data], { type: 'text/plain' })
8
+ const link = document.createElement('a')
9
+ link.href = URL.createObjectURL(blob)
10
+ link.download = fileName
11
+ link.click()
12
+ URL.revokeObjectURL(link.href)
13
+ }
14
+
15
+ export async function downloadFileByUrl(url: string, fileName: string) {
16
+ const apiBase = await getBaseUri()
17
+ const isAbsolute =
18
+ url.indexOf('http://') === 0 || url.indexOf('https://') === 0
19
+ const finalUrl = isAbsolute ? url : `${apiBase}/${url}`
20
+ const link = document.createElement('a')
21
+ link.href = finalUrl
22
+ link.download = fileName
23
+ link.click()
24
+ URL.revokeObjectURL(link.href)
25
+ }
@@ -0,0 +1,3 @@
1
+ export function errorSummary(e: any) {
2
+ return e.message ? `${e.message}\n ${e.stack}` : `'${e}'`
3
+ }
@@ -0,0 +1,11 @@
1
+ export function formatBytes(bytes: number, decimals = 2) {
2
+ if (bytes === 0) return '0 Bytes'
3
+
4
+ const k = 1024
5
+ const dm = decimals < 0 ? 0 : decimals
6
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
7
+
8
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
9
+
10
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
11
+ }
@@ -0,0 +1 @@
1
+ export type IdType = number | string
@@ -0,0 +1,8 @@
1
+ import * as Sentry from '@sentry/vue'
2
+
3
+ export function setSentryEnabled(enabled: boolean) {
4
+ const client = Sentry.getClient()
5
+ // @ts-ignore
6
+ client.getOptions().enabled = enabled
7
+ console.warn('Sentry enabled:', enabled)
8
+ }
@@ -0,0 +1,30 @@
1
+ import { useSnackbar } from '../shared/snackbar.composable'
2
+
3
+ export function uploadProgressTest(enabled: boolean) {
4
+ if (!enabled) return
5
+
6
+ const snackbar = useSnackbar()
7
+ // Nice visual test for uploads
8
+ let i = 0
9
+ let j = 0
10
+ let interval: any
11
+ // eslint-disable-next-line prefer-const
12
+ interval = setInterval(() => {
13
+ i += 2
14
+ j += 3
15
+ snackbar.openProgressMessage('1', 'file.gcode to YoParinter', i, i > 55)
16
+ if (j > 10 && j < 80) {
17
+ snackbar.openProgressMessage('2', 'file2.gcode to Beast', i, i > 80)
18
+ }
19
+ snackbar.openProgressMessage('3', 'file3.gcode to Beast', i, i > 80)
20
+ snackbar.openProgressMessage('4', 'file4.gcode to Beast', i, i > 80)
21
+ if (j > 20)
22
+ snackbar.openProgressMessage('5', 'file5.gcode to Beast', i, i > 60)
23
+ snackbar.openProgressMessage('6', 'file6.gcode to Beast', i, i > 65)
24
+ snackbar.openProgressMessage('7', 'file7.gcode to Beast', i, i > 80)
25
+
26
+ if (i >= 110 && j > 115) {
27
+ clearInterval(interval)
28
+ }
29
+ }, 200)
30
+ }
@@ -0,0 +1,2 @@
1
+ export const sleep = (time: number) =>
2
+ new Promise((res) => setTimeout(res, time))
@@ -0,0 +1,58 @@
1
+ import { FileUploadCommands } from '@/models/printers/file-upload-commands.model'
2
+ import { PrinterDto } from '@/models/printers/printer.model'
3
+ import { QueuedUpload } from '@/models/uploads/queued-upload.model'
4
+
5
+ /**
6
+ * Multiple files => 1 printer
7
+ * @param printer
8
+ * @param files
9
+ * @param printedFileName
10
+ */
11
+ export function convertPrinterMultiFileToQueue(
12
+ printer: PrinterDto,
13
+ files: File[],
14
+ printedFileName: string | null
15
+ ): QueuedUpload[] {
16
+ if (!printer) return []
17
+
18
+ return files.map((f) => {
19
+ const commands: FileUploadCommands = {
20
+ select: false,
21
+ print: false
22
+ }
23
+ if (f.name === printedFileName) {
24
+ commands.print = true
25
+ }
26
+
27
+ return {
28
+ file: f,
29
+ printer,
30
+ commands
31
+ }
32
+ }) as QueuedUpload[]
33
+ }
34
+
35
+ /**
36
+ * 1 file => multiple printers
37
+ * @param printers
38
+ * @param file
39
+ * @param commands
40
+ */
41
+ export function convertMultiPrinterFileToQueue(
42
+ printers: PrinterDto[],
43
+ file: File,
44
+ commands: FileUploadCommands = {
45
+ select: true,
46
+ print: true
47
+ }
48
+ ) {
49
+ if (!printers?.length || !file) return []
50
+
51
+ return printers.map((p) => {
52
+ return {
53
+ file,
54
+ printer: p,
55
+ commands
56
+ }
57
+ }) as QueuedUpload[]
58
+ }
@@ -0,0 +1,14 @@
1
+ export function isValidIPOrMask(str: string) {
2
+ const splitArray = str.split('.')
3
+ const subAddresses = splitArray.length
4
+ const arr = splitArray.filter((el) => {
5
+ return !/^0.|\D/g.test(el)
6
+ })
7
+
8
+ if (subAddresses <= 1) return false
9
+
10
+ return (
11
+ arr.filter((el) => el.length && parseInt(el) >= 0 && parseInt(el) <= 255)
12
+ .length === subAddresses
13
+ )
14
+ }