Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#29] - 포트폴리오 피드백 전체 평가 섹션 퍼블리싱 #30

Merged
merged 5 commits into from
Feb 16, 2025
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: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@emotion/styled": "^11.14.0",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-slot": "^1.1.2",
"dompurify": "^3.2.4",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand All @@ -48,6 +49,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.1",
"@types/dompurify": "^3.2.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vite-pwa/assets-generator": "^0.2.6",
Expand Down
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions src/common/components/bar-chart/BarChart.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { css, keyframes } from '@emotion/react';

export const barChartStyle = css`
overflow: hidden;
width: 100%;
height: 6px;
background-color: #ebeef2;
`;

export const barChartBar = (value: number) => css`
height: 100%;
background-color: #4a5468;
animation: ${widthAnimation(value)} 2s ease-in-out forwards;
`;

const widthAnimation = (value: number) => keyframes`
0% {
width: 0%;
}
100% {
width: ${value}%;
}
`;
13 changes: 13 additions & 0 deletions src/common/components/bar-chart/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as styles from './BarChart.styles';

interface BarChartProps {
value: number;
}

export default function BarChart({ value }: BarChartProps) {
return (
<div css={styles.barChartStyle}>
<div css={styles.barChartBar(value)} />
</div>
);
}
26 changes: 26 additions & 0 deletions src/common/stories/BarChart.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Meta, StoryObj } from '@storybook/react';

import BarChart from '../components/bar-chart';

const meta = {
title: 'Components/BarChart',
component: BarChart,
argTypes: {
value: { control: { type: 'number', min: 0, max: 100, step: 1 } },
},
} satisfies Meta<typeof BarChart>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
value: 50,
},
};

export const Over: Story = {
args: {
value: 300,
},
};
22 changes: 22 additions & 0 deletions src/features/total-evaluation/common/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const evaluationData = {
evaluationSummary: `<b>디퍼님은 강력한 데이터 기반 UX 디자인</b>이 돋보이는 포트폴리오를 갖고 계시네요! <br/> <b>가독성과 시각적 표현 방식을 개선</b>하면 <b>완벽에 가까운 수준</b>이 될 것 같아요`,
overallEvaluationGrade: 'A',
evaluationItems: {
jobFit: {
text: '디퍼님은 직무적합도가 매우 일치해요. 프로덕트 디자이너로서 갖춰야 할 비즈니스 관점과 전략적 사고를 보유하고 있으며, 프로젝트에 잘 녹아져 있어요.',
score: 90,
},
logicalThinking: {
text: '프로젝트별 논리적인 흐름이나 설득력이 부족한 부분이 있어요. 근거를 강화할 수 있는 구체적인 내용을 좀 더 추가하는 것이 필요해요.',
score: 73,
},
sentenceReadability: {
text: '대표 문구가 대체로 길고 복잡한 형태를 띄고 있어요. 타이틀 문구의 구조를 먼저 정하고, 핵심적으로 전달해야 할 내용을 정리해서 담아내면 좋아요.',
score: 76,
},
layoutReadability: {
text: '프로젝트별로 일관된 포맷을 유지하여 가독성이 뛰어나요. 제목, 소제목, 본문 간의 시각적 위례가 뚜렷하여 정보 전달력이 높은 레이아웃이에요.',
score: 94,
},
},
};
41 changes: 41 additions & 0 deletions src/features/total-evaluation/components/EvaluationChart.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { css } from '@emotion/react';

export const evaluationChart = css`
display: flex;
gap: 30px;
align-items: center;
`;

export const evaluationChartWrapper = css`
display: flex;
flex-direction: column;
gap: 16px;
flex: 1;
`;

export const evaluationItem = css`
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
`;

export const criteria = css`
color: #969aa2;
font-size: 14px;
font-weight: 500;
line-height: 21px;
letter-spacing: 1%;
`;

export const evaluationGrade = css`
width: 160px;
height: 160px;
border: 2px solid #4d5159;
color: #37393e;
font-size: 136.42px;
font-weight: 400;
text-align: center;
line-height: 154.7px;
background-color: #ebeef2;
`;
31 changes: 31 additions & 0 deletions src/features/total-evaluation/components/EvaluationChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import BarChart from '../../../common/components/bar-chart';
import { EvaluationItemsType } from '../types/evaluationTypes';
import { getEvaluationData } from '../utils/get-evaluation-data';

import * as styles from './EvaluationChart.styles';

interface EvaluationChartProps {
overallEvaluationGrade: string;
evaluationItems: EvaluationItemsType;
}

