Skip to content

Commit

Permalink
feat: 工作心得列表卡片修改 & 工作心得單篇頁面改版 (#1354)
Browse files Browse the repository at this point in the history
  • Loading branch information
YongChenSu authored Jul 4, 2024
1 parent e77d777 commit 6a97125
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Bad from 'common/icons/Bad';
import styles from './WorkExperiences.module.css';
import { formatSalary, formatSalaryRange } from 'common/formatter';
import { formatCreatedAt, formatWeekWorkTime } from './helper';
import OverallRating from 'common/OverallRating';

const createLinkTo = ({ pageType, id }) => ({
pathname: `/experiences/${id}`,
Expand All @@ -20,6 +21,8 @@ const createLinkTo = ({ pageType, id }) => ({

const SNIPPET_SIZE = 30;

const averageSectionRating = 3.8; // for temporary use

const ExperienceEntry = ({
pageType,
data: {
Expand Down Expand Up @@ -68,10 +71,16 @@ const ExperienceEntry = ({
)}
</div>
)}
<div className={styles.recommendToOthers}>
{recommendToOthers === 'yes' ? <Good /> : <Bad />}
{recommendToOthers === 'yes' ? '推' : '不推'}
</div>
{averageSectionRating !== null ? (
<div className={styles.overallRatingWrapper}>
<OverallRating rating={averageSectionRating} />
</div>
) : (
<div className={styles.recommendToOthers}>
{recommendToOthers === 'yes' ? <Good /> : <Bad />}
{recommendToOthers === 'yes' ? '推' : '不推'}
</div>
)}
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,7 @@
}
}
}

.overallRatingWrapper {
margin-left: 1.5rem;
}
21 changes: 6 additions & 15 deletions src/components/ExperienceDetail/Article/ArticleInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import PropTypes from 'prop-types';
import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import faLock from '@fortawesome/fontawesome-free-solid/faLock';
import { formatSalary, formatSalaryRange } from 'common/formatter';
import Good from 'common/icons/Good';
import Bad from 'common/icons/Bad';
import styles from './Article.module.css';
import InfoBlock from './InfoBlock';
import RateButtons from './RateButtons';
Expand All @@ -13,6 +11,7 @@ import {
generatePageURL,
} from 'constants/companyJobTitle';
import { originalCompanyNameSelector } from '../experienceSelector';
import RatingInfo from './RatingInfo';

const formatDate = date => `${date.getFullYear()}${date.getMonth() + 1} 月`;
const formatExperienceInYear = year => {
Expand Down Expand Up @@ -124,6 +123,7 @@ InterviewInfoBlocks.propTypes = {

const WorkInfoBlocks = ({ experience, hideContent }) => {
const expInYearText = formatExperienceInYear(experience.experience_in_year);
const averageSectionRating = 3.62; // for temporary use
return (
<Fragment>
<InfoBlock
Expand Down Expand Up @@ -166,19 +166,10 @@ const WorkInfoBlocks = ({ experience, hideContent }) => {
)}
</InfoBlock>
) : null}
{experience.recommend_to_others ? (
<InfoBlock label="是否推薦此工作">
{experience.recommend_to_others === 'yes' ? (
<div className={styles.recommendIcon}>
<Good />
</div>
) : (
<div className={styles.recommendIcon}>
<Bad /> 不推
</div>
)}
</InfoBlock>
) : null}
<RatingInfo
rating={averageSectionRating}
recommend={experience.recommend_to_others}
/>
</Fragment>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.block {
display: flex;
flex-wrap: wrap;
align-items: center;
&:not(:last-child) {
margin-bottom: 20px;
}
Expand Down
52 changes: 52 additions & 0 deletions src/components/ExperienceDetail/Article/RatingInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import Good from 'common/icons/Good';
import Bad from 'common/icons/Bad';
import styles from './Article.module.css';
import InfoBlock from './InfoBlock';
import OverallRating from 'common/OverallRating';
import PropTypes from 'prop-types';

const RecommendationIcon = ({ recommend }) => (
<div className={styles.recommendIcon}>
{recommend === 'yes' ? (
<>
<Good />
</>
) : (
<>
<Bad /> 不推
</>
)}
</div>
);

RecommendationIcon.propTypes = {
recommend: PropTypes.string.isRequired,
};

const RatingInfo = ({ rating, recommend }) => {
if (rating > 0) {
return (
<InfoBlock label="整體評價">
<OverallRating rating={rating} hasRatingLabel />
</InfoBlock>
);
}

if (recommend) {
return (
<InfoBlock label="是否推薦此工作">
<RecommendationIcon recommend={recommend} />
</InfoBlock>
);
}

return null;
};

RatingInfo.propTypes = {
rating: PropTypes.number,
recommend: PropTypes.string,
};

export default RatingInfo;
34 changes: 34 additions & 0 deletions src/components/common/OverallRating/Rating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import styles from './Rating.module.css';
import cn from 'classnames';
import { overallRatingDialogMap } from 'components/ShareExperience/common/optionMap';
import PropTypes from 'prop-types';

const Rating = ({ rating, textYellow }) => (
<div
className={cn(styles.rating, {
[styles.textYellow]: !textYellow,
})}
>
{rating}
</div>
);

Rating.propTypes = {
rating: PropTypes.number.isRequired,
textYellow: PropTypes.bool,
};

const RatingLabel = ({ rating }) => {
return (
<div className={styles.ratingLabel}>
{overallRatingDialogMap[Math.floor(rating)]}
</div>
);
};

RatingLabel.propTypes = {
rating: PropTypes.number.isRequired,
};

export { Rating, RatingLabel };
60 changes: 60 additions & 0 deletions src/components/common/OverallRating/Rating.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@value main-yellow, yellow-light from "../../common/variables.module.css";

.overallRating {
display: flex;
align-items: center;
flex-wrap: nowrap;
}

.ratingInfo {
display: flex;
}

.rating {
margin: 0 1rem 0 0;
}

.textYellow {
color: main-yellow;
}

.ratingLabel {
margin-left: 10px;
display: inline-block;
padding: 4px 16px;
background-color: yellow-light;
border: 1px solid main-yellow;
border-radius: 5px;
position: relative;
}

.ratingLabel::before {
content: '';
position: absolute;
left: -16px;
top: 50%;
transform: translateY(-50%);
border: 8px solid transparent;
border-right-color: main-yellow;
}

.ratingLabel::after {
content: '';
position: absolute;
left: -14px;
top: 50%;
transform: translateY(-50%);
border: 8px solid transparent;
border-right-color: yellow-light;
}

@media (max-width: 430px) {
.overallRating {
flex-direction: column;
align-items: flex-start;
}

.rating {
margin: 0 0 2px 0;
}
}
72 changes: 72 additions & 0 deletions src/components/common/OverallRating/Thumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import ThumbImage from './thumb.svg';
import cn from 'classnames';
import styles from './Thumbs.module.css';
import PropTypes from 'prop-types';

const GrayThumb = () => {
return (
<img
src={ThumbImage}
alt="grayThumb"
className={cn(styles.thumb, styles.grayThumb)}
/>
);
};

const YellowThumb = ({ style }) => {
return (
<img
src={ThumbImage}
alt="yellowThumb"
style={style}
className={cn(styles.thumb, styles.yellowThumb)}
/>
);
};

YellowThumb.propTypes = {
style: PropTypes.object,
};

const calculateClipX = ({ rating, order }) => {
const fraction = Math.round((rating % 1) * 100);
if (order <= rating) return 100;
if (rating + 1 > order) return fraction;
return 0;
};

const RatingThumb = ({ rating, order }) => {
const clipX = calculateClipX({ rating, order });
return (
<div className={styles.thumbContainer}>
<GrayThumb />
<YellowThumb style={{ '--clip-x': `${clipX}%` }} />
</div>
);
};

RatingThumb.propTypes = {
order: PropTypes.number.isRequired,
rating: PropTypes.number.isRequired,
};

const Thumbs = ({ rating, maxRating }) => {
const thumbs = Array.from({ length: maxRating }, (_, i) => i + 1);
const renderThumbs = thumbs.map(order => (
<RatingThumb key={order} rating={rating} order={order} />
));

return <div className={styles.thumbsContainer}>{renderThumbs}</div>;
};

Thumbs.propTypes = {
maxRating: PropTypes.number.isRequired,
rating: PropTypes.number.isRequired,
};

Thumbs.defaultProps = {
maxRating: 5,
};

export default Thumbs;
36 changes: 36 additions & 0 deletions src/components/common/OverallRating/Thumbs.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.thumbsContainer {
display: flex;
align-items: center;
width: 9rem;
justify-content: space-between;
margin-right: 1.5rem;
}

.thumb {
width: 18px;
height: 28px;
}

.thumbContainer {
position: relative;
}

.grayThumb {
filter: grayscale(100%) brightness(0.95);
}

.yellowThumb {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip-path: polygon(0 0, var(--clip-x) 0, var(--clip-x) 100%, 0 100%);
}

@media (max-width: 430px) {
.thumbsContainer {
width: 8rem;
margin-right: 0.5rem;
}
}
29 changes: 29 additions & 0 deletions src/components/common/OverallRating/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import Thumbs from './Thumbs';
import { Rating, RatingLabel } from './Rating';
import styles from './Rating.module.css';

const OverallRating = ({ rating, hasRatingLabel }) => {
return (
<div className={cn(styles.overallRating)}>
<Rating rating={rating} textYellow={hasRatingLabel} />
<div className={styles.ratingInfo}>
<Thumbs rating={rating} />
{hasRatingLabel && <RatingLabel rating={rating} />}
</div>
</div>
);
};

OverallRating.propTypes = {
hasRatingLabel: PropTypes.bool,
rating: PropTypes.number.isRequired,
};

OverallRating.defaultProps = {
hasRatingLabel: false,
};

export default OverallRating;
Loading

0 comments on commit 6a97125

Please sign in to comment.