mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +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,7 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { AlertTriangle, ChevronDown, RefreshCw } from 'lucide-react'
|
||||
import { adminApi, authApi } from '../../../api/client'
|
||||
import { getApiErrorMessage } from '../../../types'
|
||||
import { useAdminSettingsActions } from '../../../components/Admin/useAdminSettingsActions'
|
||||
import { placesGoogleOnlyHint } from '../../../utils/placeSource'
|
||||
import type { TranslationFn } from '../../../types'
|
||||
import type { useAdmin } from '../../../pages/admin/useAdmin'
|
||||
@@ -29,21 +28,16 @@ interface MAdminSettingsSectionProps {
|
||||
export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectionProps) {
|
||||
const [providerPickerOpen, setProviderPickerOpen] = useState(false)
|
||||
const {
|
||||
toast,
|
||||
setPlacesPhotosEnabled, setPlacesAutocompleteEnabled, setPlacesDetailsEnabled, setPlacesEnrichEnabled, setPlaceShadowEnabled,
|
||||
placesPhotosEnabled, setPlacesPhotosEnabledState,
|
||||
placesAutocompleteEnabled, setPlacesAutocompleteEnabledState,
|
||||
placesDetailsEnabled, setPlacesDetailsEnabledState,
|
||||
placesEnrichEnabled, setPlacesEnrichEnabledState,
|
||||
placesPhotosEnabled, placesAutocompleteEnabled, placesDetailsEnabled, placesEnrichEnabled,
|
||||
placesGoogleOnly, handleTogglePlacesGoogleOnly,
|
||||
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, savingKeys, validating, validation,
|
||||
placesProvider, savingPlacesProvider, handleSavePlacesProvider,
|
||||
managed,
|
||||
@@ -52,36 +46,10 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
handleSaveApiKeys, handleValidateKey,
|
||||
} = admin
|
||||
|
||||
const saveFileTypes = 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)
|
||||
}
|
||||
}
|
||||
|
||||
const saveOidc = 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)
|
||||
}
|
||||
}
|
||||
const {
|
||||
saveOidc, saveFileTypes, togglePlacesPhotos, togglePlacesAutocomplete, togglePlacesDetails, togglePlacesEnrich,
|
||||
togglePlaceShadow,
|
||||
} = useAdminSettingsActions(admin, t)
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
@@ -279,16 +247,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MToggle
|
||||
checked={placesPhotosEnabled}
|
||||
ariaLabel={t('admin.placesPhotos.title')}
|
||||
onChange={async (next) => {
|
||||
setPlacesPhotosEnabledState(next)
|
||||
setPlacesPhotosEnabled(next)
|
||||
try {
|
||||
await adminApi.updatePlacesPhotos(next)
|
||||
} catch {
|
||||
setPlacesPhotosEnabledState(!next)
|
||||
setPlacesPhotosEnabled(!next)
|
||||
}
|
||||
}}
|
||||
onChange={togglePlacesPhotos}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -299,16 +258,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MToggle
|
||||
checked={placesAutocompleteEnabled}
|
||||
ariaLabel={t('admin.placesAutocomplete.title')}
|
||||
onChange={async (next) => {
|
||||
setPlacesAutocompleteEnabledState(next)
|
||||
setPlacesAutocompleteEnabled(next)
|
||||
try {
|
||||
await adminApi.updatePlacesAutocomplete(next)
|
||||
} catch {
|
||||
setPlacesAutocompleteEnabledState(!next)
|
||||
setPlacesAutocompleteEnabled(!next)
|
||||
}
|
||||
}}
|
||||
onChange={togglePlacesAutocomplete}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -319,16 +269,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MToggle
|
||||
checked={placesDetailsEnabled}
|
||||
ariaLabel={t('admin.placesDetails.title')}
|
||||
onChange={async (next) => {
|
||||
setPlacesDetailsEnabledState(next)
|
||||
setPlacesDetailsEnabled(next)
|
||||
try {
|
||||
await adminApi.updatePlacesDetails(next)
|
||||
} catch {
|
||||
setPlacesDetailsEnabledState(!next)
|
||||
setPlacesDetailsEnabled(!next)
|
||||
}
|
||||
}}
|
||||
onChange={togglePlacesDetails}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -339,16 +280,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MToggle
|
||||
checked={placesEnrichEnabled}
|
||||
ariaLabel={t('admin.placesEnrich.title')}
|
||||
onChange={async (next) => {
|
||||
setPlacesEnrichEnabledState(next)
|
||||
setPlacesEnrichEnabled(next)
|
||||
try {
|
||||
await adminApi.updatePlacesEnrich(next)
|
||||
} catch {
|
||||
setPlacesEnrichEnabledState(!next)
|
||||
setPlacesEnrichEnabled(!next)
|
||||
}
|
||||
}}
|
||||
onChange={togglePlacesEnrich}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -434,16 +366,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio
|
||||
<MToggle
|
||||
checked={placeShadowEnabled}
|
||||
ariaLabel={t('admin.placeShadow.title')}
|
||||
onChange={async (next) => {
|
||||
setPlaceShadowEnabledState(next)
|
||||
setPlaceShadowEnabled(next)
|
||||
try {
|
||||
await adminApi.updatePlaceShadow(next)
|
||||
} catch {
|
||||
setPlaceShadowEnabledState(!next)
|
||||
setPlaceShadowEnabled(!next)
|
||||
}
|
||||
}}
|
||||
onChange={togglePlaceShadow}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } 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 { TREK_API_SOURCES as SOURCES, trekApiFacts, trekApiFields } from '../../../components/Admin/trekApiModel'
|
||||
import { useTranslation } from '../../../i18n'
|
||||
|
||||
/**
|
||||
@@ -19,13 +20,6 @@ import { useTranslation } from '../../../i18n'
|
||||
* wording, key for key, so the two pages cannot drift in what they claim.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Proper nouns, so they are not translated. Naming them is also a licence
|
||||
* obligation rather than 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 border-[color:var(--m-rowbr)] px-2 py-[2px] font-geist text-[0.625rem]'
|
||||
|
||||
/** One group of chips inside the disclosure: what you get, what you do not, where it comes from. */
|
||||
@@ -65,26 +59,9 @@ export function MTrekApiBlock() {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
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 facts = trekApiFacts(t)
|
||||
// The same fields the desktop card lists, reusing words TREK already has.
|
||||
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 fields = trekApiFields(t)
|
||||
|
||||
return (
|
||||
<div className="relative mt-[18px]">
|
||||
|
||||
Reference in New Issue
Block a user