AdminSettings: Workforce location picker with fetch button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-07 12:52:37 +00:00
parent 1dcf8bfbcb
commit 1ccaaed8be

View file

@ -33,6 +33,7 @@ const FIELD_LABELS: Record<string, string> = {
client_id: 'Client ID', host: 'Host', port: 'Port', database: 'Database',
graphql_endpoint: 'GraphQL Endpoint', bearer_token: 'Bearer Token',
email: 'Email', sync_hours: 'Sync Interval (hours)', user: 'Username', pass: 'Password',
location_id: 'Default Location',
}
export function AdminSettings({ user }: { user: User }) {
@ -96,6 +97,8 @@ function IntegrationCard({ integration, onSaved }: { integration: Integration; o
const [testing, setTesting] = useState(false)
const [testResult, setTestResult] = useState<{ ok: boolean; error?: string } | null>(null)
const [enabled, setEnabled] = useState(integration.enabled)
const [wfLocations, setWfLocations] = useState<{ id: string; name: string; short_name: string | null }[]>([])
const [fetchingLocs, setFetchingLocs] = useState(false)
function openForm() {
const initial: Record<string, string> = {}
@ -175,7 +178,50 @@ function IntegrationCard({ integration, onSaved }: { integration: Integration; o
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '1rem' }}>
{/* Config fields (plaintext) */}
{Object.entries(integration.config).map(([k]) => (
{Object.entries(integration.config).map(([k]) => {
if (integration.slug === 'workforce' && k === 'location_id') {
return (
<div key={k} style={{ display: 'flex', flexDirection: 'column', gap: '0.3rem' }}>
<span style={{ fontSize: '0.78rem', fontWeight: 600, color: 'var(--text-mid)' }}>
Default Location
</span>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<select value={form[k] ?? ''} onChange={e => setField(k, e.target.value)}
style={{ ...inputStyle, flex: 1 }}>
<option value=""> Select location </option>
{wfLocations.map(l => (
<option key={l.id} value={l.id}>
{l.name}{l.short_name ? ` (${l.short_name})` : ''}
</option>
))}
{wfLocations.length === 0 && form[k] && (
<option value={form[k]}>Location ID: {form[k]}</option>
)}
</select>
<button
onClick={async () => {
setFetchingLocs(true)
try {
const res = await fetch('/settings/api/integrations/workforce/locations', { credentials: 'include' })
if (res.ok) setWfLocations(await res.json())
} finally { setFetchingLocs(false) }
}}
disabled={fetchingLocs}
style={{
background: 'var(--body-bg)', border: '1px solid var(--card-border)',
borderRadius: '6px', padding: '0.45rem 0.75rem', fontSize: '0.78rem',
cursor: fetchingLocs ? 'wait' : 'pointer', color: 'var(--text-dark)',
whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: '0.35rem',
}}
>
<RefreshCw size={12} strokeWidth={1.75} style={{ animation: fetchingLocs ? 'spin 1s linear infinite' : 'none' }} />
{fetchingLocs ? 'Fetching…' : 'Fetch locations'}
</button>
</div>
</div>
)
}
return (
<label key={k} style={{ display: 'flex', flexDirection: 'column', gap: '0.3rem' }}>
<span style={{ fontSize: '0.78rem', fontWeight: 600, color: 'var(--text-mid)' }}>
{FIELD_LABELS[k] ?? k}
@ -183,7 +229,8 @@ function IntegrationCard({ integration, onSaved }: { integration: Integration; o
<input value={form[k] ?? ''} onChange={e => setField(k, e.target.value)}
style={inputStyle} />
</label>
))}
)
})}
{/* Secret fields (encrypted) */}
{Object.entries(integration.secrets).map(([k, v]) => (