Skip to content

Commit

Permalink
Feat/#41 프로젝트 상세페이지를 추가합니다. (#69)
Browse files Browse the repository at this point in the history
* fix: mock 데이터를 변경합니다.

* feat: 프로젝트 상세페이지를 추가합니다.

* refactor: 스크롤이 있을 경우를 대응하기 위해 grid를 사용한다.
  • Loading branch information
Zero-1016 authored Sep 27, 2024
1 parent 425d069 commit e9020dc
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 109 deletions.
5 changes: 5 additions & 0 deletions app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SITE_URLS } from '@/constants';
import { useSession } from '@/store';
import { useOnboarding } from '@/store/useOnboarding';
import useTabBar from '@/store/useTabBar';
import { color } from '@/styles/theme';

const tabBarOptions = {
[MAIN_NAVIGATIONS.HOME]: {
Expand Down Expand Up @@ -135,7 +136,11 @@ export default function Layout() {
return (
<Tabs
screenOptions={{
headerShadowVisible: false,
headerShown: false,
headerStyle: {
backgroundColor: color.Background.Alternative,
},
}}
tabBar={(tabBar) => <TabBar {...tabBar} />}>
<Tabs.Screen name={MAIN_NAVIGATIONS.PROJECT} />
Expand Down
17 changes: 7 additions & 10 deletions app/(app)/project/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Platform, Pressable } from 'react-native';
import { PROJECT_NAVIGATIONS } from '@/constants';
import { color } from '@/styles/theme';

import * as S from './layout.style';
import * as S from './style';

function Layout() {
const router = useRouter();
Expand All @@ -24,7 +24,6 @@ function Layout() {
name={PROJECT_NAVIGATIONS.HOME}
options={{
title: '프로젝트',
headerLeft: () => null,
headerRight: () => (
<S.ButtonGroup>
<Pressable>
Expand All @@ -51,14 +50,12 @@ function Layout() {
animation: 'flip',
title: '프로젝트 등록',
headerLeft: ({ canGoBack }) => (
<S.ButtonGroup>
<Pressable onPress={() => (canGoBack ? router.back() : router.push('/project'))}>
<Feather
name='chevron-left'
size={24}
/>
</Pressable>
</S.ButtonGroup>
<Pressable onPress={() => (canGoBack ? router.back() : router.push('/project'))}>
<Feather
name='chevron-left'
size={24}
/>
</Pressable>
),
}}
/>
Expand Down
26 changes: 20 additions & 6 deletions app/(app)/project/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ function Create() {
const [image, setImage] = useState<string | null>(null);
const [startDate, setStartDate] = useState<Date>(() => new Date());
const [endDate, setEndDate] = useState<Date>(() => new Date());
const [startDateTouched, setStartDateTouched] = useState(false);
const [endDateTouched, setEndDateTouched] = useState(false);
const [selectUserList, setSelectUserList] = useState<User[]>([]);

const [selectDate, setSelectDate] = useState<'start' | 'end'>('start');
Expand Down Expand Up @@ -160,21 +162,33 @@ function Create() {
</S.ImageBox>
</S.InputContainer>
<S.InputContainer>
<Typography
variant='Body1/Normal'
fontWeight='semiBold'
color={color.Label.Normal}>
기간
</Typography>
<S.RequiredTitleBox>
<Typography
variant='Body1/Normal'
fontWeight='semiBold'
color={color.Label.Normal}>
기간
</Typography>
<Typography
variant='Body1/Normal'
fontWeight='semiBold'
color={color.Status.Error}>
*
</Typography>
</S.RequiredTitleBox>
<S.DatePickerBox>
<DateInput
date={startDate}
touched={startDateTouched}
onPress={() => startDateOpen('start')}
onTouchEnd={() => setStartDateTouched(true)}
/>
<S.DateSplitText>-</S.DateSplitText>
<DateInput
date={endDate}
touched={endDateTouched}
onPress={() => startDateOpen('end')}
onTouchEnd={() => setEndDateTouched(true)}
/>
</S.DatePickerBox>
</S.InputContainer>
Expand Down
55 changes: 55 additions & 0 deletions app/(app)/project/detail/[id]/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Feather } from '@expo/vector-icons';
import { useNavigation, useRouter } from 'expo-router';
import { useLayoutEffect } from 'react';
import { Platform, Pressable } from 'react-native';

import { MOCK_PROJECT_DETAIL } from '@/__mock__/project';
import Typography from '@/components/common/typography';
import ProjectDetail from '@/components/project/ProjectDetail';
import { color } from '@/styles/theme';

function Page() {
const router = useRouter();
// const { id } = useLocalSearchParams(); 실제 데이터로 올 경우 해당 id를 이용하여 조회
const navigation = useNavigation();
const data = MOCK_PROJECT_DETAIL;

useLayoutEffect(() => {
navigation.setOptions({
headerStyle: {
paddingTop: 12,
},
headerTitle: data.name,
headerTintColor: color.Label.Normal,
headerTitleStyle: {
fontWeight: 600,
fontFamily: 'Pretendard-SemiBold',
fontSize: 18,
lineHeight: 26,
},
headerLeft: () => (
<Pressable onPress={router.back}>
<Feather
name='chevron-left'
size={24}
/>
</Pressable>
),
headerRight: () =>
Platform.OS !== 'web' ? (
<Pressable onPress={() => router.push('/project/create')}>
<Typography
variant='Body1/Normal'
fontWeight='medium'
color={color.Label.Alternative}>
편집
</Typography>
</Pressable>
) : null,
});
}, [navigation]);

return <ProjectDetail data={data} />;
}

export default Page;
5 changes: 5 additions & 0 deletions app/(app)/project/detail/[id]/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import styled from '@emotion/native';

export const Container = styled.SafeAreaView`
padding: 32px 20px 52px;
`;
93 changes: 8 additions & 85 deletions app/(app)/project/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { useLayoutEffect, useState } from 'react';
import { SafeAreaView } from 'react-native';

import { MOCK_PROJECT_ITEM, MOCK_PROJECT_LIST } from '@/__mock__/project';
import ProjectInviteModal from '@/components/project/ProjectInviteModal';
import ProjectList from '@/components/project/ProjectList';
import { color } from '@/styles/theme';

const inviteData = MOCK_PROJECT_ITEM;
const data = MOCK_PROJECT_LIST;

function Project() {
const [visible, setVisible] = useState(false);

const data = {
project_name: '위프로',
project_profile: 'https://picsum.photos/200',
member_length: 6,
};

const onRequestClose = () => {
setVisible(false);
};
Expand All @@ -22,91 +20,16 @@ function Project() {
setVisible(true);
}, []);

const mockList = [
{
id: 1,
name: '위프로',
profile: 'https://picsum.photos/200',
member_num: 6,
},
{
id: 2,
name: '후피',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 3,
name: 'Code Red',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 4,
name: 'Veco',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 5,
name: '위프로',
profile: 'https://picsum.photos/200',
member_num: 6,
},
{
id: 6,
name: '후피',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 7,
name: 'Code Red',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 8,
name: 'Veco',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 9,
name: '위프로',
profile: 'https://picsum.photos/200',
member_num: 6,
},
{
id: 10,
name: '후피',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 11,
name: 'Code Red',
profile: 'https://picsum.photos/200',
member_num: 3,
},
{
id: 12,
name: 'Last Project',
profile: 'https://picsum.photos/200',
member_num: 3,
},
];

return (
<SafeAreaView style={{ flex: 1, backgroundColor: color.Background.Alternative }}>
<ProjectInviteModal
project_name={data.project_name}
project_profile={data.project_profile}
member_length={data.member_length}
project_name={inviteData.name}
project_profile={inviteData.profile}
member_length={inviteData.member_num}
visible={visible}
onRequestClose={onRequestClose}
/>
<ProjectList data={mockList} />
<ProjectList data={data} />
</SafeAreaView>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ import { flexDirectionRow } from '@/styles/common';
export const ButtonGroup = styled.View`
${flexDirectionRow};
gap: 10px;
padding: 20px 20px 8px;
`;
38 changes: 38 additions & 0 deletions src/__mock__/project/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ProjectDetailType } from '@/components/project/ProjectDetail';
import type { ProjectItemType } from '@/components/project/ProjectList';

export const MOCK_PROJECT_ITEM: ProjectItemType = {
Expand Down Expand Up @@ -45,3 +46,40 @@ export const MOCK_PROJECT_LIST: ProjectItemType[] = [
member_num: 3,
},
] as const;

export const MOCK_PROJECT_DETAIL: ProjectDetailType = {
id: 1,
name: '위프로',
description: '팀원이 만들어주는 명함 서비스',
profile: 'https://picsum.photos/200',
startDate: '24.07.01',
endDate: '24.09.30',
review_count: 4,
userList: [
{
id: 1,
name: '이지형',
},
{
id: 2,
name: '이예지',
},
{
id: 3,
name: '양의진',
},
{
id: 4,
name: '조민제',
},
{
id: 5,
name: '김소현',
},
{
id: 6,
name: '김희진',
},
],
link: 'https://www.naver.com',
} as const;
14 changes: 10 additions & 4 deletions src/components/common/date-input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@ import dayjs from 'dayjs';

import Typography from '@/components/common/typography';
import { shadow } from '@/styles/shadow';
import { color } from '@/styles/theme';

import * as S from './style';

type Props = {
date: Date;
touched: boolean;
onPress: () => void;
onTouchEnd: () => void;
};

function DateInput({ onPress, date }: Props) {
function DateInput({ touched, onPress, onTouchEnd, date }: Props) {
return (
<S.Container
style={shadow[2]}
onPress={onPress}>
onPress={onPress}
onTouchEnd={onTouchEnd}>
<S.IconBox>
<AntDesign
name='calendar'
size={20}
color='#979797'
color={touched ? color.Label.Normal : '#979797'}
/>
</S.IconBox>
<Typography>{dayjs(date).format('YYYY-MM-DD')}</Typography>
<Typography color={touched ? color.Label.Normal : '#979797'}>
{dayjs(date).format('YYYY-MM-DD')}
</Typography>
</S.Container>
);
}
Expand Down
Loading

0 comments on commit e9020dc

Please sign in to comment.