diff --git a/app.config.js b/app.config.js
index 37b8242..374b754 100644
--- a/app.config.js
+++ b/app.config.js
@@ -25,7 +25,7 @@ export default {
favicon: './assets/images/logoIcon.png',
},
extra: {
- BASE_URL: 'http://192.168.0.115:8081', // 본인 pc IPv4 주소로 수정하세용
+ BASE_URL: 'http://192.168.0.181:8081', // 본인 pc IPv4 주소로 수정하세용
},
},
};
diff --git a/components/cards/Dot.js b/components/cards/Dot.js
new file mode 100644
index 0000000..ede8791
--- /dev/null
+++ b/components/cards/Dot.js
@@ -0,0 +1,36 @@
+import React, { useEffect, useRef } from 'react';
+import { Animated, StyleSheet } from 'react-native';
+import { styles } from './styles/Dot.styles';
+import { colors } from '../../constants/colors';
+
+// dot의 기본 너비와 활성화(선택) 상태일 때의 너비
+const DOT_WIDTH = 10;
+const DOT_ACTIVE_WIDTH = 16;
+
+const Dot = ({ active }) => {
+ // dot width를 위한 Animated.Value 배열
+ const animatedWidth = useRef(new Animated.Value(active ? DOT_ACTIVE_WIDTH : DOT_WIDTH)).current;
+
+ // pageIndex가 바뀔 때 dot width 애니메이션
+ useEffect(() => {
+ Animated.timing(animatedWidth, {
+ toValue: active ? DOT_ACTIVE_WIDTH : DOT_WIDTH,
+ duration: 300,
+ useNativeDriver: false,
+ }).start();
+ }, [active, animatedWidth]);
+
+ return (
+
+ );
+};
+
+export default Dot;
diff --git a/components/cards/QrCard.js b/components/cards/QrCard.js
new file mode 100644
index 0000000..3ee8a62
--- /dev/null
+++ b/components/cards/QrCard.js
@@ -0,0 +1,47 @@
+import React, { useState } from 'react';
+import { View, Text, Image } from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import QRCode from 'react-native-qrcode-svg';
+
+import NormalButton from '../../components/buttons/NormalButton';
+import { styles } from './styles/QrCard.styles';
+import { colors } from '../../constants/colors';
+import { hospitalName } from '../../mocks/hospitalData';
+
+// hasAccessAuthority: 출입 권한 여부, userVC : VC에 담을 사용자 정보, qrData : QR에 담을 JSON 문자열
+const QrCard = ({ hasAccessAuthority, did, userName, hospitalName }) => {
+ // 해당 QR의 상세 페이지로 이동 (아직 미구현)
+ //const navigation = useNavigation();
+ // const navigateToAccessListDeatail = () => {
+ // navigation.navigate('AccessListDetailPage');
+ // };
+
+ // 임시: QR에 담을 JSON 문자열
+ const qrData = JSON.stringify({ did, userName, hospitalName });
+ return (
+
+
+
+ {hasAccessAuthority ? (
+ <>
+ 임시 출입 QR
+
+ {userName}
+ {hospitalName}
+ >
+ ) : (
+ <>
+ 등록된 출입 권한이 존재하지 않습니다.
+ 방문 신청 버튼을 눌러 출입 권한을 신청해주세요.
+ >
+ )}
+
+
+ );
+};
+
+export default QrCard;
diff --git a/components/cards/QrCards.js b/components/cards/QrCards.js
new file mode 100644
index 0000000..5dfc9b1
--- /dev/null
+++ b/components/cards/QrCards.js
@@ -0,0 +1,83 @@
+import React, { useRef, useState } from 'react';
+import { FlatList, View, Dimensions, TouchableOpacity } from 'react-native';
+import QrCard from './QrCard';
+import styles from './styles/QrCards.styles';
+import Dot from './Dot';
+
+// 화면의 가로 길이 가져오기
+const { width } = Dimensions.get('window');
+const CARD_WIDTH = width;
+const SIDE_PADDING = (width - CARD_WIDTH) / 2; // 좌우 패딩 계산
+const OVERLAP = 80; // 카드가 겹치는 정도
+
+const QrCards = ({ userVC, hasAccessAuthority }) => {
+ const [pageIndex, setPageIndex] = useState(0);
+ const flatListRef = useRef(null);
+
+ // 권한 없거나 카드 데이터 없으면 안내 메시지 카드만
+ if (!hasAccessAuthority || !userVC || userVC.length === 0) {
+ return (
+
+
+
+ );
+ }
+
+ // 스크롤이 끝났을 때 현재 페이지 인덱스 계산
+ const onMomentumScrollEnd = (event) => {
+ const offsetX = event.nativeEvent.contentOffset.x;
+ const newIndex = Math.round(offsetX / (CARD_WIDTH - OVERLAP));
+ setPageIndex(newIndex);
+ };
+
+ // dot(인디케이터) 클릭 시 해당 카드로 이동
+ const handleDotPress = (index) => {
+ flatListRef.current?.scrollToIndex({ index, animated: true });
+ };
+
+ // 카드 리스트
+ return (
+
+ item.did}
+ horizontal //가로 스크롤
+ showsHorizontalScrollIndicator={false} //하단 기본 스크롤바 숨김
+ pagingEnabled={false} // snapToInterval을 사용하므로 false
+ snapToInterval={CARD_WIDTH - OVERLAP} // 카드 단위로 스냅
+ decelerationRate="fast" // 빠른 스냅 효과
+ contentContainerStyle={{
+ paddingHorizontal: SIDE_PADDING, // 양쪽에 패딩 추가로 옆 카드 살짝 보이게
+ }}
+ renderItem={({ item, index }) => (
+
+
+
+ )}
+ onMomentumScrollEnd={onMomentumScrollEnd}
+ />
+
+ {userVC.map((_, i) => (
+ handleDotPress(i)}>
+
+
+ ))}
+
+
+ );
+};
+
+export default QrCards;
diff --git a/components/cards/styles/Dot.styles.js b/components/cards/styles/Dot.styles.js
new file mode 100644
index 0000000..5d09691
--- /dev/null
+++ b/components/cards/styles/Dot.styles.js
@@ -0,0 +1,10 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../constants/colors';
+
+export const styles = StyleSheet.create({
+ dot: {
+ height: 10,
+ borderRadius: 16,
+ marginHorizontal: 4,
+ },
+});
diff --git a/components/cards/styles/QrCard.styles.js b/components/cards/styles/QrCard.styles.js
new file mode 100644
index 0000000..7f13ae1
--- /dev/null
+++ b/components/cards/styles/QrCard.styles.js
@@ -0,0 +1,65 @@
+import { StyleSheet } from 'react-native';
+import { colors } from '../../../constants/colors';
+
+export const styles = StyleSheet.create({
+ shadowWrapper: {
+ // 그림자 설정
+ shadowColor: 'black',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.2,
+ shadowRadius: 5,
+ elevation: 7, // Android용
+ borderRadius: 15,
+ width: '70%',
+ height: '80%',
+ marginVertical: '10%',
+ },
+ cardContainer: {
+ flex: 1, // 부모(shadowWrapper)의 크기를 꽉 채워 카드와 그림자 모양을 일치시킴
+ backgroundColor: colors.white,
+ borderRadius: 15,
+ padding: '10%',
+ alignItems: 'center',
+ justifyContent: 'center',
+ overflow: 'hidden', // 배경 이미지가 카드 안에 잘리게
+ position: 'relative', // 자식의 absolute 포지션 기준점
+ },
+ backgroundImage: {
+ ...StyleSheet.absoluteFillObject, // 전체 카드 덮게 만듦
+ width: '125%',
+ zIndex: 0, // 맨 뒤로
+ opacity: 0.5,
+ },
+ cardText: {
+ fontSize: 20,
+ fontWeight: 600,
+ color: colors.black,
+ textAlign: 'center',
+ marginBottom: '5%',
+ },
+ cardSubText: {
+ fontSize: 16,
+ fontWeight: 500,
+ color: colors.darkGray,
+ textAlign: 'center',
+ },
+ qrTitle: {
+ fontSize: 26,
+ fontWeight: 700,
+ color: colors.black,
+ marginBottom: '10%',
+ zIndex: 1, // QR과 텍스트를 배경 위에
+ },
+ userName: {
+ fontSize: 24,
+ fontWeight: 600,
+ color: colors.black,
+ marginVertical: '8%',
+ },
+ hospital: {
+ fontSize: 16,
+ fontWeight: 500,
+ color: colors.darkGray,
+ marginTop: '2%',
+ },
+});
diff --git a/components/cards/styles/QrCards.styles.js b/components/cards/styles/QrCards.styles.js
new file mode 100644
index 0000000..0502f92
--- /dev/null
+++ b/components/cards/styles/QrCards.styles.js
@@ -0,0 +1,11 @@
+import { StyleSheet } from 'react-native';
+
+const styles = StyleSheet.create({
+ dotContainer: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: '5%',
+ },
+});
+
+export default styles;
diff --git a/pages/MainPage.js b/pages/MainPage.js
index bcc2fbb..967dbe3 100644
--- a/pages/MainPage.js
+++ b/pages/MainPage.js
@@ -5,22 +5,35 @@ import NormalButton from '../components/buttons/NormalButton';
import { styles } from './styles/MainPage.styles';
import QRCode from 'react-native-qrcode-svg';
import { colors } from '../constants/colors';
+import QrCards from '../components/cards/QrCards';
const MainPage = () => {
// 임시: 상태변수로 출입 권한 제어
const [hasAccessAuthority, setHasAccessAuthority] = useState(true);
// 임시: VC에 담을 사용자 정보
- const userVC = {
- did: 'did:example:123456789abcdefghi',
- userName: '김짱구',
- hospital1: '짱구병원',
- hospital2: '흰둥이병원',
- hospital3: '오수병원',
- issuedAt: Date.now(),
- };
+ const userVC = [
+ {
+ did: 'did:example:123456789abcdefghi',
+ userName: '김짱구',
+ hospitalName: '짱구병원',
+ issuedAt: Date.now(),
+ },
+ {
+ did: 'did:example:123456789abcdefdhi',
+ userName: '김짱구',
+ hospitalName: '흰둥이병원',
+ issuedAt: Date.now(),
+ },
+ {
+ did: 'did:example:123456789abcdeffhi',
+ userName: '김짱구',
+ hospitalName: '오수병원',
+ issuedAt: Date.now(),
+ },
+ ];
// 임시: QR에 담을 JSON 문자열
- const qrData = JSON.stringify(userVC);
+ //const qrData = JSON.stringify(userVC);
const navigation = useNavigation();
@@ -39,37 +52,7 @@ const MainPage = () => {
source={require('../assets/images/logoGreen.png')}
resizeMode="contain" // 이미지 비율 유지
/>
-
-
-
- {hasAccessAuthority ? (
- <>
- 임시 출입 QR
-
- {userVC.userName}
- {userVC.hospital1}
- {userVC.hospital2}
- {userVC.hospital3}
- >
- ) : (
- <>
- 등록된 출입 권한이 존재하지 않습니다.
-
- 방문 신청 버튼을 눌러 출입 권한을 신청해주세요.
-
- >
- )}
-
-
+
diff --git a/pages/styles/MainPage.styles.js b/pages/styles/MainPage.styles.js
index 9321614..091dddd 100644
--- a/pages/styles/MainPage.styles.js
+++ b/pages/styles/MainPage.styles.js
@@ -4,7 +4,6 @@ import { colors } from '../../constants/colors';
export const styles = StyleSheet.create({
container: {
flex: 1,
- paddingHorizontal: '10%',
paddingTop: '5%',
},
logoImage: {
@@ -13,70 +12,10 @@ export const styles = StyleSheet.create({
marginTop: '15%',
alignSelf: 'center',
},
- shadowWrapper: {
- // 그림자 설정
- shadowColor: 'black',
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.2,
- shadowRadius: 5,
- elevation: 7, // Android용
- borderRadius: 15,
- width: '100%',
- height: '55%',
- marginVertical: '10%',
- },
- cardContainer: {
- flex: 1, // 부모(shadowWrapper)의 크기를 꽉 채워 카드와 그림자 모양을 일치시킴
- backgroundColor: colors.white,
- borderRadius: 15,
- padding: '10%',
- alignItems: 'center',
- justifyContent: 'center',
- overflow: 'hidden', // 배경 이미지가 카드 안에 잘리게
- position: 'relative', // 자식의 absolute 포지션 기준점
- },
- backgroundImage: {
- ...StyleSheet.absoluteFillObject, // 전체 카드 덮게 만듦
- width: '125%',
- zIndex: 0, // 맨 뒤로
- opacity: 0.5,
- },
- cardText: {
- fontSize: 20,
- fontWeight: 600,
- color: colors.black,
- textAlign: 'center',
- marginBottom: '5%',
- },
- cardSubText: {
- fontSize: 16,
- fontWeight: 500,
- color: colors.darkGray,
- textAlign: 'center',
- },
- qrTitle: {
- fontSize: 26,
- fontWeight: 700,
- color: colors.black,
- marginBottom: '10%',
- zIndex: 1, // QR과 텍스트를 배경 위에
- },
- userName: {
- fontSize: 24,
- fontWeight: 600,
- color: colors.black,
- marginVertical: '8%',
- },
- hospital: {
- fontSize: 16,
- fontWeight: 500,
- color: colors.darkGray,
- marginTop: '2%',
- },
buttonContainer: {
flexDirection: 'row',
justifyContent: 'center',
marginTop: '3%',
- gap: '10%',
+ gap: '5%',
},
});