mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
df139a2d96
Every import button in the client wore an outgoing arrow, so the control that pulls a booking or a place list in looked like it was about to send something out. The places-import sheet managed to contradict itself inside one screen: Download in the header, Upload on the row below it, Upload again on the file step. Imports now take Download, or FileDown where the button opens a file picker; genuine uploads and the desktop drop zones keep Upload. The mobile places-import entry point wore an ellipsis, which promises a menu. The "open in Google Maps" buttons drew the four-segment Google *search* G, flattened to one colour — a glyph that reads as "run a search". They get a map pin instead, factored into one component so the desktop and mobile copies stop drifting apart. Closes #2005 Closes #2006
71 lines
4.1 KiB
TypeScript
71 lines
4.1 KiB
TypeScript
import { createPortal } from 'react-dom'
|
|
import { FileDown } from 'lucide-react'
|
|
import type { PackingState } from './usePackingListPanel'
|
|
|
|
export function BulkImportModal(S: PackingState) {
|
|
const { setShowImportModal, t, importText, setImportText, csvInputRef, handleCsvFile, handleBulkImport, parseImportLines } = S
|
|
return createPortal(
|
|
<div style={{
|
|
position: 'fixed', inset: 0, zIndex: 1000,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
background: 'rgba(0,0,0,0.3)', backdropFilter: 'blur(3px)',
|
|
}} onClick={() => setShowImportModal(false)}>
|
|
<div style={{
|
|
width: 420, maxHeight: '80vh', background: 'var(--bg-card)', borderRadius: 16,
|
|
boxShadow: '0 16px 48px rgba(0,0,0,0.22)', padding: '22px 22px 18px',
|
|
display: 'flex', flexDirection: 'column', gap: 14,
|
|
}} onClick={e => e.stopPropagation()}>
|
|
<div style={{ fontSize: 'calc(15px * var(--fs-scale-subtitle, 1))', fontWeight: 600, color: 'var(--text-primary)' }}>{t('packing.importTitle')}</div>
|
|
<div style={{ fontSize: 'calc(12px * var(--fs-scale-body, 1))', color: 'var(--text-faint)', lineHeight: 1.5 }}>{t('packing.importHint')}</div>
|
|
<div style={{ display: 'flex', border: '1px solid var(--border-primary)', borderRadius: 10, overflow: 'hidden', background: 'var(--bg-input)' }}>
|
|
<div style={{
|
|
padding: '10px 0', fontSize: 'calc(13px * var(--fs-scale-body, 1))', fontFamily: 'monospace', lineHeight: 1.5,
|
|
color: 'var(--text-faint)', textAlign: 'right', userSelect: 'none',
|
|
background: 'var(--bg-hover)', borderRight: '1px solid var(--border-faint)',
|
|
minWidth: 32, flexShrink: 0,
|
|
}}>
|
|
{(importText || ' ').split('\n').map((_, i) => (
|
|
<div key={i} style={{ padding: '0 6px' }}>{i + 1}</div>
|
|
))}
|
|
</div>
|
|
<textarea
|
|
value={importText}
|
|
onChange={e => setImportText(e.target.value)}
|
|
rows={10}
|
|
placeholder={t('packing.importPlaceholder')}
|
|
style={{
|
|
flex: 1, border: 'none', padding: '10px 12px', fontSize: 'calc(13px * var(--fs-scale-body, 1))', fontFamily: 'monospace',
|
|
outline: 'none', boxSizing: 'border-box', color: 'var(--text-primary)',
|
|
background: 'transparent', resize: 'vertical', lineHeight: 1.5,
|
|
}}
|
|
/>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
<div>
|
|
<input ref={csvInputRef} type="file" accept=".csv,.txt" style={{ display: 'none' }} onChange={handleCsvFile} />
|
|
<button onClick={() => csvInputRef.current?.click()} style={{
|
|
display: 'flex', alignItems: 'center', gap: 5, padding: '5px 10px',
|
|
border: '1px dashed var(--border-primary)', borderRadius: 8, background: 'none',
|
|
fontSize: 'calc(11px * var(--fs-scale-caption, 1))', color: 'var(--text-faint)', cursor: 'pointer', fontFamily: 'inherit',
|
|
}}>
|
|
<FileDown size={11} /> {t('packing.importCsv')}
|
|
</button>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 8 }}>
|
|
<button onClick={() => setShowImportModal(false)} style={{
|
|
fontSize: 'calc(12px * var(--fs-scale-body, 1))', background: 'none', border: '1px solid var(--border-primary)',
|
|
borderRadius: 8, padding: '6px 14px', cursor: 'pointer', color: 'var(--text-muted)', fontFamily: 'inherit',
|
|
}}>{t('common.cancel')}</button>
|
|
<button onClick={handleBulkImport} disabled={!importText.trim()} style={{
|
|
fontSize: 'calc(12px * var(--fs-scale-body, 1))', background: 'var(--accent)', color: 'var(--accent-text)',
|
|
border: 'none', borderRadius: 8, padding: '6px 16px', cursor: 'pointer', fontWeight: 600,
|
|
fontFamily: 'inherit', opacity: importText.trim() ? 1 : 0.5,
|
|
}}>{t('packing.importAction', { count: parseImportLines(importText).length })}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
)
|
|
}
|