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
43 changes: 30 additions & 13 deletions src/entities/user/ui/user-profile/ProjectTabPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box } from "@mui/material";
import { Box, styled } from "@mui/material";
import type { JSX } from "react";

import type { ProjectListRes } from "@shared/types/project";
Expand All @@ -9,28 +9,45 @@ import EmptyProjectCard from "./EmptyProjectCard";
interface ProjectTabPanelProps {
projects: ProjectListRes[];
emptyMessage: string;
editMode?: boolean;
selectedIds?: string[];
onSelectProject?: (id: string) => void;
}

const ProjectTabPanel = ({
projects,
emptyMessage,
editMode = false,
selectedIds = [],
onSelectProject,
}: ProjectTabPanelProps): JSX.Element =>
projects && projects.length > 0 ? (
<Box
display="grid"
gridTemplateColumns={{
xs: "1fr",
sm: "repeat(2, 1fr)",
md: "repeat(3, 1fr)",
}}
gap={2}
>
{projects.slice(0, 3).map((project) => (
<ProjectCard key={project.id} project={project} simple />
<StyledGridContainer>
{projects.map((project) => (
<ProjectCard
key={project.id}
project={project}
simple
editMode={editMode}
selected={selectedIds.includes(project.id)}
onSelect={() => onSelectProject && onSelectProject(project.id)}
/>
))}
</Box>
</StyledGridContainer>
) : (
<EmptyProjectCard message={emptyMessage} />
);

export default ProjectTabPanel;

const StyledGridContainer = styled(Box)(({ theme }) => ({
display: "grid",
gridTemplateColumns: "1fr",
gap: theme.spacing(2),
[theme.breakpoints.up("sm")]: {
gridTemplateColumns: "repeat(2, 1fr)",
},
[theme.breakpoints.up("md")]: {
gridTemplateColumns: "repeat(3, 1fr)",
},
}));
27 changes: 14 additions & 13 deletions src/entities/user/ui/user-profile/TapWithBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Badge } from "@mui/material";
import type { JSX } from "react";
import { Badge, styled } from "@mui/material";
import type { JSX, ComponentType } from "react";

interface TabWithBadgeProps {
label: string;
count: number;
active: boolean;
onClick: () => void;
ProfileTabChip: any;
ProfileTabChip: ComponentType<any>;
}

const TabWithBadge = ({
Expand All @@ -16,22 +16,23 @@ const TabWithBadge = ({
onClick,
ProfileTabChip,
}: TabWithBadgeProps): JSX.Element => (
<Badge
<StyledBadge
badgeContent={count}
color={active ? "primary" : "secondary"}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
overlap="rectangular"
sx={{
"& .MuiBadge-badge": {
fontSize: "1.1rem",
fontWeight: 700,
minWidth: 24,
height: 24,
},
}}
>
<ProfileTabChip label={label} active={active} clickable onClick={onClick} />
</Badge>
</StyledBadge>
);

export default TabWithBadge;

const StyledBadge = styled(Badge)((_theme) => ({
"& .MuiBadge-badge": {
fontSize: "1.1rem",
fontWeight: 700,
minWidth: 24,
height: 24,
},
}));
73 changes: 49 additions & 24 deletions src/entities/user/ui/user-profile/UserProfileCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import {
Divider,
} from "@mui/material";
import { styled as muiStyled } from "@mui/material/styles";
import type { JSX } from "react";
import type { ComponentType, JSX } from "react";

import { useProjectStore } from "@shared/stores/projectStore";
import type { User } from "@shared/types/user";

import TabWithBadge from "./TapWithBadge";

// Chip 컴포넌트는 상위에서 import해서 prop으로 넘겨야 함

interface UserProfileCardProps {
userProfile: any;
userProfile: User;
PROFILE_TABS: { label: string; color: string }[];
likeProjects: any[];
appliedProjects: any[];
tab: number;
setTab: (idx: number) => void;
ProfileTabChip: any;
ProfileTabChip: ComponentType<any>;
}

const userRoleMap: Record<string, string> = {
Expand All @@ -33,48 +34,51 @@ const userRoleMap: Record<string, string> = {
pm: "PM",
};
const experienceMap: Record<string, string> = {
junior: "주니어 (3년 이하)",
mid: "미들 (3년 이상 10년 이하)",
senior: "시니어 (10년 이상)",
junior: "주니어 (3년 이하) 🌱",
mid: "미들 (3년 이상 10년 이하) 🌿",
senior: "시니어 (10년 이상) 🌳",
};

const UserProfileCard = ({
userProfile,
PROFILE_TABS,
likeProjects,
appliedProjects,
tab,
setTab,
ProfileTabChip,
}: UserProfileCardProps): JSX.Element => {
const { likeProjects, appliedProjects } = useProjectStore();

return (
<ProfileCard>
<ProfileCardContent>
<ProfileCardHeader>
<IconButton size="small" aria-label="프로필 수정">
<SettingsIcon />
<IconButton size="large" aria-label="프로필 수정">
<SettingsIcon sx={{ fontSize: "2rem" }} />
</IconButton>
</ProfileCardHeader>
<ProfileMainRow>
<ProfileAvatar src={userProfile.avatar} />
<ProfileInfoCol>
<Typography variant="h5" fontWeight={700}>
<Typography
variant="h2"
fontWeight={700}
sx={{ paddingBottom: "1.5rem" }}
>
{userProfile.name}
</Typography>
<Typography>
<Typography variant="body1">
{userRoleMap[userProfile.userRole] || userProfile.userRole}
</Typography>
<Typography>
<Typography variant="body2">
{experienceMap[userProfile.experience] || userProfile.experience}
</Typography>
</ProfileInfoCol>
</ProfileMainRow>
<Box mt={2} width="100%">
<Box mt={2} width="100%" padding="0 1rem">
<Typography>{userProfile.introduceMyself}</Typography>
</Box>
<Divider sx={{ my: 2 }} />
<ProfileEmail>{userProfile.email}</ProfileEmail>
<Box mt={2} display="flex" gap={1} justifyContent="center">
<TabBadgeContainer>
{PROFILE_TABS.map((tabInfo, idx) => (
<TabWithBadge
key={tabInfo.label}
Expand All @@ -89,7 +93,8 @@ const UserProfileCard = ({
ProfileTabChip={ProfileTabChip}
/>
))}
</Box>
</TabBadgeContainer>
<ProfileEmail>💌 • {userProfile.email}</ProfileEmail>
</ProfileCardContent>
</ProfileCard>
);
Expand All @@ -100,15 +105,18 @@ export default UserProfileCard;
// 스타일 컴포넌트 재사용
const ProfileCard = muiStyled(Card)(({ theme }) => ({
minWidth: 280,
maxWidth: 320,
maxWidth: "100%",
borderRadius: 12,
boxShadow: theme.shadows[2],
position: "relative",
padding: 0,
padding: "0 2rem",
maxHeight: "350px",
overflow: "auto",
}));
const ProfileCardContent = muiStyled(CardContent)(({ theme }) => ({
padding: theme.spacing(3),
paddingBottom: "16px",
position: "relative",
"&:last-child": {
paddingBottom: "16px",
},
Expand All @@ -119,12 +127,18 @@ const ProfileCardHeader = muiStyled(Box)({
justifyContent: "flex-end",
marginBottom: 8,
minHeight: 32,
position: "absolute",
top: 10,
right: -10,
});
const ProfileMainRow = muiStyled(Box)({
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 16,
justifyContent: "space-between",
flexDirection: "row-reverse",
marginTop: "3rem",
padding: "0 1rem",
});
const ProfileAvatar = muiStyled(Avatar)({
width: 100,
Expand All @@ -134,12 +148,23 @@ const ProfileAvatar = muiStyled(Avatar)({
const ProfileInfoCol = muiStyled(Box)({
display: "flex",
flexDirection: "column",
gap: 4,
gap: 2,
});
const ProfileEmail = muiStyled(Typography)(({ theme }) => ({
color: theme.palette.text.disabled,
fontSize: "0.95rem",
fontSize: "1.1rem",
fontStyle: "italic",
fontWeight: 400,
marginTop: theme.spacing(1),
textAlign: "end",
}));

const TabBadgeContainer = muiStyled(Box)(({ theme }) => ({
marginTop: theme.spacing(2),
gap: theme.spacing(1),
justifyContent: "center",
display: "none",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
Loading