nitro-v36-with-renderer-v36
/
src /
components /
variables-explorer /
VariablesExplorerConnectView.tsx
VariablesExplorerConnectView.tsx 9.4 Ko
Télécharger le fichier
import { FC, useMemo, useState } from 'react';
import {
createVariablesWebApiClient,
ExplorerConnectField,
ExplorerConnection,
explorerErrorMessage,
GetWebApiHotels,
loadRememberedKeys,
localizeWithFallback,
sanitizeRoomIdInput,
saveRememberedKeys,
validateExplorerConnect,
WebApiVariable
} from '../../api';
import { Button, DraggableWindowPosition, OctaneCardContentView, OctaneCardHeaderView, OctaneCardView, Text } from '../../common';
import { VariablesExplorerPrefill } from '../../state/variablesExplorer';
import { Checkbox } from '../../common/Checkbox';
import { Select } from '../../common/Select';
import { NativeTextTree } from '../../common/native-text/NativeTextTree';
export interface VariablesExplorerConnectViewProps {
prefill: VariablesExplorerPrefill | null;
onConnected: (connection: ExplorerConnection, variables: WebApiVariable[]) => void;
onClose: () => void;
}
const LABEL_CLASS = 'text-[11px] font-bold uppercase tracking-[0.08em] text-[#5b5648]';
const FIELD_CLASS = 'w-full rounded border border-[#b8b2a4] bg-white px-2 py-[5px] text-[12px] text-black';
export const VariablesExplorerConnectView: FC<VariablesExplorerConnectViewProps> = ({ prefill, onConnected, onClose }) => {
const hotels = useMemo(() => GetWebApiHotels(), []);
const [hotelUrl, setHotelUrl] = useState(hotels[0]?.url ?? '');
const [roomId, setRoomId] = useState(prefill?.roomId ? String(prefill.roomId) : '');
const [remembered] = useState(() => (prefill?.readKey || prefill?.writeKey ? null : loadRememberedKeys(hotels[0]?.url ?? '', prefill?.roomId ?? 0)));
const [readKey, setReadKey] = useState(prefill?.readKey ?? remembered?.readKey ?? '');
const [writeKey, setWriteKey] = useState(prefill?.writeKey ?? remembered?.writeKey ?? '');
const [remember, setRemember] = useState(!!remembered);
const [errors, setErrors] = useState<Partial<Record<ExplorerConnectField, string>>>({});
const [message, setMessage] = useState('');
const [busy, setBusy] = useState(false);
const fillRememberedKeys = () => {
if (readKey || writeKey) return;
const keys = loadRememberedKeys(hotelUrl, Number(roomId) || 0);
if (!keys) return;
setReadKey(keys.readKey);
setWriteKey(keys.writeKey);
setRemember(true);
};
const submit = async () => {
if (busy) return;
const result = validateExplorerConnect({ hotelUrl, roomId, readKey, writeKey }, hotels);
setErrors(result.errors);
setMessage('');
if (!result.connection) return;
setBusy(true);
try {
const variables = await createVariablesWebApiClient({ ...result.connection, baseUrl: result.connection.hotelUrl }).listVariables();
saveRememberedKeys(result.connection, remember);
onConnected(result.connection, variables);
} catch (error) {
setMessage(explorerErrorMessage(error));
} finally {
setBusy(false);
}
};
const fieldError = (field: ExplorerConnectField) =>
errors[field] ? (
<span className="text-[11px] text-[#a32a2a]" role="alert">
{errors[field]}
</span>
) : null;
return (
<NativeTextTree>
<OctaneCardView
className="min-w-[380px] max-w-[380px] octane-variables-explorer-window"
theme="primary-slim"
uniqueKey="variables-explorer"
windowPosition={DraggableWindowPosition.TOP_LEFT}
offsetLeft={120}
offsetTop={60}
>
<OctaneCardHeaderView headerText={localizeWithFallback('wiredmenu.variables_explorer.title', 'Variables Explorer')} onCloseClick={onClose} />
<OctaneCardContentView className="text-black bg-[#f4efe3] p-3" overflow="auto">
<form
className="rounded border border-[#c8c2b2] bg-white p-4 flex flex-col gap-3"
noValidate
onKeyDown={(event) => {
if (event.key !== 'Enter' || !(event.target instanceof HTMLInputElement) || event.target.type === 'checkbox') return;
event.preventDefault();
void submit();
}}
onSubmit={(event) => {
event.preventDefault();
void submit();
}}
>
<div className="flex flex-col gap-1">
<span className="text-[10px] font-bold uppercase tracking-[0.14em] text-[#8a8476]">
{localizeWithFallback('wiredmenu.variables_explorer.eyebrow', 'Variables Explorer')}
</span>
<Text bold className="text-[18px]">
{localizeWithFallback('wiredmenu.variables_explorer.connect.title', 'Connect to a room')}
</Text>
<Text className="text-[#555]">
{localizeWithFallback(
'wiredmenu.variables_explorer.connect.desc',
'Browse and manage variables and variable holders in your room, making use of the Variables Web API.'
)}
</Text>
</div>
<label className="flex flex-col gap-1">
<span className={LABEL_CLASS}>{localizeWithFallback('wiredmenu.variables_explorer.hotel', 'Hotel')}</span>
<Select className={FIELD_CLASS} value={hotelUrl} onChange={(event) => setHotelUrl(event.target.value)}>
{!hotels.length && <option value="">-</option>}
{hotels.map((hotel) => (
<option key={hotel.url} value={hotel.url}>
{hotel.name}
</option>
))}
</Select>
{fieldError('hotelUrl')}
</label>
<label className="flex flex-col gap-1">
<span className={LABEL_CLASS}>{localizeWithFallback('wiredmenu.variables_explorer.room_id', 'Room ID')}</span>
<input
className={FIELD_CLASS}
inputMode="numeric"
type="text"
value={roomId}
onBlur={fillRememberedKeys}
onChange={(event) => setRoomId(sanitizeRoomIdInput(event.target.value))}
/>
{fieldError('roomId')}
</label>
<label className="flex flex-col gap-1">
<span className={LABEL_CLASS}>{localizeWithFallback('wiredmenu.variables_explorer.read_key', 'Wired read key')}</span>
<input
autoComplete="off"
className={`${FIELD_CLASS} font-mono`}
spellCheck={false}
type="password"
value={readKey}
onChange={(event) => setReadKey(event.target.value.trim())}
/>
{fieldError('readKey')}
</label>
<label className="flex flex-col gap-1">
<span className={LABEL_CLASS}>{localizeWithFallback('wiredmenu.variables_explorer.write_key', 'Wired write key')}</span>
<input
autoComplete="off"
className={`${FIELD_CLASS} font-mono`}
spellCheck={false}
type="password"
value={writeKey}
onChange={(event) => setWriteKey(event.target.value.trim())}
/>
{fieldError('writeKey')}
</label>
<label className="flex items-center gap-2 cursor-pointer">
<Checkbox checked={remember} onCheckedChange={(nextChecked) => setRemember(nextChecked)} />
<Text small>{localizeWithFallback('wiredmenu.variables_explorer.remember', 'Remember the keys for this room on this device')}</Text>
</label>
{!!message && (
<div className="rounded border border-[#d9a3a3] bg-[#fbeaea] px-2 py-1 text-[12px] text-[#8c2424]" role="alert">
{message}
</div>
)}
<Button disabled={busy || !hotels.length} variant="success" onClick={() => void submit()}>
{localizeWithFallback('wiredmenu.variables_explorer.open', 'Open explorer')}
</Button>
</form>
</OctaneCardContentView>
</OctaneCardView>
</NativeTextTree>
);
};
