New README with up to date informations
This commit is contained in:
288
README.md
288
README.md
@ -1,20 +1,278 @@
|
||||
# Gyoza (gyoza2)
|
||||
# Gyoza
|
||||
|
||||
HFSPlay stock management
|
||||
> Inventory and issue tracking for an arcade machine restoration organization (HFSPlay).
|
||||
|
||||
## TODO
|
||||
- [ ] fix style
|
||||
- [x] handle file upload
|
||||
- [ ] editable categories
|
||||
- [x] qrcode generation
|
||||
- [x] fix search to be persistent
|
||||
- [x] fix comments to be more legible
|
||||
- [ ] fix comments to allow resolution
|
||||
- [ ] batch print QR codes
|
||||
- [x] fix tables pagination
|
||||
A single-page application built with **Quasar Framework (Vue 3)** on the frontend and **PocketBase** as the backend. Gyoza replaces a previous Firebase-based version and provides a comprehensive system for cataloging, managing, and tracking items (machines, accessories) with per-item event history and task management.
|
||||
|
||||
## Ideas
|
||||
---
|
||||
|
||||
### For real production
|
||||
## Architecture
|
||||
|
||||
Add auto backup to a remote minio server
|
||||
```
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Gyoza Architecture │
|
||||
├──────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Browser (SPA) │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ Quasar Framework (Vue 3 + Vite) │ │
|
||||
│ │ ├── Vuex Store (core module) │ │
|
||||
│ │ ├── Vue Router (hash mode) │ │
|
||||
│ │ ├── Quasar Plugins (Dialog, Notify, etc.) │ │
|
||||
│ │ ├── PocketBase JS SDK (superuser auth) │ │
|
||||
│ │ └── QRCode / Image Compression │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ HTTP REST API │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ PocketBase Server (Go binary) │ │
|
||||
│ │ ├── SQLite Database (pb_data/data.db) │ │
|
||||
│ │ │ ├── categories │ │
|
||||
│ │ │ ├── stock (unique ref, short_id) │ │
|
||||
│ │ │ └── history (ref index, file uploads) │ │
|
||||
│ │ ├── File Storage (history images) │ │
|
||||
│ │ └── pb_migrations (schema versioning) │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Auth: SHA-256 credential check (UI gate) + │
|
||||
│ PocketBase superuser for all API calls │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|---|---|
|
||||
| **Framework** | Quasar v2.16 (Vue 3.4, Composition & Options API) |
|
||||
| **Routing** | Vue Router 4 (hash mode) |
|
||||
| **State** | Vuex 4 |
|
||||
| **Backend** | PocketBase v0.26.1 (Go binary, SQLite, REST API, file storage) |
|
||||
| **Storage** | SQLite (`pb_data/data.db`) + file storage for images |
|
||||
| **Build** | Vite via `@quasar/app-vite` |
|
||||
| **Lint** | ESLint (Standard + Vue 3 Essential) |
|
||||
| **Key Libs** | `pocketbase` SDK, `qrcode`, `browser-image-compression`, `axios` |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Authentication
|
||||
- Custom frontend login with SHA-256 credential verification
|
||||
- Credentials persisted in `localStorage`
|
||||
- Router guard redirects unauthenticated users to login
|
||||
- Separate from PocketBase auth (which uses hardcoded superuser credentials for all API calls)
|
||||
|
||||
### Dashboard (`/dashboard`)
|
||||
- **Global Search** — search items by reference (`ref`) or name (minimum 3 characters). Query persists in the URL.
|
||||
- **Activity Feed** — last 15 history entries across all items, with relative timestamps and links to the associated item.
|
||||
|
||||
### Category Browsing (`/category/:categoryCode`)
|
||||
- Paginated, sortable table of all items in a category
|
||||
- Columns: Reference, Name, Condition, Working, In Stock, Owner, Deleted
|
||||
- Toggle to show/hide soft-deleted items
|
||||
- In-table search with URL-persisted query
|
||||
- Condition state shown as a colored chip:
|
||||
- **Neuf** (green), **Excellent** (green), **Bon** (lime), **Moyen** (orange), **Abimé** (red), **Inutilisable** (black)
|
||||
- "Add Item" button per category in the sidebar generates the next sequential reference (e.g., `CONS-0004`)
|
||||
|
||||
### Item Detail (`/item/:itemRef`)
|
||||
- **Edit fields:** Name, Owner, Cosmetic Condition, Working (true/false/null), In Stock (available), Deleted (soft delete), Comment
|
||||
- **QR Code generation** — generates a QR code encoding the item's URL, previewable in a dialog and printable in a new window
|
||||
- **History timeline** — chronological list of all events/comments for the item:
|
||||
- Add new entries as either **Info** (comment) or **Task** (issue)
|
||||
- Upload photos with client-side compression (browser-image-compression, max 1MB, 2000px)
|
||||
- Click an entry to see full details, photos, and metadata
|
||||
- Mark tasks as resolved (records `solved_at` and `solved_by`)
|
||||
- Delete entries with a confirmation dialog
|
||||
|
||||
### Item Detail (short ID) (`/:shortId`)
|
||||
- Short numeric IDs can be used to access an item directly (extracted from QR codes)
|
||||
- Automatically resolves the `short_id` to the full `ref` and redirects
|
||||
|
||||
### Tasks (`/tasks`)
|
||||
- Consolidated view of all issue-type history entries across all items
|
||||
- Shows item context (category, name, reference) per task
|
||||
- Mark tasks as done directly
|
||||
- Delete tasks
|
||||
- Navigate to the associated item
|
||||
|
||||
### Print Settings (`/print-settings`)
|
||||
- Preview layout with configurable columns and rows (grid)
|
||||
- Adjustable gap between cells
|
||||
- A4 print page generation with QR codes + item names for the latest 24 items
|
||||
- Generates batched QR code sheets in a printable HTML page
|
||||
|
||||
### Sidebar Navigation
|
||||
- Category list with item counts (the latest sequential reference number per category)
|
||||
- Quick-add button per category
|
||||
- **Utilitaires** section: Tasks and Print shortcuts
|
||||
|
||||
### User Identity
|
||||
- On first visit after login, the app prompts for a display name (pseudo)
|
||||
- Stored in `localStorage` and displayed in the header
|
||||
- Used as the author for history entries and task resolutions
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### `categories` collection
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `id` | text (PK) | Auto-generated |
|
||||
| `code` | text | Short code (e.g., `CONS`, `ACC`) |
|
||||
| `name` | text | Display name (e.g., `Console`, `Accessoire`) |
|
||||
| `created` / `updated` | autodate | Timestamps |
|
||||
|
||||
### `stock` collection
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `id` | text (PK) | Auto-generated |
|
||||
| `ref` | text (unique) | Sequential reference (e.g., `CONS-0001`) |
|
||||
| `short_id` | number | Numeric short ID for QR codes |
|
||||
| `name` | text | Item name |
|
||||
| `type` | text | Category name (e.g., `Console`) |
|
||||
| `state` | text | Condition: `Neuf`, `Excellent`, `Bon`, `Moyen`, `Abimé`, `Inutilisable` |
|
||||
| `working` | bool/null | Functional status (true/false/null) |
|
||||
| `available` | bool | Currently in stock |
|
||||
| `owner` | text | Owner (default: `HFS`) |
|
||||
| `comment` | text | Free-form notes |
|
||||
| `deleted` | bool | Soft delete flag |
|
||||
| `created` / `updated` | autodate | Timestamps |
|
||||
|
||||
### `history` collection
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `id` | text (PK) | Auto-generated |
|
||||
| `ref` | text (indexed) | References `stock.ref` |
|
||||
| `type` | select | `comment` or `issue` |
|
||||
| `text` | text | Event description |
|
||||
| `user` | text | Display name of the author |
|
||||
| `date` | date | Event date |
|
||||
| `image_file` | file | Uploaded photo (thumb: 300x300f, max 1 file) |
|
||||
| `image` | text | Legacy Firebase image name |
|
||||
| `solved_at` | date | When the issue was resolved |
|
||||
| `solved_by` | text | Who resolved the issue |
|
||||
| `created` / `updated` | autodate | Timestamps |
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
- **Node.js** >= 16
|
||||
- **pnpm** (recommended) or npm
|
||||
- PocketBase binary (place `pocketbase` in `./pocketbase/`)
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Start PocketBase (in a separate terminal)
|
||||
./pocketbase/pocketbase serve --dir ./pocketbase/pb_data
|
||||
|
||||
# Start the development server
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
The frontend runs on `http://localhost:9000` and connects to PocketBase at `http://127.0.0.1:8090` in development, or `https://stock.hfsplay.fr` in production (see `src/boot/pocketbase.js`).
|
||||
|
||||
### Production Build
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Output goes to `dist/spa/`.
|
||||
|
||||
---
|
||||
|
||||
## Scripts
|
||||
|
||||
Data migration scripts are located in `scripts/`:
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| `import_categories.js` | Import categories from legacy Firebase JSON backup |
|
||||
| `import_stock.js` | Import stock items from legacy Firebase JSON backup |
|
||||
| `import_history.js` | Import history entries from legacy Firebase JSON backup |
|
||||
| `import_history_files.js` | Upload historical images to PocketBase storage (with sharp compression) |
|
||||
| `renumerate.js` | Re-assign sequential `short_id` values to all stock items |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
gyoza2/
|
||||
├── pocketbase/ # PocketBase backend
|
||||
│ ├── pb_data/ # SQLite DB + storage (gitignored)
|
||||
│ └── pb_migrations/ # Schema migration files (9 migrations)
|
||||
├── src/
|
||||
│ ├── boot/ # Startup files (pocketbase.js, helper.js)
|
||||
│ ├── components/ # Feed.vue, GlobalSearch.vue
|
||||
│ ├── css/ # Global styles + Geist font
|
||||
│ ├── layouts/ # DefaultLayout.vue, FullscreenLayout.vue
|
||||
│ ├── pages/ # Dashboard, StockItem, StockItems, TasksPage, PrintSettings, Index
|
||||
│ ├── router/ # Route definitions (hash mode)
|
||||
│ └── store/core/ # Vuex module (state, actions, mutations)
|
||||
├── scripts/ # Data migration utilities
|
||||
├── public/ # Static assets (icons, fonts, logos)
|
||||
├── quasar.config.js # Quasar build configuration
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
- **Superuser-as-client:** PocketBase SDK authenticates as a superuser in the browser. All API calls have full privileges; the frontend login is a UI-level gate only. This simplifies permissions but the superuser credentials are hardcoded in source files.
|
||||
- **Full data load:** All collections are loaded into Vuex on startup via `getFullList()`. There is currently no real-time subscription or incremental sync.
|
||||
- **Hash routing:** The app uses `hash` mode for Vue Router, meaning all URLs are client-side (`/#/dashboard`, etc.). This avoids server-side routing requirements.
|
||||
- **Soft delete:** Items are never truly deleted from the database — the `deleted` boolean flag hides them from normal views.
|
||||
- **French UI:** The entire user interface is in French.
|
||||
|
||||
---
|
||||
|
||||
## Roadmap & Technical Debt
|
||||
|
||||
### Security (high priority)
|
||||
- [ ] **Remove hardcoded superuser credentials** from `src/boot/pocketbase.js` — the password is exposed in every browser. Move to server-side proxy or use PocketBase's built-in auth with proper user records and row-level collection rules.
|
||||
- [ ] **Replace the dual-auth system** — the SHA-256 credential check on the frontend is a UI-only gate while PocketBase superuser creds power the API. Unify into a single PocketBase user auth flow so row-level security can be enforced.
|
||||
- [ ] **Move credential hash to environment variables** — the hardcoded hash in `src/boot/helper.js` should not live in source.
|
||||
|
||||
### Scalability & Performance
|
||||
- [ ] **Replace client-side filtering/pagination with server-side queries** — `StockItems.vue` loads all records then paginates in the browser. Switch to PocketBase's `getList()` with `page`/`perPage` params and server-side `filter`/`sort` strings.
|
||||
- [ ] **Avoid full `getFullList()` on every navigation** — `loadAppData()` fetches all collections each time a page mounts. Implement caching or differential loading.
|
||||
- [ ] **Re-enable real-time subscriptions** — there is commented-out Firebase realtime code in `DefaultLayout.vue`. PocketBase supports realtime SSE subscriptions; use `pb.collection('stock').subscribe('*', ...)` and `pb.collection('history').subscribe('*', ...)` to incrementally update Vuex.
|
||||
|
||||
### Architecture & Refactoring
|
||||
- [ ] **Renamme Vuex state keys from `firebase`** — the store still uses `state.firebase.categories`, `state.firebase.stock`, `state.firebase.history` as legacy names. Rename to something meaningful (e.g., `state.data`, `state.collections`).
|
||||
- [ ] **Add a database relation between `history.ref` and `stock`** — currently `history.ref` is a plain text field with only an index. Convert to a PocketBase relation field for proper referential integrity and expand queries.
|
||||
- [ ] **Clean up unused boilerplate files** — `EssentialLink.vue`, `IndexPage.vue`, `Error404.vue`, `ErrorNotFound.vue`, `MainLayout.vue`, `store/module-example/`.
|
||||
- [ ] **Add proper error handling on all API calls** — most `dispatch` calls in components have `.then()` without a `.catch()`. Network failures or API errors will fail silently.
|
||||
- [ ] **Add TypeScript support** — the project already has `types.d.ts`, `store-flag.d.ts`, and `jsconfig.json`. Migrate incrementally to `lang="ts"` in Vue SFCs.
|
||||
|
||||
### Features
|
||||
- [ ] **Editable categories** — categories are currently imported via script and cannot be managed through the UI.
|
||||
- [ ] **Batch print QR codes** with selectable items and configurable grid layout (Print Settings page currently disabled in sidebar).
|
||||
- [ ] **Resolvable comments** — allow marking non-issue history entries as addressed/resolved.
|
||||
- [ ] **Bulk operations** — multi-select items for batch edit (e.g., bulk owner change, bulk state update).
|
||||
- [ ] **Export inventory to CSV/PDF** — generate reports of current stock.
|
||||
- [ ] **Search by `issue` status** — filter tasks page by open/closed state.
|
||||
- [ ] **Image gallery per item** — the history list already shows photo thumbnails; add a dedicated gallery view for an item's images.
|
||||
- [ ] **Item duplication** — clone an existing item (without history) as a quick way to add similar entries.
|
||||
- [ ] **Remote backup** — auto-backup PocketBase SQLite database and storage files to MinIO or S3.
|
||||
|
||||
### UI / UX
|
||||
- [ ] **Fix Print Settings page** — the sidebar link has `disable` and the page has no way to select which items to print.
|
||||
- [ ] **Persistent sidebar state** — `leftDrawer` opens by default on every page load. Persist the open/closed preference in `localStorage`.
|
||||
- [ ] **Loading/empty states** — many views show raw empty arrays during initial load rather than skeleton loaders or empty-state placeholders.
|
||||
- [ ] **Mobile responsiveness** — the 2-column layout on `StockItem.vue` stacks to a single column below `lg` breakpoint, but the table and sidebar could use further mobile polish.
|
||||
- [ ] **Fix style inconsistencies** — various styling issues noted in commit history and original TODO.
|
||||
|
||||
Reference in New Issue
Block a user