diff --git a/CHANGELOG.md b/CHANGELOG.md index 49caaf7..8a724c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,22 @@ ADR-007 and ADR-002. ### Changed +- Equipment -> machines rename (backend). The equipment plugin is now the + machines plugin: `/api/equipment` -> `/api/machines`, tables + `equipment`/`equipmenttypes` -> `machines`/`machinetypes` (columns + `equipmentid` -> `machineid`, `equipmenttypeid` -> `machinetypeid`, + `equipmenttype` -> `machinetype`), permissions `equipment.*` -> + `machines.*`, assettype value `equipment` -> `machine`. The legacy core + `machinetypes` lookup (it types the vendor MODELS catalog, not machine + instances) is renamed to `modeltypes` (`/api/machinetypes` -> + `/api/modeltypes`, `models.machinetypeid` -> `models.modeltypeid`) to + free the name. Data flips migrate assettypes, auditlog entitytype, + settings keys (`identifier_*_equipment_enabled` -> + `identifier_*_machine_enabled`, `search_equipment_enabled` -> + `search_machine_enabled`), and permission rows in place; plugins.json + registry entries carry over automatically. Upgrade: run + `flask db upgrade` then `flask plugin upgrade-all`. + - Inter (variable) replaces Roboto, bundled locally - no Google Fonts fetch, so air-gapped installs render correctly. Tables use tabular numerals. diff --git a/CLAUDE.md b/CLAUDE.md index e16f076..4fe0147 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ Refactor phases 0-5 landed; phase 6 (multi-site distribution readiness) in progr - 340 tests passing, naming/style check green, Gitea Actions CI (backend + naming + frontend build) - `__contract_version__` at 0.6.0 (product `__version__` 0.5.0 - distinct series, ADR-007) -- 11 bundled plugins all satisfy contract: computers, employees, equipment, knowledgebase, measuringtools, network, notifications, printers, slides, usb, warranty +- 11 bundled plugins all satisfy contract: computers, employees, knowledgebase, machines, measuringtools, network, notifications, printers, slides, usb, warranty - Single core Alembic chain: baseline `68b3947ae14f` -> head `7d16_directoryemployees` (23 migrations). A fresh site runs `flask db upgrade` from empty; it is reproducible and idempotent. - Pre-1.0 framework; sister sites should pin tight `core_version` ranges until contract reaches 1.0 diff --git a/README.md b/README.md index bc0e1eb..5224e1e 100644 --- a/README.md +++ b/README.md @@ -1,233 +1,233 @@ -# ShopDB Flask - -A modern rewrite of the classic ASP/VBScript ShopDB application using Flask (Python) and Vue 3. This application manages shop floor machines, PCs, printers, applications, and related infrastructure for manufacturing environments. - -## Overview - -ShopDB tracks and manages: -- **Machines** - CNC equipment, CMMs, inspection systems, etc. -- **PCs** - Shopfloor computers, engineering workstations -- **Printers** - Network printers with Zabbix integration -- **Applications** - Software deployed across the shop floor -- **Knowledge Base** - Documentation and troubleshooting guides - -## Tech Stack - -**Backend:** -- Python 3.x with Flask -- SQLAlchemy ORM -- MySQL 5.6+ database -- JWT authentication -- Plugin architecture for extensibility - -**Frontend:** -- Vue 3 with Composition API -- Vue Router for navigation -- Pinia for state management -- Vite build system - -## Project Structure - -``` -shopdb-flask/ -├── shopdb/ # Flask application -│ ├── core/ -│ │ ├── api/ # REST API endpoints -│ │ ├── models/ # SQLAlchemy models -│ │ ├── schemas/ # Validation schemas -│ │ └── services/ # Business logic -│ ├── plugins/ # Plugin system -│ └── utils/ # Shared utilities -├── frontend/ # Vue 3 application -│ ├── src/ -│ │ ├── api/ # API client -│ │ ├── components/ # Reusable components -│ │ ├── views/ # Page components -│ │ ├── router/ # Route definitions -│ │ └── stores/ # Pinia stores -│ └── public/ # Static assets -├── plugins/ # Bundled and external plugins -├── migrations/ # Alembic migration chain (flask db upgrade) -├── scripts/ # Import and utility scripts -└── tests/ # Test suite -``` - -## Naming Conventions - -To maintain consistency with the legacy ShopDB database and codebase, the following naming standards apply: - -### Database - -- **Table names:** Lowercase, single word, no underscores or dashes - - Examples: `machines`, `pctypes`, `machinetypes`, `businessunits` -- **Column names:** Lowercase, single word, no underscores or dashes - - Examples: `machineid`, `machinenumber`, `pctypeid`, `isactive`, `createddate` -- **Foreign keys:** Referenced table name + `id` - - Examples: `locationid`, `vendorid`, `modelnumberid`, `pctypeid` -- **Boolean columns:** Prefixed with `is` or `has` - - Examples: `isactive`, `isshopfloor`, `isvnc`, `iswinrm`, `islicenced` - -### Code - -- **Python variables:** Follow database naming where applicable (lowercase, no underscores for model fields) -- **JavaScript variables:** camelCase for local variables, but match API field names from backend -- **Vue components:** PascalCase for component names -- **CSS classes:** Lowercase with dashes for multi-word classes - -### API - -- **Endpoints:** Lowercase, plural nouns - - Examples: `/api/machines`, `/api/pctypes`, `/api/locations` -- **Query parameters:** Lowercase, single word - - Examples: `?type=pc`, `?locationid=5`, `?isactive=true` - -## Style Guidelines - -- No emojis in code, comments, documentation, or UI -- Keep UI functional and professional -- Dark theme is the default -- Consistent table layouts across all list views - -## Setup - -### Prerequisites - -- Python 3.8+ -- Node.js 18+ -- MySQL 5.7+ (5.6 works with extra utf8mb4 config; see docs/DEPLOY.md) - -ShopDB Flask uses MySQL as the canonical database. SQLite is used only for the -test suite (`TestingConfig` in `shopdb/config.py` points at an in-memory -SQLite). Do not run dev or production against SQLite. - -### Distribution - -The application is distributed internally through the GE Aerospace Gitea. Clone -it from there; there is no public package or image registry. - -### Fast path (Docker) - -The Docker image builds the Vue frontend and serves it from the API container, -so a container deploy needs no separate Node build step. - -```bash -cp .env.example .env -# Edit .env: set SECRET_KEY, JWT_SECRET_KEY, DATABASE_URL, CORS_ORIGINS, -# and the MYSQL_* passwords (see docs/CONFIG.md for every variable). - -docker compose up -d --build - -# Create the schema and seed the platform data (idempotent, safe to re-run): -docker compose exec api flask db upgrade -docker compose exec api flask seed permissions -docker compose exec api flask seed settings -docker compose exec api flask seed reference-data -``` - -Then browse to the site and complete the first-run setup wizard at `/setup` -(it creates the first admin account and captures site identity). To create the -admin headlessly instead of using the wizard: - -```bash -docker compose exec api flask seed admin --username admin --email admin@facility.example.com -``` - -### Manual path (venv + Node) - -```bash -# Backend -python -m venv venv -source venv/bin/activate -pip install -r requirements.txt -cp .env.example .env -# Edit .env with your database credentials and secrets. - -flask db upgrade -flask seed permissions -flask seed settings -flask seed reference-data -flask run - -# Frontend (separate terminal) -cd frontend -npm install -npm run dev # dev server on :5173 -npm run build # production build into frontend/dist (served by Flask) -``` - -Complete first-run setup at `/setup`, or run `flask seed admin` for a headless -admin account. - -To import data from the legacy ShopDB MySQL database (one-time, see -`migrations/DATA_MIGRATION_GUIDE.md`): - -```bash -python scripts/import_from_mysql.py -``` - -For the full per-site deployment runbook see [docs/DEPLOY.md](docs/DEPLOY.md); -for every environment variable and Setting key see [docs/CONFIG.md](docs/CONFIG.md). - -## Configuration - -Environment variables (`.env`): - -| Variable | Description | -|----------|-------------| -| `DATABASE_URL` | MySQL connection string | -| `SECRET_KEY` | Flask secret key | -| `JWT_SECRET_KEY` | JWT signing key | -| `JWT_ACCESS_TOKEN_EXPIRES` | Access token TTL (seconds) | -| `LOG_LEVEL` | Logging verbosity | - -## API Documentation - -The REST API follows standard conventions: - -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/machines` | List machines (filterable by type) | -| GET | `/api/machines/:id` | Get machine details | -| POST | `/api/machines` | Create machine | -| PUT | `/api/machines/:id` | Update machine | -| DELETE | `/api/machines/:id` | Soft delete machine | - -Query parameters for list endpoints: -- `page` - Page number (default: 1) -- `per_page` - Items per page (default: 25) -- `sort` - Sort field -- `order` - Sort direction (asc/desc) -- `search` - Search term -- `type` - Filter by machine type (pc, printer, equipment) - -## Plugin System - -ShopDB supports plugins for extending functionality. See `CONTRIBUTING.md` for plugin development guidelines. - -The image bundles ten plugins; only the ones a site installs are loaded: - -- **computers** - Shopfloor PCs and workstations -- **employees** - Employee directory -- **equipment** - CNC, CMM, and inspection equipment -- **knowledgebase** - Documentation and troubleshooting guides -- **network** - Network devices -- **notifications** - Shopfloor notifications and recognition feed -- **printers** - Extended printer management with Zabbix integration -- **slides** - TV/kiosk slideshows -- **usb** - CMMC USB check-in/out tracking -- **warranty** - Dell warranty lookups - -## Legacy Migration - -This project replicates functionality from the classic ASP/VBScript ShopDB site. Key mappings: - -| Legacy | Modern | -|--------|--------| -| ASP/VBScript | Flask/Python | -| Classic ADO | SQLAlchemy | -| Server-side HTML | Vue 3 SPA | -| Session auth | JWT tokens | - -## License - -Internal use only. +# ShopDB Flask + +A modern rewrite of the classic ASP/VBScript ShopDB application using Flask (Python) and Vue 3. This application manages shop floor machines, PCs, printers, applications, and related infrastructure for manufacturing environments. + +## Overview + +ShopDB tracks and manages: +- **Machines** - CNC equipment, CMMs, inspection systems, etc. +- **PCs** - Shopfloor computers, engineering workstations +- **Printers** - Network printers with Zabbix integration +- **Applications** - Software deployed across the shop floor +- **Knowledge Base** - Documentation and troubleshooting guides + +## Tech Stack + +**Backend:** +- Python 3.x with Flask +- SQLAlchemy ORM +- MySQL 5.6+ database +- JWT authentication +- Plugin architecture for extensibility + +**Frontend:** +- Vue 3 with Composition API +- Vue Router for navigation +- Pinia for state management +- Vite build system + +## Project Structure + +``` +shopdb-flask/ +├── shopdb/ # Flask application +│ ├── core/ +│ │ ├── api/ # REST API endpoints +│ │ ├── models/ # SQLAlchemy models +│ │ ├── schemas/ # Validation schemas +│ │ └── services/ # Business logic +│ ├── plugins/ # Plugin system +│ └── utils/ # Shared utilities +├── frontend/ # Vue 3 application +│ ├── src/ +│ │ ├── api/ # API client +│ │ ├── components/ # Reusable components +│ │ ├── views/ # Page components +│ │ ├── router/ # Route definitions +│ │ └── stores/ # Pinia stores +│ └── public/ # Static assets +├── plugins/ # Bundled and external plugins +├── migrations/ # Alembic migration chain (flask db upgrade) +├── scripts/ # Import and utility scripts +└── tests/ # Test suite +``` + +## Naming Conventions + +To maintain consistency with the legacy ShopDB database and codebase, the following naming standards apply: + +### Database + +- **Table names:** Lowercase, single word, no underscores or dashes + - Examples: `machines`, `pctypes`, `machinetypes`, `businessunits` +- **Column names:** Lowercase, single word, no underscores or dashes + - Examples: `machineid`, `machinenumber`, `pctypeid`, `isactive`, `createddate` +- **Foreign keys:** Referenced table name + `id` + - Examples: `locationid`, `vendorid`, `modelnumberid`, `pctypeid` +- **Boolean columns:** Prefixed with `is` or `has` + - Examples: `isactive`, `isshopfloor`, `isvnc`, `iswinrm`, `islicenced` + +### Code + +- **Python variables:** Follow database naming where applicable (lowercase, no underscores for model fields) +- **JavaScript variables:** camelCase for local variables, but match API field names from backend +- **Vue components:** PascalCase for component names +- **CSS classes:** Lowercase with dashes for multi-word classes + +### API + +- **Endpoints:** Lowercase, plural nouns + - Examples: `/api/machines`, `/api/pctypes`, `/api/locations` +- **Query parameters:** Lowercase, single word + - Examples: `?type=pc`, `?locationid=5`, `?isactive=true` + +## Style Guidelines + +- No emojis in code, comments, documentation, or UI +- Keep UI functional and professional +- Dark theme is the default +- Consistent table layouts across all list views + +## Setup + +### Prerequisites + +- Python 3.8+ +- Node.js 18+ +- MySQL 5.7+ (5.6 works with extra utf8mb4 config; see docs/DEPLOY.md) + +ShopDB Flask uses MySQL as the canonical database. SQLite is used only for the +test suite (`TestingConfig` in `shopdb/config.py` points at an in-memory +SQLite). Do not run dev or production against SQLite. + +### Distribution + +The application is distributed internally through the GE Aerospace Gitea. Clone +it from there; there is no public package or image registry. + +### Fast path (Docker) + +The Docker image builds the Vue frontend and serves it from the API container, +so a container deploy needs no separate Node build step. + +```bash +cp .env.example .env +# Edit .env: set SECRET_KEY, JWT_SECRET_KEY, DATABASE_URL, CORS_ORIGINS, +# and the MYSQL_* passwords (see docs/CONFIG.md for every variable). + +docker compose up -d --build + +# Create the schema and seed the platform data (idempotent, safe to re-run): +docker compose exec api flask db upgrade +docker compose exec api flask seed permissions +docker compose exec api flask seed settings +docker compose exec api flask seed reference-data +``` + +Then browse to the site and complete the first-run setup wizard at `/setup` +(it creates the first admin account and captures site identity). To create the +admin headlessly instead of using the wizard: + +```bash +docker compose exec api flask seed admin --username admin --email admin@facility.example.com +``` + +### Manual path (venv + Node) + +```bash +# Backend +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +cp .env.example .env +# Edit .env with your database credentials and secrets. + +flask db upgrade +flask seed permissions +flask seed settings +flask seed reference-data +flask run + +# Frontend (separate terminal) +cd frontend +npm install +npm run dev # dev server on :5173 +npm run build # production build into frontend/dist (served by Flask) +``` + +Complete first-run setup at `/setup`, or run `flask seed admin` for a headless +admin account. + +To import data from the legacy ShopDB MySQL database (one-time, see +`migrations/DATA_MIGRATION_GUIDE.md`): + +```bash +python scripts/import_from_mysql.py +``` + +For the full per-site deployment runbook see [docs/DEPLOY.md](docs/DEPLOY.md); +for every environment variable and Setting key see [docs/CONFIG.md](docs/CONFIG.md). + +## Configuration + +Environment variables (`.env`): + +| Variable | Description | +|----------|-------------| +| `DATABASE_URL` | MySQL connection string | +| `SECRET_KEY` | Flask secret key | +| `JWT_SECRET_KEY` | JWT signing key | +| `JWT_ACCESS_TOKEN_EXPIRES` | Access token TTL (seconds) | +| `LOG_LEVEL` | Logging verbosity | + +## API Documentation + +The REST API follows standard conventions: + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/machines` | List machines (filterable by type) | +| GET | `/api/machines/:id` | Get machine details | +| POST | `/api/machines` | Create machine | +| PUT | `/api/machines/:id` | Update machine | +| DELETE | `/api/machines/:id` | Soft delete machine | + +Query parameters for list endpoints: +- `page` - Page number (default: 1) +- `per_page` - Items per page (default: 25) +- `sort` - Sort field +- `order` - Sort direction (asc/desc) +- `search` - Search term +- `type` - Filter by asset type (computer, printer, machine, network_device) + +## Plugin System + +ShopDB supports plugins for extending functionality. See `CONTRIBUTING.md` for plugin development guidelines. + +The image bundles ten plugins; only the ones a site installs are loaded: + +- **computers** - Shopfloor PCs and workstations +- **employees** - Employee directory +- **machine** - CNC, CMM, and other shop-floor machines +- **knowledgebase** - Documentation and troubleshooting guides +- **network** - Network devices +- **notifications** - Shopfloor notifications and recognition feed +- **printers** - Extended printer management with Zabbix integration +- **slides** - TV/kiosk slideshows +- **usb** - CMMC USB check-in/out tracking +- **warranty** - Dell warranty lookups + +## Legacy Migration + +This project replicates functionality from the classic ASP/VBScript ShopDB site. Key mappings: + +| Legacy | Modern | +|--------|--------| +| ASP/VBScript | Flask/Python | +| Classic ADO | SQLAlchemy | +| Server-side HTML | Vue 3 SPA | +| Session auth | JWT tokens | + +## License + +Internal use only. diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 7c3f3f8..4503c8f 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -146,7 +146,7 @@ them under `instance/branding/`. |-----|---------|-------| | `site_logo` | `/ge-aerospace-logo.svg` | Header and login-page logo. | | `qr_logo` | `/ge-monogram.svg` | Logo composited in printer QR labels. Blank = no overlay. | -| `badge_logo` | `/ge-aerospace-logo.svg` | Logo on the equipment badge print page. | +| `badge_logo` | `/ge-aerospace-logo.svg` | Logo on the machine badge print page. | | `site_favicon` | (empty) | Browser tab favicon. Blank = shipped `/favicon.svg`. | | `brand_primary_color` | (empty) | Primary brand color as a CSS color value (maps to `--primary`). Blank = built-in theme color. | | `brand_primary_dark_color` | (empty) | Primary hover/active color (maps to `--primary-dark`). Blank = auto-derived by darkening the primary color ~15%. | diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index ff80013..f80ee47 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -104,12 +104,12 @@ docker compose exec api flask seed reference-data ## Step 5: Pick plugins to enable -The image bundles ten plugins (computers, employees, equipment, knowledgebase, network, notifications, printers, slides, usb, warranty). Only enabled plugins are loaded. +The image bundles eleven plugins (computers, employees, knowledgebase, machines, measuringtools, network, notifications, printers, slides, usb, warranty). Only enabled plugins are loaded. ```bash docker compose exec api flask plugin list docker compose exec api flask plugin install computers -docker compose exec api flask plugin install equipment +docker compose exec api flask plugin install machines # ... repeat for each plugin the site tracks ``` diff --git a/docs/PLUGIN-GUIDE.md b/docs/PLUGIN-GUIDE.md index 95d4d11..01683a9 100644 --- a/docs/PLUGIN-GUIDE.md +++ b/docs/PLUGIN-GUIDE.md @@ -109,7 +109,7 @@ def meta(self) -> PluginMeta: The plugin owns two tables (`plugins/measuringtools/models/measuringtool.py`): - `measuringtooltypes` - the site-managed lookup (Caliper, Micrometer, ...) with a - display color, the same shape as `equipmenttypes` / `computertypes`. + display color, the same shape as `machinetypes` / `computertypes`. - `measuringtools` - the one-to-one extension of a core `Asset`, carrying only the metrology domain fields. @@ -523,7 +523,7 @@ form is `requiresAuth`; the settings subtype page is `requiresAuth + requiresAdm **API client, addition only.** `frontend/src/api/index.js` gets a `measuringtoolsApi` object appended after `warrantyApi` (`list`, `get`, `create`, `update`, `remove`, `calibrationReport`, and a nested `types` CRUD). Do not -reorganize the file; just add the block, mirroring `equipmentApi`. +reorganize the file; just add the block, mirroring `machinesApi`. **Views mirror the master templates.** The frontend has master templates the frontend CLAUDE.md points to (`PrintersList.vue` for lists, `PrinterDetail.vue` diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md index 6508aaa..75d5fcd 100644 --- a/docs/PLUGINS.md +++ b/docs/PLUGINS.md @@ -8,7 +8,7 @@ These plugins are in `plugins/` in this repo. Enable per site with `flask plugin | Plugin | Tracks | Notes | |--------|--------|-------| -| `equipment` | Manufacturing machinery: 5-axis mills, lathes, broachers, heat treatment ovens | Manually entered. See [ADR-005](adr/ADR-005-equipment-vs-measuringtools.md). Subtype tables for FOCAS / CLM / MTConnect controller protocols (planned). | +| `machines` | Manufacturing machinery: 5-axis mills, lathes, broachers, heat treatment ovens | Manually entered. See [ADR-005](adr/ADR-005-equipment-vs-measuringtools.md). Subtype tables for FOCAS / CLM / MTConnect controller protocols (planned). | | `computers` | Shop-floor PCs and engineering workstations | Fed by the PXE pipeline collector per [ADR-006](adr/ADR-006-collector-contract.md). | | `printers` | Network and shop-floor printers | Optional Zabbix integration for supply tracking. Legacy `PrinterData` retiring per ADR-001. | | `network` | Switches, routers, access points, IDFs as locations | Asset-only; cleanest of the bundled set. | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 89ae0fa..6a5a08f 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -33,7 +33,7 @@ shopdb-flask is at `__contract_version__ = '0.5.0'` (pre-1.0). This document cap - Frontend scaffolding skill (the backend has `flask plugin new`; the frontend stub is currently manual copy-paste). - Marketplace listing site (PLUGINS.md is a one-pager; a proper listing with links to sister-site plugins becomes useful when there are more than three external plugins). - Plugin contract surface diff tooling. Today version bumps are manual judgment; a CI check that diffs the contract surface against the previous tag would catch missed bumps. See ADR-002. -- Calibration cycles, maintenance windows, downtime tracking (domain extensions; would likely live in the `equipment` and `measuringtools` plugins). +- Calibration cycles, maintenance windows, downtime tracking (domain extensions; would likely live in the `machines` and `measuringtools` plugins). ### Deferred (out of scope for 1.0) diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 6c34eee..82f0e41 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -58,45 +58,45 @@ export const authApi = { } } -// Equipment API (plugin) -export const equipmentApi = { +// Machines API (plugin) +export const machinesApi = { list(params = {}) { - return api.get('/equipment', { params }) + return api.get('/machines', { params }) }, get(id) { - return api.get(`/equipment/${id}`) + return api.get(`/machines/${id}`) }, getByAsset(assetId) { - return api.get(`/equipment/by-asset/${assetId}`) + return api.get(`/machines/by-asset/${assetId}`) }, create(data) { - return api.post('/equipment', data) + return api.post('/machines', data) }, update(id, data) { - return api.put(`/equipment/${id}`, data) + return api.put(`/machines/${id}`, data) }, delete(id) { - return api.delete(`/equipment/${id}`) + return api.delete(`/machines/${id}`) }, dashboardSummary() { - return api.get('/equipment/dashboard/summary') + return api.get('/machines/dashboard/summary') }, - // Equipment types + // Machine types types: { list(params = {}) { - return api.get('/equipment/types', { params }) + return api.get('/machines/types', { params }) }, get(id) { - return api.get(`/equipment/types/${id}`) + return api.get(`/machines/types/${id}`) }, create(data) { - return api.post('/equipment/types', data) + return api.post('/machines/types', data) }, update(id, data) { - return api.put(`/equipment/types/${id}`, data) + return api.put(`/machines/types/${id}`, data) }, remove(id) { - return api.delete(`/equipment/types/${id}`) + return api.delete(`/machines/types/${id}`) } } } @@ -178,19 +178,19 @@ export const relationshipTypesApi = { } } -// Machine Types API -export const machinetypesApi = { +// Model Types API (the vendor models catalog: modeltypeid) +export const modeltypesApi = { list(params = {}) { - return api.get('/machinetypes', { params }) + return api.get('/modeltypes', { params }) }, create(data) { - return api.post('/machinetypes', data) + return api.post('/modeltypes', data) }, update(id, data) { - return api.put(`/machinetypes/${id}`, data) + return api.put(`/modeltypes/${id}`, data) }, delete(id) { - return api.delete(`/machinetypes/${id}`) + return api.delete(`/modeltypes/${id}`) } } @@ -667,8 +667,8 @@ export const reportsApi = { list() { return api.get('/reports') }, - equipmentByType(params = {}) { - return api.get('/reports/equipment-by-type', { params }) + machinesByType(params = {}) { + return api.get('/reports/machines-by-type', { params }) }, assetsByStatus(params = {}) { return api.get('/reports/assets-by-status', { params }) diff --git a/frontend/src/assets/style.css b/frontend/src/assets/style.css index c41b18a..906345e 100644 --- a/frontend/src/assets/style.css +++ b/frontend/src/assets/style.css @@ -976,7 +976,7 @@ td.actions { width: 180px; height: 180px; object-fit: contain; - background: rgba(0, 0, 0, 0.3); + background: var(--bg); border-radius: 0.25rem; padding: 1rem; } @@ -1208,15 +1208,16 @@ td.actions { align-items: center; justify-content: space-between; padding: 0.75rem; - background: rgba(0, 0, 0, 0.3); + background: var(--bg); + border: 1px solid var(--border); border-radius: 0.25rem; text-decoration: none; color: inherit; - transition: background 0.15s; + transition: border-color 0.15s; } .equipment-item:hover { - background: rgba(255, 255, 255, 0.1); + border-color: var(--primary); } .equipment-info { @@ -1251,7 +1252,8 @@ td.actions { .network-item { padding: 0.75rem; - background: rgba(0, 0, 0, 0.3); + background: var(--bg); + border: 1px solid var(--border); border-radius: 0.25rem; } diff --git a/frontend/src/components/AssetRelationships.vue b/frontend/src/components/AssetRelationships.vue index c01a1bb..a8e19c2 100644 --- a/frontend/src/components/AssetRelationships.vue +++ b/frontend/src/components/AssetRelationships.vue @@ -27,14 +27,14 @@ :key="rel.relationshipid" class="relationship-item" > -
+
{{ rel.targetasset?.name || rel.targetasset?.assetnumber || 'Unknown' }}
{{ rel.relationshiptypename }} - {{ rel.targetasset?.assettype }} + {{ rel.targetasset?.assettypename || rel.targetasset?.assettype }}
{{ rel.notes }}
@@ -59,14 +59,14 @@ :key="rel.relationshipid" class="relationship-item" > -
+
{{ rel.sourceasset?.name || rel.sourceasset?.assetnumber || 'Unknown' }}
{{ rel.relationshiptypename }} - {{ rel.sourceasset?.assettype }} + {{ rel.sourceasset?.assettypename || rel.sourceasset?.assettype }}
{{ rel.notes }}
@@ -173,7 +173,7 @@ diff --git a/frontend/src/components/ShopFloorMap.vue b/frontend/src/components/ShopFloorMap.vue index 428734d..fb534cd 100644 --- a/frontend/src/components/ShopFloorMap.vue +++ b/frontend/src/components/ShopFloorMap.vue @@ -1,783 +1,783 @@ - - - - - + + + + + diff --git a/frontend/src/composables/identifierSettings.js b/frontend/src/composables/identifierSettings.js index a9c72b4..d50b203 100644 --- a/frontend/src/composables/identifierSettings.js +++ b/frontend/src/composables/identifierSettings.js @@ -14,7 +14,7 @@ const state = reactive({ let inflight = null -const KEY_RE = /^identifier_(.+?)(?:_(equipment|computer|printer|network_device))?_enabled$/ +const KEY_RE = /^identifier_(.+?)(?:_(machine|computer|printer|network_device))?_enabled$/ function applySetting(key, value) { const match = KEY_RE.exec(key) diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 3d812df..af2d639 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -66,9 +66,9 @@ const routes = [ }, // Print pages (standalone, no sidebar/header) { - path: '/print/equipment-badge/:id', - name: 'print-equipment-badge', - component: () => import('../views/print/EquipmentBadge.vue') + path: '/print/machine-badge/:id', + name: 'print-machine-badge', + component: () => import('../views/print/MachineBadge.vue') }, { path: '/print/printer-qr', diff --git a/frontend/src/router/routes/core.js b/frontend/src/router/routes/core.js index d95db54..d3a9927 100644 --- a/frontend/src/router/routes/core.js +++ b/frontend/src/router/routes/core.js @@ -60,9 +60,9 @@ export default [ meta: { requiresAuth: true, requiresAdmin: true } }, { - path: 'settings/machinetypes', - name: 'machinetypes', - component: () => import('../../views/settings/MachineTypesList.vue'), + path: 'settings/modeltypes', + name: 'modeltypes', + component: () => import('../../views/settings/ModelTypesList.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, { @@ -108,9 +108,9 @@ export default [ meta: { requiresAuth: true, requiresAdmin: true, plugin: 'slides' } }, { - path: 'settings/equipmenttypes', - name: 'equipment-types', - component: () => import('../../views/settings/EquipmentTypesList.vue'), + path: 'settings/machinetypes', + name: 'machinetypes', + component: () => import('../../views/settings/MachineTypesList.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, { diff --git a/frontend/src/router/routes/equipment.js b/frontend/src/router/routes/machines.js similarity index 72% rename from frontend/src/router/routes/equipment.js rename to frontend/src/router/routes/machines.js index 011dd87..a3e4660 100644 --- a/frontend/src/router/routes/equipment.js +++ b/frontend/src/router/routes/machines.js @@ -1,29 +1,29 @@ /** - * Equipment plugin routes + * Machines plugin routes */ export default [ { path: 'machines', name: 'machines', component: () => import('../../views/machines/MachinesList.vue'), - meta: { plugin: 'equipment' } + meta: { plugin: 'machines' } }, { path: 'machines/new', name: 'machine-new', component: () => import('../../views/machines/MachineForm.vue'), - meta: { requiresAuth: true, plugin: 'equipment' } + meta: { requiresAuth: true, plugin: 'machines' } }, { path: 'machines/:id', name: 'machine-detail', component: () => import('../../views/machines/MachineDetail.vue'), - meta: { plugin: 'equipment' } + meta: { plugin: 'machines' } }, { path: 'machines/:id/edit', name: 'machine-edit', component: () => import('../../views/machines/MachineForm.vue'), - meta: { requiresAuth: true, plugin: 'equipment' } + meta: { requiresAuth: true, plugin: 'machines' } } ] diff --git a/frontend/src/utils/assetTypes.js b/frontend/src/utils/assetTypes.js index 677cf91..f110a54 100644 --- a/frontend/src/utils/assetTypes.js +++ b/frontend/src/utils/assetTypes.js @@ -4,7 +4,7 @@ // because the API sends the underscore form and some map data the spaced form. const ASSET_TYPE_LABELS = { - 'equipment': 'Equipment', + 'machine': 'Machines', 'computer': 'Computers', 'printer': 'Printers', 'network_device': 'Network Devices', @@ -12,7 +12,7 @@ const ASSET_TYPE_LABELS = { } const ASSET_TYPE_ROUTES = { - 'equipment': '/machines', + 'machine': '/machines', 'computer': '/pcs', 'printer': '/printers', 'network_device': '/network', @@ -21,7 +21,7 @@ const ASSET_TYPE_ROUTES = { // Plugin-specific id field inside asset.typedata for each asset type. const ASSET_TYPE_ID_KEYS = { - 'equipment': 'equipmentid', + 'machine': 'machineid', 'computer': 'computerid', 'printer': 'printerid', 'network_device': 'networkdeviceid', diff --git a/frontend/src/utils/mapColors.js b/frontend/src/utils/mapColors.js index e7988e3..89c4bdf 100644 --- a/frontend/src/utils/mapColors.js +++ b/frontend/src/utils/mapColors.js @@ -3,7 +3,7 @@ // ShopFloorMap.vue - keep the two in sync if the palette changes. export const assetTypeColorsMap = { - equipment: '#F44336', // Red + machine: '#F44336', // Red computer: '#2196F3', // Blue printer: '#4CAF50', // Green 'network device': '#FF9800', // Orange @@ -15,7 +15,7 @@ const DEFAULT_COLOR = '#BDBDBD' // Canonical form for comparing asset-type strings. The API sends the machine // type as 'network_device' but subtype keys and labels use 'network device', // so normalize underscores to spaces before any comparison. Without this, -// network-device subtypes silently fail to match (equipment/computer/printer +// network-device subtypes silently fail to match (machine/computer/printer // are single words and were unaffected, which is why only network broke). export function normalizeAssetType(assettype) { return (assettype || '').toLowerCase().replace(/_/g, ' ') @@ -33,7 +33,7 @@ export function getAssetTypeColor(assettype) { export function getSubtypeId(asset) { if (!asset || !asset.typedata) return null const typeLower = normalizeAssetType(asset.assettype) - if (typeLower === 'equipment') return asset.typedata.equipmenttypeid + if (typeLower === 'machine') return asset.typedata.machinetypeid if (typeLower === 'computer') return asset.typedata.computertypeid if (typeLower === 'network device') return asset.typedata.networkdevicetypeid if (typeLower === 'printer') return asset.typedata.printertypeid diff --git a/frontend/src/utils/siteSettings.js b/frontend/src/utils/siteSettings.js index 5133c86..e240e19 100644 --- a/frontend/src/utils/siteSettings.js +++ b/frontend/src/utils/siteSettings.js @@ -50,7 +50,7 @@ export async function getQrLogo() { return (value === undefined || value === null) ? '/ge-monogram.svg' : value } -// Logo printed on equipment inspection badges. +// Logo printed on machine inspection badges. export async function getBadgeLogo() { return getSetting('badge_logo', '/ge-aerospace-logo.svg') } diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index d0821c0..76bdfb6 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -15,7 +15,7 @@
Active
-
{{ stats.activemachines || 0 }}
+
{{ stats.activeassets || 0 }}
In Repair
diff --git a/frontend/src/views/MapEditor.vue b/frontend/src/views/MapEditor.vue index e4c452f..fa9b3a6 100644 --- a/frontend/src/views/MapEditor.vue +++ b/frontend/src/views/MapEditor.vue @@ -14,7 +14,7 @@

Assets

Authoritative gauge lab asset reference (if tracked)
-
+
- +

Machine Hardware

- +
@@ -294,7 +294,7 @@ {{ pc.assetnumber }}{{ pc.name ? ` (${pc.name})` : '' }} - Select the PC that controls this equipment + Select the PC that controls this machine
@@ -312,7 +312,7 @@ {{ rt.relationshiptype }} - How the PC connects to this equipment + How the PC connects to this machine
@@ -328,8 +328,8 @@ > - - + +
{{ error }}
@@ -347,7 +347,7 @@ diff --git a/frontend/src/views/measuringtools/MeasuringToolDetail.vue b/frontend/src/views/measuringtools/MeasuringToolDetail.vue index fcfb729..d63903f 100644 --- a/frontend/src/views/measuringtools/MeasuringToolDetail.vue +++ b/frontend/src/views/measuringtools/MeasuringToolDetail.vue @@ -131,6 +131,9 @@ + + +

Notes

@@ -159,6 +162,7 @@ import { colorStyle } from '@/utils/colorStyle' import { measuringtoolsApi } from '../../api' import CustomFieldsSection from '../../components/CustomFieldsSection.vue' import WarrantyPanel from '../../components/WarrantyPanel.vue' +import AssetRelationships from '../../components/AssetRelationships.vue' import { useWarrantyBadge } from '../../composables/warrantyBadge' const route = useRoute() diff --git a/frontend/src/views/pcs/PCDetail.vue b/frontend/src/views/pcs/PCDetail.vue index d59b27f..10070de 100644 --- a/frontend/src/views/pcs/PCDetail.vue +++ b/frontend/src/views/pcs/PCDetail.vue @@ -1,498 +1,502 @@ - - - - - + + + + + diff --git a/frontend/src/views/print/EquipmentBadge.vue b/frontend/src/views/print/MachineBadge.vue similarity index 74% rename from frontend/src/views/print/EquipmentBadge.vue rename to frontend/src/views/print/MachineBadge.vue index a9aeb9d..df7fa50 100644 --- a/frontend/src/views/print/EquipmentBadge.vue +++ b/frontend/src/views/print/MachineBadge.vue @@ -1,168 +1,168 @@ - - - - - + + + + + diff --git a/frontend/src/views/printers/PrinterDetail.vue b/frontend/src/views/printers/PrinterDetail.vue index 77e27b9..6cde8c4 100644 --- a/frontend/src/views/printers/PrinterDetail.vue +++ b/frontend/src/views/printers/PrinterDetail.vue @@ -127,6 +127,9 @@ + + +

Notes

@@ -267,6 +270,7 @@ import { printersApi } from '../../api' import LocationMapTooltip from '../../components/LocationMapTooltip.vue' import CustomFieldsSection from '../../components/CustomFieldsSection.vue' import WarrantyPanel from '../../components/WarrantyPanel.vue' +import AssetRelationships from '../../components/AssetRelationships.vue' import { useWarrantyBadge } from '../../composables/warrantyBadge' import { useIdentifierFlags } from '../../composables/identifierSettings' diff --git a/frontend/src/views/reports/ReportsIndex.vue b/frontend/src/views/reports/ReportsIndex.vue index 43908d4..7e08bbb 100644 --- a/frontend/src/views/reports/ReportsIndex.vue +++ b/frontend/src/views/reports/ReportsIndex.vue @@ -107,8 +107,8 @@
Loading report...
- -
+ +

Total: {{ reportData.total }}

@@ -119,8 +119,8 @@ - - + + @@ -235,7 +235,7 @@ const categoryOrder = ['inventory', 'compliance', 'usage'] // server-side filters each inline report accepts (query params on its endpoint) const reportFilterFields = { - 'equipment-by-type': ['businessunit'], + 'machines-by-type': ['businessunit'], 'assets-by-status': ['assettype', 'businessunit'], 'asset-inventory': ['businessunit', 'location'], 'kb-popularity': ['limit'], @@ -388,8 +388,8 @@ async function runReport(report) { try { let response switch (report.id) { - case 'equipment-by-type': - response = await reportsApi.equipmentByType(params) + case 'machines-by-type': + response = await reportsApi.machinesByType(params) break case 'assets-by-status': response = await reportsApi.assetsByStatus(params) diff --git a/frontend/src/views/reports/WarrantyReport.vue b/frontend/src/views/reports/WarrantyReport.vue index dd27073..5aa499f 100644 --- a/frontend/src/views/reports/WarrantyReport.vue +++ b/frontend/src/views/reports/WarrantyReport.vue @@ -70,7 +70,7 @@ const bucketOrder = [ function formatDate(d) { return new Date(d + 'T00:00:00').toLocaleDateString() } function cardStyle(color) { return { borderTop: `3px solid ${color}` } } function assetLink(a) { - const map = { computer: '/pcs/', printer: '/printers/', network_device: '/network/', equipment: '/machines/' } + const map = { computer: '/pcs/', printer: '/printers/', network_device: '/network/', machine: '/machines/' } return (map[a.assettypename] || '/assets/') + a.assetid } diff --git a/frontend/src/views/settings/AuditLogs.vue b/frontend/src/views/settings/AuditLogs.vue index 54a87c6..c0686a8 100644 --- a/frontend/src/views/settings/AuditLogs.vue +++ b/frontend/src/views/settings/AuditLogs.vue @@ -1,367 +1,367 @@ - - - - - + + + + + diff --git a/frontend/src/views/settings/EquipmentTypesList.vue b/frontend/src/views/settings/EquipmentTypesList.vue deleted file mode 100644 index 29c901b..0000000 --- a/frontend/src/views/settings/EquipmentTypesList.vue +++ /dev/null @@ -1,145 +0,0 @@ - - - diff --git a/frontend/src/views/settings/MachineTypesList.vue b/frontend/src/views/settings/MachineTypesList.vue index 84ec488..3339aa9 100644 --- a/frontend/src/views/settings/MachineTypesList.vue +++ b/frontend/src/views/settings/MachineTypesList.vue @@ -2,188 +2,101 @@
Loading...
-
- - - -
- - diff --git a/frontend/src/views/settings/ModelTypesList.vue b/frontend/src/views/settings/ModelTypesList.vue new file mode 100644 index 0000000..8fd1dad --- /dev/null +++ b/frontend/src/views/settings/ModelTypesList.vue @@ -0,0 +1,277 @@ + + + + + diff --git a/frontend/src/views/settings/ModelsList.vue b/frontend/src/views/settings/ModelsList.vue index 297317f..0a0a38f 100644 --- a/frontend/src/views/settings/ModelsList.vue +++ b/frontend/src/views/settings/ModelsList.vue @@ -44,7 +44,7 @@ {{ m.description }} - +
{{ item.equipmenttype }}
{{ item.machinetype }} {{ item.description }} {{ item.count }}
{{ m.vendor || '-' }}{{ m.machinetype || '-' }}{{ m.modeltype || '-' }} View Docs @@ -108,11 +108,11 @@
- - -
@@ -181,7 +181,7 @@