import { useState } from 'react' import { Modal, View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator, KeyboardAvoidingView, Platform, } from 'react-native' import { COLORS } from '../constants/Config' /** * 기능 #66 — 반려 사유 입력 모달 * - 반려 사유 템플릿 빠른 선택 + 직접 입력 * - 최소 10자 검증 (빈 값/10자 미만이면 제출 차단) */ const MIN_LEN = 10 const TEMPLATES = [ '요청 정보가 불충분하여 반려합니다.', '변경 일정이 운영 정책과 충돌합니다.', '영향 범위 분석이 누락되어 보완이 필요합니다.', '롤백 계획이 명시되지 않았습니다.', '승인 권한 범위를 초과하는 요청입니다.', ] interface Props { visible: boolean targetTitle?: string onClose: () => void /** 반려 확정 — reason 은 10자 이상 보장됨 */ onSubmit: (reason: string) => Promise | void } export default function RejectReason({ visible, targetTitle, onClose, onSubmit }: Props) { const [reason, setReason] = useState('') const [submitting, setSubmitting] = useState(false) const trimmed = reason.trim() const valid = trimmed.length >= MIN_LEN const reset = () => { setReason(''); setSubmitting(false) } const handleClose = () => { reset(); onClose() } const handleSubmit = async () => { if (!valid || submitting) return setSubmitting(true) try { await onSubmit(trimmed) reset() } catch { setSubmitting(false) } } return ( 반려 사유 입력 {!!targetTitle && {targetTitle}} 빠른 템플릿 {TEMPLATES.map((t, i) => ( setReason(t)}> {t} ))} 사유 (최소 {MIN_LEN}자) {trimmed.length} / {MIN_LEN}자 {valid ? '✔' : '(부족)'} 취소 {submitting ? : 반려} ) } const s = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)', justifyContent: 'flex-end' }, sheet: { backgroundColor: '#fff', borderTopLeftRadius: 18, borderTopRightRadius: 18, paddingHorizontal: 18, paddingTop: 10, paddingBottom: 28 }, handle: { alignSelf: 'center', width: 40, height: 4, borderRadius: 2, backgroundColor: COLORS.border, marginBottom: 12 }, title: { fontSize: 16, fontWeight: '800', color: COLORS.text }, subtitle: { fontSize: 12, color: COLORS.muted, marginTop: 2 }, label: { fontSize: 12, fontWeight: '700', color: COLORS.text, marginTop: 16, marginBottom: 8 }, chips: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 }, chip: { backgroundColor: COLORS.light, borderRadius: 14, paddingHorizontal: 10, paddingVertical: 6, maxWidth: '100%' }, chipText: { fontSize: 11, color: COLORS.blue }, input: { borderWidth: 1, borderColor: COLORS.border, borderRadius: 10, padding: 12, minHeight: 90, fontSize: 13, color: COLORS.text, backgroundColor: '#fff' }, counter: { fontSize: 11, marginTop: 6, textAlign: 'right' }, counterOk: { color: COLORS.success }, counterBad: { color: COLORS.danger }, actions: { flexDirection: 'row', gap: 10, marginTop: 18 }, btn: { flex: 1, borderRadius: 10, paddingVertical: 13, alignItems: 'center' }, btnGhost: { backgroundColor: '#f1f5f9' }, btnGhostText: { color: COLORS.text, fontWeight: '700', fontSize: 14 }, btnDanger: { backgroundColor: COLORS.danger }, btnDangerText: { color: '#fff', fontWeight: '700', fontSize: 14 }, btnDisabled: { opacity: 0.45 }, })