import { Ionicons } from '@expo/vector-icons'; import type { ReactNode } from 'react'; import { Pressable, StyleSheet, View, type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native'; import { AppText } from './app-text'; import { colors, radius, spacing, type SubjectTone } from './tokens'; export type ClassAttendanceState = 'pending' | 'attended' | 'absent' | 'cancelled'; type Props = { title: string; timeRange: string; subjectTone: SubjectTone; room?: string; kind?: string; classType?: string; state?: ClassAttendanceState; isNow?: boolean; topAction?: ReactNode; footer?: ReactNode; onPress?: (event: GestureResponderEvent) => void; style?: StyleProp; testID?: string; }; /** Editorial calendar block: subject color leads; attendance changes only the card state. */ export function ScheduleEventCard({ title, timeRange, subjectTone, room, kind, classType = 'Lecture', state = 'pending', isNow = false, topAction, footer, onPress, style, testID }: Props) { const subject = colors.subject[subjectTone]; const cancelled = state === 'cancelled'; const absent = state === 'absent'; const attended = state === 'attended'; const backgroundColor = cancelled ? colors.neutral.surfaceSubtle : absent ? colors.semantic.danger.soft : subject.surface; const foreground = cancelled ? colors.neutral.textMuted : absent ? colors.semantic.danger.text : subject.accent; const content = <> {title} {topAction ? {topAction} : null} {kind || 'Class'} {room ? {room} : null} {classType} {isNow ? ( Now ) : cancelled ? ( Cancelled ) : attended ? ( Attended ) : absent ? ( Absent ) : ( )} {footer ? {footer} : null} ; const cardStyle = [styles.card, { backgroundColor }, cancelled && styles.cancelled, style]; if (!onPress) return {content}; return [cardStyle, pressed && styles.pressed]}>{content}; } function ParticipantStack({ accent }: { accent: string }) { return {['A', 'K', 'S'].map((initial, index) => {initial} )} +8 ; } const styles = StyleSheet.create({ card: { minHeight: 116, overflow: 'hidden', borderRadius: radius.feature, borderCurve: 'continuous', padding: spacing[5] }, titleRow: { minHeight: 40, flexDirection: 'row', alignItems: 'flex-start', gap: spacing[2] }, title: { flex: 1, paddingTop: spacing[1] }, topAction: { marginRight: -spacing[3], marginTop: -spacing[3] }, meta: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: spacing[3], marginTop: spacing[1] }, code: { flexShrink: 1 }, room: { maxWidth: '52%', alignItems: 'flex-end' }, bottomRow: { minHeight: 30, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: spacing[3], marginTop: spacing[3] }, typeRow: { flexDirection: 'row', alignItems: 'center', gap: spacing[2] }, now: { borderRadius: radius.pill, backgroundColor: colors.brand.coral, paddingHorizontal: spacing[3], paddingVertical: 3 }, statusConfirmed: { flexDirection: 'row', alignItems: 'center', gap: 4 }, participants: { flexDirection: 'row', alignItems: 'center' }, avatar: { width: 24, height: 24, alignItems: 'center', justifyContent: 'center', borderRadius: radius.pill, borderWidth: 2, borderColor: colors.neutral.surface }, more: { marginLeft: spacing[2] }, footer: { marginTop: spacing[3] }, cancelled: { opacity: 0.72 }, pressed: { opacity: 0.78 }, });