fix: parse kiosk qr pass urls
This commit is contained in:
@@ -149,12 +149,12 @@ export const getPass = (id: number) => request<VisitRequestView>(`/passes/${id}`
|
|||||||
export const passQrUrl = (id: number) => `/api/passes/${id}/qr.png`;
|
export const passQrUrl = (id: number) => `/api/passes/${id}/qr.png`;
|
||||||
|
|
||||||
// ===== Public pass + self-service kiosk (no login, token-based) =====
|
// ===== Public pass + self-service kiosk (no login, token-based) =====
|
||||||
export const getPublicPass = (token: string) => request<PublicPass>(`/public/passes/${token}`);
|
export const getPublicPass = (token: string) => request<PublicPass>(`/public/passes/${encodeURIComponent(token)}`);
|
||||||
export const publicPassQrUrl = (token: string) => `/api/public/passes/${token}/qr.png`;
|
export const publicPassQrUrl = (token: string) => `/api/public/passes/${encodeURIComponent(token)}/qr.png`;
|
||||||
export const publicCheckIn = (token: string) =>
|
export const publicCheckIn = (token: string) =>
|
||||||
request<AccessAction>(`/public/passes/${token}/check-in`, { method: 'POST' });
|
request<AccessAction>(`/public/passes/${encodeURIComponent(token)}/check-in`, { method: 'POST' });
|
||||||
export const publicCheckOut = (token: string) =>
|
export const publicCheckOut = (token: string) =>
|
||||||
request<AccessAction>(`/public/passes/${token}/check-out`, { method: 'POST' });
|
request<AccessAction>(`/public/passes/${encodeURIComponent(token)}/check-out`, { method: 'POST' });
|
||||||
|
|
||||||
// ===== Stats =====
|
// ===== Stats =====
|
||||||
export const getStatsSummary = () => request<StatsSummary>('/stats/summary');
|
export const getStatsSummary = () => request<StatsSummary>('/stats/summary');
|
||||||
|
|||||||
@@ -5,6 +5,27 @@ import { formatVisitRange } from '../status';
|
|||||||
import { useQrScanner } from '../useQrScanner';
|
import { useQrScanner } from '../useQrScanner';
|
||||||
import { playChime } from '../chime';
|
import { playChime } from '../chime';
|
||||||
|
|
||||||
|
function extractQrToken(raw: string): string {
|
||||||
|
const text = raw.trim();
|
||||||
|
if (!text) return '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(text);
|
||||||
|
const passMatch = url.pathname.match(/\/pass\/([^/?#]+)/);
|
||||||
|
if (passMatch?.[1]) return decodeURIComponent(passMatch[1]);
|
||||||
|
|
||||||
|
const apiMatch = url.pathname.match(/\/public\/passes\/([^/?#]+)/);
|
||||||
|
if (apiMatch?.[1]) return decodeURIComponent(apiMatch[1]);
|
||||||
|
} catch {
|
||||||
|
// Not a URL; fall through to path/token parsing.
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathMatch = text.match(/\/pass\/([^/?#]+)/) ?? text.match(/\/public\/passes\/([^/?#]+)/);
|
||||||
|
if (pathMatch?.[1]) return decodeURIComponent(pathMatch[1]);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public entrance kiosk (no login). The visitor scans their phone QR, the
|
* Public entrance kiosk (no login). The visitor scans their phone QR, the
|
||||||
* approved pass is shown, and they tap 입장/퇴장 to self check-in/out.
|
* approved pass is shown, and they tap 입장/퇴장 to self check-in/out.
|
||||||
@@ -19,7 +40,7 @@ export const KioskPage: React.FC = () => {
|
|||||||
const [manual, setManual] = useState('');
|
const [manual, setManual] = useState('');
|
||||||
|
|
||||||
const resolveToken = async (raw: string) => {
|
const resolveToken = async (raw: string) => {
|
||||||
const t = raw.trim();
|
const t = extractQrToken(raw);
|
||||||
if (!t || busy || token) return;
|
if (!t || busy || token) return;
|
||||||
setError(null);
|
setError(null);
|
||||||
scanner.stop();
|
scanner.stop();
|
||||||
|
|||||||
Reference in New Issue
Block a user