Commit 45a9945d authored by Soo Hyun Kim's avatar Soo Hyun Kim
Browse files

0803 세부카테고리 추가수정삭제 및 아코디언

parent 139ee982
...@@ -4,20 +4,16 @@ import editApi from './db/editOption.api'; ...@@ -4,20 +4,16 @@ import editApi from './db/editOption.api';
import AntDesign from 'react-native-vector-icons/AntDesign'; import AntDesign from 'react-native-vector-icons/AntDesign';
import InputBox from './components/InputBox'; import InputBox from './components/InputBox';
import StyledButton from './components/StyledButton'; import StyledButton from './components/StyledButton';
import Accordion from './components/Accordion'; import Accordion, { AccordionItem } from './components/Accordion';
const INIT_OPTION = { id: 0, value: '' } const INIT_OPTION = { id: 0, value: '' }
const TEST_OPTION = { id: 15, value: 'testtest' } const INIT_SUBOPTION = { id: 0, value: '', foreign_id: 0 }
const TEST_SUB = [{
id: 1, value: 'test1',
},
{
id: 2, value: 'test2'
}]
const EditOption = ({ route }) => { const EditOption = ({ route }) => {
console.log('catEdit: type_id ', route.params) console.log('catEdit: type_id ', route.params)
const type = route.params ? 'category' : 'asset'
const type_id = route.params const type_id = route.params
const [options, setOptions] = useState([]) const [options, setOptions] = useState([])
const [option, setOption] = useState(INIT_OPTION) const [option, setOption] = useState(INIT_OPTION)
...@@ -28,122 +24,86 @@ const EditOption = ({ route }) => { ...@@ -28,122 +24,86 @@ const EditOption = ({ route }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
if (type_id > 0) { loadOptions()
loadCat()
} else {
loadAsset()
}
}, []) }, [])
const loadCat = async () => { const loadOptions = async () => {
try { try {
const catArray = await editApi.selectCategories(type_id) let optionArray = []
setOptions(catArray) if (type === 'asset') {
} catch (error) { optionArray = await editApi.selectAssetsType()
} else if (type === 'category') {
} optionArray = await editApi.selectCategories(type_id)
} }
setOptions(optionArray)
const loadAsset = async () => {
try {
const assetArray = await editApi.selectAssetsType()
setOptions(assetArray)
} catch (error) { } catch (error) {
} }
} }
const handleUpdate = () => { const handleUpdate = async () => {
if (type_id > 0) {
updateCat()
} else {
updateAsset()
}
}
const updateAsset = async () => {
try { try {
if (type === 'asset') {
const res = await editApi.updateOption('assets_type', { id: option.id, name: 'assets', value: option.value }) const res = await editApi.updateOption('assets_type', { id: option.id, name: 'assets', value: option.value })
console.log(res) console.log(res)
loadAsset() } else if (type === 'category') {
modalClose() if (option.foreign_id && option.foreign_id > 0) {
} catch (error) { const res = await editApi.updateOption('subcategories', { id: option.id, name: 'subcategory', value: option.value })
return console.log(res)
} }
}
const updateCat = async () => {
try {
const res = await editApi.updateOption('categories', { id: option.id, name: 'category', value: option.value }) const res = await editApi.updateOption('categories', { id: option.id, name: 'category', value: option.value })
console.log(res) console.log(res)
loadCat()
modalClose()
} catch (error) {
}
} }
} catch (error) {
const handleDelete = (item) => { } finally {
if (type_id > 0) { loadOptions()
deleteCat(item) modalClose()
} else {
deleteAsset(item)
} }
} }
const deleteAsset = async (item) => { const handleDelete = async (item) => {
try { try {
if (type === 'asset') {
const res = await editApi.deleteOption('assets_type', { id: item.id, name: 'assets' }) const res = await editApi.deleteOption('assets_type', { id: item.id, name: 'assets' })
console.log(res) console.log(res)
loadAsset() } else if (type === 'category') {
modalClose() if (item.foreign_id && item.foreign_id > 0) {
} catch (error) { const res = await editApi.deleteOption('subcategories', { id: item.id, name: 'subcategory'})
return console.log(res)
}
} }
const deleteCat = async (item) => {
try {
const res = await editApi.deleteOption('categories', { id: item.id, name: 'category' }) const res = await editApi.deleteOption('categories', { id: item.id, name: 'category' })
console.log(res) console.log(res)
loadCat()
modalClose()
} catch (error) {
}
} }
} catch (error) {
const handleAdd = () => { } finally {
if (type_id > 0) { loadOptions()
addCat()
} else {
addAsset()
} }
} }
const addAsset = async () => { const handleAdd = async () => {
try { try {
if (type === 'asset') {
const res = await editApi.addOption('assets_type', { name: 'assets', value: option.value }) const res = await editApi.addOption('assets_type', { name: 'assets', value: option.value })
console.log(res) console.log(res)
loadAsset() } else if (type === 'category') {
modalClose() if (option.foreign_id && option.foreign_id > 0) {
} catch (error) { const res = await editApi.addOption('subcategories', { name: 'subcategory', value: option.value, foreign_name: 'category', foreign_id: option.foreign_id })
return console.log(res)
}
} }
const addCat = async () => {
try {
const res = await editApi.addOption('categories', { name: 'category', value: option.value, foreign_name: 'type', foreign_id: type_id }) const res = await editApi.addOption('categories', { name: 'category', value: option.value, foreign_name: 'type', foreign_id: type_id })
console.log(res) console.log(res)
loadCat() }
modalClose()
} catch (error) { } catch (error) {
} finally {
loadOptions()
modalClose()
} }
} }
const renderOptionItem = ({ item }) => ( const renderAssetItem = ({ item }) => (
<View style={[style.flexRow, style.catBox]}> <View style={[style.flexRow, style.catBox]}>
<View style={style.flexRow}> <View style={style.flexRow}>
{item.deletable && <AntDesign name='minuscircle' style={style.cancelIcon} onPress={() => { handleDelete(item) }} />} {item.deletable && <AntDesign name='minuscircle' style={style.cancelIcon} onPress={() => { handleDelete(item) }} />}
...@@ -151,36 +111,56 @@ const EditOption = ({ route }) => { ...@@ -151,36 +111,56 @@ const EditOption = ({ route }) => {
{item.value} {item.value}
</Text> </Text>
</View> </View>
<View style={[style.flexRow, style.flexCenter]}>
{item.deletable && <AntDesign name='edit' style={style.icon} onPress={() => { setOption(item); setModalOpen(true) }} />} {item.deletable && <AntDesign name='edit' style={style.icon} onPress={() => { setOption(item); setModalOpen(true) }} />}
<AntDesign name='right' style={style.rightIcon} onPress={() => console.log('서브 카테고리 더보기')} />
</View>
</View> </View>
); );
const renderCatItem = ({ item }) => (
<Accordion
title={item.value}
left={item.deletable && <AntDesign name='closecircle' style={style.cancelIcon} onPress={() => { handleDelete(item) }} />}
right={item.deletable && <AntDesign name='edit' style={style.icon} onPress={() => { setOption(item); setModalOpen(true) }} />}
titleStyle={style.optionText}
backgroundColor='lightgray'
>
{item.subOptions.length !== 0 &&
<FlatList
data={item.subOptions}
renderItem={({ item }) => (
<AccordionItem
title={item.value}
left={<AntDesign name='closecircle' style={style.cancelIcon} onPress={() => { handleDelete(item) }} />}
right={<AntDesign name='edit' style={style.icon} onPress={() => { setOption(item); setModalOpen(true) }} />}
titleStyle={style.optionText}
backgroundColor='#b0b0b0'
marginLeft={30}
/>
)}
keyExtractor={item => item.id.toString()}
/>
}
<Pressable
style={[style.flexRow, { backgroundColor: "#b0b0b0", paddingVertical: 10 }]}
onPress={() => {
setOption({...INIT_SUBOPTION, ['foreign_id']: item.id});
setModalOpen(true)
}}
>
<AntDesign name='plus' style={[style.addIcon, { marginLeft: 35 }]} />
<Text style={style.optionText} >추가하기</Text>
</Pressable>
</Accordion>
);
return ( return (
<> <>
{console.log(option)} {console.log(option)}
<View> <View>
<FlatList <FlatList
data={options} data={options}
renderItem={renderOptionItem} renderItem={type === 'asset' ? renderAssetItem : renderCatItem}
keyExtractor={item => item.id.toString()} keyExtractor={item => item.id.toString()}
/> />
<Accordion subItems={TEST_SUB}>
<View style={[style.flexRow, style.catBox]}>
<View style={style.flexRow}>
<AntDesign name='minuscircle' style={style.cancelIcon} onPress={() => { handleDelete({ TEST_OPTION }) }} />
<Text style={style.optionText} >
{TEST_OPTION.value}
</Text>
</View>
<View style={[style.flexRow, style.flexCenter]}>
<AntDesign name='edit' style={style.icon} onPress={() => { setOption(TEST_OPTION); setModalOpen(true) }} />
<AntDesign name='right' style={style.rightIcon} onPress={() => console.log('서브 카테고리 더보기')} />
</View>
</View>
</Accordion>
<Pressable style={style.addButton} onPress={() => setModalOpen(true)}> <Pressable style={style.addButton} onPress={() => setModalOpen(true)}>
<AntDesign name='plus' style={style.icon} /> <AntDesign name='plus' style={style.icon} />
<Text style={style.optionText} >추가하기</Text> <Text style={style.optionText} >추가하기</Text>
...@@ -246,9 +226,14 @@ const style = StyleSheet.create({ ...@@ -246,9 +226,14 @@ const style = StyleSheet.create({
fontSize: 30, fontSize: 30,
color: 'black', color: 'black',
}, },
addIcon: {
marginHorizontal: 5,
fontSize: 25,
color: 'black',
},
cancelIcon: { cancelIcon: {
marginHorizontal: 5, marginHorizontal: 5,
fontSize: 30, fontSize: 25,
color: 'red', color: 'red',
}, },
rightIcon: { rightIcon: {
......
import React, { useEffect, useRef, useState } from 'react'; import React, { useState } from 'react';
import { Animated, Text, View, StyleSheet, TouchableOpacity, FlatList } from 'react-native'; import { Animated, Text, View, StyleSheet, Pressable } from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
const renderOptionItem = ({ item }) => (
<View style={[style.flexRow, style.catBox]}>
<View style={style.flexRow}>
{/* { item.deletable && <AntDesign name='minuscircle' style={style.cancelIcon} onPress={() => { handleDelete(item) }} />} */}
<Text style={style.optionText} >
{item.value}
</Text>
</View>
{/* <View style={[style.flexRow, style.flexCenter]}>
{ item.deletable && <AntDesign name='edit' style={style.icon} onPress={() => { setOption(item); setModalOpen(true) }} /> }
<AntDesign name='right' style={style.rightIcon} onPress={() => console.log('서브 카테고리 더보기')}/>
</View> */}
</View>
);
const Accordion = ({ const Accordion = ({
item, title,
subItems, left,
children right,
children,
titleStyle = style.text,
backgroundColor = 'lightgray',
}) => { }) => {
let layoutHeight = 0; const [opened, setOpened] = useState(false);
const [bodyMounted, setBodyMounted] = useState(false);
const [bodyHeight, setBodyHeight] = useState(0);
let dropDownAnimValueList = useRef(new Animated.Value(0)).current;
const handleBodyLayout = (e) => {
if (bodyMounted) return;
console.log(e.nativeEvent.layout)
const { height } = e.nativeEvent.layout;
layoutHeight = height;
setBodyMounted(true);
setBodyHeight(height);
}
const [open, setOpen] = useState(false);
// const openList = () => {
// Animated.timing(dropDownAnimValueList, {
// toValue: 0,
// duration: 500,
// useNativeDriver: true,
// })
// }
// const closeList = () => { const handleOpen = () => { setOpened(!opened) };
// Animated.timing(dropDownAnimValueList, {
// toValue: -bodyHeight,
// duration: 500,
// useNativeDriver: true,
// })
// }
useEffect(() => {
if (bodyMounted) dropDownAnimValueList.setValue(open ? -layoutHeight : 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bodyMounted]);
useEffect(() => {
// if (shouldAnimate) {
// if (!opened) {
// Animated.timing(dropDownAnimValueList, {
// toValue: 0,
// duration: animDuration || 300,
// useNativeDriver: true,
// }).start();
// return;
// }
// Animated.timing(dropDownAnimValueList, {
// toValue: -bodyHeight,
// duration: animDuration || 300,
// useNativeDriver: true,
// }).start();
// } else {
const targetValue = open ? -bodyHeight : 0;
dropDownAnimValueList.setValue(targetValue);
// }
// if (open) dropDownAnimValueList.setValue(open ? -layoutHeight : 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
console.log(open)
return ( return (
<Animated.View style={[ <View>
{ <View>
overflow: 'hidden', <Pressable
transform: [{ style={[style.flexRow, style.catBox, { backgroundColor: backgroundColor }]}
translateY: 0, onPress={handleOpen}
}], >
}, <View style={[style.flexRow, style.flexCenter]}>
]}> {left ? left : null}
<View style={[style.boxContainer]}> <Text style={titleStyle}>
<TouchableOpacity onPress={() => { setOpen(!open) }}> {title}
{children} </Text>
</TouchableOpacity>
</View> </View>
<View style={[style.flexRow, style.flexCenter]}>
<Animated.View style={{ {right ? right : null}
height: !bodyMounted ? undefined : bodyHeight, <AntDesign
transform: [ name={opened ? 'caretup' : 'caretdown'}
{ color='black'
translateY: dropDownAnimValueList, size={24}
}, style={style.rightIcon}
],
}}
onLayout={handleBodyLayout}>
<FlatList
data={subItems}
renderItem={renderOptionItem}
keyExtractor={item => item.id.toString()}
/> />
</Animated.View> </View>
</Animated.View> </Pressable>
) </View>
}
const Accordion {opened ?
children
: null}
</View>
)
};
export const AccordionItem = ({
title,
left,
right,
titleStyle = style.text,
backgroundColor = 'lightgray',
marginLeft = 20,
}) => {
return (
<View style={[style.flexRow, style.flexCenter, style.catBox, { backgroundColor: backgroundColor }]}>
<View style={[style.flexRow, style.flexCenter, { marginLeft: marginLeft }]}>
{left ? left : null}
<Text style={titleStyle}>
{title}
</Text>
</View>
{right ? right : null}
</View>
);
};
const style = StyleSheet.create({ const style = StyleSheet.create({
boxContainer: {
width: '100%',
position: 'relative',
},
flexRow: { flexRow: {
flexDirection: 'row', flexDirection: 'row',
}, },
...@@ -139,35 +78,16 @@ const style = StyleSheet.create({ ...@@ -139,35 +78,16 @@ const style = StyleSheet.create({
catBox: { catBox: {
justifyContent: 'space-between', justifyContent: 'space-between',
paddingVertical: 10, paddingVertical: 10,
backgroundColor: 'lightgray',
},
zIndex: {
position: 'absolute',
zIndex: 15,
elevation: 1000,
},
icon: {
marginHorizontal: 5,
fontSize: 30,
color: 'black',
},
cancelIcon: {
marginHorizontal: 5,
fontSize: 30,
color: 'red',
}, },
rightIcon: { rightIcon: {
marginHorizontal: 5, marginHorizontal: 5,
fontSize: 20, fontSize: 20,
color: 'black', color: 'black',
}, },
optionText: { text: {
fontSize: 20, fontSize: 20,
marginHorizontal: 10, marginHorizontal: 10,
}, },
items: {
overflow: "hidden"
}
}) })
export default Accordion export default Accordion
\ No newline at end of file
...@@ -12,20 +12,50 @@ const selectCategories = async (type_id) => { ...@@ -12,20 +12,50 @@ const selectCategories = async (type_id) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.transaction(async (tx) => { db.transaction(async (tx) => {
console.log("카테고리 부르기"); console.log("카테고리 부르기");
const [txn, results] = await tx.executeSql(`SELECT * FROM categories WHERE type_id=${type_id}`); const [txn, results] = await tx.executeSql(
`select cat.category_id, category_name, subcat_id, subcat_name from categories as cat
left join (
SELECT category_id,
group_concat(subcategory_id, '|') as subcat_id,
group_concat(subcategory_name, '|') as subcat_name
from subcategories group by category_id
) as subcat
on cat.category_id=subcat.category_id
where cat.type_id='${type_id}'`
);
console.log('item length', results.rows.length); console.log('item length', results.rows.length);
const temp = []; const temp = [];
for (let i = 0; i < results.rows.length; i++) { for (let i = 0; i < results.rows.length; i++) {
const tempId = results.rows.item(i).category_id; const tempId = results.rows.item(i).category_id;
const tempName = results.rows.item(i).category_name; const tempName = results.rows.item(i).category_name;
if (tempId < CAT_LIMIT_ID) { let deletable = (tempId < CAT_LIMIT_ID) ? false : true;
temp.push({ id: tempId, value: tempName, deletable: false }); const tempSubOptions = [];
} else { if (results.rows.item(i).subcat_id) {
temp.push({ id: tempId, value: tempName, deletable: true }); const tempSubId = results.rows.item(i).subcat_id.split('|');
const tempSubValue = results.rows.item(i).subcat_name.split('|');
for (let i = 0; i < tempSubId.length; i++) {
tempSubOptions.push({ id: tempSubId[i], value: tempSubValue[i], foreign_id: tempId });
}
} }
temp.push({ id: tempId, value: tempName, deletable: deletable, subOptions: tempSubOptions });
} }
console.log(temp) console.log(temp)
resolve(temp); resolve(temp);
// console.log("카테고리 부르기");
// const [txn, results] = await tx.executeSql(`SELECT * FROM categories WHERE type_id=${type_id}`);
// console.log('item length', results.rows.length);
// const temp = [];
// for (let i = 0; i < results.rows.length; i++) {
// const tempId = results.rows.item(i).category_id;
// const tempName = results.rows.item(i).category_name;
// if (tempId < CAT_LIMIT_ID) {
// temp.push({ id: tempId, value: tempName, deletable: false });
// } else {
// temp.push({ id: tempId, value: tempName, deletable: true });
// }
// }
// console.log(temp)
// resolve(temp);
}) })
}) })
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment