mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
refactor(admin): share the admin settings saves and the TREK API lists between desktop and phone
useAdminSettingsActions now holds the OIDC and file type saves and the five place switches, writing through the admin bag both shells already receive. trekApiModel keeps the TREK Places API fields, facts and sources the desktop card and the phone block list.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { adminApi, authApi } from '../../api/client'
|
||||
import { adminApi } from '../../api/client'
|
||||
import { getApiErrorMessage } from '../../types'
|
||||
import {
|
||||
AlertTriangle, CheckCircle, Eye, EyeOff, FileType, Fingerprint, KeyRound, Loader2, LockKeyhole, LogIn,
|
||||
@@ -16,6 +16,7 @@ import GoogleOptions from './GoogleOptions'
|
||||
import GoogleDailyLimitRow from '../../components/Admin/GoogleDailyLimitRow'
|
||||
import ProviderBlock from './ProviderBlock'
|
||||
import TrekApiCard from './TrekApiCard'
|
||||
import { useAdminSettingsActions } from '../../components/Admin/useAdminSettingsActions'
|
||||
import { placesGoogleOnlyHint } from '../../utils/placeSource'
|
||||
import type { TranslationFn } from '../../types'
|
||||
import type { useAdmin } from './useAdmin'
|
||||
@@ -112,22 +113,18 @@ function SaveBar({ onClick, saving, label }: { onClick: () => void; saving: bool
|
||||
export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): React.ReactElement {
|
||||
const {
|
||||
toast,
|
||||
setPlacesPhotosEnabled, setPlacesAutocompleteEnabled, setPlacesDetailsEnabled, setPlacesEnrichEnabled, setPlaceShadowEnabled,
|
||||
placesPhotosEnabled, setPlacesPhotosEnabledState,
|
||||
placesAutocompleteEnabled, setPlacesAutocompleteEnabledState,
|
||||
placesDetailsEnabled, setPlacesDetailsEnabledState,
|
||||
placesEnrichEnabled, setPlacesEnrichEnabledState,
|
||||
placesPhotosEnabled, placesAutocompleteEnabled, placesDetailsEnabled, placesEnrichEnabled,
|
||||
placesGoogleOnly, handleTogglePlacesGoogleOnly,
|
||||
transitProvider, setTransitProviderState,
|
||||
transitGoogleKeySource, setTransitGoogleKeySource,
|
||||
placeShadowEnabled, setPlaceShadowEnabledState,
|
||||
oidcConfig, setOidcConfig, savingOidc, setSavingOidc,
|
||||
placeShadowEnabled,
|
||||
oidcConfig, setOidcConfig, savingOidc,
|
||||
passwordLogin, setPasswordLogin, passwordRegistration, setPasswordRegistration,
|
||||
oidcLogin, setOidcLogin, oidcRegistration, setOidcRegistration,
|
||||
envOverrideOidcOnly, oidcConfigured, requireMfa,
|
||||
passkeyLogin, setPasskeyLogin, passkeyConfigured,
|
||||
webauthnRpId, setWebauthnRpId, webauthnOrigins, setWebauthnOrigins, savingWebauthn, handleSaveWebauthn,
|
||||
allowedFileTypes, setAllowedFileTypes, savingFileTypes, setSavingFileTypes,
|
||||
allowedFileTypes, setAllowedFileTypes, savingFileTypes,
|
||||
mapsKey, setMapsKey, unsplashKey, setUnsplashKey, amapKey, setAmapKey, hasMapsKey, hasAmapKey, keyInputProps, mapsKeyTestable, showKeys, savingKeys, validating, validation,
|
||||
placesProvider, savingPlacesProvider, handleSavePlacesProvider,
|
||||
managed,
|
||||
@@ -135,6 +132,10 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
handleToggleAuthSetting, handleToggleRequireMfa,
|
||||
toggleKey, handleSaveApiKeys, handleValidateKey,
|
||||
} = admin
|
||||
const {
|
||||
saveOidc, saveFileTypes, togglePlacesPhotos, togglePlacesAutocomplete, togglePlacesDetails, togglePlacesEnrich,
|
||||
togglePlaceShadow,
|
||||
} = useAdminSettingsActions(admin, t)
|
||||
|
||||
const passwordLoginLocked = !passwordLogin && !oidcLogin
|
||||
const oidcLoginLocked = !passwordLogin && oidcLogin
|
||||
@@ -345,19 +346,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<SaveBar
|
||||
label={t('common.save')}
|
||||
saving={savingOidc}
|
||||
onClick={async () => {
|
||||
setSavingOidc(true)
|
||||
try {
|
||||
const payload: Record<string, unknown> = { issuer: oidcConfig.issuer, client_id: oidcConfig.client_id, display_name: oidcConfig.display_name, discovery_url: oidcConfig.discovery_url }
|
||||
if (oidcConfig.client_secret) payload.client_secret = oidcConfig.client_secret
|
||||
await adminApi.updateOidc(payload)
|
||||
toast.success(t('admin.oidcSaved'))
|
||||
} catch (err: unknown) {
|
||||
toast.error(getApiErrorMessage(err, t('common.error')))
|
||||
} finally {
|
||||
setSavingOidc(false)
|
||||
}
|
||||
}}
|
||||
onClick={saveOidc}
|
||||
/>
|
||||
</SettingsCard>
|
||||
)}
|
||||
@@ -380,14 +369,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<SaveBar
|
||||
label={t('common.save')}
|
||||
saving={savingFileTypes}
|
||||
onClick={async () => {
|
||||
setSavingFileTypes(true)
|
||||
try {
|
||||
await authApi.updateAppSettings({ allowed_file_types: allowedFileTypes })
|
||||
toast.success(t('admin.fileTypesSaved'))
|
||||
} catch { toast.error(t('common.error')) }
|
||||
finally { setSavingFileTypes(false) }
|
||||
}}
|
||||
onClick={saveFileTypes}
|
||||
/>
|
||||
</SettingsCard>
|
||||
|
||||
@@ -466,12 +448,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<ToggleSwitch
|
||||
on={placesPhotosEnabled}
|
||||
label={t('admin.placesPhotos.title')}
|
||||
onToggle={async () => {
|
||||
const next = !placesPhotosEnabled
|
||||
setPlacesPhotosEnabledState(next)
|
||||
setPlacesPhotosEnabled(next)
|
||||
try { await adminApi.updatePlacesPhotos(next) } catch { setPlacesPhotosEnabledState(!next); setPlacesPhotosEnabled(!next) }
|
||||
}}
|
||||
onToggle={() => togglePlacesPhotos(!placesPhotosEnabled)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -482,12 +459,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<ToggleSwitch
|
||||
on={placesAutocompleteEnabled}
|
||||
label={t('admin.placesAutocomplete.title')}
|
||||
onToggle={async () => {
|
||||
const next = !placesAutocompleteEnabled
|
||||
setPlacesAutocompleteEnabledState(next)
|
||||
setPlacesAutocompleteEnabled(next)
|
||||
try { await adminApi.updatePlacesAutocomplete(next) } catch { setPlacesAutocompleteEnabledState(!next); setPlacesAutocompleteEnabled(!next) }
|
||||
}}
|
||||
onToggle={() => togglePlacesAutocomplete(!placesAutocompleteEnabled)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -498,12 +470,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<ToggleSwitch
|
||||
on={placesDetailsEnabled}
|
||||
label={t('admin.placesDetails.title')}
|
||||
onToggle={async () => {
|
||||
const next = !placesDetailsEnabled
|
||||
setPlacesDetailsEnabledState(next)
|
||||
setPlacesDetailsEnabled(next)
|
||||
try { await adminApi.updatePlacesDetails(next) } catch { setPlacesDetailsEnabledState(!next); setPlacesDetailsEnabled(!next) }
|
||||
}}
|
||||
onToggle={() => togglePlacesDetails(!placesDetailsEnabled)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -514,12 +481,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<ToggleSwitch
|
||||
on={placesEnrichEnabled}
|
||||
label={t('admin.placesEnrich.title')}
|
||||
onToggle={async () => {
|
||||
const next = !placesEnrichEnabled
|
||||
setPlacesEnrichEnabledState(next)
|
||||
setPlacesEnrichEnabled(next)
|
||||
try { await adminApi.updatePlacesEnrich(next) } catch { setPlacesEnrichEnabledState(!next); setPlacesEnrichEnabled(!next) }
|
||||
}}
|
||||
onToggle={() => togglePlacesEnrich(!placesEnrichEnabled)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -667,12 +629,7 @@ export default function AdminSettingsTab({ admin, t }: AdminSettingsTabProps): R
|
||||
<ToggleSwitch
|
||||
on={placeShadowEnabled}
|
||||
label={t('admin.placeShadow.title')}
|
||||
onToggle={async () => {
|
||||
const next = !placeShadowEnabled
|
||||
setPlaceShadowEnabledState(next)
|
||||
setPlaceShadowEnabled(next)
|
||||
try { await adminApi.updatePlaceShadow(next) } catch { setPlaceShadowEnabledState(!next); setPlaceShadowEnabled(!next) }
|
||||
}}
|
||||
onToggle={() => togglePlaceShadow(!placeShadowEnabled)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Check, ChevronRight, Globe2, KeyRound, Library, ShieldOff, WifiOff, X } from 'lucide-react'
|
||||
import { Check, ChevronRight, Library, X } from 'lucide-react'
|
||||
import TrekMark from '../../components/shared/TrekMark'
|
||||
import { fs } from '../../components/shared/DialogShell'
|
||||
import { StatusPill } from '../../components/Settings/settingsKit'
|
||||
import type { TranslationFn } from '../../types'
|
||||
import { TREK_API_SOURCES as SOURCES, trekApiFacts, trekApiFields } from '../../components/Admin/trekApiModel'
|
||||
|
||||
interface TrekApiCardProps {
|
||||
t: TranslationFn
|
||||
@@ -21,13 +22,6 @@ interface TrekApiCardProps {
|
||||
* is the row count of the current index, and "no queries logged" is enforced in
|
||||
* three places on the server rather than promised here.
|
||||
*/
|
||||
/**
|
||||
* Proper nouns, so they are not translated. Naming them is also a licence
|
||||
* obligation, not decoration: ODbL and CC BY-SA both require attribution
|
||||
* wherever their content is shown.
|
||||
*/
|
||||
const SOURCES = ['Overture Maps Foundation', 'OpenStreetMap', 'Wikivoyage', 'Wikimedia']
|
||||
|
||||
const CHIP = 'rounded-full border px-2.5 py-[3px] font-medium'
|
||||
|
||||
/** One of the three lists under "more": an icon and a name, chips, a note. */
|
||||
@@ -45,27 +39,8 @@ function FactGroup({ icon, title, note, children }: { icon: React.ReactNode; tit
|
||||
}
|
||||
|
||||
export default function TrekApiCard({ t }: TrekApiCardProps): React.ReactElement {
|
||||
// Reuses the words TREK already has for these fields wherever it has them,
|
||||
// so the chip row costs two new strings instead of ten.
|
||||
const fields = [
|
||||
t('places.formName'),
|
||||
t('collections.coordinates'),
|
||||
t('places.formCategory'),
|
||||
t('places.formAddress'),
|
||||
t('admin.trekApi.fieldPhone'),
|
||||
t('common.email'),
|
||||
t('places.formWebsite'),
|
||||
t('places.formDescription'),
|
||||
t('inspector.openingHours'),
|
||||
t('admin.trekApi.fieldStableId'),
|
||||
]
|
||||
|
||||
const facts = [
|
||||
{ Icon: Globe2, text: t('admin.trekApi.factPlaces') },
|
||||
{ Icon: KeyRound, text: t('admin.trekApi.factNoKey') },
|
||||
{ Icon: WifiOff, text: t('admin.trekApi.factOffline') },
|
||||
{ Icon: ShieldOff, text: t('admin.trekApi.factPrivacy') },
|
||||
]
|
||||
const fields = trekApiFields(t)
|
||||
const facts = trekApiFacts(t)
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-[14px] border bg-surface-card shadow-sm"
|
||||
|
||||
Reference in New Issue
Block a user