-
Notifications
You must be signed in to change notification settings - Fork 4
[Feat] 이용약관 및 개인정보처리방침 모달, 페이지 구현 #162
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
Changes from 11 commits
d7e19f3
2b90d5a
235820f
4a403e8
2a2fb47
405f84b
7ba628d
4ad1dda
4e6ad76
aa3002f
3eb508f
421908a
dd9b4f9
2ff1fc6
06667ad
0f6e5dd
4658aea
6919883
b246e98
6649060
7b0c65a
ad92565
7d20951
c563804
9a94bbd
e50c4b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| import { style } from "@vanilla-extract/css"; | ||
| import { recipe } from "@vanilla-extract/recipes"; | ||
|
|
||
| import { themeVars } from "@/app/styles"; | ||
|
|
||
| export const wrapper = style({ | ||
| width: "40rem", | ||
| paddingBottom: "1.6rem", | ||
| }); | ||
|
|
||
| export const modalHeader = style({ | ||
| width: "100%", | ||
| position: "relative", | ||
| display: "flex", | ||
| justifyContent: "center", | ||
| alignItems: "center", | ||
| padding: "1.6rem", | ||
| borderBottom: `1px solid ${themeVars.color.normal}`, | ||
| ...themeVars.fontStyles.hline_b_18, | ||
| }); | ||
|
|
||
| export const buttonWrapper = style({ | ||
| position: "absolute", | ||
| right: "1.6rem", | ||
| width: "2.4rem", | ||
| height: "2.4rem", | ||
| }); | ||
|
|
||
| export const modalContent = style({ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| gap: "2.4rem", | ||
| textAlign: "left", | ||
| height: "28rem", | ||
| padding: "2rem 3rem", | ||
| marginBottom: "2rem", | ||
| overflowY: "auto", | ||
|
|
||
| selectors: { | ||
| "&::-webkit-scrollbar": { | ||
| width: "1.2rem", | ||
| }, | ||
| "&::-webkit-scrollbar-thumb": { | ||
| backgroundColor: themeVars.color.gray300, | ||
| height: "5rem", | ||
| borderRadius: "100px", | ||
| backgroundClip: "padding-box", | ||
| border: `4px solid transparent`, | ||
| }, | ||
| "&::-webkit-scrollbar-track": { | ||
| backgroundColor: "transparent", | ||
| margin: "0.8rem 1.2rem", | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| export const title = style({ | ||
| color: themeVars.color.gray800, | ||
| ...themeVars.fontStyles.body_b_14, | ||
| fontWeight: 600, | ||
| marginBottom: "0.8rem", | ||
| }); | ||
|
|
||
| export const subTitle = style({ | ||
| color: themeVars.color.gray800, | ||
| ...themeVars.fontStyles.cap_m_12, | ||
| fontWeight: 500, | ||
| }); | ||
|
|
||
| export const content = style({ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| color: themeVars.color.gray500, | ||
| ...themeVars.fontStyles.cap_m_12, | ||
| fontWeight: 500, | ||
| whiteSpace: "pre-wrap", | ||
| }); | ||
|
|
||
| export const textStyle = recipe({ | ||
| base: { | ||
| color: themeVars.color.gray800, | ||
| }, | ||
| variants: { | ||
| type: { | ||
| title1: { | ||
| ...themeVars.fontStyles.body_b_16, | ||
| fontWeight: 700, | ||
| }, | ||
| title2: { | ||
| ...themeVars.fontStyles.body_b_14, | ||
| }, | ||
| title3: { | ||
| ...themeVars.fontStyles.body_r_14, | ||
| fontWeight: 400, | ||
| }, | ||
| }, | ||
| }, | ||
| defaultVariants: { | ||
| type: "title1", | ||
| }, | ||
| }); | ||
|
|
||
| export const flexColumn = recipe({ | ||
| base: { | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| }, | ||
| variants: { | ||
| gap: { | ||
| 8: { gap: "0.8rem" }, | ||
| 16: { gap: "1.6rem" }, | ||
| 24: { gap: "2.4rem" }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| export const tableWrapper = style({ | ||
| width: "100%", | ||
| overflowX: "auto", | ||
| selectors: { | ||
| "&::-webkit-scrollbar": { | ||
| display: "none", | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| export const table = style({ | ||
| width: "max-content", | ||
| minWidth: "100%", | ||
| borderCollapse: "collapse", | ||
| }); | ||
|
|
||
| export const tCell = style({ | ||
| minWidth: "10rem", | ||
| maxWidth: "25rem", | ||
| padding: "0.8rem", | ||
| border: `1px solid ${themeVars.color.gray200}`, | ||
| fontWeight: 400, | ||
| verticalAlign: "top", | ||
| wordBreak: "keep-all", | ||
| }); | ||
|
|
||
| export const thead = style({ | ||
| backgroundColor: themeVars.color.gray100, | ||
| }); | ||
|
|
||
| export const th = style({ | ||
| whiteSpace: "nowrap", | ||
| padding: "1rem 0.8rem", | ||
| }); | ||
|
|
||
| export const tableText = style({ | ||
| fontWeight: 400, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { Button, Modal } from "@/shared/ui"; | ||
|
|
||
| import * as styles from "./policy-modal.css"; | ||
| import { PrivacyPolicyContent } from "./privacy-policy-content"; | ||
| import { UsePolicyContent } from "./use-policy-content"; | ||
|
|
||
| interface PolicyModalProps { | ||
| type: "USE" | "PRIVACY"; | ||
| onClose: () => void; | ||
| } | ||
|
|
||
| export const PolicyModal = ({ type, onClose }: PolicyModalProps) => { | ||
| return ( | ||
| <div className={styles.wrapper}> | ||
| <div className={styles.modalHeader}> | ||
| <h2>{type === "USE" ? "이용약관" : "개인정보처리방침"}</h2> | ||
| <div className={styles.buttonWrapper}> | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <Modal.XButton /> | ||
| </div> | ||
| </div> | ||
| <Modal.Content> | ||
| <div className={styles.modalContent}> | ||
| {type === "USE" ? <UsePolicyContent /> : <PrivacyPolicyContent />} | ||
|
||
| </div> | ||
| </Modal.Content> | ||
| <Modal.Buttons> | ||
| <Button variant="primary" size="full" onClick={onClose}> | ||
| 확인 | ||
| </Button> | ||
| </Modal.Buttons> | ||
| </div> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { | ||
| TERMS_OF_PRIVACY_INFO, | ||
| type Article, | ||
| type Section, | ||
| } from "@/shared/config"; | ||
|
|
||
| import * as styles from "./policy-modal.css"; | ||
|
|
||
| export const PrivacyPolicyContent = () => { | ||
| return ( | ||
| <div className={`${styles.content} ${styles.flexColumn({ gap: 24 })}`}> | ||
| {/** 개인정보처리방침 타이틀 */} | ||
| <div className={styles.flexColumn({ gap: 16 })}> | ||
| <div> | ||
| <h3 className={styles.textStyle()}>{TERMS_OF_PRIVACY_INFO.title}</h3> | ||
| <p>{TERMS_OF_PRIVACY_INFO.date}</p> | ||
| </div> | ||
| <p>{TERMS_OF_PRIVACY_INFO.description}</p> | ||
| </div> | ||
| {/** 조항 리스트 (ex. 1. 개인정보의 수집 및 이용) */} | ||
| {TERMS_OF_PRIVACY_INFO.sections.map((section: Section) => ( | ||
| <section key={section.title} className={styles.flexColumn({ gap: 8 })}> | ||
| {/* 조항 타이틀 및 설명 */} | ||
| <div> | ||
| <h4 className={styles.textStyle({ type: "title2" })}> | ||
| {section.title} | ||
| </h4> | ||
| <p>{section.description}</p> | ||
| </div> | ||
| {/** 조항 상세설명 */} | ||
| <div className={styles.flexColumn({ gap: 16 })}> | ||
| {section.articles?.map((article: Article) => ( | ||
| <article | ||
| key={article.title} | ||
| className={styles.flexColumn({ gap: 8 })} | ||
| > | ||
| {/** 조항 상세설명의 타이틀 (ex. 가. 회원가입 및 계정 관리) */} | ||
| {article.title && ( | ||
| <p className={styles.textStyle({ type: "title3" })}> | ||
| {article.title} | ||
| </p> | ||
| )} | ||
| <div> | ||
| {article.content && ( | ||
| <div> | ||
| {/* 줄글 형태의 컨텐츠 */} | ||
| {"text" in article.content && ( | ||
| <div>{article.content.text}</div> | ||
| )} | ||
| {/* 테이블 형태의 컨텐츠 */} | ||
| {"table" in article.content && ( | ||
| <div className={`${styles.tableWrapper}`}> | ||
| <table className={`${styles.table}`}> | ||
| <thead className={styles.thead}> | ||
| <tr> | ||
| {article.content.table.header.map( | ||
| (th, thIdx) => ( | ||
| <th | ||
| key={thIdx} | ||
| className={`${styles.tCell} ${styles.th}`} | ||
| > | ||
| {th} | ||
| </th> | ||
| ) | ||
| )} | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {article.content.table.rows.map((row, rowIdx) => ( | ||
| <tr key={rowIdx}> | ||
| {row.map((td, tdIdx) => ( | ||
| <td key={tdIdx} className={styles.tCell}> | ||
| {td} | ||
| </td> | ||
| ))} | ||
| </tr> | ||
| ))} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| )} | ||
| </div> | ||
| )} | ||
| </div> | ||
| </article> | ||
| ))} | ||
| </div> | ||
| {/** 조항 추가사항 */} | ||
| {section.alert && <div>{section.alert}</div>} | ||
| </section> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { TERMS_OF_USE, type Chapters, type Chapter } from "@/shared/config"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as styles from "./policy-modal.css"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const UsePolicyContent = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return TERMS_OF_USE.map((policy: Chapters) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <section key={policy.chapterTitle}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h2 className={styles.title}>{policy.chapterTitle}</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {policy.chapter.map((chapter: Chapter, idx) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <article key={idx}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {chapter.title && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h3 className={styles.subTitle}>{chapter.title}</h3> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.content}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {chapter.contents.map((content) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>{content}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </article> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 컴포넌트 구조가 잘 구현되어 있어요! 시맨틱 HTML 태그( 한 가지 작은 개선 포인트: Line 11과 Line 17에서 ♻️ 변수명 구분 제안- {policy.chapter.map((chapter: Chapter, idx) => (
- <article key={idx}>
+ {policy.chapter.map((chapter: Chapter, chapterIdx) => (
+ <article key={chapterIdx}>
{chapter.title && (
<h3 className={styles.subTitle}>{chapter.title}</h3>
)}
<div className={styles.content}>
- {chapter.contents.map((content, idx) => (
- <p key={idx}>{content}</p>
+ {chapter.contents.map((content, contentIdx) => (
+ <p key={contentIdx}>{content}</p>
))}
</div>
</article>
))}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
fontStyles와 개별
fontWeight를 혼용하고 있습니다.themeVars.fontStyles를 이미 사용하고 있는데fontWeight를 별도로 덮어쓰는 패턴(Line 60, 67, 75, 87, 94, 153)이 반복됩니다. 해당 값들은 토큰으로 통일해 주세요.As per coding guidelines "
src/**/*.css.ts: font 스타일은 themeVars.fontStyles만 사용 ... fontSize / lineHeight / fontWeight를 개별 속성으로 작성하지 않음".Also applies to: 79-101, 152-154
🤖 Prompt for AI Agents