From 9a75843d5db8203e72c4bb6f4e8db14552781a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=98=88=EC=A7=84?= Date: Wed, 18 Sep 2024 17:54:20 +0900 Subject: [PATCH] feat: add SharedProfilePage --- src/app/routes/share.$key.tsx | 40 ++++++++++++++++ src/pages/profile/ProfilePage.tsx | 7 +-- .../SharedProfilePage.module.css | 40 ++++++++++++++++ .../SharedProfilePage.stories.tsx | 16 +++++++ .../shared_profile/SharedProfilePage.tsx | 46 +++++++++++++++++++ .../ui/ImageLayout/ImageLayout.module.css | 43 +++++++++++++++++ src/shared/ui/ImageLayout/ImageLayout.tsx | 11 +++++ 7 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 src/app/routes/share.$key.tsx create mode 100644 src/pages/shared_profile/SharedProfilePage.module.css create mode 100644 src/pages/shared_profile/SharedProfilePage.stories.tsx create mode 100644 src/pages/shared_profile/SharedProfilePage.tsx create mode 100644 src/shared/ui/ImageLayout/ImageLayout.module.css create mode 100644 src/shared/ui/ImageLayout/ImageLayout.tsx diff --git a/src/app/routes/share.$key.tsx b/src/app/routes/share.$key.tsx new file mode 100644 index 0000000..2fe1b98 --- /dev/null +++ b/src/app/routes/share.$key.tsx @@ -0,0 +1,40 @@ +import { LoaderFunction } from '@remix-run/node'; +import { authenticate } from '../server/authenticate'; +import { getInfoBySharingId } from '../../types'; +import { useLoaderData } from '@remix-run/react'; +import { useMemo } from 'react'; +import { convertDtoToProfile } from '../../entities/profile/model/convertProfileToDto'; +import { MyProfileProvider } from '../../entities/profile/model/myProfileStore'; +import { ProfilePage } from '../../pages/profile/ProfilePage'; + +export const loader: LoaderFunction = async ({ request, params }) => { + const accessToken = await authenticate(request); + + const { key } = params; + + if (!key) { + throw new Response('', { + status: 404, + statusText: 'Not Found', + }); + } + + const { data } = await getInfoBySharingId(key, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + return { profile: data }; +}; + +export default function Page() { + const { profile } = useLoaderData(); + const profileInitialState = useMemo(() => convertDtoToProfile(profile.userInfo), [profile.userInfo]); + + return ( + + + + ); +} diff --git a/src/pages/profile/ProfilePage.tsx b/src/pages/profile/ProfilePage.tsx index a7e5291..6eaed42 100644 --- a/src/pages/profile/ProfilePage.tsx +++ b/src/pages/profile/ProfilePage.tsx @@ -11,6 +11,7 @@ import { useIdealPartnerStore } from 'src/entities/ideal_partner/model/idealPart import { Link } from '@remix-run/react'; import { Header } from 'src/shared/ui/layout/Header/Header'; import { Theme } from 'src/shared/styles/constants'; +import { ImageLayout } from '../../shared/ui/ImageLayout/ImageLayout'; export const ProfilePage = () => { const { ref, inView } = useInView(); @@ -57,11 +58,7 @@ export const ProfilePage = () => { )} -
- {urls.map((url) => ( - {'프로필 - ))} -
+

{profile.name}({profile.gender}, {age})

diff --git a/src/pages/shared_profile/SharedProfilePage.module.css b/src/pages/shared_profile/SharedProfilePage.module.css new file mode 100644 index 0000000..44f34eb --- /dev/null +++ b/src/pages/shared_profile/SharedProfilePage.module.css @@ -0,0 +1,40 @@ +.Wrapper { + display: flex; + flex-direction: column; + overflow: hidden; + height: 100%; +} + +.Header { + flex-shrink: 0; + width: 100%; + display: grid; + grid-template-columns: 1fr 3fr 1fr; + align-items: center; + text-align: center; + height: 44px; + padding: 0 20px; +} + +.HeaderIconSection { + display: flex; + gap: 4px; + justify-content: end; +} + +.Body { + flex-grow: 1; + overflow: hidden; + position: relative; +} + +.Name { + font-size: 24px; + font-weight: 600; + margin: 24px 0; + padding: 0 20px; +} + +.ContentWrapper { + padding: 20px 20px 0; +} \ No newline at end of file diff --git a/src/pages/shared_profile/SharedProfilePage.stories.tsx b/src/pages/shared_profile/SharedProfilePage.stories.tsx new file mode 100644 index 0000000..8f916d6 --- /dev/null +++ b/src/pages/shared_profile/SharedProfilePage.stories.tsx @@ -0,0 +1,16 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { SharedProfilePage } from './SharedProfilePage'; +import { MyProfileProvider } from 'src/entities/profile/model/myProfileStore'; +import { fullProfileMock } from '../../entities/profile/api/__mock__/fullProfile.mock'; + +const meta: Meta = { + component: SharedProfilePage, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, + decorators: [(fn) => {fn()}], +}; diff --git a/src/pages/shared_profile/SharedProfilePage.tsx b/src/pages/shared_profile/SharedProfilePage.tsx new file mode 100644 index 0000000..64a677d --- /dev/null +++ b/src/pages/shared_profile/SharedProfilePage.tsx @@ -0,0 +1,46 @@ +import { calculateAge, convertDateObjectToDate } from 'src/shared/vo/date'; +import styles from './SharedProfilePage.module.css'; +import { ScrollView } from 'src/shared/ui/ScrollView/ScrollView'; +import { useInView } from 'react-intersection-observer'; +import { useMyProfileStore } from 'src/entities/profile/model/myProfileStore'; +import { MyProfileView } from '../../entities/profile/ui/MyProfile/MyProfile'; +import { Header } from '../../shared/ui/layout/Header/Header'; +import { useTranslation } from 'react-i18next'; +import { ImageLayout } from '../../shared/ui/ImageLayout/ImageLayout'; + +export const SharedProfilePage = () => { + const { t } = useTranslation(); + const { ref, inView } = useInView(); + + const profile = useMyProfileStore((state) => state); + const age = calculateAge(convertDateObjectToDate(profile.birthDate)); + + const urls = [ + '/images/googoo_1.png', + '/images/googoo_2.gif', + '/images/googoo_3.png', + '/images/googoo_4.png', + '/images/logo.png', + ]; // useDataUrlListFromFiles(profile.images); + + return ( +
+ {inView ? ( + + ) : ( +
} suffixSlot={<>}> + {profile.name}({t(profile.gender)}, {age}) +
+ )} + + +

+ {profile.name}({profile.gender}, {age}) +

+
+ +
+
+
+ ); +}; diff --git a/src/shared/ui/ImageLayout/ImageLayout.module.css b/src/shared/ui/ImageLayout/ImageLayout.module.css new file mode 100644 index 0000000..606d64e --- /dev/null +++ b/src/shared/ui/ImageLayout/ImageLayout.module.css @@ -0,0 +1,43 @@ + +.ImageLayout { + width: 100%; + background-color: var(--color-neutral-10); + + display: grid; + overflow: hidden; + + & img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + } + + &[data-itemcount='5'], &[data-itemcount='4'] { + grid: 1fr 1fr / 2fr 1fr 1fr; + + & img:nth-child(1) { + grid-row: 1 / span 2; + } + } + + &[data-itemcount='3'] { + grid: 1fr 1fr / 2fr 1fr; + + & img:nth-child(1) { + grid-row: 1 / span 2; + } + } + + &[data-itemcount='2'] { + grid-template-columns: 1fr 1fr; + } + + &[data-itemcount='1'] { + height: 50vw; + + & img { + height: 100%; + } + } +} diff --git a/src/shared/ui/ImageLayout/ImageLayout.tsx b/src/shared/ui/ImageLayout/ImageLayout.tsx new file mode 100644 index 0000000..af3464c --- /dev/null +++ b/src/shared/ui/ImageLayout/ImageLayout.tsx @@ -0,0 +1,11 @@ +import styles from './ImageLayout.module.css'; + +export const ImageLayout = ({ urls }: { urls: string[] }) => { + return ( +
+ {urls.map((url) => ( + {'프로필 + ))} +
+ ); +};