/* * 티켓 카드 — SCR-M15 리스트 항목. * 행사명·권종·매수·상태 배지 + 미니 QR(placeholder) + "입장 QR 보기". * 사용됨/취소 티켓은 흐리게(dim). 좌측 상태 액센트 바. */ import { Ionicons } from '@expo/vector-icons'; import React from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import { colors, radius, spacing, touch, type } from '../../theme'; import { QrPlaceholder } from './QrPlaceholder'; import { STATUS_META, type SampleTicket } from './sampleTickets'; const badgeStyle: Record<'success' | 'muted' | 'error', { bg: string; fg: string }> = { success: { bg: '#E7F6EF', fg: colors.success }, muted: { bg: colors.neutral050, fg: colors.neutral500 }, error: { bg: '#FEF3F2', fg: colors.error }, }; export function TicketCard({ ticket, onOpenQr, onDetail, }: { ticket: SampleTicket; onOpenQr: (t: SampleTicket) => void; onDetail: (t: SampleTicket) => void; }) { const dim = ticket.status !== 'usable'; const sm = STATUS_META[ticket.status]; const bs = badgeStyle[sm.tone]; return ( {ticket.eventName} {sm.label} {ticket.period} {ticket.hallLabel} 권종 {ticket.ticketType} 수량 {ticket.quantity}매 예매번호 {ticket.bookingNoMasked} {ticket.status === 'usable' ? ( [styles.qrBtn, pressed && { opacity: 0.85 }]} onPress={() => onOpenQr(ticket)} > 입장 QR 보기 ) : ( {ticket.status === 'used' ? '입장 완료된 티켓입니다' : '취소된 티켓입니다'} )} onDetail(ticket)} > 예매 상세·취소 ); } const styles = StyleSheet.create({ card: { flexDirection: 'row', backgroundColor: colors.white, borderWidth: 1, borderColor: colors.neutral200, borderRadius: radius.md, overflow: 'hidden', }, cardDim: { opacity: 0.6 }, accent: { width: 4 }, body: { flex: 1, padding: spacing.md, gap: 8 }, topRow: { flexDirection: 'row', justifyContent: 'space-between', gap: 8 }, eventName: { flex: 1, fontSize: type.h3.fontSize, fontWeight: '700', color: colors.neutral900 }, badge: { alignSelf: 'flex-start', borderRadius: radius.sm, paddingHorizontal: 8, paddingVertical: 3, }, badgeText: { fontSize: 11, fontWeight: '700' }, metaRow: { flexDirection: 'row', alignItems: 'center', gap: 6 }, metaText: { fontSize: type.caption.fontSize, color: colors.neutral700 }, divider: { borderTopWidth: 1, borderTopColor: colors.neutral200, borderStyle: 'dashed', marginVertical: 4, }, infoRow: { flexDirection: 'row', gap: 12, alignItems: 'center' }, miniQrWrap: { borderWidth: 1, borderColor: colors.neutral200, borderRadius: radius.sm, padding: 3, }, infoCols: { flex: 1, flexDirection: 'row', flexWrap: 'wrap', gap: 12 }, infoCol: { minWidth: 60 }, infoLabel: { fontSize: 11, color: colors.neutral500, marginBottom: 2 }, infoValue: { fontSize: type.body.fontSize, fontWeight: '700', color: colors.neutral900 }, infoMono: { fontSize: type.caption.fontSize, color: colors.neutral700, fontVariant: ['tabular-nums'] }, qrBtn: { minHeight: touch.min, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, backgroundColor: colors.primary600, borderRadius: radius.sm, marginTop: 4, }, qrBtnText: { color: colors.white, fontSize: type.h3.fontSize, fontWeight: '700' }, qrBtnDisabled: { backgroundColor: colors.neutral050, borderWidth: 1, borderColor: colors.neutral200 }, qrBtnDisabledText: { color: colors.neutral500, fontSize: type.body.fontSize, fontWeight: '600' }, detailBtn: { minHeight: 40, alignItems: 'center', justifyContent: 'center' }, detailText: { fontSize: type.caption.fontSize, color: colors.neutral500, textDecorationLine: 'underline', }, });