Merge branch '50-plusscreen' into 'main'

Resolve "PlusScreen"

Closes #50

See merge request thschleicher/interaktive-systeme!39
This commit is contained in:
jastornig 2024-01-05 00:05:20 +00:00
commit afb52998d9
19 changed files with 595 additions and 66 deletions

View file

@ -25,6 +25,7 @@ export default function Layout() {
tabBarInactiveTintColor: colors.tabIconDefault,
headerShown: false,
tabBarStyle: styles.tabBar,
tabBarHideOnKeyboard: true
}
if(!authState?.authenticated){

View file

@ -12,10 +12,10 @@ export default function _Layout() {
initialRouteName="index"
screenOptions={{
contentStyle: {
backgroundColor:colors.backgroundColor,
backgroundColor:colors.containerColor,
},
headerStyle: {
backgroundColor: colors.backgroundColor
backgroundColor: colors.containerColor
},
headerTintColor: colors.primaryText
@ -24,7 +24,9 @@ export default function _Layout() {
title: "test",
headerShown: false,
}}/>
<Stack.Screen name="addItem"/>
<Stack.Screen name="addItem" options={{
title: "new Expense",
}}/>
<Stack.Screen name="userSettings" options={{
animation: "slide_from_left",
title: "User Settings",

View file

@ -1,10 +1,93 @@
import { View, Text } from 'react-native'
import React from 'react'
import { View, Text, StyleSheet, Alert } from 'react-native'
import React, { useRef, useState } from 'react'
import { SIZES } from '../../../constants/theme'
import { useTheme } from '../../contexts/ThemeContext'
import { AutoDecimalInput, CategorySelector, CategorySelectorModal, DateSelectorButton, RoundedButton, TextInputBar } from '../../../components'
import { Category } from '../../../types/dbItems'
import DateTimePicker from '@react-native-community/datetimepicker';
import { addExpense } from '../../../services/database'
import { SimpleDate } from '../../../util/SimpleDate'
import { useRouter } from 'expo-router'
export default function AddItem() {
const {colors} = useTheme();
const router = useRouter();
const [formatedValue, setFormatedValue] = useState<string>("");
const [selectorModalVisible, setSelecorModalVisible] = useState<boolean>(false);
const [selectedCategory, setSelectedCategory] = useState<Category|undefined>()
const [expenseName, setExpenseName] = useState<string>("");
const [datePickerShown, setDatePickerShown] = useState<boolean>(false);
const [selectedDate, setSelectedDate] = useState<Date>(new Date())
const handleValueChange = (formatedValue: string) => {
setFormatedValue(formatedValue);
}
const handleCategorySelect = (category : Category) => {
setSelecorModalVisible(false);
setSelectedCategory(category);
}
const validateInput = ():boolean => {
if(formatedValue == "" || expenseName == "" || selectedCategory === undefined || selectedDate === null){
return false;
}
return true;
}
const submit = () => {
const insert = async () => {
await addExpense(expenseName, selectedCategory?.guid!, new SimpleDate(selectedDate).format("YYYY-MM-DD"), Number(formatedValue))
}
if(validateInput()){
insert();
router.back();
}else {
Alert.alert("Invalid input", "One of the Props is not properly defined")
}
}
export default function addItem() {
return (
<View>
<Text>addItem</Text>
<View style={styles.container}>
<CategorySelectorModal visible={selectorModalVisible} onRequestClose={()=>{setSelecorModalVisible(false)}} onCategoryTap={handleCategorySelect}></CategorySelectorModal>
<AutoDecimalInput onValueChange={handleValueChange} label='Amount'></AutoDecimalInput>
<CategorySelector onPress={()=>{setSelecorModalVisible(true)}} selectedCategory={selectedCategory}/>
<TextInputBar placeholder='Name' onChangeText={(text)=>setExpenseName(text)}/>
<DateSelectorButton selectedDate={selectedDate} onPress={()=>{setDatePickerShown(true)}}/>
{datePickerShown &&
<DateTimePicker
value={new Date()}
maximumDate={new Date()}
onChange={(event, date)=>{
setDatePickerShown(false);
if(date){
setSelectedDate(date);
}
}}
/>}
<RoundedButton color={colors.accentColor} style={styles.save} onPress={submit}>
<Text style={[styles.submitText, {color: colors.primaryText}]}>Save</Text>
</RoundedButton>
</View>
)
}
}
const styles = StyleSheet.create({
save: {
marginTop: 40,
padding: 10,
},
container: {
margin: SIZES.normal,
display: "flex",
gap: 10
},
submitText: {
fontSize: SIZES.large
}
})

View file

@ -1,9 +1,9 @@
import React, { useRef, useState, useMemo } from 'react';
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native';
import { Calendar } from 'react-native-calendars';
import { FlatList } from 'react-native-gesture-handler';
import { FlatList, RefreshControl } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import { ExpenseItem, LoadingSymbol, Plus, SearchBar, Welcome } from '../../../components';
import { ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components';
import useFetch from '../../../hooks/useFetch';
import { addExpense, executeQuery } from "../../../services/database";
@ -63,7 +63,7 @@ export default function Page() {
}
}
const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid;", args: []});
const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid ORDER BY expense_datetime desc;", args: []});
const expenseDates = useMemo(()=>
constructMarkedDates(data)
@ -74,15 +74,15 @@ export default function Page() {
return (
<SafeAreaView edges={["left", "right", "top"]} style={[styles.safeAreaViewStyle, {backgroundColor: colors.containerColor}]}>
{plusShow && <Plus onPress={()=>{
// router.push("/(tabs)/home/addItem");
router.push("/(tabs)/home/addItem");
executeQuery({sql: "SELECT guid FROM category", args: []}).then((result) => {
if("rows" in result[0]) {
newExpense("Test Title", result[0]["rows"][0]["guid"], "69.69.1234", 100).then(() => {
reFetch();
});
}
})
// executeQuery({sql: "SELECT guid FROM category", args: []}).then((result) => {
// if("rows" in result[0]) {
// newExpense("Test Title", result[0]["rows"][0]["guid"], "69.69.1234", 100).then(() => {
// reFetch();
// });
// }
// })
}}/>}
{isLoading && <LoadingSymbol/>}
@ -92,7 +92,7 @@ export default function Page() {
ListHeaderComponent={
<>
<Welcome name="My Dude" image={profile} onPress={() => {router.push("/home/userSettings")}}/>
<Calendar key={theme} maxDate={SimpleDate.now().format("YYYY-MM-DD")} style={{margin: 10, borderRadius: 20, padding:10}} theme={{
<Calendar key={theme} maxDate={SimpleDate.now().format("YYYY-MM-DD")} style={{borderRadius: 20, padding:10}} theme={{
dayTextColor: colors.primaryText,
textDisabledColor: colors.secondaryText,
todayTextColor: colors.accentColor,
@ -106,7 +106,7 @@ export default function Page() {
>
</Calendar>
<SearchBar placeholder='Type to Search...'></SearchBar>
<TextInputBar placeholder='Type to Search...' style={{marginBottom: 20}}></TextInputBar>
</>
}
renderItem = {({item}) => <ExpenseItem category={item.category_name} color={item.category_color} date={item.expense_datetime} title={item.expense_name} value={item.expense_amount}/>}
@ -114,6 +114,9 @@ export default function Page() {
ItemSeparatorComponent={() => {
return (<View style={styles.itemSeperatorStyle}/>);
}}
refreshControl={
<RefreshControl refreshing={isLoading} onRefresh={reFetch}/>
}
onScroll={handleScroll}
scrollEventThrottle={20}
/>
@ -124,6 +127,7 @@ export default function Page() {
const styles = StyleSheet.create({
safeAreaViewStyle: {
flex: 1,
paddingHorizontal: 10
},
itemSeperatorStyle: {
marginVertical: 5,

View file

@ -1,5 +1,5 @@
import { StyleSheet, Text, TouchableHighlight, View } from "react-native";
import SearchBar from "../common/SearchBar";
import TextInputBar from "../common/TextInputBar";
import { useTheme } from "../../app/contexts/ThemeContext";
type BudgetHeaderProperties = {
@ -34,7 +34,7 @@ const BudgetHeader = (properties: BudgetHeaderProperties) => {
}}
/>
</View>
<SearchBar placeholder='Search...'></SearchBar>
<TextInputBar placeholder='Search...'></TextInputBar>
</>);
}

View file

@ -0,0 +1,94 @@
import { View, Text, TouchableOpacity, TextInput, StyleSheet, NativeSyntheticEvent, TextInputKeyPressEventData } from 'react-native'
import React, {LegacyRef, MutableRefObject, useRef, useState} from 'react'
import { SIZES } from '../../constants/theme';
import { useTheme } from '../../app/contexts/ThemeContext';
const formatDecimal = (value: string)=>{
switch(value.length){
case 0:
return "";
case 1:
return "0.0"+value
case 2:
return "0."+value
default:
return value.substring(0, value.length - 2) + "." + value.substring(value.length - 2, value.length)
}
}
interface AutoDecimalInputProps{
onValueChange?: (formattedValue: string) => void | undefined
label: string,
}
const AutoDecimalInput: React.FC<AutoDecimalInputProps> = ({onValueChange, label}) => {
const { colors } = useTheme();
const inputRef = useRef<TextInput>(null);
const [pressedNumbers, setPressedNumbers] = useState<string>("");
const update = (newValues : string) => {
if(onValueChange){
onValueChange(formatDecimal(newValues))
}
setPressedNumbers(newValues);
}
const handleInput = (e: NativeSyntheticEvent<TextInputKeyPressEventData>)=>{
const pressedKey:string = e.nativeEvent.key
if(Number.isInteger(Number.parseInt(pressedKey))){
if(pressedNumbers.length === 0 && pressedKey === "0"){
return
}
update(pressedNumbers + pressedKey)
}else if(pressedKey === "Backspace"){
update(pressedNumbers.substring(0, pressedNumbers.length - 1))
}
}
return (
<TouchableOpacity activeOpacity={1} style={[styles.inputContainer, {backgroundColor: colors.elementDefaultColor}]} onPress={()=>{
if(inputRef.current)
inputRef.current.focus()
}}>
<Text style={[styles.text, {color: colors.primaryText}]}>{label}</Text>
<View style={styles.currencyWrapper}>
<TextInput
style={[styles.text, {color: colors.primaryText}]}
keyboardType='number-pad'
ref={inputRef}
onKeyPress={handleInput}
placeholder='0.00'
textAlign='right'
placeholderTextColor={colors.secondaryText}
value={formatDecimal(pressedNumbers)}
/>
<Text style={styles.currency}>EUR</Text>
</View>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
inputContainer: {
minHeight: 50,
borderRadius: 20,
flexDirection: "row",
justifyContent: 'space-between',
alignItems: "center"
},
text:{
fontSize: SIZES.large,
marginHorizontal: 15,
},
currency: {
fontSize: SIZES.normal,
color: "#007acc",
marginRight: 15,
},
currencyWrapper: {
flexDirection:"row",
justifyContent: 'flex-end',
alignItems: "center"
}
})
export default AutoDecimalInput;

View file

@ -0,0 +1,66 @@
import { StyleSheet, Text, View, Pressable } from 'react-native'
import React from 'react'
import { Category } from '../../types/dbItems'
import CustomCard from './CustomCard';
import { SIZES } from '../../constants/theme';
import { useTheme } from '../../app/contexts/ThemeContext';
interface CategoryListItemProps{
category: Category;
onPress?: (category: Category) =>void | undefined
}
const CategoryListItem: React.FC<CategoryListItemProps> = (props: CategoryListItemProps) => {
const {category, onPress} = props;
const {colors} = useTheme();
const handlePress = ()=>{
if(onPress){
onPress(category);
}
}
return (
<Pressable onPress={handlePress}>
<View style={[styles.tile, {backgroundColor: colors.backgroundColor}]}>
<View style={[styles.colorTip, {backgroundColor: category.color}]}/>
<View style={[styles.textWrapper]}>
<Text style={[styles.text, {color: colors.primaryText}]}>{category.name ?? "#noData#"}</Text>
</View>
<View style={styles.tileTail}/>
</View>
</Pressable>
)
}
export default CategoryListItem
const styles = StyleSheet.create({
tile: {
height: 60,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
borderRadius: 20
},
colorTip:{
height: "100%",
width: 30,
borderTopLeftRadius:20,
borderBottomLeftRadius: 20
},
textWrapper: {
},
tileTail:{
height: "100%",
width: 30,
borderTopRightRadius:20,
borderBottomRightRadius: 20
},
text: {
fontSize: SIZES.large,
}
})

View file

@ -39,10 +39,7 @@ export default function CustomCard(props : ViewProps) {
const styles = StyleSheet.create({
container:{
flexDirection: "row",
alignItems: "stretch",
alignContent: "space-between",
borderRadius: 10,
borderRadius: 20,
marginHorizontal: 10,
},
boxShadow: {},

View file

@ -0,0 +1,32 @@
import { StyleSheet, Text, View, ViewProps, TouchableOpacity } from 'react-native'
import React from 'react'
import { SIZES } from '../../constants/theme';
interface RoundedButtonProps extends ViewProps{
onPress?: ()=> void | undefined;
color: string;
}
const RoundedButton: React.FC<RoundedButtonProps> = (props: RoundedButtonProps) => {
const {onPress, color, style, ...restProps} = props;
return (
<TouchableOpacity onPress={onPress}>
<View style={[{backgroundColor: color}, styles.btn, style]}{...restProps}>
{restProps.children}
</View>
</TouchableOpacity>
)
}
export default RoundedButton
const styles = StyleSheet.create({
btn:{
justifyContent: 'center',
alignItems: 'center',
borderRadius: 80
},
text: {
fontSize: SIZES.normal
}
})

View file

@ -1,14 +1,18 @@
import { AntDesign } from '@expo/vector-icons';
import React from 'react';
import React, { useState } from 'react';
import { StyleSheet, TextInput, TouchableOpacity, View, ViewProps } from 'react-native';
import { SIZES } from '../../constants/theme';
import { useTheme } from '../../app/contexts/ThemeContext';
type SearchBarProps = {placeholder: string} & ViewProps
interface SearchBarProps extends ViewProps {
placeholder? : string;
onChangeText? : (text: string) => void | undefined
}
export default function SearchBar(props: SearchBarProps) {
export default function TextInputBar(props: SearchBarProps) {
const [isActive, setIsactive] = React.useState(false);
const { colors } = useTheme();
const [text, setText] = useState<string>("");
const textColor = colors
const backgroundColor = colors.elementDefaultColor;
@ -23,6 +27,10 @@ export default function SearchBar(props: SearchBarProps) {
setIsactive(false)
}
}
if(props.onChangeText){
props.onChangeText(text)
}
setText(text)
}
// cant apply the background color otherwise
@ -34,12 +42,12 @@ export default function SearchBar(props: SearchBarProps) {
//TODO: Handle textCancel
// changed styles.container to containerStyle
return (
<View style={containerStyle}>
<TextInput onChangeText = {handleChange} style={[{fontSize: SIZES.normal, height: "100%"}, styles.TextInput]} autoCorrect={false} keyboardType='web-search' placeholder={props.placeholder}></TextInput>
<View style={[containerStyle, props.style]}>
<TextInput placeholderTextColor={colors.secondaryText} onChangeText = {handleChange} style={[{fontSize: SIZES.normal, height: "100%", color:colors.primaryText}, styles.TextInput]} autoCorrect={false} keyboardType='default' placeholder={props.placeholder} value={text} onFocus={()=>handleChange(text)} onEndEditing={()=>setIsactive(false)}/>
{isActive &&
<TouchableOpacity style={styles.cancel}>
<AntDesign size={15} name='closecircle'></AntDesign>
<TouchableOpacity style={styles.cancel} onPress={()=>{handleChange("")}}>
<AntDesign size={15} name='closecircle' color={colors.primaryText}></AntDesign>
</TouchableOpacity>
}
</View>
@ -48,8 +56,6 @@ export default function SearchBar(props: SearchBarProps) {
const styles = StyleSheet.create({
container: {
marginHorizontal: 10,
marginBottom: 20,
flexDirection: 'row',
justifyContent: "center",
alignItems: "center",

View file

@ -0,0 +1,60 @@
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'
import React, { useState } from 'react'
import { useTheme } from '../../../app/contexts/ThemeContext'
import { SIZES } from '../../../constants/theme';
import { Category } from '../../../types/dbItems';
import CategorySelectorModal from './CategorySelectorModal';
interface CategorySelectorProps {
onPress? : () => void | undefined;
selectedCategory? : Category;
}
const CategorySelector: React.FC<CategorySelectorProps> = (props : CategorySelectorProps) => {
const {colors} = useTheme();
return (
<>
<TouchableOpacity style={[styles.tile, {backgroundColor: colors.backgroundColor}]} onPress={props.onPress}>
<View style={[styles.colorTip, {backgroundColor: props.selectedCategory?.color}]}>
</View>
<View style={[styles.textWrapper]}>
<Text style={[styles.text, {color: colors.primaryText}]}>{props.selectedCategory?.name ?? "Tap to select Categroy"}</Text>
</View>
<View style={styles.tileTail}></View>
</TouchableOpacity>
</>
)
}
export default CategorySelector
const styles = StyleSheet.create({
tile: {
height: 60,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
borderRadius: 20
},
colorTip:{
height: "100%",
width: 30,
borderTopLeftRadius:20,
borderBottomLeftRadius: 20
},
textWrapper: {
},
tileTail:{
height: "100%",
width: 30,
borderTopRightRadius:20,
borderBottomRightRadius: 20
},
text: {
fontSize: SIZES.large,
}
})

View file

@ -0,0 +1,93 @@
import { Modal, NativeSyntheticEvent, StyleSheet, Text, View, FlatList } from 'react-native'
import React, { useEffect, useMemo, useState } from 'react'
import { Category } from '../../../types/dbItems';
import { useTheme } from '../../../app/contexts/ThemeContext';
import CategoryListItem from '../../common/CategoryListItem';
import { SIZES } from '../../../constants/theme';
import useFetch from '../../../hooks/useFetch';
import TextInputBar from '../../common/TextInputBar';
interface CategorySelectorModalProps{
visible: boolean;
onCategoryTap?: (category : Category) => void | undefined;
selectMulitple?: boolean;
onRequestClose?: ((event: NativeSyntheticEvent<any>) => void) | undefined;
}
//TODO: select Multiple
const CategorySelectorModal: React.FC<CategorySelectorModalProps> = (props : CategorySelectorModalProps) => {
const {visible, onCategoryTap, selectMulitple} = props;
const {data, reFetch} = useFetch({sql: "SELECT * FROM category;", args:[]});
const {colors} = useTheme();
const [searchtext, setSearchtext] = useState<string>("");
const handleSearchText = (text : string) => {
setSearchtext(text);
}
const categories = useMemo<Category[]>(()=>{
return data.map((elem) => {
return {name: elem["name"], color: elem["color"], guid: elem["guid"]}
})
}, [data])
const filteredCategories = categories.filter((category) => category.name?.toLowerCase().includes(searchtext.toLowerCase()))
useEffect(()=>{
if(visible){
//reFetch(); Uncomment if newly added categories do not appear
handleSearchText("");
}
}, [visible])
return (
<Modal visible={visible} transparent={true} onRequestClose={props.onRequestClose}>
<View style={styles.main}>
<View style={[styles.modal, {backgroundColor: colors.containerColor}]}>
<View>
<Text style={[styles.heading, {color: colors.primaryText}]}>{selectMulitple ? "Categories" : "Category"}</Text>
</View>
<TextInputBar placeholder='TypeToSearch' onChangeText={handleSearchText} style={{marginBottom: 10}}></TextInputBar>
<FlatList
data={filteredCategories}
keyExtractor={(item) => item.guid!}
renderItem={({item})=> <CategoryListItem category={item} onPress={onCategoryTap}/>}
ItemSeparatorComponent={() => <View style={styles.itemSeperatorStyle}/>}
ListFooterComponent={() => <View style={styles.itemSeperatorStyle}/>}
keyboardShouldPersistTaps="always"
>
</FlatList>
</View>
</View>
</Modal>
)
}
export default CategorySelectorModal
const styles = StyleSheet.create({
main: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
height: "100%",
width: "100%",
alignItems: "center",
},
modal: {
height: "70%",
width: "90%",
top: "10%",
borderRadius: 30,
paddingVertical: 20,
paddingHorizontal: 20
},
heading: {
fontSize: SIZES.xlarge,
fontWeight: "bold"
},
itemSeperatorStyle: {
paddingBottom: 10
}
})

View file

@ -0,0 +1,38 @@
import { View, Text, StyleSheet, TouchableOpacity, TouchableOpacityProps } from 'react-native'
import React, { useState } from 'react'
import { useTheme } from '../../../app/contexts/ThemeContext';
import { SIZES } from '../../../constants/theme';
import { SimpleDate } from '../../../util/SimpleDate';
import { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes';
interface DateSelectorProps extends ViewProps {
onPress?: ()=>void | undefined;
selectedDate: Date
}
const DateSelectorButton:React.FC<DateSelectorProps> = (props: DateSelectorProps) => {
const {onPress, selectedDate, ...restProps} = props;
const {colors} = useTheme();
return (
<TouchableOpacity onPress={onPress} {...restProps} style={[styles.inputContainer, {backgroundColor: colors.elementDefaultColor}]}>
<Text style={[styles.text, {color: colors.primaryText}]}>Date:</Text>
<Text style={[styles.text, {color: colors.primaryText}]}>{new SimpleDate(selectedDate).format("DD.MM.YYYY")}</Text>
</TouchableOpacity>
)
}
const styles = StyleSheet.create({
inputContainer: {
minHeight: 50,
borderRadius: 20,
flexDirection: "row",
justifyContent: 'space-between',
alignItems: "center"
},
text:{
fontSize: SIZES.large,
marginHorizontal: 15,
},
})
export default DateSelectorButton

View file

@ -9,28 +9,29 @@ export default function ExpenseItem(itemProps : ExpenseItemProps) {
const { colors } = useTheme();
return (
<CustomCard>
<View style={[styles.colorTip, {backgroundColor: itemProps.color}]}></View>
<View style={[styles.textSection, {backgroundColor: colors.backgroundColor}]}>
<Text style={{
fontSize: SIZES.normal,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.category}</Text>
<Text style={{
fontSize: SIZES.large,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.title}</Text>
<Text style={{
fontSize: SIZES.small,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.date}</Text>
</View>
<View style={[styles.valueSection, {backgroundColor: colors.backgroundColor}]}>
<Text style={{
fontSize: SIZES.xxLarge,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.value}</Text>
</View>
<View style={styles.tile}>
<View style={[styles.colorTip, {backgroundColor: itemProps.color}]}></View>
<View style={[styles.textSection, {backgroundColor: colors.backgroundColor}]}>
<Text style={{
fontSize: SIZES.normal,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.category}</Text>
<Text style={{
fontSize: SIZES.large,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.title}</Text>
<Text style={{
fontSize: SIZES.small,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.date}</Text>
</View>
<View style={[styles.valueSection, {backgroundColor: colors.backgroundColor}]}>
<Text style={{
fontSize: SIZES.xxLarge,
color: colors.primaryText
}} numberOfLines={1}>{itemProps.value}</Text>
</View>
</View>
</CustomCard>
)
@ -42,6 +43,12 @@ const styles = StyleSheet.create({
borderTopLeftRadius: 20,
borderBottomLeftRadius: 20,
},
tile: {
flexDirection: "row",
alignItems: "stretch",
alignContent: "space-between",
},
textSection: {
flexDirection: "column",

View file

@ -3,14 +3,21 @@ import { ButtonSetting, ToggleSetting } from "./home/Setting"
import Welcome from "./home/Welcome"
import ExpenseItem from "./home/expenseItem"
//home/addItem
import CategorySelector from "./home/addItem/CategorySelector"
import CategorySelectorModal from "./home/addItem/CategorySelectorModal"
import DateSelectorButton from "./home/addItem/DateSelectorButton"
//common
import CustomCard from "./common/CustomCard"
import SearchBar from "./common/SearchBar"
import NavigationButton from "./common/button"
import LoadingSymbol from "./common/loadingSymbol"
import Plus from "./common/plus"
import TextInputBar from "./common/TextInputBar"
import AutoDecimalInput from "./common/AutoDecimalInput"
import RoundedButton from "./common/RoundedButton"
//budget
//login
import BudgetHeader from "./budget/budgetHeader"
import CategoryItem from "./budget/categoryItem"
import CustomColorPicker from "./budget/customColorPicker"
@ -20,8 +27,24 @@ import TypeSelectorSwitch from "./budget/typeSelectorSwitch"
import Input from "./login/input"
export {
BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input,
LoadingSymbol, NavigationButton, Plus,
SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome
BudgetHeader,
ButtonSetting,
CustomCard,
ExpenseItem,
Input,
LoadingSymbol,
Plus,
TextInputBar,
ToggleSetting,
Welcome,
AutoDecimalInput,
CategorySelector,
CategorySelectorModal,
DateSelectorButton,
RoundedButton,
CategoryItem,
TypeSelectorSwitch,
NavigationButton,
CustomColorPicker
}

9
package-lock.json generated
View file

@ -10,6 +10,7 @@
"dependencies": {
"@expo/vector-icons": "^13.0.0",
"@react-native-async-storage/async-storage": "1.18.2",
"@react-native-community/datetimepicker": "7.2.0",
"@react-navigation/native": "^6.0.2",
"expo": "~49.0.15",
"expo-font": "~11.4.0",
@ -6275,6 +6276,14 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@react-native-community/datetimepicker": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-7.2.0.tgz",
"integrity": "sha512-dO1sQy83M/EvnHE2egto05iwXZX7EYn5f/VDMp6afZFRFXRiRo7CzB3VFg4B55gJRJMNBv06NYMLPM3SlpnEGQ==",
"dependencies": {
"invariant": "^2.2.4"
}
},
"node_modules/@react-native/assets-registry": {
"version": "0.72.0",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz",

View file

@ -41,7 +41,8 @@
"react-native-screens": "~3.22.0",
"react-native-uuid": "^2.0.1",
"react-native-web": "~0.19.6",
"reanimated-color-picker": "^2.4.2"
"reanimated-color-picker": "^2.4.2",
"@react-native-community/datetimepicker": "7.2.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",

View file

@ -1,6 +1,6 @@
import React from 'react';
import { View, Button, Alert } from 'react-native';
import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase } from './database';
import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase, deleteDatabase } from './database';
import uuid from 'react-native-uuid';
const randomColors = ["red", "blue", "green", "purple", "yellow"];
@ -19,6 +19,11 @@ const getRandomNumber = () => {
const DebugMenu = () => {
const deleteDBFile = () => {
console.warn("Deleting DB. App Restart is required")
return deleteDatabase();
}
const handleNukeDatabase = () => {
return deleteExpenses(), deleteCategories()
};
@ -60,6 +65,7 @@ const DebugMenu = () => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Delete DB file" onPress={deleteDBFile}/>
<Button title="Nuke Database" onPress={handleNukeDatabase} />
<Button title="DEV_populateDatabase" onPress={handlePopulateDatabase} />
<Button title="Delete All Expenses" onPress={handleDeleteExpenses} />

7
types/dbItems.ts Normal file
View file

@ -0,0 +1,7 @@
export interface Category {
guid? : string;
name? : string;
color? :string;
type? : "expense" | "saving"
allocatedAmount? : number
}