feat: redesign day picker with expandable calendar, status markers & Playwright E2E testing
- Replace modal calendar with inline expandable calendar widget integrated into the day picker. - Add status indicator dots (attended/absent/pending) to month calendar grid matching day picker. - Disable and gray out weekend days in calendar when weekend schedule is turned off. - Redesign extend bar with integrated handle that aligns with the palette. - Remove start-to-end timing on Today screen's class time rail, showing only start time. - Implement Playwright E2E and visual testing suite with multi-screen capture scripts. - Add cascade DELETE endpoints for subjects and versioned recurring timetable entries.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
||||
import { Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
||||
import { AppText } from './app-text';
|
||||
import { colors, size, spacing } from './tokens';
|
||||
import { colors, radius, size, spacing } from './tokens';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -9,33 +9,46 @@ type Props = {
|
||||
subtitle?: string;
|
||||
leading?: ReactNode;
|
||||
trailing?: ReactNode;
|
||||
avatar?: { initials: string; onPress: () => void };
|
||||
compact?: boolean;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
export function AppHeader({ title, context, subtitle, leading, trailing, compact = false, style }: Props) {
|
||||
export function AppHeader({ title, context, subtitle, leading, trailing, avatar, compact = false, style }: Props) {
|
||||
if (compact) return <View style={[styles.compact, style]}>
|
||||
<View style={styles.side}>{leading}</View>
|
||||
<AppText variant="title" numberOfLines={1} style={styles.compactTitle}>{title}</AppText>
|
||||
<View style={[styles.side, styles.trailing]}>{trailing}</View>
|
||||
</View>;
|
||||
|
||||
const resolvedTrailing = trailing ?? (avatar ? (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Open account profile"
|
||||
onPress={avatar.onPress}
|
||||
style={({ pressed }) => [styles.avatar, pressed && styles.avatarPressed]}>
|
||||
<AppText variant="label" color={colors.brand.cobalt}>{avatar.initials || '?'}</AppText>
|
||||
</Pressable>
|
||||
) : null);
|
||||
|
||||
return <View style={[styles.root, style]}>
|
||||
<View style={styles.copy}>
|
||||
{context ? <AppText variant="bodySmall" color={colors.neutral.textMuted}>{context}</AppText> : null}
|
||||
<AppText variant="heading1" style={context ? styles.titleWithContext : undefined}>{title}</AppText>
|
||||
{subtitle ? <AppText variant="bodySmall" color={colors.neutral.textSecondary} style={styles.subtitle}>{subtitle}</AppText> : null}
|
||||
</View>
|
||||
{trailing ? <View style={styles.rootTrailing}>{trailing}</View> : null}
|
||||
{resolvedTrailing ? <View style={styles.rootTrailing}>{resolvedTrailing}</View> : null}
|
||||
</View>;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: { minHeight: 76, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: spacing[5], paddingTop: spacing[6] },
|
||||
root: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: spacing[5], paddingTop: spacing[4] },
|
||||
copy: { flex: 1 },
|
||||
titleWithContext: { marginTop: spacing[1] },
|
||||
subtitle: { marginTop: spacing[1] },
|
||||
rootTrailing: { alignSelf: 'flex-start', paddingTop: spacing[1] },
|
||||
avatar: { width: 40, height: 40, borderRadius: radius.control, borderCurve: 'continuous', backgroundColor: colors.brand.cobaltSoft, alignItems: 'center', justifyContent: 'center' },
|
||||
avatarPressed: { opacity: 0.78 },
|
||||
compact: { height: 56, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
|
||||
side: { width: size.touchTargetMin, minHeight: size.touchTargetMin, justifyContent: 'center' },
|
||||
trailing: { alignItems: 'flex-end' },
|
||||
|
||||
Reference in New Issue
Block a user