feat(aerial): 위성 요청 취소 기능 추가
- SatRequest status에 '취소' 상태 추가 - 필터 탭에 '취소' 추가 - 대기/촬영중 상태 모두 취소 가능 (confirm 팝업) - 취소된 요청은 빨간 ✕ 배지 + 투명도 60% - satRequests를 상태(state)로 관리하여 실시간 상태 변경 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
부모
0c4bfb2f24
커밋
00e7a3e70a
@ -14,7 +14,7 @@ interface SatRequest {
|
|||||||
requestDate: string
|
requestDate: string
|
||||||
expectedReceive: string
|
expectedReceive: string
|
||||||
resolution: string
|
resolution: string
|
||||||
status: '촬영중' | '대기' | '완료'
|
status: '촬영중' | '대기' | '완료' | '취소'
|
||||||
provider?: string
|
provider?: string
|
||||||
purpose?: string
|
purpose?: string
|
||||||
requester?: string
|
requester?: string
|
||||||
@ -85,6 +85,7 @@ const SAT_MAP_STYLE: StyleSpecification = {
|
|||||||
type SatModalPhase = 'none' | 'provider' | 'blacksky' | 'up42'
|
type SatModalPhase = 'none' | 'provider' | 'blacksky' | 'up42'
|
||||||
|
|
||||||
export function SatelliteRequest() {
|
export function SatelliteRequest() {
|
||||||
|
const [requests, setRequests] = useState(satRequests)
|
||||||
const [statusFilter, setStatusFilter] = useState('전체')
|
const [statusFilter, setStatusFilter] = useState('전체')
|
||||||
const [modalPhase, setModalPhase] = useState<SatModalPhase>('none')
|
const [modalPhase, setModalPhase] = useState<SatModalPhase>('none')
|
||||||
const [selectedRequest, setSelectedRequest] = useState<SatRequest | null>(null)
|
const [selectedRequest, setSelectedRequest] = useState<SatRequest | null>(null)
|
||||||
@ -125,13 +126,14 @@ export function SatelliteRequest() {
|
|||||||
if (modalPhase === 'up42') loadSatPasses()
|
if (modalPhase === 'up42') loadSatPasses()
|
||||||
}, [modalPhase, loadSatPasses])
|
}, [modalPhase, loadSatPasses])
|
||||||
|
|
||||||
const allRequests = showMoreCompleted ? satRequests : satRequests.filter(r => r.status !== '완료' || r.id === 'SAT-003')
|
const allRequests = showMoreCompleted ? requests : requests.filter(r => (r.status !== '완료' && r.status !== '취소') || r.id === 'SAT-003')
|
||||||
|
|
||||||
const filtered = allRequests.filter(r => {
|
const filtered = allRequests.filter(r => {
|
||||||
if (statusFilter === '전체') return true
|
if (statusFilter === '전체') return true
|
||||||
if (statusFilter === '대기') return r.status === '대기'
|
if (statusFilter === '대기') return r.status === '대기'
|
||||||
if (statusFilter === '진행') return r.status === '촬영중'
|
if (statusFilter === '진행') return r.status === '촬영중'
|
||||||
if (statusFilter === '완료') return r.status === '완료'
|
if (statusFilter === '완료') return r.status === '완료'
|
||||||
|
if (statusFilter === '취소') return r.status === '취소'
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -144,6 +146,9 @@ export function SatelliteRequest() {
|
|||||||
if (s === '대기') return (
|
if (s === '대기') return (
|
||||||
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-semibold font-korean" style={{ background: 'rgba(59,130,246,.15)', border: '1px solid rgba(59,130,246,.3)', color: 'var(--blue)' }}>⏳ 대기</span>
|
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-semibold font-korean" style={{ background: 'rgba(59,130,246,.15)', border: '1px solid rgba(59,130,246,.3)', color: 'var(--blue)' }}>⏳ 대기</span>
|
||||||
)
|
)
|
||||||
|
if (s === '취소') return (
|
||||||
|
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-semibold font-korean" style={{ background: 'rgba(239,68,68,.1)', border: '1px solid rgba(239,68,68,.2)', color: 'var(--red)' }}>✕ 취소</span>
|
||||||
|
)
|
||||||
return (
|
return (
|
||||||
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-semibold font-korean" style={{ background: 'rgba(34,197,94,.1)', border: '1px solid rgba(34,197,94,.2)', color: 'var(--green)' }}>✅ 완료</span>
|
<span className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[10px] font-semibold font-korean" style={{ background: 'rgba(34,197,94,.1)', border: '1px solid rgba(34,197,94,.2)', color: 'var(--green)' }}>✅ 완료</span>
|
||||||
)
|
)
|
||||||
@ -156,7 +161,7 @@ export function SatelliteRequest() {
|
|||||||
{ value: '0.5m', label: '최고 해상도', color: 'var(--cyan)' },
|
{ value: '0.5m', label: '최고 해상도', color: 'var(--cyan)' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const filters = ['전체', '대기', '진행', '완료']
|
const filters = ['전체', '대기', '진행', '완료', '취소']
|
||||||
|
|
||||||
const up42Filtered = up42Satellites.filter(s => s.type === up42SubTab)
|
const up42Filtered = up42Satellites.filter(s => s.type === up42SubTab)
|
||||||
|
|
||||||
@ -231,7 +236,7 @@ export function SatelliteRequest() {
|
|||||||
gridTemplateColumns: '60px 1fr 100px 100px 120px 80px 90px',
|
gridTemplateColumns: '60px 1fr 100px 100px 120px 80px 90px',
|
||||||
borderColor: 'rgba(255,255,255,.04)',
|
borderColor: 'rgba(255,255,255,.04)',
|
||||||
background: selectedRequest?.id === r.id ? 'rgba(99,102,241,.06)' : r.status === '촬영중' ? 'rgba(234,179,8,.03)' : 'transparent',
|
background: selectedRequest?.id === r.id ? 'rgba(99,102,241,.06)' : r.status === '촬영중' ? 'rgba(234,179,8,.03)' : 'transparent',
|
||||||
opacity: r.status === '완료' ? 0.7 : 1,
|
opacity: (r.status === '완료' || r.status === '취소') ? 0.6 : 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="text-[11px] font-mono text-text-2">{r.id}</div>
|
<div className="text-[11px] font-mono text-text-2">{r.id}</div>
|
||||||
@ -266,8 +271,18 @@ export function SatelliteRequest() {
|
|||||||
{r.status === '완료' && (
|
{r.status === '완료' && (
|
||||||
<button className="px-3 py-1.5 text-[10px] font-semibold font-korean rounded border cursor-pointer hover:bg-bg-hover transition-colors" style={{ background: 'rgba(34,197,94,.08)', borderColor: 'rgba(34,197,94,.2)', color: 'var(--green)' }}>📥 영상 다운로드</button>
|
<button className="px-3 py-1.5 text-[10px] font-semibold font-korean rounded border cursor-pointer hover:bg-bg-hover transition-colors" style={{ background: 'rgba(34,197,94,.08)', borderColor: 'rgba(34,197,94,.2)', color: 'var(--green)' }}>📥 영상 다운로드</button>
|
||||||
)}
|
)}
|
||||||
{r.status === '대기' && (
|
{(r.status === '대기' || r.status === '촬영중') && (
|
||||||
<button className="px-3 py-1.5 text-[10px] font-semibold font-korean rounded border cursor-pointer hover:bg-bg-hover transition-colors" style={{ background: 'rgba(239,68,68,.08)', borderColor: 'rgba(239,68,68,.2)', color: 'var(--red)' }}>✕ 요청 취소</button>
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (confirm(`${r.id} (${r.zone}) 위성 촬영 요청을 취소하시겠습니까?`)) {
|
||||||
|
setRequests(prev => prev.map(req => req.id === r.id ? { ...req, status: '취소' as const } : req))
|
||||||
|
setSelectedRequest(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="px-3 py-1.5 text-[10px] font-semibold font-korean rounded border cursor-pointer hover:bg-bg-hover transition-colors"
|
||||||
|
style={{ background: 'rgba(239,68,68,.08)', borderColor: 'rgba(239,68,68,.2)', color: 'var(--red)' }}
|
||||||
|
>✕ 요청 취소</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user