mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 00:01:03 +00:00
68e2e0639b
Both settings shells now read their state, the save and test handlers and the canSave rule from one useAirTrailSettings hook and keep only their own markup.
112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
import React from 'react'
|
|
import { Loader2, Plane, Save } from 'lucide-react'
|
|
import { useTranslation } from '../../i18n'
|
|
import { useAirTrailSettings } from './useAirTrailSettings'
|
|
import AirTrailIcon from '../shared/AirTrailIcon'
|
|
import Section from './Section'
|
|
import ToggleSwitch from './ToggleSwitch'
|
|
import { SETTINGS_BUTTON, SETTINGS_BUTTON_PRIMARY, SettingRow, SettingRows, SettingsHint, StatusPill } from './settingsKit'
|
|
import { EditorField, GRID_2, INPUT } from '../shared/dialogParts'
|
|
import { fs } from '../shared/DialogShell'
|
|
|
|
/**
|
|
* Settings → Integrations → AirTrail. Per-user connection to a self-hosted
|
|
* AirTrail instance (URL + Bearer API key). Mirrors the photo-provider (Immich)
|
|
* connection layout: the two fields side by side, the switches as rows, then
|
|
* Save / Test-connection, with the connection state as a pill in the card's band. The key is stored encrypted and never prefilled.
|
|
*/
|
|
export default function AirTrailConnectionSection(): React.ReactElement {
|
|
const { t } = useTranslation()
|
|
|
|
const {
|
|
url,
|
|
setUrl,
|
|
apiKey,
|
|
setApiKey,
|
|
allowInsecureTls,
|
|
toggleInsecureTls,
|
|
writeEnabled,
|
|
toggleWriteEnabled,
|
|
connected,
|
|
loading,
|
|
saving,
|
|
testing,
|
|
canSave,
|
|
handleSave,
|
|
handleTest,
|
|
} = useAirTrailSettings()
|
|
|
|
const fieldId = (name: string) => `airtrail-${name}`
|
|
|
|
return (
|
|
<Section
|
|
title={t('settings.airtrail.title')}
|
|
icon={AirTrailIcon}
|
|
badge={
|
|
<StatusPill tone={connected ? 'success' : 'neutral'} icon={<span className="h-1.5 w-1.5 rounded-full bg-current" />}>
|
|
{connected ? t('settings.airtrail.connected') : t('settings.airtrail.notConnected')}
|
|
</StatusPill>
|
|
}
|
|
>
|
|
<div className={GRID_2}>
|
|
<EditorField label={t('settings.airtrail.url')} htmlFor={fieldId('url')}>
|
|
<input
|
|
id={fieldId('url')}
|
|
type="url"
|
|
value={url}
|
|
onChange={e => setUrl(e.target.value)}
|
|
placeholder="https://airtrail.example.com"
|
|
className={INPUT}
|
|
/>
|
|
</EditorField>
|
|
|
|
<EditorField label={t('settings.airtrail.apiKey')} htmlFor={fieldId('key')} hint={t('settings.airtrail.apiKeyHint')}>
|
|
<input
|
|
id={fieldId('key')}
|
|
type="password"
|
|
value={apiKey}
|
|
onChange={e => setApiKey(e.target.value)}
|
|
autoComplete="off"
|
|
placeholder={connected && !apiKey ? '••••••••' : t('settings.airtrail.apiKeyPlaceholder')}
|
|
className={INPUT}
|
|
/>
|
|
</EditorField>
|
|
</div>
|
|
|
|
<SettingRows>
|
|
<SettingRow
|
|
label={t('settings.airtrail.allowInsecureTls')}
|
|
control={<ToggleSwitch on={allowInsecureTls} onToggle={toggleInsecureTls} label={t('settings.airtrail.allowInsecureTls')} />}
|
|
/>
|
|
<SettingRow
|
|
label={t('settings.airtrail.writeBack')}
|
|
hint={t('settings.airtrail.writeBackHint')}
|
|
control={<ToggleSwitch on={writeEnabled} onToggle={toggleWriteEnabled} label={t('settings.airtrail.writeBack')} />}
|
|
/>
|
|
</SettingRows>
|
|
|
|
<div className="flex flex-wrap items-center gap-2" style={fs(13, 'body')}>
|
|
<button type="button"
|
|
onClick={handleSave}
|
|
disabled={saving || loading || !canSave}
|
|
className={SETTINGS_BUTTON_PRIMARY}
|
|
>
|
|
<Save size={14} strokeWidth={2.2} /> {t('common.save')}
|
|
</button>
|
|
<button type="button"
|
|
onClick={handleTest}
|
|
disabled={testing || loading || !url.trim()}
|
|
className={SETTINGS_BUTTON}
|
|
>
|
|
{testing
|
|
? <Loader2 size={14} strokeWidth={2.2} className="animate-spin" />
|
|
: <Plane size={14} strokeWidth={2.2} />}
|
|
{t('settings.airtrail.test.button')}
|
|
</button>
|
|
</div>
|
|
|
|
<SettingsHint>{t('settings.airtrail.hint')}</SettingsHint>
|
|
</Section>
|
|
)
|
|
}
|