export default function EvaluationChart({
overallEvaluationGrade,
evaluationItems,
}: EvaluationChartProps) {
const evaluationData = getEvaluationData(evaluationItems);

return (
<div css={styles.evaluationChart}>
<div css={styles.evaluationChartWrapper}>
{evaluationData.map(({ criteria, label, score }) => (
<div key={criteria} css={styles.evaluationItem}>
<span css={styles.criteria}>{label}</span>
<BarChart value={score} />
</div>
))}
</div>
<div css={styles.evaluationGrade}>{overallEvaluationGrade}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { css } from '@emotion/react';

export const evaluationSummary = css`
margin: 0;
color: #acb0b7;
font-size: 24px;
font-weight: 700;
line-height: 36px;
letter-spacing: 1%;

b {
color: #37393e;
}
`;
15 changes: 15 additions & 0 deletions src/features/total-evaluation/components/EvaluationSummary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import DOMPurify from 'dompurify';

import * as styles from './EvaluationSummary.styles';
interface EvaluationSummaryProps {
evaluationSummary: string;
}

export default function EvaluationSummary({ evaluationSummary }: EvaluationSummaryProps) {
/** XSS 방지를 위해 HTML을 정제 */
const sanitizedEvaluation = DOMPurify.sanitize(evaluationSummary);

return (
<p css={styles.evaluationSummary} dangerouslySetInnerHTML={{ __html: sanitizedEvaluation }} />
);
}
45 changes: 45 additions & 0 deletions src/features/total-evaluation/components/EvaluationTable.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { css } from '@emotion/react';

export const evaluationTableWrapper = css`
display: flex;
flex-direction: column;
gap: 20px;
`;

export const tableRow = css`
display: flex;
gap: 52px;
align-items: flex-start;
padding-bottom: 20px;
border-bottom: 1px solid #eceef2;
`;

export const label = css`
width: 102px;
color: #4a5468;
font-size: 16px;
font-weight: 600;
line-height: 150%;
white-space: nowrap;
font-style: normal;
letter-spacing: 0.16px;
`;

export const detailText = css`
margin: 0;
color: #73767d;
font-size: 16px;
font-weight: 500;
line-height: 170%;
font-style: normal;
letter-spacing: 0.16px;
`;

export const score = css`
color: #000;
font-size: 30px;
font-style: normal;
font-weight: 400;
line-height: normal;
margin-left: auto;
`;
24 changes: 24 additions & 0 deletions src/features/total-evaluation/components/EvaluationTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { EvaluationItemsType } from '../types/evaluationTypes';
import { getEvaluationData } from '../utils/get-evaluation-data';

import * as styles from './EvaluationTable.styles';

interface EvaluationTableProps {
evaluationItems: EvaluationItemsType;
}

export default function EvaluationTable({ evaluationItems }: EvaluationTableProps) {
const evaluationData = getEvaluationData(evaluationItems);

return (
<div css={styles.evaluationTableWrapper}>
{evaluationData.map(({ criteria, label, score, text }) => (
<div key={criteria} css={styles.tableRow}>
<label css={styles.label}>{label}</label>
<p css={styles.detailText}>{text}</p>
<b css={styles.score}> {score}</b>
</div>
))}
</div>
);
}
9 changes: 9 additions & 0 deletions src/features/total-evaluation/config/evaluationConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EvaluationCriteria } from '../types/evaluationTypes';

/** 전체 평가 항목에 해당하는 라벨 */
export const criteriaLabelConfig: Record<EvaluationCriteria, string> = {
jobFit: '직무 적합도',
logicalThinking: '논리적 사고',
sentenceReadability: '문장 가독성',
layoutReadability: '레이아웃 가독성',
};
8 changes: 8 additions & 0 deletions src/features/total-evaluation/index.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { css } from '@emotion/react';

export const totalEvaluationPage = css`
display: flex;
flex-direction: column;
gap: 50px;
width: 100%;
`;
21 changes: 21 additions & 0 deletions src/features/total-evaluation/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { evaluationData } from './common/data';
import EvaluationChart from './components/EvaluationChart';
import EvaluationSummary from './components/EvaluationSummary';
import EvaluationTable from './components/EvaluationTable';

import * as styles from './index.styles';

export default function TotalEvalutionPage() {
const { evaluationSummary, overallEvaluationGrade, evaluationItems } = evaluationData;

return (
<div css={styles.totalEvaluationPage}>
<EvaluationSummary evaluationSummary={evaluationSummary} />
<EvaluationChart
overallEvaluationGrade={overallEvaluationGrade}
evaluationItems={evaluationItems}
/>
<EvaluationTable evaluationItems={evaluationItems} />
</div>
);
}
15 changes: 15 additions & 0 deletions src/features/total-evaluation/types/evaluationTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** 평가 항목의 종류를 나타내는 타입 */
export type EvaluationCriteria =
| 'jobFit' // 직무 적합도
| 'logicalThinking' // 논리적 사고
| 'sentenceReadability' // 문장 가독성
| 'layoutReadability'; // 레이아웃 가독성

/** 각 평가 항목에 대한 세부 데이터를 나타내는 타입 */
export type EvaluationItemsType = Record<
EvaluationCriteria,
{
text: string; // 해당 평가 항목에 대한 설명
score: number; // 해당 평가 항목에 대한 점수
}
>;
Loading