From 1b7b33d43fe155e291f6cdf929d925b9c1b450ce Mon Sep 17 00:00:00 2001 From: Rifa Achrinza <25147899+achrinza@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:53:04 +0800 Subject: [PATCH] feat: initial diary and graph components Signed-off-by: Rifa Achrinza <25147899+achrinza@users.noreply.github.com> --- app/calendar/index.tsx | 51 +++++++++ app/diary/index.tsx | 254 +++++++++++++++++++++++++++++++++++++++++ components/Graph.tsx | 124 ++++++++++++++++++++ 3 files changed, 429 insertions(+) create mode 100644 app/calendar/index.tsx create mode 100644 app/diary/index.tsx create mode 100644 components/Graph.tsx diff --git a/app/calendar/index.tsx b/app/calendar/index.tsx new file mode 100644 index 0000000..da1b000 --- /dev/null +++ b/app/calendar/index.tsx @@ -0,0 +1,51 @@ +import moment from 'moment'; +import {Text, View} from 'react-native'; + +export type CalendarProps = { + view: 'week' | 'month'; + activeWeekOrMonth?: number; + activeYear?: number; + selectedDate: Date; +}; + +export function Calendar({ + view, + activeWeekOrMonth, + activeYear, + selectedDate, +}: CalendarProps) { + function isLeapYear(year: number) { + if (year % 4 !== 0) return false; + if (year % 100 === 0 && year % 400 !== 0) return false; + return true; + } + + const activeMonth = selectedDate.getUTCMonth(); + activeYear = selectedDate.getUTCFullYear(); + const activeMonthStartDay = selectedDate.getDay(); + + const monthDayCount = [ + 31, + (() => (isLeapYear(activeYear) ? 29 : 28))(), + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31, + ]; + + const monthStartDay = activeMonth; + + return ( + + {moment('MMMM')} + + ); +} + +export default function CalendarWrapper() {} diff --git a/app/diary/index.tsx b/app/diary/index.tsx new file mode 100644 index 0000000..2b89da3 --- /dev/null +++ b/app/diary/index.tsx @@ -0,0 +1,254 @@ +import {ScrollView, StyleSheet, Text, View} from 'react-native'; +import {Graph} from '@/components/Graph'; + +function GraphSection({title, subtitle, children}) { + const styles = StyleSheet.create({ + root: { + alignItems: 'center', + maxWidth: 400, + marginVertical: 30, + }, + title: { + color: '#765000', + fontWeight: 'bold', + fontSize: 24, + alignSelf: 'stretch', + }, + subtitle: { + color: '#765000', + fontSize: 16, + alignSelf: 'stretch', + }, + chartContainer: { + marginTop: 30, + height: 200, + width: 300, + alignItems: 'center', + }, + }); + + return ( + + {title} + {subtitle} + {children} + + ); +} + +type InsightsProps = { + insights: { + trend: 'up' | 'down'; + text: string; + }[]; +}; + +export function Insights({insights}: InsightsProps) { + const styles = StyleSheet.create({ + root: { + borderRadius: 12, + paddingVertical: 12, + paddingHorizontal: 12, + backgroundColor: '#765000', + }, + title: { + color: '#FFF', + fontWeight: 'bold', + fontSize: 20, + }, + text: { + color: '#FFF', + fontSize: 16, + }, + insight: { + flexDirection: 'row', + }, + }); + + return ( + + Insights + {insights.map(({trend, text}, index) => ( + + {trend} + {text} + + ))} + + ); +} + +type Recommendation = { + title: string; + backgroundColor: string; + image: object; + onPress?: object; +}; + +type RecommendationsProps = { + recommendations: Recommendation[]; +}; + +function Recommendations({recommendations}: RecommendationsProps) { + const styles = StyleSheet.create({ + root: { + marginVertical: 30, + }, + title: { + color: '#765000', + fontWeight: 'bold', + fontSize: 24, + alignSelf: 'stretch', + }, + recommendationContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + recommendation: { + borderRadius: 30, + padding: 20, + width: '30%', + maxWidth: 200, + }, + recommendationText: { + fontWeight: 'bold', + fontSize: 14, + }, + }); + + return ( + + Recommended + + {recommendations.map(({title, image, backgroundColor}, i) => ( + + {title} + + ))} + + + ); +} + +export default function Demo() { + const styles = StyleSheet.create({ + root: { + backgroundColor: '#FFF', + margin: 20, + }, + title: { + fontSize: 30, + textAlign: 'center', + color: '#756000', + fontWeight: 'bold', + }, + }); + + const recommendations: Recommendation[] = [ + { + title: 'How to feel better article', + backgroundColor: '#A1AEFF', + image: {}, + }, + { + title: 'Try Deep Breathing', + backgroundColor: '#AEFFA1', + image: {}, + }, + { + title: 'Anxiety and Panic Attack', + backgroundColor: '#FFE7E7', + image: {}, + }, + ]; + + return ( + + My Diary + + + + + + + + + + ); +} + +const pointColors = [ + '#8DFAB7', + '#FADC8D', + '#8DD0FA', + '#FA9C93', + '#FADC8D', + '#FADC8D', + '#FADC8D', +]; + +const scalesData = [ + { + point: 50, + label: 'M', + label2: '18', + }, + { + point: 20, + label: 'T', + label2: '19', + }, + { + point: 30, + label: 'W', + label2: '20', + }, + { + point: 60, + label: 'T', + label2: '21', + highlight: true, + }, + { + point: 20, + label: 'F', + label2: '22', + }, + { + point: 30, + label: 'S', + label2: '23', + }, + { + point: 90, + label: 'S', + label2: '24', + }, +]; diff --git a/components/Graph.tsx b/components/Graph.tsx new file mode 100644 index 0000000..1795dca --- /dev/null +++ b/components/Graph.tsx @@ -0,0 +1,124 @@ +import {StyleSheet, Text, View} from 'react-native'; + +export type GraphProps = { + style?: CommonStyleProps; + min?: number; + max?: number; + scales: ScaleProps[]; +}; + +type CommonStyleProps = { + pointColors?: string[]; + pointHeight?: number; + pointColor?: string; + slideWidth?: number; + slideGap?: number; + slideColor?: string; +}; + +type ScaleProps = { + style?: CommonStyleProps; + point: number; + label?: string; + label2?: string; + color?: string; + min?: number; + max?: number; + highlight?: boolean; +}; + +function Scale({ + style, + label, + label2, + point, + color, + min, + max, + highlight, +}: ScaleProps) { + const slideColor = style?.slideColor || '#F7F7F7'; + const slideWidth = style?.slideWidth || 25; + const pointHeight = style?.pointHeight || 25; + const pointColor = color || 'red'; + min = min || 0; + max = max || 100; + const pointPosition = `${((point - min) / max) * 100}%`; + + const styles = StyleSheet.create({ + slideOuter: { + height: '100%', + alignItems: 'center', + }, + slideInner: { + backgroundColor: slideColor, + borderRadius: 6, + width: slideWidth, + flexGrow: 1, + }, + point: { + position: 'absolute', + // @ts-ignore + bottom: pointPosition, + backgroundColor: pointColor, + borderRadius: 6, + height: pointHeight, + width: '100%', + }, + label: { + fontSize: 20, + }, + label2: { + fontSize: 20, + fontWeight: 900, + }, + labelHighlight: { + borderWidth: 3, + borderColor: '#DDD', + borderRadius: 5000, + }, + }); + + return ( + + + + + {label ? label : ' '} + + {label2 ? label2 : ' '} + + + ); +} + +export function Graph({style, scales, min, max}: GraphProps) { + const styles = StyleSheet.create({ + root: { + height: '100%', + width: '100%', + gap: 25, + flexDirection: 'row', + justifyContent: 'space-between', + }, + }); + + return ( + + {scales.map(({point, color, label, label2}, index) => ( + + ))} + + ); +}