Merge branch '57-implement-and-split-edit-category-from-add-category' into 'main'

Resolve "Implement and Split edit Category from add Category"

Closes #57

See merge request thschleicher/interaktive-systeme!46
This commit is contained in:
jastornig 2024-01-05 16:45:25 +00:00
commit 123e358f9e
8 changed files with 146 additions and 21 deletions

View file

@ -25,13 +25,14 @@ stages:
lint_job: lint_job:
stage: lint stage: lint
image: node image: "reactnativecommunity/react-native-android:latest"
rules: rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual when: manual
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script: script:
- npm install - yarn install
- npx expo install
- npx tsc - npx tsc
cache: cache:
policy: pull-push policy: pull-push

View file

@ -5,6 +5,7 @@ export default function _Layout() {
<Stack initialRouteName="index" screenOptions={{headerShown: false}}> <Stack initialRouteName="index" screenOptions={{headerShown: false}}>
<Stack.Screen name="index"/> <Stack.Screen name="index"/>
<Stack.Screen name="addCategory" options={{presentation: "modal"}}/> <Stack.Screen name="addCategory" options={{presentation: "modal"}}/>
<Stack.Screen name="editCategory" options={{presentation: "modal"}}/>
<Stack.Screen name="category" options={{headerShown: false, animation: "slide_from_right"}}/> <Stack.Screen name="category" options={{headerShown: false, animation: "slide_from_right"}}/>
</Stack> </Stack>
); );

View file

