Move Resos API key to central Settings service; add sidebar scrollbar styling
Removes the standalone resos_api_key from the forecasting app's own system_config table. All credential fetches now go through central_settings.get_resos_credentials() / get_resos_credentials_sync() which pull from the Settings app (LXC 116) via the internal integration endpoint — the same pattern already used for NewBook. The Resos API Config section is removed from the forecasting Settings page; users manage the key in the central Settings app instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9057d764fa
commit
3650c92175
7 changed files with 51 additions and 282 deletions
|
|
@ -361,3 +361,18 @@ tr:hover td { background: #f8fafc; }
|
|||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar scrollbar */
|
||||
.nav-scroll::-webkit-scrollbar,
|
||||
.sidebar::-webkit-scrollbar,
|
||||
.sidebar-nav::-webkit-scrollbar { width: 4px; }
|
||||
.nav-scroll::-webkit-scrollbar-track,
|
||||
.sidebar::-webkit-scrollbar-track,
|
||||
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }
|
||||
.nav-scroll::-webkit-scrollbar-thumb,
|
||||
.sidebar::-webkit-scrollbar-thumb,
|
||||
.sidebar-nav::-webkit-scrollbar-thumb { background: rgba(201,168,76,0.35); border-radius: 2px; }
|
||||
.nav-scroll::-webkit-scrollbar-thumb:hover,
|
||||
.sidebar::-webkit-scrollbar-thumb:hover,
|
||||
.sidebar-nav::-webkit-scrollbar-thumb:hover { background: rgba(201,168,76,0.65); }
|
||||
.nav-scroll, .sidebar, .sidebar-nav { scrollbar-width: thin; scrollbar-color: rgba(201,168,76,0.35) transparent; }
|
||||
|
|
|
|||
|
|
@ -2115,11 +2115,6 @@ const NewbookPage: React.FC = () => {
|
|||
// RESOS SETTINGS PAGE
|
||||
// ============================================
|
||||
|
||||
interface ResosSettings {
|
||||
resos_api_key: string | null
|
||||
resos_api_key_set: boolean
|
||||
}
|
||||
|
||||
interface ResosCustomField {
|
||||
id: string
|
||||
name: string
|
||||
|
|
@ -2156,11 +2151,7 @@ const ResosPage: React.FC = () => {
|
|||
return (
|
||||
<div style={styles.section}>
|
||||
<h2 style={styles.sectionTitle}>Resos Settings</h2>
|
||||
<p style={styles.hint}>Configure your Resos API connection and sync settings for restaurant reservation management.</p>
|
||||
|
||||
<ResosAPIConfigSection />
|
||||
|
||||
<div style={styles.divider} />
|
||||
<p style={styles.hint}>Configure Resos sync settings for restaurant reservation management. The Resos API key is managed centrally in the <strong>Settings</strong> app.</p>
|
||||
|
||||
<ResosCustomFieldMappingSection />
|
||||
|
||||
|
|
@ -2183,149 +2174,6 @@ const ResosPage: React.FC = () => {
|
|||
)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RESOS API CONFIGURATION SECTION
|
||||
// ============================================
|
||||
|
||||
const ResosAPIConfigSection: React.FC = () => {
|
||||
const [apiKey, setApiKey] = useState('')
|
||||
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'success' | 'error'>('idle')
|
||||
const [testStatus, setTestStatus] = useState<'idle' | 'testing' | 'success' | 'error'>('idle')
|
||||
const [testMessage, setTestMessage] = useState('')
|
||||
|
||||
const { data: settings, isLoading } = useQuery({
|
||||
queryKey: ['resos-settings'],
|
||||
queryFn: async () => {
|
||||
const response = await fetch('/forecasting/api/config/settings/resos')
|
||||
if (!response.ok) throw new Error('Failed to fetch settings')
|
||||
return response.json() as Promise<ResosSettings>
|
||||
},
|
||||
staleTime: 30000,
|
||||
})
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaveStatus('saving')
|
||||
try {
|
||||
const response = await fetch('/forecasting/api/config/settings/resos', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
resos_api_key: apiKey || undefined,
|
||||
})
|
||||
})
|
||||
if (!response.ok) throw new Error('Failed to save')
|
||||
setSaveStatus('success')
|
||||
setApiKey('')
|
||||
setTimeout(() => setSaveStatus('idle'), 3000)
|
||||
} catch {
|
||||
setSaveStatus('error')
|
||||
setTimeout(() => setSaveStatus('idle'), 3000)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTestConnection = async () => {
|
||||
setTestStatus('testing')
|
||||
setTestMessage('')
|
||||
try {
|
||||
const response = await fetch('/forecasting/api/config/settings/resos/test', {
|
||||
method: 'POST',
|
||||
})
|
||||
const data = await response.json()
|
||||
if (response.ok) {
|
||||
setTestStatus('success')
|
||||
setTestMessage(data.message || 'Connection successful!')
|
||||
} else {
|
||||
setTestStatus('error')
|
||||
setTestMessage(data.detail || 'Connection failed')
|
||||
}
|
||||
} catch {
|
||||
setTestStatus('error')
|
||||
setTestMessage('Connection failed')
|
||||
}
|
||||
setTimeout(() => {
|
||||
setTestStatus('idle')
|
||||
setTestMessage('')
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div style={styles.loading}>Loading settings...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.apiConfigRow}>
|
||||
<div style={styles.apiConfigLeft}>
|
||||
<h3 style={styles.subsectionTitle}>API Configuration</h3>
|
||||
|
||||
<div style={styles.form}>
|
||||
<label style={styles.label}>
|
||||
<span>API Key</span>
|
||||
<div style={styles.inputWithStatus}>
|
||||
<input
|
||||
type="password"
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
placeholder={settings?.resos_api_key_set ? '••••••••' : 'Enter API key'}
|
||||
style={styles.input}
|
||||
/>
|
||||
{settings?.resos_api_key_set && (
|
||||
<span style={styles.keyStatus}>Key configured</span>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div style={styles.buttonRow}>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saveStatus === 'saving'}
|
||||
style={mergeStyles(
|
||||
buttonStyle('primary'),
|
||||
saveStatus === 'success' ? { background: colors.success } : {},
|
||||
saveStatus === 'error' ? { background: colors.error } : {}
|
||||
)}
|
||||
>
|
||||
{saveStatus === 'saving' ? 'Saving...' :
|
||||
saveStatus === 'success' ? 'Saved!' :
|
||||
saveStatus === 'error' ? 'Error' : 'Save Settings'}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleTestConnection}
|
||||
disabled={testStatus === 'testing'}
|
||||
style={buttonStyle('outline')}
|
||||
>
|
||||
{testStatus === 'testing' ? 'Testing...' : 'Test Connection'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{testMessage && (
|
||||
<div style={{
|
||||
...styles.statusMessage,
|
||||
background: testStatus === 'success' ? colors.successBg : colors.errorBg,
|
||||
color: testStatus === 'success' ? colors.success : colors.error,
|
||||
}}>
|
||||
{testMessage}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={styles.apiConfigRight}>
|
||||
<h3 style={styles.subsectionTitle}>Connection Status</h3>
|
||||
<div style={styles.statusGridVertical}>
|
||||
<div style={styles.statusItem}>
|
||||
<span style={styles.statusLabel}>API Key</span>
|
||||
<span style={settings?.resos_api_key_set ? styles.statusOk : styles.statusPending}>
|
||||
{settings?.resos_api_key_set ? 'Configured' : 'Not set'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RESOS CUSTOM FIELD MAPPING SECTION
|
||||
// ============================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue