Fix stale dashboard counts + app pointing at dev Strapi host
Dashboard: the mobile index is keep-alive'd, so the openso-count/shipped useFetch never re-ran on revisit (stale counts). Capture refresh and re-fetch on page re-activation and on app foreground.
App Strapi host: the bundled app baked config.public.strapi from the local .env (a dev host, unreachable from devices) while /api goes to prod — breaking product thumbnails and media uploads. In the app build, strapi base/token now follow apiBase (CAP_STRAPI_BASE/CAP_STRAPI_TOKEN override), so /files-api and /media-api resolve on the same prod host as the API. Web build unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bundled app is a production app (apiBase = app.logship.de), so build it with the prod env. This bakes the PROD Strapi token (dev≠prod), fixing media uploads, and aligns all public config to prod. For a dev app build, override the env/CAP_* vars.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The chat window (/chat) was bundled into the app but unreachable — the desktop <ChatBadge> lives in navbars the /mobile/* pages don't use, and the mobile layout has no shared nav. Add the chat entry to the mobile dashboard: - <ChatBadge> in the header (live unread badge; taps → /chat on mobile), - a gated "Chat" tile in the task grid for discoverability.
Both reuse useChatEligible/useChatUnread (no new state, no server change) and self-hide for non-staff / when chat is disabled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restructure + restyle the mobile dashboard header (Option A)
Modernize the cramped header: replace the centered "LogShip Mobile" title and the 4-button cluster with a clean profile-driven bar.
- Left: gradient avatar (user initials) + first name + warehouse/org; tapping opens the profile sheet. - Right: only the two task actions — scanner toggle and chat (with a live red unread-count badge). - Theme toggle + logout move into the profile sheet (UModal, now titled "Profil"), decluttering the bar.
Chat unread badge reuses useChatUnread; the chat button is gated by useChatEligible (hidden for non-staff). No server change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chat: push to all devices, not only when the user is fully offline
Presence is per-USER (online if ANY socket is connected), but the FCM/web-push fallback was gated on the whole user being offline. With two devices on one login, a foreground device kept the user "online", so the server skipped the push entirely and the backgrounded 2nd device got nothing.
Send the native FCM to ALL the recipient's devices on every message, regardless of presence. Foreground devices receive it silently (no pushNotificationReceived handler → no tray; the live socket already rendered it), so there's no double-notify; backgrounded/killed devices now get the tray notification. Web push stays offline-only (an active browser tab already showed it live).
Applied in both the text (ws.ts) and voice-note (voice.post.ts) paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix background chat push not displaying on Android + in-app toast deep-link
Background FCM pushes played a sound but showed nothing in the tray/heads-up: the 'chat' notification channel was only created from JS (never runs on a killed app), with no small icon and no manifest fallback — so Android 8+ silently dropped the notification (posted to a non-existent channel).
- MainActivity: create the high-importance 'chat' channel NATIVELY in onCreate so it always exists (persists when killed) → heads-up + shade. - ic_stat_notify.xml: a proper white notification small icon. - AndroidManifest: firebase default_notification_icon + default_notification_channel_id='chat'. - fcmNotifier: log sends (no-credential / no-tokens / sent N/M / per-token error) so we can confirm pushes are actually sent vs a display problem. - useChatSocket: the in-app toast's "open" now navigates to /chat on mobile (it previously only toggled the desktop drawer, which doesn't render on mobile).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds image messages to the staff chat, stored self-contained in chat.db like voice notes (no Strapi/iDempiere coupling):
- chatDb: image_notes table + insert/get/link fns + retention sweep. - POST /api/chat/image: multipart upload → image_notes + a message carrying meta {kind:'image', imageId}, fanned out over the socket + pushed (📷 Bild label). Type guard (jpeg/png/webp/gif) + 8MB cap. No post-processing. - GET /api/chat/image/:id: serves the blob, party-authorized like the voice route. - useChat.sendImage (optimistic localUrl preview) + a shared downscaleImage util (≤1600px / q0.82, GIFs passed through) so blobs stay small in sqlite. - Drawer.vue + pages/chat.vue (kept in sync): a 📷 composer button (hidden <input accept=image/*> → Android camera/gallery), an image bubble (thumbnail opens full-size), and image CSS.
Also fixes media loading in the bundled app: <img>/<audio> element loads bypass the fetch wrapper, so chatMediaUrl() makes voice/image URLs absolute + token-bearing in the app, and the voice/image serve routes accept ?access_token= as an auth fallback (web is unchanged — relative + cookie). This also makes VOICE playback work in the app for the first time.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>