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
2 changes: 1 addition & 1 deletion app/(protected)/my-soss/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const Page = async () => {
userId={myProfile.userId}
allTabContent={
<>
<div className="flex gap-[30px]">
<div className="grid grid-cols-1 gap-[30px] xl:grid-cols-2 max-xl:[&>*]:h-auto! max-xl:[&>*]:w-full!">
<Suspense fallback={<TagCardLoading />}>
<TagCardEntry userLink={myProfile.userLink} />
</Suspense>
Expand Down
2 changes: 1 addition & 1 deletion app/(protected)/profile/[userLink]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const Page = async ({ params }: ProfilePageProps) => {
userId={profile.userId}
allTabContent={
<>
<div className="flex gap-[30px]">
<div className="flex flex-col gap-[30px] lg:flex-row">
<Suspense fallback={<TagCardLoading />}>
<TagCardEntry userLink={profile.userLink} />
</Suspense>
Expand Down
6 changes: 3 additions & 3 deletions app/(public)/profile-examples/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ const ProfileExamplesPage = () => (
userId={dummyProfile.userId}
allTabContent={
<>
<div className="flex gap-[30px]">
<div className="grid grid-cols-1 gap-[30px] xl:grid-cols-2 max-xl:[&>*]:h-auto! max-xl:[&>*]:w-full!">
<ProfileStatCard
title="받은 태그"
info
infoLabel="동료들이 남긴 후기에서 많이 선택된 태그예요"
className="h-[652px] w-[585px]"
className="h-auto w-full lg:h-[652px] lg:w-[585px]"
bodyClassName="gap-8"
>
<TagTop3Section tags={dummyReceivedTags.top3Tags} />
Expand All @@ -33,7 +33,7 @@ const ProfileExamplesPage = () => (
title="소프트 스킬 스펙트럼"
info
infoLabel="동료들의 평가를 기반으로 자동 산출된 협업 성향이에요"
className="h-[652px] w-[585px]"
className="h-auto w-full lg:h-[652px] lg:w-[585px]"
>
<SpectrumAverageSection spectrumInfo={dummySpectrumInfo} />
<SpectrumDistributionSection spectrumInfo={dummySpectrumInfo} />
Expand Down
12 changes: 10 additions & 2 deletions features/profile/components/profile-avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { Avatar } from 'radix-ui';

import { cn } from '@/shared/lib/cn';

/** 업로드 이미지가 없을 때 표시하는 기본 프로필 이미지 */
const DEFAULT_PROFILE_IMAGE_SRC = '/default-profile.png';

interface Props {
username: string;
profileImageUrl: string | null;
className?: string;
}

export const ProfileAvatar = ({ username, profileImageUrl }: Props) => {
export const ProfileAvatar = ({ username, profileImageUrl, className }: Props) => {
const fallbackText = username.trim().slice(0, 1) || '이름';
const avatarSrc = profileImageUrl || DEFAULT_PROFILE_IMAGE_SRC;

return (
<Avatar.Root className="bg-action-gray-light flex size-25 shrink-0 items-center justify-center overflow-hidden rounded-full">
<Avatar.Root
className={cn(
'bg-action-gray-light flex size-25 shrink-0 items-center justify-center overflow-hidden rounded-full',
className,
)}
>
<Avatar.Image className="h-full w-full object-cover" src={avatarSrc} alt={`${username} 프로필 이미지`} />
<Avatar.Fallback className="text-heading-lg text-text-subtle font-bold" delayMs={600}>
{fallbackText}
Expand Down
14 changes: 7 additions & 7 deletions features/profile/components/profile-section-loading.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
export const ProfileSectionLoading = () => (
<section className="mb-10 flex w-full flex-row justify-between pt-8">
<div className="flex flex-row gap-6">
<div className="bg-action-gray-light size-25 shrink-0 animate-pulse rounded-full" />
<div className="flex flex-1 flex-col gap-4 py-1">
<div className="bg-action-gray-light h-8 w-40 animate-pulse rounded-sm" />
<div className="flex flex-col gap-1">
<section className="mb-10 flex w-full flex-col gap-6 pt-8 lg:flex-row lg:justify-between lg:gap-0">
<div className="grid grid-cols-[60px_minmax(0,1fr)] gap-x-4 gap-y-3 lg:flex lg:flex-row lg:gap-6">
<div className="bg-action-gray-light size-15 shrink-0 animate-pulse rounded-full lg:size-25" />
<div className="contents lg:flex lg:flex-1 lg:flex-col lg:gap-4 lg:py-1">
<div className="bg-action-gray-light col-start-2 row-start-1 h-8 w-40 animate-pulse self-center rounded-sm lg:col-auto lg:row-auto lg:self-auto" />
<div className="col-span-2 flex flex-col gap-1 lg:col-auto">
<div className="bg-action-gray-light h-4 w-20 animate-pulse rounded-sm" />
<div className="bg-action-gray-light h-5 w-64 animate-pulse rounded-sm" />
</div>
</div>
</div>
<div className="bg-action-gray-light h-11 w-44 shrink-0 animate-pulse rounded-md" />
<div className="bg-action-gray-light h-11 w-full shrink-0 animate-pulse rounded-md lg:w-44" />
</section>
);
6 changes: 3 additions & 3 deletions features/profile/components/profile-share-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ export const ProfileShareActions = ({
onCloseShareTooltip,
onShareProfile,
}: Props) => (
<div className="ml-auto flex shrink-0 items-start gap-2">
<div className="relative inline-flex">
<div className="flex w-full shrink-0 items-start gap-2 lg:ml-auto lg:w-auto">
<div className="relative inline-flex w-full lg:w-auto">
<Button
type="button"
variant="primary"
size="medium"
leftIcon={<ShareIcon aria-hidden />}
className={cn(
'h-11',
'h-11 w-full lg:w-auto',
isShareTooltipOpen &&
'bg-button-primary-fill-pressed hover:bg-button-primary-fill-pressed focus:bg-button-primary-fill-pressed active:bg-button-primary-fill-pressed',
)}
Expand Down
26 changes: 15 additions & 11 deletions features/profile/components/profile-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ interface Props {
}

export const ProfileSummary = ({ profile, shareActions }: Props) => (
<section className="mb-10 flex w-full flex-row justify-between pt-8">
<div className="flex flex-row gap-6">
<ProfileAvatar username={profile.username} profileImageUrl={profile.profileImageUrl} />
<div className="flex flex-1 flex-col gap-4 py-1">
<div className="flex flex-col gap-2">
<h2 className="text-heading-lg text-text-basic font-bold">{profile.username}</h2>
<section className="mb-10 flex w-full flex-col gap-6 pt-8 lg:flex-row lg:justify-between lg:gap-0">
<div className="grid grid-cols-[60px_minmax(0,1fr)] gap-x-4 gap-y-3 lg:flex lg:flex-row lg:gap-6">
<ProfileAvatar
username={profile.username}
profileImageUrl={profile.profileImageUrl}
className="size-15 lg:size-25"
/>
<div className="contents lg:flex lg:flex-1 lg:flex-col lg:gap-4 lg:py-1">
<div className="col-start-2 row-start-1 flex flex-col justify-center gap-2 lg:col-auto lg:row-auto lg:justify-start">
<h2 className="text-body-lg text-text-basic lg:text-heading-lg font-bold">{profile.username}</h2>
<ProfilePositionTags positions={profile.defaultPositions} />
</div>
{profile.bio ? (
<div className="flex flex-col gap-1">
<p className="text-heading-xs text-text-basic font-medium">한 줄 소개</p>
<p className="text-body-base text-text-subtle font-normal">{profile.bio}</p>
<div className="col-span-2 flex flex-col gap-1 lg:col-auto">
<p className="text-body-sm text-text-basic lg:text-heading-xs font-medium">한 줄 소개</p>
<p className="text-body-sm text-text-subtle lg:text-body-base font-normal">{profile.bio}</p>
</div>
) : null}
{profile.links.length > 0 ? (
<div className="flex flex-col gap-2">
<p className="text-heading-xs text-text-basic font-medium">하드 스킬</p>
<div className="col-span-2 flex flex-col gap-2 lg:col-auto">
<p className="text-body-sm text-text-basic lg:text-heading-xs font-medium">하드 스킬</p>
<ProfileLinkList links={profile.links} />
</div>
) : null}
Expand Down
32 changes: 24 additions & 8 deletions features/review/components/review-list/review-list-card.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';

import { useState } from 'react';
import { Fragment, useState } from 'react';

import { DownIcon } from '@/shared/assets/icons';
import { Button } from '@/shared/components/button';
import { EmptyState } from '@/shared/components/empty-state';
import { ProfileStatCard } from '@/shared/components/profile-stat-card';
import { SortDropdown, type SortOrder } from '@/shared/components/sort-dropdown';
import { sortOptions, type SortOrder } from '@/shared/components/sort-dropdown';
import { cn } from '@/shared/lib/cn';
import { formatIsoDateToDots } from '@/shared/lib/format-date';

Expand Down Expand Up @@ -51,12 +51,28 @@ export const ReviewListCard = ({

const headerAction =
showSort && reviews.length > 0 ? (
<SortDropdown
value={selectedSort}
onValueChange={setSelectedSort}
ariaLabel="후기 정렬"
triggerClassName="h-10"
/>
<div role="group" aria-label="후기 정렬" className="text-detail-xs flex items-center gap-2 font-medium">
{sortOptions.map((option, index) => (
<Fragment key={option.value}>
{index > 0 ? (
<span aria-hidden className="text-text-disabled">
·
</span>
) : null}
<button
type="button"
aria-pressed={selectedSort === option.value}
onClick={() => setSelectedSort(option.value)}
className={cn(
'focus-visible:ring-border-primary cursor-pointer rounded-xs transition-colors outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
selectedSort === option.value ? 'text-text-subtler' : 'text-text-disabled',
)}
>
{option.label}
</button>
</Fragment>
))}
</div>
) : null;

return (
Expand Down
49 changes: 16 additions & 33 deletions features/spectrum/components/spectrum-average-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import {
} from '../spectrum.constants';
import { toMarkerLeft } from '../spectrum.lib';

/** 4축 × (h-7 + gap-1) */
const SPECTRUM_TRACK_HEIGHT_PX = 136;

interface Props {
spectrumInfo: SpectrumInfo;
}
Expand All @@ -22,51 +19,37 @@ export const SpectrumAverageSection = ({ spectrumInfo }: Props) => (
<div className="w-full">
<h3 className="text-heading-xs text-text-subtle pb-2 font-bold">평균 지표</h3>

<div className="flex items-center gap-4" style={{ height: SPECTRUM_TRACK_HEIGHT_PX }}>
<div className="flex w-[108px] shrink-0 flex-col gap-1" style={{ height: SPECTRUM_TRACK_HEIGHT_PX }}>
{spectrumAxisLabels.map((row) => (
<p key={`${row.left}-left`} className="text-body-xs text-text-subtle flex h-7 items-center font-medium">
{row.left}
</p>
))}
</div>

<div className="flex min-w-0 flex-1 flex-col gap-1" style={{ height: SPECTRUM_TRACK_HEIGHT_PX }}>
{(spectrumInfo.spectrumInfoResDtos ?? []).map((axis) => {
const markerLeft = toMarkerLeft(axis.averageStrength);
<div className="flex flex-col gap-4 lg:grid lg:grid-cols-[108px_minmax(0,1fr)_max-content] lg:items-center lg:gap-x-4 lg:gap-y-1">
{(spectrumInfo.spectrumInfoResDtos ?? []).map((axis, index) => {
const labels = spectrumAxisLabels[index];
const markerLeft = toMarkerLeft(axis.averageStrength);

return (
<div key={axis.axisName} className="relative flex h-7 min-h-7 items-center">
return (
<div key={axis.axisName} className="flex flex-wrap items-center gap-x-4 gap-y-1 lg:contents">
<p className="text-body-xs text-text-subtle order-1 shrink-0 font-medium lg:order-none">{labels.left}</p>
<div className="relative order-3 flex h-7 min-h-7 w-full min-w-0 items-center lg:order-none lg:w-auto">
<div className="relative h-3 w-full overflow-hidden rounded">
<div aria-hidden className={cn('absolute inset-y-0 left-0 w-1/2', SPECTRUM_LEFT_TRACK_CLASS)} />
<div aria-hidden className={cn('absolute inset-y-0 right-0 w-1/2', SPECTRUM_RIGHT_TRACK_CLASS)} />
<div aria-hidden className="absolute inset-0 grid grid-cols-6">
{SPECTRUM_TRACK_SEGMENT_KEYS.map((key, index) => (
{SPECTRUM_TRACK_SEGMENT_KEYS.map((key, segIndex) => (
<span
key={key}
className={cn(
index < SPECTRUM_TRACK_SEGMENT_KEYS.length - 1 && 'border-input-surface border-r-[0.5px]',
segIndex < SPECTRUM_TRACK_SEGMENT_KEYS.length - 1 && 'border-input-surface border-r-[0.5px]',
)}
/>
))}
</div>
</div>
<div aria-hidden className={SPECTRUM_MARKER_CLASS} style={{ left: markerLeft }} />
</div>
);
})}
</div>

<div className="flex shrink-0 flex-col gap-1" style={{ height: SPECTRUM_TRACK_HEIGHT_PX }}>
{spectrumAxisLabels.map((row) => (
<p
key={`${row.right}-right`}
className="text-body-xs text-text-subtle flex h-7 items-center font-medium whitespace-nowrap"
>
{row.right}
</p>
))}
</div>
<p className="text-body-xs text-text-subtle order-2 ml-auto shrink-0 font-medium whitespace-nowrap lg:order-none lg:ml-0">
{labels.right}
</p>
</div>
);
})}
</div>
</div>
);
4 changes: 2 additions & 2 deletions features/spectrum/components/spectrum-card-gate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const SpectrumCardGate = ({ userLink, projectId, showDistribution }: Prop
<RetryErrorCard
title="스펙트럼을 불러오지 못했어요."
description="연결 상태를 확인한 뒤 다시 시도해 주세요."
className="border-border-gray flex h-[652px] w-[588px] flex-col rounded-2xl border"
actionClassName="w-80"
className="border-border-gray flex h-auto min-h-[360px] w-full flex-col rounded-2xl border lg:h-[652px] lg:min-h-0 lg:w-[588px]"
actionClassName="w-full max-w-80"
onRetry={resetErrorBoundary}
/>
)}
Expand Down
14 changes: 7 additions & 7 deletions features/spectrum/components/spectrum-card-loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ const SpectrumSkeleton = () => (
<div className={`${pulse} h-6 w-16`} />

<div className="mt-3 flex h-[136px] items-start">
<div className="flex h-[136px] w-[93px] flex-col gap-2">
<div className="flex h-[136px] w-20 shrink-0 flex-col gap-2 lg:w-[93px]">
{SPECTRUM_ROW_KEYS.map((key) => (
<div key={key} className="flex h-7 items-center">
<div className={`${pulse} h-3.5 w-full`} />
</div>
))}
</div>

<div className="mx-6 flex h-[136px] w-[286px] flex-col gap-2">
<div className="mx-3 flex h-[136px] min-w-0 flex-1 flex-col gap-2 lg:mx-6 lg:w-[286px] lg:flex-none">
{SPECTRUM_ROW_KEYS.map((key) => (
<div key={key} className="flex h-7 items-center">
<div className={`${pulse} h-1.5 w-full rounded-full`} />
</div>
))}
</div>

<div className="flex h-[136px] w-[113px] flex-col gap-2">
<div className="flex h-[136px] w-16 shrink-0 flex-col gap-2 lg:w-[113px]">
{SPECTRUM_ROW_KEYS.map((key) => (
<div key={key} className="flex h-7 items-center justify-end">
<div className={`${pulse} h-3.5 w-full`} />
Expand All @@ -49,11 +49,11 @@ const DistributionSkeleton = () => (
<div className="mt-[41px]">
<div className={`${pulse} h-6 w-44`} />

<div className="mx-auto mt-4 flex w-[513px] items-end justify-between" style={{ height: 242 }}>
<div className="mt-4 flex w-full items-end justify-between gap-1 lg:h-[242px]">
{DISTRIBUTION_BARS.map(({ key, height }) => (
<div key={key} className="flex w-[61px] flex-col items-center">
<div key={key} className="flex min-w-0 flex-1 flex-col items-center lg:w-[61px] lg:flex-none">
<div className={`${pulse} mb-0.5 h-[18px] w-8`} />
<div className={`${pulse} w-[50px] rounded-t`} style={{ height }} />
<div className={`${pulse} w-full rounded-t lg:w-[50px]`} style={{ height }} />
<div className={`${pulse} mt-2 h-9 w-full`} />
</div>
))}
Expand All @@ -64,7 +64,7 @@ const DistributionSkeleton = () => (
export const SpectrumCardLoading = () => (
<section
aria-hidden
className="border-border-gray flex h-[652px] w-[585px] flex-col overflow-hidden rounded-2xl border bg-white"
className="border-border-gray flex h-auto w-full flex-col overflow-hidden rounded-2xl border bg-white lg:h-[652px] lg:w-[585px]"
>
<div className="bg-surface-gray-subtler flex items-center px-6 py-5">
<div className={`${pulse} h-5 w-44`} />
Expand Down
2 changes: 1 addition & 1 deletion features/spectrum/components/spectrum-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const SpectrumCard = ({ userLink, projectId, showDistribution = true }: P
title="소프트 스킬 스펙트럼"
info
infoLabel="동료들의 평가를 기반으로 자동 산출된 협업 성향이에요"
className={cn('w-[585px]', showDistribution ? 'h-[652px]' : 'h-auto')}
className={cn('w-full lg:w-[585px]', showDistribution ? 'h-auto lg:h-[652px]' : 'h-auto')}
>
{spectrumInfo.totalCount === 0 ? (
<EmptyState title="받은 후기가 없어요" />
Expand Down
Loading
Loading