Skip to content

Commit e6ef07c

Browse files
authored
feat: move to Vue/Vuetify 3 (#4290)
1 parent dabe442 commit e6ef07c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+4897
-4250
lines changed

.eslintrc.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ module.exports = {
1212
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
1313
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
1414
'eslint:recommended',
15-
'plugin:vue/essential',
15+
'plugin:vue/vue3-essential',
16+
'plugin:vuetify/base',
1617
'plugin:prettier/recommended',
1718
],
1819
// required to lint *.vue files
19-
plugins: ['vue', 'babel'],
20+
plugins: ['vue', 'babel', 'vuetify'],
2021
// add your custom rules here
2122
rules: {
2223
// allow async-await
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
Vue 2 and Vuetify 2 are EOL so now it's time to refactor the frontend in order to use the new majors of both projects.
2+
3+
## Chore
4+
5+
Check for vue2 deps and bump to vue3 version of them.
6+
7+
- [ ] v-snackbars: drop in favor of vuetify-sonner
8+
- [ ] vue: bump to 3
9+
- [ ] vue-prism-editor: use ^2.0.0-alpha.2
10+
- [ ] vue-router bump to latest v4
11+
- [ ] vuedraggable: bump to v4
12+
- [ ] vuetify: bump to v3
13+
- [ ] add eslint-plugin-vuetify to auto fix some compatibility issues (add it to eslint config)
14+
- [ ] drop @vitejs/plugin-vue2 in favor of @vitejs/plugin-vue
15+
- [ ] Bump all vite-related deps to latest
16+
- [ ] Fix vite config
17+
18+
Once this step is finished run `npm run lint-fix` to auto fix some lint issues then start with the UI refactor.
19+
20+
## Vue 3
21+
22+
Follow migration guide: <https://v3-migration.vuejs.org/>
23+
24+
Additional steps:
25+
26+
- [ ] check for duplicate attributes on specific components
27+
- [ ] Use `defineAsyncComponent` to load async components
28+
- [ ] check if the conversion value-> modelValue was done on all component props
29+
- [ ] The `.native` modifier for v-on has been removed.
30+
- [ ] Ensure that the component emit the right event in the `emits` property
31+
- [ ] Array watchers should use `deep: 1`. Reacivity in Vue3 use Proxies and they don't intercept push/splice etc operations on array like vue2
32+
33+
## Vuetify 3
34+
35+
Follow migration guide: <https://vuetifyjs.com/en/getting-started/upgrade-guide/#setup>
36+
37+
Additional steps:
38+
39+
- [ ] Stepper is quite different and should be migrated carefully [VStepper](https://vuetifyjs.com/en/components/steppers/#dynamic-steps)
40+
- [ ] Use [Vuetify global defaults](https://vuetifyjs.com/en/features/global-configuration) to to set default prop values globally or per component when setting up your application. Example in order to keep inputs like them are in Vuetify 2 we should set default variant to `underlined`. For buttons we should set default variant to `text` and use `icon` property on button instead of putting the v-icon inside button default slot
41+
- [ ] `$vuetify.breakpoint` renamed to `$vuetify.display`
42+
- [ ] `v-list-item-content` has been removed. Now v-list-item have `title` `subtitle` props and `<template v-slot:append>` `<template v-slot:prepend>`, use them. In our lists we should use `append` slot
43+
- [ ] `v-list-item-icon` has been removed, wrap the `v-icon` into a `<template #prepend>`
44+
- [ ] `v-image`, `contains` is removed, use `cover`
45+
- [ ] `v-avatar`, remove `min-width`, `min-height`, replace `width` and `height` with `size`
46+
- [ ] `nudge-*` attributes must be replaced with `offset`, you can pass a pair of numbers to get the exact feeling as before
47+
- [ ] `lazy-validation` has been removed, use `validate-on="lazy"`
48+
- [ ] `v-date-picker` `range` ahs been removed, use `multiple="range"`, `locale` has been removed, check if the mapping is done correctly in the Vuetify option ([see](https://vuetifyjs.com/en/components/date-pickers/#internationalization))
49+
- [ ] `v-list-item-group` has been removed, assign the item’s key to the value prop of each v-list-item and bind v-model:selected on the v-list to get the selected value
50+
- [ ] `v-list-item-avatar` have been removed, wrap the `v-avatar` into a `<template #prepend>`
51+
- [ ] `v-data-table`, `item-class` and `item-style` have been combined into `row-props`
52+
- [ ] Server side tables using `server-items-length` must be replaced with `<v-data-table-server items-length />`
53+
- [ ] Forms `validate()` function is now async
54+
- [ ] Replace `var(--` with Vue3 CSS `v-bind` using current theme
55+
- [ ] Seems that theme `secondary` color could no more be set, on dark or light theme is always a `teal` color (see top bar). I've tried to set the old one for dark theme (dark grey) but when switch to light remains always the same color
56+
57+
## General
58+
59+
- [ ] move to [Vuetify Sonner](https://github.com/wobsoriano/vuetify-sonner) for notifications, drop v-snackbars
60+
- [ ] `v-edit-dialog` is not available in Vuetify 3, [this issue](https://github.com/vuetifyjs/vuetify/issues/19028) suggests to use `v-confirm-edit`
61+
- [ ] Find all `<draggable` usage and change:
62+
- `list` prop becomes `v-model`
63+
- Add `item-key` prop to unique identify items in draggable
64+
- Remove `v-for` from default slot. Use item slot instead `<template #item="{ element, index }">`
65+
66+
After doing all the changes ensure that `npm run dev` command works, if there are errors fix them then run it again till all errors are fixed.
67+
68+
Do the same with `npm run lint-fix` command, if it show some errors try to fix them

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
18.14.2
1+
v20.19.4

CLAUDE.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build and Development Commands
6+
7+
### Frontend (UI)
8+
9+
- `npm run dev` - Start development server on port 8092 with hot reloading
10+
- `npm run build:ui` - Build the Vue.js frontend application
11+
- `npm run dev-https` - Start development server with HTTPS enabled
12+
13+
### Backend (API/Server)
14+
15+
- `npm run dev:server` - Start backend server in development mode with nodemon
16+
- `npm run server` - Start production server using compiled TypeScript
17+
- `npm run build:server` - Compile TypeScript API code to JavaScript
18+
- `npm run start` - Start production server from compiled code
19+
20+
### Full Stack
21+
22+
- `npm run build` - Build both frontend and backend
23+
- `npm run bundle` - Bundle the application using esbuild
24+
25+
### Testing and Quality
26+
27+
- `npm run test` - Run all tests (both server and UI)
28+
- `npm run test:server` - Run backend tests with Mocha
29+
- `npm run test:ui` - Run frontend tests
30+
- `npm run lint` - Run ESLint and markdownlint
31+
- `npm run lint-fix` - Auto-fix linting issues
32+
- `npm run coverage` - Run tests with coverage reporting
33+
34+
### Development Tools
35+
36+
- `npm run fake-stick` - Start mock Z-Wave controller for testing
37+
- `npm run docs` - Serve documentation with Docsify
38+
- `npm run pkg` - Create binary packages
39+
- `npm run docker:build` - Build Docker image
40+
41+
## Project Architecture
42+
43+
### Technology Stack
44+
45+
- **Backend**: Node.js, Express, TypeScript, Socket.IO, MQTT, Z-Wave JS
46+
- **Frontend**: Vue 2.7, Vuetify, Pinia (state management)
47+
- **Build Tools**: Vite (frontend), esbuild (backend), TypeScript
48+
49+
### Core Components
50+
51+
#### Backend Architecture (`api/`)
52+
53+
- **app.ts**: Main Express application with middleware, routing, and Socket.IO setup
54+
- **lib/ZwaveClient.ts**: Z-Wave JS driver integration and device management
55+
- **lib/Gateway.ts**: Core gateway logic connecting Z-Wave to MQTT
56+
- **lib/MqttClient.ts**: MQTT broker communication
57+
- **lib/SocketManager.ts**: WebSocket management for real-time UI updates
58+
- **lib/BackupManager.ts**: Handles NVM and configuration backups
59+
- **lib/ZnifferManager.ts**: Z-Wave network sniffing functionality
60+
61+
#### Frontend Architecture (`src/`)
62+
63+
- **main.js**: Vue application entry point
64+
- **App.vue**: Root component
65+
- **views/**: Page components (ControlPanel, Settings, Mesh, etc.)
66+
- **components/**: Reusable UI components
67+
- **stores/**: Pinia state management
68+
- **router/**: Vue Router configuration
69+
70+
#### Key Directories
71+
72+
- **api/**: TypeScript backend code
73+
- **src/**: Vue frontend code
74+
- **server/**: Compiled JavaScript backend (build output)
75+
- **dist/**: Built frontend assets
76+
- **store/**: Runtime data (logs, backups, sessions, Z-Wave data)
77+
- **docs/**: Documentation with Docsify
78+
79+
### Development Patterns
80+
81+
#### API Structure
82+
83+
- RESTful APIs under `/api/` prefix
84+
- Socket.IO events for real-time communication
85+
- JWT authentication when enabled
86+
- Rate limiting on sensitive endpoints
87+
88+
#### Z-Wave Integration
89+
90+
- Uses Z-Wave JS library for device communication
91+
- Gateway pattern translates Z-Wave events to MQTT
92+
- Supports both named and value ID based topics
93+
- Home Assistant MQTT discovery integration
94+
95+
#### State Management
96+
97+
- Backend state managed through Z-Wave JS and custom stores
98+
- Frontend uses Pinia for reactive state
99+
- Real-time updates via Socket.IO events
100+
101+
### Configuration and Settings
102+
103+
- Settings stored in `store/settings.json`
104+
- Runtime configuration through environment variables
105+
- Custom device configurations in `store/customDevices.json`
106+
- Backup configurations in `store/backups/`
107+
108+
### Testing Strategy
109+
110+
- Backend tests use Mocha with TypeScript support
111+
- Frontend tests use Mocha with Babel
112+
- Test files use `.test.ts` or `.test.js` extensions
113+
- Coverage reporting with c8
114+
115+
### Build and Deployment
116+
117+
- Backend compiled from TypeScript to JavaScript
118+
- Frontend bundled with Vite
119+
- Docker support with multi-stage builds
120+
- Binary packaging with `@yao-pkg/pkg`
121+
122+
## Development Workflow
123+
124+
1. **Starting Development**: Use `npm run dev` for frontend and `npm run dev:server` for backend
125+
2. **Code Changes**: Backend changes trigger nodemon restart, frontend has hot reloading
126+
3. **Testing**: Run `npm run test` before committing changes
127+
4. **Linting**: Use `npm run lint-fix` to fix code style issues
128+
5. **Building**: Use `npm run build` for production builds
129+
130+
## Important Notes
131+
132+
- The project is currently on the `vue3-refactor` branch
133+
- **Migration Status**: Currently migrating from Vue 2.7 + Vuetify 2 to Vue 3 + Vuetify 3
134+
- Backend code is in TypeScript, frontend in JavaScript (Vue 3)
135+
- Z-Wave functionality requires physical hardware or mock server
136+
- MQTT integration is optional but commonly used
137+
- Authentication can be enabled/disabled in settings
138+
- The application serves both as a Z-Wave control panel and MQTT gateway
139+
140+
## Vue 3 Migration Progress
141+
142+
### ✅ Completed
143+
144+
- Updated package.json dependencies: Vue 3, Vuetify 3, Vue Router 4, vuedraggable v4
145+
- Replaced `v-snackbars` with `vuetify-sonner` for notifications
146+
- Updated build tools: `@vitejs/plugin-vue` (replacing vue2 plugin), Vite 6
147+
- Added `eslint-plugin-vuetify` for automatic migration assistance
148+
- Updated ESLint configuration for Vue 3 compatibility
149+
- Updated Vite configuration to use Vue 3 plugin
150+
- **Core Framework Migration:**
151+
- Updated `main.js` to use Vue 3 `createApp()` API
152+
- Updated Vue Router to use `createRouter()` and `createWebHashHistory()`
153+
- Updated Vuetify plugin to use `createVuetify()` with V3 theme system
154+
- Updated Pinia plugin for Vue 3 compatibility
155+
- Fixed Vue 3 lifecycle hook duplicates
156+
- Migrated notification system from v-snackbars to vuetify-sonner
157+
- **Development Server:** Now successfully starts with Vue 3 + Vuetify 3
158+
159+
### ✅ Additional Completed
160+
161+
- **Systematic Vuetify Component Migration:**
162+
- Fixed all `v-subheader``v-list-subheader` (22 instances across 6 files)
163+
- Fixed all `v-list-item-icon` → template slots (6 instances)
164+
- Removed all deprecated `dark` props (50+ instances across 15+ files)
165+
- Fixed all `fab``variant="fab"` (23 instances across 7 files)
166+
- Fixed `nudge-*` props → `:offset` array (2 instances)
167+
- Fixed `item-style``row-props` (1 instance)
168+
- Fixed `offset-y``location` positioning (2 instances)
169+
- Updated draggable components to Vue 3 syntax (3 files)
170+
- Fixed Vue 3 template key placement (1 instance)
171+
172+
### ✅ Recently Completed (Major Cleanup)
173+
174+
- **v-list-item-content** replacement (15+ instances across 3 files)
175+
- **v-tabs-items/v-tab-item** updates (9 instances across 2 files)
176+
- **lazy-validation** removal (7 instances across 5 dialog files)
177+
- **dense** prop removal (10+ instances across 4 files)
178+
- **app** prop removal (2 instances in App.vue)
179+
- **v-list-item-avatar** replacement (1 instance)
180+
- **positioning props** (overlap/fixed/position-x/position-y) (8+ instances)
181+
- **v-list-item-icon** template slot migration (8+ instances)
182+
- **v-subheader** final fixes (2 instances)
183+
- **unused imports** cleanup (1 instance)
184+
185+
### 🔄 Final Remaining Issues (28 → down from 204 - 86% reduction!)
186+
187+
- **hide-mode-switch/flat** props (ValueId.vue - 2 instances)
188+
- **@change** event updates (ListInput.vue - 1 instance)
189+
- **v-list-item-content** (DialogHealthCheck.vue - 1 instance)
190+
- **Vue 3 template key** placement (2 instances)
191+
- **duplicate attributes** (NodeDetails.vue, NodeScheduler.vue - 8 instances)
192+
- **deprecated colors** (SmartView.vue - 1 instance)
193+
- **deprecated .native** modifiers (index.vue - 3 instances)
194+
- **text-color** prop (index.vue - 1 instance)
195+
- **remaining dark** props (Settings.vue - 3 instances)
196+
- **minor parsing errors** (6 instances)
197+
198+
### 📋 Manual Migration Tasks Required
199+
200+
1. Update all Vue components to use Vue 3 Composition API patterns
201+
2. Replace `v-model` with `v-model:modelValue` where needed
202+
3. Update all Vuetify components per migration guide
203+
4. Replace draggable components with Vue 3 compatible syntax
204+
5. Update router configuration for Vue Router 4
205+
6. Test all functionality after migration

api/app.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ async function parseDir(dir: string): Promise<StoreFileEntry[]> {
461461
// hide config-db
462462
continue
463463
}
464-
entry.children = await parseDir(entry.path)
464+
entry.children = []
465465
sortStore(entry.children)
466466
} else {
467467
entry.ext = file.split('.').pop()
@@ -1382,7 +1382,9 @@ app.get('/api/store', storeLimiter, isAuthenticated, async function (req, res) {
13821382
// lgtm [js/path-injection]
13831383
data = await fs.readFile(reqPath, 'utf8')
13841384
} else {
1385-
throw Error('Path is not a file')
1385+
// read directory
1386+
// lgtm [js/path-injection]
1387+
data = await parseDir(reqPath)
13861388
}
13871389
} else {
13881390
data = [

api/lib/ZwaveClient.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ export type ZUIDeviceClass = {
398398
}
399399

400400
export type ZUINodeGroups = {
401-
text: string
401+
title: string
402402
value: number
403403
endpoint: number
404404
maxNodes: number
@@ -537,7 +537,7 @@ export type ZUINode = {
537537
measured0dBm?: number
538538
maxLongRangePowerlevel?: number
539539
RFRegion?: RFRegion
540-
rfRegions?: { text: string; value: number }[]
540+
rfRegions?: { title: string; value: number }[]
541541
isFrequentListening?: FLiRS
542542
isRouting?: boolean
543543
keepAwake?: boolean
@@ -1693,7 +1693,7 @@ class ZwaveClient extends TypedEventEmitter<ZwaveClientEventCallbacks> {
16931693
for (const [groupIndex, group] of groups) {
16941694
// https://zwave-js.github.io/node-zwave-js/#/api/controller?id=associationgroup-interface
16951695
node.groups.push({
1696-
text: group.label,
1696+
title: group.label,
16971697
endpoint: endpoint,
16981698
value: groupIndex,
16991699
maxNodes: group.maxNodes,
@@ -6235,12 +6235,12 @@ class ZwaveClient extends TypedEventEmitter<ZwaveClientEventCallbacks> {
62356235
.getSupportedRFRegions()
62366236
?.map((region) => ({
62376237
value: region,
6238-
text: getEnumMemberName(RFRegion, region),
6238+
title: getEnumMemberName(RFRegion, region),
62396239
disabled:
62406240
region === RFRegion.Unknown ||
62416241
region === RFRegion['Default (EU)'],
62426242
}))
6243-
.sort((a, b) => a.text.localeCompare(b.text)) ?? []
6243+
.sort((a, b) => a.title.localeCompare(b.title)) ?? []
62446244
}
62456245
}
62466246

0 commit comments

Comments
 (0)