Skip to content

Commit

Permalink
Merge pull request #89 from Myongji-Graduate/doublemajor,submajor/#87
Browse files Browse the repository at this point in the history
Implement several major in ResultCategoryCard/#87
  • Loading branch information
yougyung authored May 2, 2024
2 parents 07b9e20 + 50e168c commit c01b239
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 63 deletions.
18 changes: 15 additions & 3 deletions app/(sub-page)/result/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import ResultCategoryCard from '@/app/ui/result/result-category-card';
import ResultCategoryDetail from '@/app/ui/result/result-category-detail/result-category-detail';
import ResultCategoryCard from '@/app/ui/result/result-category-card/result-category-card';
import UserInfoCard from '@/app/ui/user/user-info-card/user-info-card';
import ContentContainer from '@/app/ui/view/atom/content-container';
import { cn } from '@/app/utils/shadcn/utils';
import { RESULT_CATEGORY } from '@/app/utils/key/result-category.key';
import ResultCategoryDetail from '@/app/ui/result/result-category-detail/result-category-detail';

interface ResultPageProp {
searchParams: { category: string };
}

function ResultPage({ searchParams }: ResultPageProp) {
const { category } = searchParams;
const DUMMY_DATA = {
category: 'COMMON_CULTURE' as keyof typeof RESULT_CATEGORY,
totalCredit: 70,
takenCredit: 68,
completed: false,
};

return (
<div className="flex justify-center items-end">
Expand All @@ -23,7 +30,12 @@ function ResultPage({ searchParams }: ResultPageProp) {
)}
>
{Array.from({ length: 8 }).map((_, index) => (
<ResultCategoryCard key={index} />
<ResultCategoryCard
key={index}
category={RESULT_CATEGORY[DUMMY_DATA.category]}
totalCredit={DUMMY_DATA.totalCredit}
takenCredit={DUMMY_DATA.takenCredit}
/>
))}
</div>
{category && <ResultCategoryDetail category={category} />}
Expand Down
2 changes: 2 additions & 0 deletions app/store/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const initialState = {

const dialogAtom = atom(initialState);

export const isDialogOpenAtom = atom(false);

export const updateDialogAtom = atom(
(get) => get(dialogAtom),
(get, set, [key, value]) => {
Expand Down
50 changes: 0 additions & 50 deletions app/ui/result/result-category-card.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Meta, StoryObj } from '@storybook/react';
import ResultCategoryCard from './result-category-card';
import { RESULT_CATEGORY } from '@/app/utils/key/result-category.key';

const meta = {
title: 'ui/result/category-card',
component: ResultCategoryCard,
decorators: [(Story) => <Story />],
} as Meta<typeof ResultCategoryCard>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
category: RESULT_CATEGORY.COMMON_CULTURE,
totalCredit: 40,
takenCredit: 30,
completed: false,
},
};

export const CompletedSubMajor: Story = {
args: {
category: RESULT_CATEGORY.SUB_MAJOR,
totalCredit: 40,
takenCredit: 40,
completed: true,
},
};

export const UncompletedDualMajor = {
args: {
category: RESULT_CATEGORY.DUAL_BASIC_ACADEMICAL_CULTURE,
totalCredit: 70,
takenCredit: 40,
completed: false,
},
};
90 changes: 90 additions & 0 deletions app/ui/result/result-category-card/result-category-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use client';
import { cn } from '@/app/utils/shadcn/utils';

import Book from '@/public/assets/book.svg';
import Image from 'next/image';
import useDialog from '@/app/hooks/useDialog';
import * as React from 'react';
import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import Link from 'next/link';
import { useSetAtom } from 'jotai';
import { isDialogOpenAtom } from '@/app/store/dialog';
import Button from '../../view/atom/button/button';
import PieChart from '../../view/molecule/pie-chart/pie-chart';
import { RESULT_CATEGORY, RESULT_CATEGORY_KO, ResultCategoryKey } from '@/app/utils/key/result-category.key';

interface ResultCategoryCardProps {
category: ResultCategoryKey;
totalCredit: number;
takenCredit: number;
completed?: boolean;
}

const filterSeveralMajor = (category: ResultCategoryKey) => {
const { DUAL_MANDATORY_MAJOR, DUAL_ELECTIVE_MAJOR, DUAL_BASIC_ACADEMICAL_CULTURE, SUB_MAJOR } = RESULT_CATEGORY;

switch (category) {
case DUAL_MANDATORY_MAJOR:
case DUAL_ELECTIVE_MAJOR:
case DUAL_BASIC_ACADEMICAL_CULTURE:
return <Button label="복수전공" variant="outlined" size="xs" role="none presentation" />;
case SUB_MAJOR:
return <Button label="부전공" variant="outlined" size="xs" role="none presentation" />;
default:
return <></>;
}
};

function ResultCategoryCard({ category, totalCredit, takenCredit }: ResultCategoryCardProps) {
const { toggle } = useDialog(DIALOG_KEY.RESULT_CATEGORY);
const setIsOpenDialog = useSetAtom(isDialogOpenAtom);

const percentage = Number(((takenCredit / totalCredit) * 100).toFixed(0));

function handleClickButton() {
toggle();
setIsOpenDialog(true);
}
return (
<div
className={cn('flex flex-col gap-6 zIndex-1 rounded-xl shadow-lg bg-white p-[0.4rem]', 'md:w-80 md:p-[1.8rem]')}
>
<div className="flex justify-between items-center">
<div className={cn('flex gap-4 font-bold text-sm', 'md:text-xl')}>
<Image src={Book} width={24} height={24} alt="category-img" />
<h3>{RESULT_CATEGORY_KO[category]}</h3>
</div>
{filterSeveralMajor(category)}
</div>
<div className="m-auto">
<PieChart percentage={percentage} />
</div>
<div className={cn('flex text-xs font-medium justify-between items-end', 'md:gap-4 md:text-base md:px-2')}>
<div>
<div className={cn('flex', 'md:gap-2')}>
<span>기준학점</span>
<span className="font-bold">{totalCredit}</span>
</div>
<div className={cn('flex', 'md:gap-2')}>
<span>이수학점</span>
<span className={cn('font-bold', percentage === 100 ? 'text-point-blue' : 'text-etc-red')}>
{takenCredit}
</span>
</div>
</div>
<Link
href={{
pathname: '/result',
query: {
category: 'COMMON_CULTURE',
},
}}
>
<Button size="sm" label="과목 확인" onClick={handleClickButton} />
</Link>
</div>
</div>
);
}

export default ResultCategoryCard;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { screen } from '@storybook/testing-library';
import { delay } from 'msw';

const meta = {
title: 'ui/result-category/ResultCategoryDetailContent',
title: 'ui/result/result-category-detail-content',
component: ResultCategoryDetailContent,
args: { info: mockDatabase.getResultCategoryDetailInfo() },
decorators: [(Story) => <Story />],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@ import { DIALOG_KEY } from '@/app/utils/key/dialog-key.util';
import Drawer from '../../view/molecule/drawer/drawer';
import Responsive from '@/app/ui/responsive';
import React, { useEffect } from 'react';
import { useAtomValue } from 'jotai';
import { isDialogOpenAtom } from '@/app/store/dialog';
import useDialog from '@/app/hooks/useDialog';
import { useRouter } from 'next/navigation';

interface ResultCategoryDetailDialogProp {
children: React.ReactNode;
querystring?: string;
}

export default function ResultCategoryDetailDialog({ children, querystring }: ResultCategoryDetailDialogProp) {
const { replace } = useRouter();
const isOpenDialog = useAtomValue(isDialogOpenAtom);

const { isOpen, open } = useDialog(DIALOG_KEY.RESULT_CATEGORY);

useEffect(() => {
if (querystring && !isOpen) open();
}, []);

const handleCloseDialog = () => {
replace('/result');
if (isOpenDialog) window.history.go(-1);
window.history.replaceState({}, '', '/result');
};

return (
Expand Down
12 changes: 10 additions & 2 deletions app/ui/view/atom/button/button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const meta = {
docs: {
description: {
component: `
- variant값으로 "primary" | "secondary" | "text" | "delete" 중 하나를 선택할 수 있습니다.\n
- variant값으로 "primary" | "secondary" | "list" | "outlined" 중 하나를 선택할 수 있습니다.\n
- size값으로 "lg" | "md" | "sm" | "xs" | "default" 중 하나를 선택할 수 있습니다.\n
- label 값으로 button 태그에 존재하는 text를 의미하고 필수적으로 할당해야 합니다
`,
Expand All @@ -24,7 +24,7 @@ const meta = {
type: { summary: 'ButtonVariant' },
defaultValue: { summary: 'primary' },
},
options: ['primary', 'secondary', 'text', 'delete'],
options: ['primary', 'secondary', 'text', 'list', 'outlined'],
control: {
type: 'radio',
},
Expand Down Expand Up @@ -69,6 +69,14 @@ export const SecondaryButton: Story = {
},
};

export const OutlinedButton: Story = {
args: {
size: 'sm',
variant: 'outlined',
label: '주전공',
},
};

export const ListActionButton: Story = {
args: {
size: 'default',
Expand Down
3 changes: 2 additions & 1 deletion app/ui/view/atom/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'default';

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
label: string;
variant?: 'primary' | 'secondary' | 'text' | 'list';
variant?: 'primary' | 'secondary' | 'text' | 'list' | 'outlined';
size?: ButtonSize;
loading?: boolean;
disabled?: boolean;
Expand All @@ -17,6 +17,7 @@ export const ButtonVariants = cva(`flex justify-center items-center`, {
variants: {
variant: {
primary: 'bg-primary rounded-[100px] text-white border-0 hover:bg-primary-hover',
outlined: 'rounded-[100px] text-primary border-solid border-[1px] border-primary',
secondary: 'bg-white rounded-[100px] border-solid border-[1px] border-gray-6 hover:bg-white-hover',
text: 'font-medium text-slate-400 text-sm hover:text-slate-600',
list: 'py-1 px-3 bg-blue-500 rounded-[7px] text-white leading-5 font-medium text-base hover:bg-blue-500',
Expand Down
12 changes: 11 additions & 1 deletion app/ui/view/molecule/pie-chart/pie-chart.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
position: absolute;
border-radius: 50%;
inset: 0;
background:
radial-gradient(farthest-side, #ff6d6d 98%, #0000) top/1.1rem 1.5rem no-repeat,
conic-gradient(#ff6d6d calc(var(--percentage) * 1%), #0000 0);
}

.piechart.complete::before {
background:
radial-gradient(farthest-side, #7590ff 98%, #0000) top/1.1rem 1.5rem no-repeat,
conic-gradient(#7590ff calc(var(--percentage) * 1%), #0000 0);
Expand All @@ -28,6 +34,10 @@
border-radius: 46%;
inset: calc(50% - 0.74rem);
position: absolute;
background: #7590ff;
background: #ff6d6d;
transform: rotate(calc(var(--percentage) * 3.6deg)) translateY(calc(50% - 9rem / 2));
}

.piechart.complete::after {
background: #7590ff;
}
13 changes: 11 additions & 2 deletions app/ui/view/molecule/pie-chart/pie-chart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '@/app/ui/view/molecule/pie-chart/pie-chart.css';
import { cn } from '@/app/utils/shadcn/utils';

export interface PieChartProp {
percentage: number;
Expand All @@ -9,10 +10,18 @@ function PieChart({ percentage }: PieChartProp) {

return (
<div
className={`piechart relative aspect-square rounded-full bg-light-blue-1 w-[9rem] h-[9rem]`}
className={cn(
'piechart relative aspect-square rounded-full w-[9rem] h-[9rem]',
percentage === 100 ? 'bg-light-blue-1 complete' : 'bg-etc-pink',
)}
style={{ '--percentage': percentage }}
>
<div className="absolute rounded-full bg-white text-light-blue-6 font-bold text-xl w-[6rem] h-[6rem] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex justify-center items-center">
<div
className={cn(
'absolute rounded-full bg-white font-bold text-xl w-[6rem] h-[6rem] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex justify-center items-center',
percentage === 100 ? 'text-light-blue-6' : 'text-etc-red',
)}
>
{filterdPercentage}%
</div>
</div>
Expand Down
Loading

0 comments on commit c01b239

Please sign in to comment.