diff --git a/App.js b/App.js index 9263190..e0210b8 100644 --- a/App.js +++ b/App.js @@ -1,10 +1,12 @@ import React, { useCallback, useEffect, useState } from "react"; -import { Image } from "react-native"; +import { Image, View } from "react-native"; import * as SplashScreen from "expo-splash-screen"; import * as Font from "expo-font"; import { Asset } from "expo-asset"; import { Block, GalioProvider } from "galio-framework"; import { NavigationContainer } from "@react-navigation/native"; +import { Provider } from 'react-redux'; +import store from './store/store'; // 스토어 파일의 위치에 맞게 경로를 조정하세요. // Before rendering any navigation stack import { enableScreens } from "react-native-screens"; @@ -46,7 +48,14 @@ export default function App() { await _loadResourcesAsync(); // Pre-load fonts, make any API calls you need to do here await Font.loadAsync({ - ArgonExtra: require("./assets/font/argon.ttf"), + ArgonExtra: require("./assets/font/Orbit-Regular.ttf"), + KoPubWorldBatang_Pro_Bold: require("./assets/font/KoPubWorld_Batang_Pro_Bold.otf"), + KoPubWorldBatang_Pro_Light: require("./assets/font/KoPubWorld_Batang_Pro_Light.otf"), + KoPubWorldBatang_Pro_Medium: require("./assets/font/KoPubWorld_Batang_Pro_Medium.otf"), + KoPubWorldDotum_Pro_Bold: require("./assets/font/KoPubWorld_Dotum_Pro_Bold.otf"), + KoPubWorldDotum_Pro_Light: require("./assets/font/KoPubWorld_Dotum_Pro_Light.otf"), + KoPubWorldDotum_Pro_Medium: require("./assets/font/KoPubWorld_Dotum_Pro_Medium.otf"), + Gugi: require("./assets/font/Gugi-Regular.ttf"), }); } catch (e) { console.warn(e); @@ -73,12 +82,14 @@ export default function App() { } return ( - - - - - - - + + + + + + + + + ); } diff --git a/app.json b/app.json index b1702aa..14032d9 100644 --- a/app.json +++ b/app.json @@ -3,10 +3,7 @@ "name": "Argon FREE React Native", "slug": "argon-free-react-native", "privacy": "public", - "platforms": [ - "ios", - "android" - ], + "platforms": ["ios", "android", "web"], "version": "1.7.1", "orientation": "portrait", "icon": "./assets/icon.png", @@ -18,9 +15,7 @@ "updates": { "fallbackToCacheTimeout": 0 }, - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true }, diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/font/Gugi-Regular.ttf b/assets/font/Gugi-Regular.ttf new file mode 100644 index 0000000..b500dbf Binary files /dev/null and b/assets/font/Gugi-Regular.ttf differ diff --git a/assets/font/KoPubWorld_Batang_Pro_Bold.otf b/assets/font/KoPubWorld_Batang_Pro_Bold.otf new file mode 100644 index 0000000..9f14b15 Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Bold.otf differ diff --git a/assets/font/KoPubWorld_Batang_Pro_Light.otf b/assets/font/KoPubWorld_Batang_Pro_Light.otf new file mode 100644 index 0000000..ed115b9 Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Light.otf differ diff --git a/assets/font/KoPubWorld_Batang_Pro_Medium.otf b/assets/font/KoPubWorld_Batang_Pro_Medium.otf new file mode 100644 index 0000000..3e438b4 Binary files /dev/null and b/assets/font/KoPubWorld_Batang_Pro_Medium.otf differ diff --git a/assets/font/KoPubWorld_Dotum_Pro_Bold.otf b/assets/font/KoPubWorld_Dotum_Pro_Bold.otf new file mode 100644 index 0000000..3cd1a6b Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Bold.otf differ diff --git a/assets/font/KoPubWorld_Dotum_Pro_Light.otf b/assets/font/KoPubWorld_Dotum_Pro_Light.otf new file mode 100644 index 0000000..ae977db Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Light.otf differ diff --git a/assets/font/KoPubWorld_Dotum_Pro_Medium.otf b/assets/font/KoPubWorld_Dotum_Pro_Medium.otf new file mode 100644 index 0000000..0168953 Binary files /dev/null and b/assets/font/KoPubWorld_Dotum_Pro_Medium.otf differ diff --git a/assets/font/Orbit-Regular.ttf b/assets/font/Orbit-Regular.ttf new file mode 100644 index 0000000..db9562d Binary files /dev/null and b/assets/font/Orbit-Regular.ttf differ diff --git a/assets/font/argon.ttf b/assets/font/argon.ttf deleted file mode 100755 index 4240bc9..0000000 Binary files a/assets/font/argon.ttf and /dev/null differ diff --git a/assets/imgs/.DS_Store b/assets/imgs/.DS_Store new file mode 100644 index 0000000..4cf4ba0 Binary files /dev/null and b/assets/imgs/.DS_Store differ diff --git a/assets/imgs/argon-logo-onboarding.png b/assets/imgs/argon-logo-onboarding.png index 4350b4e..53ca2e9 100644 Binary files a/assets/imgs/argon-logo-onboarding.png and b/assets/imgs/argon-logo-onboarding.png differ diff --git a/assets/imgs/argon-logo-onboarding@2x.png b/assets/imgs/argon-logo-onboarding@2x.png index a59cc29..53ca2e9 100644 Binary files a/assets/imgs/argon-logo-onboarding@2x.png and b/assets/imgs/argon-logo-onboarding@2x.png differ diff --git a/assets/imgs/argon-logo.png b/assets/imgs/argon-logo.png index 2ca2750..0bd08a9 100644 Binary files a/assets/imgs/argon-logo.png and b/assets/imgs/argon-logo.png differ diff --git a/assets/imgs/argon-logo@2x.png b/assets/imgs/argon-logo@2x.png deleted file mode 100644 index 82e3fa8..0000000 Binary files a/assets/imgs/argon-logo@2x.png and /dev/null differ diff --git a/assets/imgs/backg1.png b/assets/imgs/backg1.png new file mode 100644 index 0000000..65ffb22 Binary files /dev/null and b/assets/imgs/backg1.png differ diff --git a/assets/imgs/backg2.png b/assets/imgs/backg2.png new file mode 100644 index 0000000..277858e Binary files /dev/null and b/assets/imgs/backg2.png differ diff --git a/assets/imgs/bg.png b/assets/imgs/bg.png index 14840c4..d9e919b 100644 Binary files a/assets/imgs/bg.png and b/assets/imgs/bg.png differ diff --git a/assets/imgs/bg2.png b/assets/imgs/bg2.png new file mode 100644 index 0000000..14840c4 Binary files /dev/null and b/assets/imgs/bg2.png differ diff --git a/assets/imgs/defaultpill.jpg b/assets/imgs/defaultpill.jpg new file mode 100644 index 0000000..142932c Binary files /dev/null and b/assets/imgs/defaultpill.jpg differ diff --git a/assets/imgs/kakao.png b/assets/imgs/kakao.png new file mode 100644 index 0000000..c882acc Binary files /dev/null and b/assets/imgs/kakao.png differ diff --git a/assets/imgs/logo_sub.png b/assets/imgs/logo_sub.png new file mode 100644 index 0000000..66847de Binary files /dev/null and b/assets/imgs/logo_sub.png differ diff --git a/assets/imgs/profile-screen-bg.png b/assets/imgs/profile-screen-bg.png index ee37fb0..d9e919b 100644 Binary files a/assets/imgs/profile-screen-bg.png and b/assets/imgs/profile-screen-bg.png differ diff --git a/assets/imgs/profile-screen-bg2.png b/assets/imgs/profile-screen-bg2.png new file mode 100644 index 0000000..ee37fb0 Binary files /dev/null and b/assets/imgs/profile-screen-bg2.png differ diff --git a/assets/splash.png b/assets/splash.png index 89a8eaf..62230fd 100644 Binary files a/assets/splash.png and b/assets/splash.png differ diff --git a/babel.config.js b/babel.config.js index c0d6e65..d69299a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -9,7 +9,7 @@ module.exports = function (api) { extensions: [".tsx", ".ts", ".js", ".json"], }, ], - "react-native-reanimated/plugin", + ["react-native-reanimated/plugin"], ], }; }; diff --git a/components/DrawerItem.js b/components/DrawerItem.js index 36ba698..f693ec6 100644 --- a/components/DrawerItem.js +++ b/components/DrawerItem.js @@ -1,58 +1,64 @@ import React from "react"; import { StyleSheet, TouchableOpacity, Linking } from "react-native"; import { Block, Text, theme } from "galio-framework"; +import { FontAwesome, MaterialIcons, Feather, MaterialCommunityIcons } from '@expo/vector-icons'; import Icon from "./Icon"; import argonTheme from "../constants/Theme"; class DrawerItem extends React.Component { + + iconColor = "#67B779"; + renderIcon = () => { const { title, focused } = this.props; + const iconColor = focused ? "white" : this.iconColor; + switch (title) { - case "Home": + case "홈": return ( ); - case "Elements": + case "복용 기록 확인": return ( ); - case "Articles": + case "복용 알람": return ( ); - case "Profile": + case "중독 위험도": return ( ); - case "Account": + case "프로필": return ( ); case "Getting Started": @@ -113,7 +119,7 @@ const styles = StyleSheet.create({ paddingHorizontal: 16 }, activeStyle: { - backgroundColor: argonTheme.COLORS.ACTIVE, + backgroundColor: "#67B779", borderRadius: 4 }, shadow: { diff --git a/components/DrugCard.js b/components/DrugCard.js new file mode 100644 index 0000000..65362cb --- /dev/null +++ b/components/DrugCard.js @@ -0,0 +1,162 @@ +import React, { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { + ScrollView, + StyleSheet, + Text, + View, + Image, + Button, +} from "react-native"; +import ModalComponent from "./ModalComponent"; +import { addRecord } from "../store/actions/recordActions"; + +// decodeHtmlEntity 함수를 적용하여 출력 +const renderTextWithNewLines = (text) => { + const formattedText = decodeHtmlEntity(text); + return formattedText.split("\n").map((line, index) => ( + + {line} + + )); +}; + +function decodeHtmlEntity(str) { + const entities = { + "<": "<", + ">": ">", + "&": "&", + """: '"', + "'": "'", + " ": " ", + "©": "©", + "®": "®", + // 필요한 추가 HTML 엔티티를 여기에 추가하세요. + }; + + let decodedString = Object.keys(entities).reduce((acc, entity) => { + const regex = new RegExp(entity, "g"); + return acc.replace(regex, entities[entity]); + }, str); + + // 문자열의 시작과 끝에 있는 따옴표 제거 + decodedString = decodedString.replace(/^"|"$/g, ""); + // 이스케이프된 줄바꿈 문자 처리 + decodedString = decodedString.replace(/\\n/g, "\n"); + + return decodedString; +} + + +// 컴포넌트의 props에서 item을 받아오도록 수정 +const DrugCard = ({ item }) => { + const { itemName, image, efficiency, warn, sideEffect, typeName } = item; // item 객체에서 필요한 데이터 추출 + const accessToken = useSelector((state) => state.auth.accessToken); // Redux 스토어에서 accessToken 가져오기 + + const [modalVisible, setModalVisible] = useState(false); + const [startDate, setStartDate] = useState(new Date()); + const [endDate, setEndDate] = useState(new Date()); + const [isTaking, setIsTaking] = useState(true); + const [timesPerDay, setTimesPerDay] = useState(1); + + const dispatch = useDispatch(); + + const onSubmit = (newRecord) => { + // newRecord 객체 구조를 확인하고 적절히 구성해야 함 + dispatch(addRecord({ ...newRecord, accessToken })); // Redux 액션 디스패치 + setModalVisible(false); + }; + + // 모달을 열고 닫는 함수 + const toggleModal = () => setModalVisible(!modalVisible); + + return ( + + {itemName} + {image && } + {efficiency && ( + <> + 효능 + {renderTextWithNewLines(efficiency)} + + )} + {warn && ( + <> + 주의 + {renderTextWithNewLines(warn)} + + )} + {sideEffect && ( + <> + 부작용 + {renderTextWithNewLines(sideEffect)} + + )} + {typeName && ( + <> + 위험분류 + {renderTextWithNewLines(typeName)} + + )} + - - - ); - } + renderTabs = () => { const { tabs, tabIndex, navigation } = this.props; const defaultTab = tabs && tabs[0] && tabs[0].id; @@ -157,8 +41,6 @@ class Header extends React.Component { if (search || tabs || options) { return ( - {search ? this.renderSearch() : null} - {options ? this.renderOptions() : null} {tabs ? this.renderTabs() : null} ); @@ -185,7 +67,6 @@ class Header extends React.Component { title={title} style={navbarStyles} transparent={transparent} - right={this.renderRight()} rightStyle={{ alignItems: 'center' }} left={ ; } return ; diff --git a/components/ImagePickerComponent.js b/components/ImagePickerComponent.js new file mode 100644 index 0000000..439b285 --- /dev/null +++ b/components/ImagePickerComponent.js @@ -0,0 +1,120 @@ +import React, { useState, useEffect } from "react"; +import { TouchableOpacity, Text, View } from "react-native"; +import * as ImagePicker from "expo-image-picker"; +import Constants from "expo-constants"; +import axios from "axios"; +import { withNavigation } from "@react-navigation/compat"; + +const ImagePickerComponent = ({ navigation }) => { + const [selectedImage, setSelectedImage] = useState(null); + const [selectedImageData, setSelectedImageData] = useState(null); + + useEffect(() => { + (async () => { + if (Constants.platform.ios) { + const { status } = + await ImagePicker.requestMediaLibraryPermissionsAsync(); + if (status !== "granted") { + alert( + "죄송합니다. 이 기능을 사용하려면 카메라 롤 권한이 필요합니다!" + ); + } + } + })(); + }, []); + + // ImagePickerComponent + useEffect(() => { + if (selectedImageData) { + navigation.push("NewStackScreen", { + selectedImageData: selectedImageData, + }); + } + }, [selectedImageData]); + + const pickImageAndSend = async () => { + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [4, 3], + quality: 1, + }); + + if (!result.cancelled) { + setSelectedImage({ uri: result.uri }); + sendImageToBackend(result.uri); + } + }; + + const sendImageToBackend = async (imageUri) => { + const formData = new FormData(); + formData.append("imageFile", { + uri: imageUri, + type: "image/jpeg", + name: "photo.jpg", + }); + + try { + const response = await axios.post( + "http://35.216.104.91:8080/medicine", + formData, + { + headers: { + "Content-Type": "multipart/form-data", + }, + timeout: 1000000000, // 10초 시간 제한 설정 + } + ); + + + // Handle response from backend + console.log(response.data); + setSelectedImageData(response.data); + + // Open a new stack screen after image selection + navigation.push("NewStackScreen", { + selectedImageData: response.data, + }); + } catch (error) { + console.error(error); + } + }; + + return ( + + 갤러리 이미지로 분석하기 + + ); +}; + +const styles = { + buttonContainer: { + flex: 1, + marginHorizontal: 5, // Adjust margin as needed + borderRadius: 15, // Add border radius for rounded corners + overflow: "hidden", // Ensure content stays within rounded borders + elevation: 5, // Add shadow elevation + }, + cameraButton: { + justifyContent: "center", + alignItems: "center", + paddingVertical: 15, // Adjust this value as needed to control the button height + borderRadius: 15, // Make it round + backgroundColor: "purple", + marginVertical: 20, + height: 100, // Fixed height for the button + }, + greenButton: { + backgroundColor: "#67B779" + }, + buttonText: { + fontSize: 25, + color: "white", + fontFamily: "Gugi", + }, +}; + +export default withNavigation(ImagePickerComponent); diff --git a/components/KakaoLogin.js b/components/KakaoLogin.js new file mode 100644 index 0000000..70d84dc --- /dev/null +++ b/components/KakaoLogin.js @@ -0,0 +1,109 @@ +import React, { useState } from "react"; +import { + View, + Button, + Modal, + TouchableOpacity, + Text, + Image, +} from "react-native"; +import { WebView } from "react-native-webview"; +import axios from "axios"; +import { useDispatch } from "react-redux"; +import { setAccessToken } from "../store/reducers/authActions"; + + +const runFirst = `window.ReactNativeWebView.postMessage("this is message from web");`; + +const KakaoLogin = ({ navigation, onLoginSuccess }) => { + const dispatch = useDispatch(); + const [modalVisible, setModalVisible] = useState(false); + + const handleLoginButton = () => { + setModalVisible(true); + }; + + function LogInProgress(data) { + const exp = "code="; + var condition = data.indexOf(exp); + + if (condition !== -1) { + var request_code = data.substring(condition + exp.length); + console.log("Authorization Code:", request_code); + sendAuthCodeToBackend(request_code); + } + } + + + const sendAuthCodeToBackend = async (authorizationCode) => { + var backend_url = "http://35.216.104.91:8080/login/kakao"; + + axios({ + method: "post", + url: backend_url, + data: { + authorizationCode: authorizationCode, + }, + }) + .then(function (response) { + console.log("Backend Response:", response); + // 여기서 액세스 토큰을 Redux 스토어에 저장합니다. + dispatch(setAccessToken(response.data.accessToken)); + // 카카오 로그인이 성공했을 때 콜백 함수 호출 + onLoginSuccess(); + }) + .catch(function (error) { + console.log("Backend Error:", error); + }); + }; + + return ( + + + + + { + setModalVisible(false); + }} + > + { + if ( + event.nativeEvent["url"].startsWith( + "http://35.216.104.91:8080/login/kakao" + ) + ) { + LogInProgress(event.nativeEvent["url"]); + setModalVisible(false); + } + }} + /> + + + ); +}; + +export default KakaoLogin; diff --git a/components/ModalComponent.js b/components/ModalComponent.js new file mode 100644 index 0000000..f1594dd --- /dev/null +++ b/components/ModalComponent.js @@ -0,0 +1,169 @@ +import React from "react"; +import { + View, + Text, + StyleSheet, + Modal, + Button, + TouchableOpacity, +} from "react-native"; +import RadioButton from "react-native-radio-button"; +import { Picker } from "@react-native-picker/picker"; +import DateTimePicker from "@react-native-community/datetimepicker"; +import axios from "axios"; // 상단에 axios import 추가 + + + +const ModalComponent = (props) => { + return ( + + + + 복용 정보 입력 + {props.drugName} + + + 복용시작일: + { + const currentDate = selectedDate || props.startDate; + props.onStartDateChange(currentDate); + }} + /> + + + + 복용마감일: + { + const currentDate = selectedDate || props.endDate; + props.onEndDateChange(currentDate); + }} + /> + + + + 현재 복용중 여부: + + + Yes + + No + + + + + 하루 복용 횟수: + + + + + + + + + + + + @@ -67,7 +57,7 @@ class Onboarding extends React.Component { const styles = StyleSheet.create({ container: { - backgroundColor: theme.COLORS.BLACK + fontFamily: 'KoPubWorldDotum_Pro_Bold', }, padded: { paddingHorizontal: theme.SIZES.BASE * 2, @@ -76,24 +66,26 @@ const styles = StyleSheet.create({ zIndex: 2, }, button: { - width: width - theme.SIZES.BASE * 4, - height: theme.SIZES.BASE * 3, + width: 300, shadowRadius: 0, - shadowOpacity: 0 + shadowOpacity: 0, + backgroundColor: "#67B779", + alignItems: "center", + justifyContent: "center", }, logo: { - width: 200, - height: 60, + width: 194, + height: 210, zIndex: 2, - position: 'relative', - marginTop: '-50%' + position: "relative", + marginTop: "-50%", }, title: { - marginTop:'-5%' + marginTop: "-5%", }, subTitle: { - marginTop: 20 - } + marginTop: 20, + }, }); export default Onboarding; diff --git a/screens/Profile.js b/screens/Profile.js index e70150f..3f47ae1 100644 --- a/screens/Profile.js +++ b/screens/Profile.js @@ -1,24 +1,61 @@ import React from "react"; +import { connect } from "react-redux"; // Redux 스토어에 연결하기 위해 필요 +import axios from "axios"; import { StyleSheet, Dimensions, ScrollView, Image, ImageBackground, - Platform + Platform, } from "react-native"; import { Block, Text, theme } from "galio-framework"; - import { Button } from "../components"; import { Images, argonTheme } from "../constants"; import { HeaderHeight } from "../constants/utils"; -const { width, height } = Dimensions.get("screen"); +import * as Font from "expo-font"; -const thumbMeasure = (width - 48 - 32) / 3; +const { width, height } = Dimensions.get("screen"); class Profile extends React.Component { + state = { + profileImage: "", + nickname: "", + }; + + componentDidMount() { + this.fetchProfileData(); + console.log("accessToken", this.props.accessToken); + } + + fetchProfileData = async () => { + const { accessToken } = this.props; // Redux 스토어에서 액세스 토큰을 props로 받아옵니다. + + if (!accessToken) { + console.log("No accessToken found. User is not logged in."); + return; // Early return if no accessToken is present + } + + try { + const response = await axios.get("http://35.216.104.91:8080/profile", { + headers: { + Authorization: `Bearer ${accessToken}`, // 헤더에 액세스 토큰 추가 + }, + }); + this.setState({ + profileImage: response.data.profileImage, + nickname: response.data.nickname, + }); + console.log("프로필 성공", response.data); + } catch (error) { + console.error("Error fetching data: ", error); + } + }; + render() { + const { profileImage, nickname } = this.state; + return ( @@ -29,251 +66,46 @@ class Profile extends React.Component { > - - - - - - - - - 2K - - Orders - - - - 10 - - Photos - - - - 89 - - Comments - - - + - - Jessica Jones, 27 - - - San Francisco, USA - - - - - - - An artist of considerable range, Jessica name taken by - Melbourne … + {nickname || "로그인이 필요합니다"} - - - - - Album + 나만의 단골 약사로 약물 위험에서 벗어나세요! - - - - {Images.Viewed.map((img, imgIndex) => ( - - ))} - + + + - {/* - - - - - - - - - - - - - - 2K - - Orders - - - - 10 - - Photos - - - - 89 - - Comments - - - - - - - Jessica Jones, 27 - - - San Francisco, USA - - - - - - - - An artist of considerable range, Jessica name taken by - Melbourne … - - - - - - Album - - - - - - - - {Images.Viewed.map((img, imgIndex) => ( - - ))} - - - - - */} ); } @@ -282,61 +114,60 @@ class Profile extends React.Component { const styles = StyleSheet.create({ profile: { marginTop: Platform.OS === "android" ? -HeaderHeight : 0, - // marginBottom: -HeaderHeight * 2, - flex: 1 + flex: 1, }, profileContainer: { width: width, height: height, padding: 0, - zIndex: 1 + zIndex: 1, }, profileBackground: { width: width, - height: height / 2 + height: height, }, profileCard: { - // position: "relative", padding: theme.SIZES.BASE, marginHorizontal: theme.SIZES.BASE, marginTop: 65, - borderTopLeftRadius: 6, - borderTopRightRadius: 6, + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderRadius: 8, backgroundColor: theme.COLORS.WHITE, shadowColor: "black", shadowOffset: { width: 0, height: 0 }, shadowRadius: 8, shadowOpacity: 0.2, - zIndex: 2 + zIndex: 2, }, info: { - paddingHorizontal: 40 + paddingHorizontal: 40, }, avatarContainer: { position: "relative", - marginTop: -80 + marginTop: -80, }, avatar: { width: 124, height: 124, borderRadius: 62, - borderWidth: 0 + borderWidth: 0, }, nameInfo: { - marginTop: 35 + marginTop: 35, + fontFamily: 'KoPubWorldDotum_Pro_Bold', }, divider: { width: "90%", borderWidth: 1, - borderColor: "#E9ECEF" + borderColor: "#E9ECEF", }, - thumb: { - borderRadius: 4, - marginVertical: 4, - alignSelf: "center", - width: thumbMeasure, - height: thumbMeasure - } }); -export default Profile; +// Redux 스토어의 상태를 컴포넌트의 props로 매핑하는 함수 +const mapStateToProps = (state) => ({ + accessToken: state.auth.accessToken, +}); + +// connect 함수를 사용해 Redux 스토어와 Profile 컴포넌트를 연결 +export default connect(mapStateToProps)(Profile); diff --git a/store/actions/recordActions.js b/store/actions/recordActions.js new file mode 100644 index 0000000..21d7cd6 --- /dev/null +++ b/store/actions/recordActions.js @@ -0,0 +1,33 @@ +// actions/recordActions.js +import { FETCH_RECORDS, ADD_RECORD } from "./types"; +import axios from "axios"; + +// 약물 정보 불러오기 액션 +export const fetchRecords = () => async (dispatch, getState) => { + const response = await axios.get("http://35.216.104.91:8080/record", { + headers: { + Authorization: `Bearer ${getState().auth.accessToken}`, + }, + }); + dispatch({ + type: FETCH_RECORDS, + payload: response.data, + }); +}; + +// 약물 정보 추가 액션 +export const addRecord = (record) => async (dispatch, getState) => { + const response = await axios.post( + "http://35.216.104.91:8080/medicine/save", + record, + { + headers: { + Authorization: `Bearer ${getState().auth.accessToken}`, + }, + } + ); + dispatch({ + type: ADD_RECORD, + payload: response.data, + }); +}; diff --git a/store/actions/types.js b/store/actions/types.js new file mode 100644 index 0000000..53ecea7 --- /dev/null +++ b/store/actions/types.js @@ -0,0 +1,3 @@ +// actions/types.js +export const FETCH_RECORDS = "FETCH_RECORDS"; +export const ADD_RECORD = "ADD_RECORD"; diff --git a/store/reducers/authActions.js b/store/reducers/authActions.js new file mode 100644 index 0000000..04974a3 --- /dev/null +++ b/store/reducers/authActions.js @@ -0,0 +1,13 @@ +// src/store/actions/authActions.js +export const setAccessToken = (token) => { + return { + type: "SET_ACCESS_TOKEN", + payload: token, + }; +}; + +export const removeAccessToken = () => { + return { + type: "REMOVE_ACCESS_TOKEN", + }; +}; diff --git a/store/reducers/authReducer.js b/store/reducers/authReducer.js new file mode 100644 index 0000000..901cfa7 --- /dev/null +++ b/store/reducers/authReducer.js @@ -0,0 +1,23 @@ +// src/store/reducers/authReducer.js +const initialState = { + accessToken: null, +}; + +const authReducer = (state = initialState, action) => { + switch (action.type) { + case "SET_ACCESS_TOKEN": + return { + ...state, + accessToken: action.payload, + }; + case "REMOVE_ACCESS_TOKEN": + return { + ...state, + accessToken: null, + }; + default: + return state; + } +}; + +export default authReducer; diff --git a/store/reducers/recordReducer.js b/store/reducers/recordReducer.js new file mode 100644 index 0000000..6d313dd --- /dev/null +++ b/store/reducers/recordReducer.js @@ -0,0 +1,23 @@ +// reducers/recordReducer.js +import { FETCH_RECORDS, ADD_RECORD } from "../actions/types"; + +const initialState = { + records: [], +}; + +export default function (state = initialState, action) { + switch (action.type) { + case FETCH_RECORDS: + return { + ...state, + records: action.payload, + }; + case ADD_RECORD: + return { + ...state, + records: [...state.records, action.payload], + }; + default: + return state; + } +} diff --git a/store/store.js b/store/store.js new file mode 100644 index 0000000..f18a2f3 --- /dev/null +++ b/store/store.js @@ -0,0 +1,16 @@ +import { applyMiddleware, combineReducers } from "redux"; +import { configureStore } from '@reduxjs/toolkit'; +import { composeWithDevTools } from "redux-devtools-extension"; +import thunk from "redux-thunk"; +import authReducer from "./reducers/authReducer"; +import recordReducer from "./reducers/recordReducer"; + +const store = configureStore({ + reducer: { + auth: authReducer, + records: recordReducer, + }, + // 필요한 경우 여기에 다른 설정을 추가할 수 있습니다. +}); + +export default store;