mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
3e9626fce9
* feat(places): enrich list-imported places via the Places API (#886) Google/Naver list imports only carry a name and coordinates, so the places open as bare pins — the Maps tab jumps to coordinates, with no photo, address or open/closed. Add an opt-in "Enrich places via Google" toggle to the list-import dialog, shown only when a Google Maps key is configured. When enabled, after the (fast, unchanged) import the server runs a background pass that re-resolves each place by name — biased to and validated against the imported coordinates so a common-name search cannot overwrite the wrong place — and fills the empty address/website/phone/photo columns plus the resolved google_place_id, pushing each row over the live sync. Opening hours and the proper Maps link then work on demand from the stored id. Enrichment only fills empty fields, runs detached so a long list never blocks the import, and no-ops when no key is configured. * fix(places): use the ToggleSwitch component for the enrich toggle Match the rest of the app — the import-enrichment opt-in used a raw checkbox; swap it for the shared ToggleSwitch (text left, switch right) like the settings toggles.
99 lines
4.6 KiB
TypeScript
99 lines
4.6 KiB
TypeScript
import ReactDOM from 'react-dom'
|
|
import ToggleSwitch from '../Settings/ToggleSwitch'
|
|
import type { SidebarState } from './usePlacesSidebar'
|
|
|
|
export function ListImportModal(S: SidebarState) {
|
|
const {
|
|
setListImportOpen, setListImportUrl, t, hasMultipleListImportProviders, availableListImportProviders,
|
|
listImportProvider, setListImportProvider, listImportUrl, listImportLoading, handleListImport,
|
|
listImportEnrich, setListImportEnrich, canEnrichImport,
|
|
} = S
|
|
return ReactDOM.createPortal(
|
|
<div
|
|
onClick={() => { setListImportOpen(false); setListImportUrl('') }}
|
|
className="bg-[rgba(0,0,0,0.4)]"
|
|
style={{ position: 'fixed', inset: 0, zIndex: 99999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 }}
|
|
>
|
|
<div
|
|
onClick={e => e.stopPropagation()}
|
|
className="bg-surface-card"
|
|
style={{ borderRadius: 16, width: '100%', maxWidth: 440, padding: 24, boxShadow: '0 8px 32px rgba(0,0,0,0.2)' }}
|
|
>
|
|
<div className="text-content" style={{ fontSize: 15, fontWeight: 700, marginBottom: 4 }}>
|
|
{t('places.importList')}
|
|
</div>
|
|
{hasMultipleListImportProviders && (
|
|
<div style={{ display: 'flex', gap: 6, marginBottom: 10 }}>
|
|
{availableListImportProviders.map(provider => (
|
|
<button
|
|
key={provider}
|
|
onClick={() => setListImportProvider(provider)}
|
|
className={listImportProvider === provider ? 'bg-accent text-accent-text' : 'bg-surface-tertiary text-content-muted'}
|
|
style={{
|
|
padding: '6px 10px', borderRadius: 20, border: 'none', cursor: 'pointer',
|
|
fontSize: 11, fontWeight: 600, fontFamily: 'inherit',
|
|
}}
|
|
>
|
|
{provider === 'google' ? t('places.importGoogleList') : t('places.importNaverList')}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
<div className="text-content-faint" style={{ fontSize: 12, marginBottom: 16 }}>
|
|
{t(listImportProvider === 'google' ? 'places.googleListHint' : 'places.naverListHint')}
|
|
</div>
|
|
<input
|
|
type="text"
|
|
value={listImportUrl}
|
|
onChange={e => setListImportUrl(e.target.value)}
|
|
onKeyDown={e => { if (e.key === 'Enter' && !listImportLoading) handleListImport() }}
|
|
placeholder={listImportProvider === 'google' ? 'https://maps.app.goo.gl/...' : 'https://naver.me/...'}
|
|
autoFocus
|
|
className="bg-surface-tertiary text-content"
|
|
style={{
|
|
width: '100%', padding: '10px 14px', borderRadius: 10,
|
|
border: '1px solid var(--border-primary)',
|
|
fontSize: 13, outline: 'none',
|
|
fontFamily: 'inherit', boxSizing: 'border-box',
|
|
}}
|
|
/>
|
|
{canEnrichImport && (
|
|
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, marginTop: 12 }}>
|
|
<div style={{ flex: 1, minWidth: 0 }}>
|
|
<div className="text-content" style={{ fontSize: 12, fontWeight: 600 }}>{t('places.enrichOnImport')}</div>
|
|
<div className="text-content-faint" style={{ fontSize: 12, marginTop: 2 }}>{t('places.enrichOnImportHint')}</div>
|
|
</div>
|
|
<ToggleSwitch on={listImportEnrich} onToggle={() => setListImportEnrich(!listImportEnrich)} />
|
|
</div>
|
|
)}
|
|
<div style={{ display: 'flex', gap: 8, marginTop: 16, justifyContent: 'flex-end' }}>
|
|
<button
|
|
onClick={() => { setListImportOpen(false); setListImportUrl('') }}
|
|
className="text-content"
|
|
style={{
|
|
padding: '8px 16px', borderRadius: 10, border: '1px solid var(--border-primary)',
|
|
background: 'none', fontSize: 13, fontWeight: 500,
|
|
cursor: 'pointer', fontFamily: 'inherit',
|
|
}}
|
|
>
|
|
{t('common.cancel')}
|
|
</button>
|
|
<button
|
|
onClick={handleListImport}
|
|
disabled={!listImportUrl.trim() || listImportLoading}
|
|
className={!listImportUrl.trim() || listImportLoading ? 'bg-surface-tertiary text-content-faint' : 'bg-accent text-accent-text'}
|
|
style={{
|
|
padding: '8px 16px', borderRadius: 10, border: 'none',
|
|
fontSize: 13, fontWeight: 500, cursor: !listImportUrl.trim() || listImportLoading ? 'default' : 'pointer',
|
|
fontFamily: 'inherit',
|
|
}}
|
|
>
|
|
{listImportLoading ? t('common.loading') : t('common.import')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
)
|
|
}
|