diff --git a/manager_dashboard/src/pages/Settings/tabs/AppInfoTab.jsx b/manager_dashboard/src/pages/Settings/tabs/AppInfoTab.jsx
index 330fd6a..3a858df 100644
--- a/manager_dashboard/src/pages/Settings/tabs/AppInfoTab.jsx
+++ b/manager_dashboard/src/pages/Settings/tabs/AppInfoTab.jsx
@@ -302,7 +302,7 @@ function DataTransferSection() {
}
-function QRModal({ url, onClose }) {
+function QRModal({ url, caption = 'Σαρώστε με το κινητό για σύνδεση στο σύστημα.', onClose }) {
return (
-
- Σαρώστε με το κινητό για σύνδεση στο σύστημα.
-
+
{caption}
)
}
+const IPV4 = /^\d{1,3}(\.\d{1,3}){3}$/
+
+// Address phones use on the LAN. HOST_IP from the server wins; otherwise, if this
+// dashboard was itself opened by IP (http://192.168.x.x:8081), that IP is the server.
+function resolveLanIp(status) {
+ if (status?.lan_ip) return status.lan_ip
+ const host = window.location.hostname
+ return IPV4.test(host) && !host.startsWith('127.') ? host : null
+}
+
+// Pairing URL: opens the waiter app when scanned with the phone camera, and the
+// native app reads the same code (?pair=) to pair with this venue.
+function waiterPairUrl(lanIp, siteId) {
+ return `http://${lanIp}/` + (siteId ? `?pair=${encodeURIComponent(siteId)}` : '')
+}
+
function formatUptime(seconds) {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
@@ -334,7 +348,7 @@ export default function AppInfoTab() {
const user = useAuthStore(s => s.user)
const qc = useQueryClient()
const [refreshing, setRefreshing] = useState(false)
- const [qrOpen, setQrOpen] = useState(false)
+ const [qr, setQr] = useState(null) // { url, caption } | null
const { data: status, isLoading } = useQuery({
queryKey: ['system-status'],
queryFn: () => client.get('/api/system/status').then(r => r.data),
@@ -414,13 +428,46 @@ export default function AppInfoTab() {
{new Date(status.expires_at).toLocaleDateString('el-GR')}
>
)}
+ {(() => {
+ const lanIp = resolveLanIp(status)
+ if (!lanIp) {
+ return (
+ <>
+ Τοπική πρόσβαση
+
+ Άγνωστη IP του server — ορίστε HOST_IP στο .env
+
+ >
+ )
+ }
+ const pairUrl = waiterPairUrl(lanIp, status?.site_id)
+ return (
+ <>
+ Σερβιτόροι (WiFi)
+
+ http://{lanIp}
+
+
+ Διαχείριση (WiFi)
+ http://{lanIp}/manager
+ >
+ )
+ })()}
{status?.waiter_domain && (
<>
Waiter Domain
{status.waiter_domain}
)}
- {qrOpen && status?.waiter_domain && (
- setQrOpen(false)} />
- )}
+ {qr && setQr(null)} />}
)
}