@ -1,21 +1,23 @@
import { router, useLocalSearchParams } from "expo-router"; import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native";
import { CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components";
import { addCategory } from "../../../services/database";
import { useTheme } from "../../contexts/ThemeContext"; import { useTheme } from "../../contexts/ThemeContext";
const addCategory = () => { export default function Page() {
const {colors} = useTheme(); const {colors} = useTheme();
const parameters = useLocalSearchParams(); const parameters = useLocalSearchParams();
const [categoryName, setCategoryName] = useState<string>("Enter Category Name..."); const [categoryName, setCategoryName] = useState<string>("Enter Category Name...");
const [categoryColor, setCategoryColor] = useState<null|string>(null); const [categoryColor, setCategoryColor] = useState<string>('#' + Math.floor(Math.random()*16777215).toString(16));
const [selectedType, setSelectedType] = useState<string>("expense"); const [selectedType, setSelectedType] = useState<string>("expense");
const [amount, setAmount] = useState(0);
return ( return (
<SafeAreaView style={[styles.safeAreaViewStyle, {backgroundColor: colors.backgroundColor}]}> <SafeAreaView style={[styles.safeAreaViewStyle, {backgroundColor: colors.backgroundColor}]}>
<Text style={[styles.headingTextStyle, {color: colors.primaryText}]}>Category Editor</Text> <Text style={[styles.headingTextStyle, {color: colors.primaryText}]}>Add Category</Text>
<View style={[styles.containerStyle, {backgroundColor: colors.containerColor}]}> <View style={[styles.containerStyle, {backgroundColor: colors.containerColor}]}>
<View style={[styles.textInputViewStyle, {backgroundColor: colors.elementDefaultColor}]}> <View style={[styles.textInputViewStyle, {backgroundColor: colors.elementDefaultColor}]}>
@ -24,6 +26,12 @@ const addCategory = () => {
}}/> }}/>
</View> </View>
<View style={styles.budgetInput}>
<AutoDecimalInput label={"Allocated:"} onValueChange={(value) => {
setAmount(!Number.isNaN(Number.parseFloat(value)) ? Number.parseFloat(value) : 0);
}}/>
</View>
<TypeSelectorSwitch <TypeSelectorSwitch
currentSelected={selectedType} currentSelected={selectedType}
handleButtonPress={(type) => { handleButtonPress={(type) => {
@ -32,7 +40,7 @@ const addCategory = () => {
/> />
<View> <View>
<CustomColorPicker currentColor={categoryColor} handleColorChange={(color) => { <CustomColorPicker color={categoryColor} handleColorChange={(color) => {
setCategoryColor(color); setCategoryColor(color);
}}/> }}/>
</View> </View>
@ -42,7 +50,7 @@ const addCategory = () => {
router.back(); router.back();
}}/> }}/>
<NavigationButton text="Save" onPress={() => { <NavigationButton text="Save" onPress={() => {
console.log("Implement Saving here!"); addCategory(categoryName, categoryColor, selectedType, amount);
router.back(); router.back();
}}/> }}/>
</View> </View>
@ -51,8 +59,6 @@ const addCategory = () => {
); );
} }
export default addCategory;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
containerStyle: { containerStyle: {
flex: 1, flex: 1,
@ -82,5 +88,9 @@ const styles = StyleSheet.create({
textInputStyle: { textInputStyle: {
paddingHorizontal: 10, paddingHorizontal: 10,
fontSize: 25, fontSize: 25,
},
budgetInput: {
marginBottom: 10,
marginHorizontal: 10,
} }
}); });

View file

@ -7,12 +7,14 @@ import { useTheme } from "../../contexts/ThemeContext";
export default function Page() { export default function Page() {
const {colors} = useTheme(); const {colors} = useTheme();
const {category_guid, category_name} = useLocalSearchParams(); const {category_guid} = useLocalSearchParams();
const {category_amount, category_color, category_name, category_type} = fetchCategoryInformation(category_guid.toString());
const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_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 FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]}); const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_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 FROM expense e JOIN category c ON e.category_guid = c.guid WHERE c.guid = ? ORDER BY expense_datetime desc;", args: [category_guid]});
const handleEditCategory = () => { const handleEditCategory = () => {
console.log("edit category"); router.push({pathname: "./(tabs)/budget/editCategory", params: {category_guid: category_guid, category_color: category_color, category_amount: category_amount, category_name: category_name, category_type: category_type}});
} }
const handleBackButton = () => { const handleBackButton = () => {
@ -54,6 +56,25 @@ export default function Page() {
); );
} }
const fetchCategoryInformation = (guid: string) => {
const {data} = useFetch({sql: "SELECT * FROM category WHERE guid = ?", args: [guid]});
let category_name = "";
let category_color = "";
let category_amount = 0;
let category_type = "";
if (data && data[0]) {
if ("name" in data[0]) category_name = data[0].name as string;
if ("color" in data[0]) category_color = data[0].color as string;
if ("allocated_amount" in data[0]) category_amount = data[0].allocated_amount as number;
if ("type" in data[0]) category_type = data[0].type as string;
}
return {category_name, category_color, category_amount, category_type};
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
safeAreaView: { safeAreaView: {
flex: 1, flex: 1,

View file

@ -0,0 +1,96 @@
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native";
import { AutoDecimalInput, CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components";
import { useTheme } from "../../contexts/ThemeContext";
const addCategory = () => {
const {colors} = useTheme();
const {category_guid, category_amount, category_color, category_name, category_type} = useLocalSearchParams();
const [categoryName, setCategoryName] = useState(category_name.toString());
const [categoryColor, setCategoryColor] = useState(category_color.toString());
const [selectedType, setSelectedType] = useState(category_type.toString());
const [amount, setAmount] = useState(Number.parseFloat(category_amount.toString()));
return (
<SafeAreaView style={[styles.safeAreaViewStyle, {backgroundColor: colors.backgroundColor}]}>
<Text style={[styles.headingTextStyle, {color: colors.primaryText}]}>Edit Category</Text>
<View style={[styles.containerStyle, {backgroundColor: colors.containerColor}]}>
<View style={[styles.textInputViewStyle, {backgroundColor: colors.elementDefaultColor}]}>
<TextInput placeholder={categoryName} placeholderTextColor={colors.secondaryText} style={[styles.textInputStyle, {color: colors.primaryText}]} onChangeText={(newName: string) => {
setCategoryName(newName);
}}/>
</View>
<View style={styles.budgetInput}>
<AutoDecimalInput label={"Allocated:"} onValueChange={(value) => {
setAmount(!Number.isNaN(Number.parseFloat(value)) ? Number.parseFloat(value) : 0);
}}/>
</View>
<TypeSelectorSwitch
currentSelected={selectedType}
handleButtonPress={(type) => {
setSelectedType(type);
}}
/>
<View>
<CustomColorPicker color={categoryColor} handleColorChange={(color) => {
setCategoryColor(color);
}}/>
</View>
<View style={styles.navigationButtonViewStyle}>
<NavigationButton text="Back" onPress={() => {
router.back();
}}/>
<NavigationButton text="Save" onPress={() => {
console.log("Implement Saving here!");
router.back();
}}/>
</View>
</View>
</SafeAreaView>
);
}
export default addCategory;
const styles = StyleSheet.create({
containerStyle: {
flex: 1,
margin: 10,
borderRadius: 10,
},
safeAreaViewStyle: {
flex: 1,
flexDirection: "column"
},
headingTextStyle: {
fontSize: 40,
fontWeight: "bold",
alignSelf: "center",
marginVertical: 10,
},
navigationButtonViewStyle: {
flexDirection: "row",
justifyContent: "center",
marginBottom: 10,
},
textInputViewStyle: {
borderRadius: 10,
paddingVertical: 10,
margin: 10,
},
textInputStyle: {
paddingHorizontal: 10,
fontSize: 25,
},
budgetInput: {
marginBottom: 10,
marginHorizontal: 10,
}
});

View file

@ -38,7 +38,7 @@ export default function Page() {
}; };
const handleCategoryPress = (item: {[column: string]: any;}) => { const handleCategoryPress = (item: {[column: string]: any;}) => {
router.push({pathname: "/(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}}) router.push({pathname: "./(tabs)/budget/category", params: {category_guid: item.category_guid, category_name: item.category_name}})
} }
return ( return (

View file

@ -16,12 +16,12 @@ const CategoryItem = (properties: CategoryItemProps) => {
const { colors } = useTheme(); const { colors } = useTheme();
const subText = `${properties.total_expenses} / ${properties.allocated_amount}`; const subText = `${properties.total_expenses.toFixed(2)} / ${properties.allocated_amount}`;
return ( return (
<TouchableOpacity onPress={properties.onPress}> <TouchableOpacity onPress={properties.onPress}>
<CustomCard style={styles.customCardStyle}> <CustomCard style={styles.customCardStyle}>
<View style={[styles.colorTipStyle, {backgroundColor: "blue"}]}/> <View style={[styles.colorTipStyle, {backgroundColor: properties.color}]}/>
<View style={[styles.textViewStyle]}> <View style={[styles.textViewStyle]}>
<Text style={[styles.categoryNameStyle, {color: colors.primaryText}]}> <Text style={[styles.categoryNameStyle, {color: colors.primaryText}]}>
{properties.category} {properties.category}

View file

@ -2,14 +2,14 @@ import { StyleSheet } from "react-native";
import ColorPicker, { BrightnessSlider, HueSlider, Preview, SaturationSlider, } from "reanimated-color-picker"; import ColorPicker, { BrightnessSlider, HueSlider, Preview, SaturationSlider, } from "reanimated-color-picker";
export type CustomColorPickerProperties = { export type CustomColorPickerProperties = {
currentColor?: string | null, color: string,
handleColorChange: (color: string) => void | undefined, handleColorChange: (color: string) => void | undefined,
} }
const CustomColorPicker = (properties: CustomColorPickerProperties) => { const CustomColorPicker = (properties: CustomColorPickerProperties) => {
return ( return (
<ColorPicker <ColorPicker
value={properties.currentColor ?? generateRandomColor()} value={properties.color}
onChange={(color) => { onChange={(color) => {
properties.handleColorChange(color["hex"]) properties.handleColorChange(color["hex"])
}} }}
@ -30,10 +30,6 @@ const CustomColorPicker = (properties: CustomColorPickerProperties) => {
); );
} }
const generateRandomColor = (): string => {
return '#' + Math.floor(Math.random()*16777215).toString(16);
}
export default CustomColorPicker; export default CustomColorPicker;
const styles = StyleSheet.create({ const styles = StyleSheet.create({