Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions src/apis/follow.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import getQueryKey from '@/apis/getQueryKey';
import apiInstance from '@/apis/instance.api';
import { type FollowMemberType } from '@/apis/schema/member';
import { type MissionItemTypeWithRecordId } from '@/apis/schema/mission';
import { useQuery, type UseQueryOptions } from '@tanstack/react-query';

interface FollowMember {
memberId: number;
nickname: string;
profileImageUrl: string;
}

type GetFollowMembersResponse = FollowMember[];
type GetFollowMembersResponse = FollowMemberType[];

interface GetFollowMissionsResponse {
symbolStack: number;
Expand Down
6 changes: 5 additions & 1 deletion src/apis/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ type GetRecordsParams = {
yearMonth: string;
};

type GetRecordsResponse = RecordType[];
interface GetRecordsResponse {
missionFinishedAt: string;
missionStartedAt: string;
missionRecords: RecordType[];
}

type GetRecordDetailResponse = RecordType & {
sinceDay: number;
Expand Down
6 changes: 5 additions & 1 deletion src/apis/schema/member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export interface MemberType {
username: string;
}

export interface FollowMemberType {
memberId: number;
nickname: string;
profileImageUrl: string;
}

export type FollowStatusType = 'FOLLOWING' | 'FOLLOWED_BY_ME' | 'NOT_FOLLOWING';

Expand All @@ -25,4 +30,3 @@ export enum FileExtension {
JPG = 'JPG',
PNG = 'PNG',
}

21 changes: 18 additions & 3 deletions src/app/guest/mission/stopwatch/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ export default function GuestMissionStopwatchPage() {
</section>
<section className={buttonContainerCss}>
{step === 'ready' && (
<Button variant="cta" size="large" type="button" onClick={onStart}>
시작
</Button>
<div className={fixedButtonContainerCss}>
<Button variant="primary" size="large" type="button" onClick={onStart}>
시작
</Button>
</div>
)}
{step === 'progress' && (
<>
Expand Down Expand Up @@ -167,3 +169,16 @@ const buttonContainerCss = css({
justifyContent: 'center',
gap: '12px',
});

const fixedButtonContainerCss = css({
position: 'fixed',
left: '16px',
right: '16px',
bottom: '16px',
width: '100%',
maxWidth: 'calc(475px - 48px)',
margin: '0 auto',
'@media (max-width: 475px)': {
maxWidth: 'calc(100vw - 48px)',
},
});
20 changes: 15 additions & 5 deletions src/app/home/FollowContent.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
import { useSearchParams } from 'next/navigation';
import { useFollowMembers } from '@/apis/follow';
import { type FollowMemberType } from '@/apis/schema/member';
import FollowMissionList from '@/app/home/FollowMissionList';
import FollowSummary from '@/app/home/FollowSummary';
import MissionList from '@/app/home/MissionList';
import { flex } from '@styled-system/patterns';

function FollowContent() {
const { data } = useFollowMembers();
const searchParams = useSearchParams();
const selectedFollowData = useGetSelectFollowData();

const id = searchParams.get('id') ? Number(searchParams.get('id')) : null;
const selectedFollowData = data?.find((profile) => profile.memberId === id);
if (!selectedFollowData)
return (
<div className={containerCss}>
<MissionList />
</div>
);

return (
<div className={containerCss}>
<FollowSummary followId={selectedFollowData.memberId} followNickname={selectedFollowData.nickname} />
<FollowSummary {...selectedFollowData} />
<FollowMissionList followId={selectedFollowData.memberId} />
</div>
);
}

export default FollowContent;

const useGetSelectFollowData = (): FollowMemberType | null => {
const { data } = useFollowMembers();
const searchParams = useSearchParams();

if (!searchParams.get('id')) return null;

const id = Number(searchParams.get('id'));
const selectedFollowData = data?.find((profile) => profile.memberId === id);
return selectedFollowData ?? null;
};

const containerCss = flex({
flexDirection: 'column',
padding: '0 16px 30px',
Expand Down
24 changes: 16 additions & 8 deletions src/app/home/FollowSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from 'next/link';
import { useGetMissionStack } from '@/apis/mission';
import { type FollowMemberType } from '@/apis/schema/member';
import Banner from '@/components/Banner/Banner';
import LevelProgressBar from '@/components/Graph/LevelProgressBar';
import Icon from '@/components/Icon';
Expand All @@ -11,12 +12,9 @@ import { calcProgress, getLevel } from '@/utils/result';
import { css, cx } from '@styled-system/css';
import { flex } from '@styled-system/patterns';

interface FollowSummaryProps {
followId: number;
followNickname: string;
}
type FollowSummaryProps = FollowMemberType;

function FollowSummary({ followId, followNickname }: FollowSummaryProps) {
function FollowSummary({ memberId: followId, nickname: followNickname, profileImageUrl }: FollowSummaryProps) {
const { data: stackData } = useGetMissionStack(followId.toString());
const symbolStack = stackData?.symbolStack ?? 0;
const currentLevel = getLevel(symbolStack);
Expand All @@ -25,7 +23,7 @@ function FollowSummary({ followId, followNickname }: FollowSummaryProps) {
return (
<div>
<div className={followSummaryTitleCss}>
<Thumbnail size={'h36'} />
<Thumbnail size={'h18'} url={profileImageUrl} variant="filled" />
<Link href={ROUTER.PROFILE.DETAIL(followId)}>
<p className={followSummaryTextCss}>
{followNickname} <Icon name={'arrow-forward'} size={12} />
Expand All @@ -40,7 +38,9 @@ function FollowSummary({ followId, followNickname }: FollowSummaryProps) {
<span className={cx(levelLabelCss, gradientTextCss)}>{symbolStack}</span>
</div>
<p className={LevelNameCss}>{currentLevel.label}</p>
<LevelProgressBar current={progress} isLabel={false} backgroundColor={'purple.purple500'} />
<div className={levelProgressBarWrapperCss}>
<LevelProgressBar current={progress} isLabel={false} backgroundColor={'purple.purple500'} />
</div>
</div>
</div>
</div>
Expand All @@ -51,8 +51,11 @@ export default FollowSummary;

const followLevelInfoCss = flex({
flexDirection: 'column',
paddingRight: '24px',
justifyContent: 'center',
width: '90px',
flex: 0,
minWidth: '90px',
maxWidth: '90px',
});

const LevelNameCss = css({
Expand All @@ -78,6 +81,7 @@ const followBannerCss = flex({
gap: '24px',
marginBottom: '20px',
});

const followSummaryTitleCss = flex({
padding: '12px 4px',
flexDirection: 'row',
Expand All @@ -92,3 +96,7 @@ const followSummaryTextCss = flex({
alignItems: 'center',
gap: '4px',
});

const levelProgressBarWrapperCss = css({
width: '70px',
});
16 changes: 13 additions & 3 deletions src/app/mission/[id]/detail/MissionCalender/MissionCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function MissionCalendar({
missionId,
yearMonth: getYearMonth(currentDate),
});
const missionStartedAt = data?.missionStartedAt || '';

return (
<section>
Expand All @@ -46,12 +47,21 @@ function MissionCalendar({
<tr key={i}>
{week.map((day, index) => {
if (!day) return <td key={'calender-null-' + index} className={missionCalendarTdCss} />;
const { routerLink, ...restProps } = getMissionCalendarItemProps(day.date, data || [], isFollow);
const { routerLink, ...restProps } = getMissionCalendarItemProps(
missionStartedAt,
day,
data?.missionRecords || [],
isFollow,
);
return (
<td key={`${day.year}-${day.month}-${day.date}`} className={missionCalendarTdCss}>
<Link href={getMissionCalendarItemProps(day.date, data || [], isFollow).routerLink}>
{routerLink ? (
<Link href={routerLink}>
<MissionCalendarItem date={day.date} {...restProps} isActive={day.date === selectedDate} />
</Link>
) : (
<MissionCalendarItem date={day.date} {...restProps} isActive={day.date === selectedDate} />
</Link>
)}
</td>
);
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type RecordType } from '@/apis/schema/record';
import { ROUTER } from '@/constants/router';
import dayjs from 'dayjs';

const getWeekArray = (totalDate: number, offsetDate: number) => {
return Array.from({ length: 7 }, (_, i) => {
Expand Down Expand Up @@ -53,8 +54,32 @@ export const getCalenderInfo = (currentMonth: number, currentYear: number) => {
return { monthCalendarData };
};

export const getMissionCalendarItemProps = (date: number, records: RecordType[], isFollow?: boolean) => {
const filterRecord = records.filter((record) => record.missionDay === date);
export const getMissionCalendarItemProps = (
missionStartAt: string,
day: {
year: number;
month: number;
date: number;
},
records: RecordType[],
isFollow?: boolean,
) => {
console.log({ missionStartAt, day });
const isMissionStarted =
dayjs(missionStartAt.split(' ')[0]).isBefore(dayjs(`${day.year}-${day.month}-${day.date}`)) ||
dayjs(missionStartAt.split(' ')[0]).isSame(dayjs(`${day.year}-${day.month}-${day.date}`));
const filterRecord = records.filter((record) => record.missionDay === day.date);
console.log({
isMissionStarted,
filterRecord,
missionStartAt,
});
if (!isMissionStarted) {
return {
routerLink: '',
imageUrl: undefined,
};
}
if (filterRecord.length > 0) {
return {
routerLink: isFollow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ function MissionHistoryBannerApi({ missionId }: { missionId: string }) {
const description = data.content;
const imageUrl = MISSION_CATEGORY_LABEL[data.category].imgUrl;

// TODO: 디자인 시스템으로 변경 필요
return <MissionHistoryBanner title={title} description={description} imageUrl={imageUrl} />;
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/mission/[id]/detail/MissionHistoryTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ const missionHistoryTabCss = css({
display: 'flex',
flexDirection: 'column',
gap: '20px',
padding: '24px 16px 172px 16px',
padding: '24px 16px 192px 16px',
});
21 changes: 18 additions & 3 deletions src/app/mission/[id]/stopwatch/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,11 @@ export default function StopwatchPage() {
</section>
<section className={cx(buttonContainerCss, opacityAnimation)}>
{step === 'ready' && (
<Button variant="cta" size="large" type="button" onClick={onStart} disabled={isStopwatchPending}>
시작
</Button>
<div className={fixedButtonContainerCss}>
<Button variant="primary" size="large" type="button" onClick={onStart} disabled={isStopwatchPending}>
시작
</Button>
</div>
)}
{step === 'progress' && (
<>
Expand Down Expand Up @@ -293,3 +295,16 @@ const buttonContainerCss = css({
const opacityAnimation = css({
animation: 'fadeIn .7s',
});

const fixedButtonContainerCss = css({
position: 'fixed',
left: '16px',
right: '16px',
bottom: '16px',
width: '100%',
maxWidth: 'calc(475px - 48px)',
margin: '0 auto',
'@media (max-width: 475px)': {
maxWidth: 'calc(100vw - 48px)',
},
});
9 changes: 9 additions & 0 deletions src/app/record/[id]/detail/HistoryThumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ const historyThumbnailWrapperCss = css({
width: '100%',
aspectRatio: '1 / 1',
position: 'relative',
borderRadius: '22px',
overflow: 'hidden',
maxWidth: 'calc(475px - 32px)',
maxHeight: 'calc(475px - 32px)',

'@media (max-width: 475px)': {
maxWidth: 'calc(100vw - 32px)',
maxHeight: 'calc(100vw - 32px)',
},
});

const positionCss = css({
Expand Down
4 changes: 4 additions & 0 deletions src/app/record/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ const imageWrapperCss = css({
background: 'rgba(0, 0, 0, 0.1)',
pointerEvents: 'none',
},

'& img': {
objectFit: 'cover',
},
});

const imageIconCss = css({
Expand Down
3 changes: 2 additions & 1 deletion src/app/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { css } from '@/styled-system/css';

function SearchPage() {
const [input, setInput] = useState('');
const filteredInput = input.trim();
return (
<>
<SearchBar placeholder="닉네임을 검색해 주세요." value={input} onChange={setInput} />
Expand All @@ -25,7 +26,7 @@ function SearchPage() {
</>
}
>
<List nickname={input} />
<List nickname={filteredInput} />
</Suspense>
</>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/AppBarBottom/AppBarBottomVIew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ function AppBarBottomView(props: Props) {
<div
onClick={() => props.onClick?.(item)}
className={css(itemCss, {
color: isActive ? 'purple.purple800' : 'text.quaternary',
color: isActive ? 'purple.purple800' : 'purple.purple100',
})}
>
<Icon name={item.icon} color={isActive ? 'purple.purple800' : 'text.quaternary'} />
<Icon name={item.icon} color={isActive ? 'purple.purple800' : 'purple.purple100'} />
<span>{item.name}</span>
</div>
</Link>
Expand All @@ -36,7 +36,6 @@ export default AppBarBottomView;

const containerCss = flex({
padding: '12px 16px 14px',
backgroundColor: 'rgba(42, 42, 51, 0.80)',
backdropFilter: 'blur(20px)',
width: 'fit-content',
borderRadius: '24px',
Expand All @@ -46,6 +45,7 @@ const containerCss = flex({
right: 0,
margin: '16px auto',
zIndex: 'appBar',
background: 'linear-gradient(91deg, rgba(35, 40, 52, 0.70) 0%, rgba(39, 40, 62, 0.70) 100%)',
});

const itemCss = {
Expand Down
Loading