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
32 changes: 26 additions & 6 deletions docs/guides/CODING_CONVENTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,27 @@

---

## 2. 네이밍 규칙
## 2. SCSS import 규칙

SCSS 파일에서 `styles/abstracts`를 가져올 때는 반드시 `@/` 절대경로를 사용한다.

```scss
/* 올바른 예 */
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;
@use '@/styles/abstracts/mixins' as m;

/* 금지 */
@use '../../styles/abstracts/variables' as v;
@use '../../../../../styles/abstracts/functions' as fn;
```

상대경로(`../`)로 작성하지 않는다.
파일 이동 시 경로가 깨지는 것을 방지하고 일관성을 유지하기 위함이다.

---

## 3. 네이밍 규칙

| 대상 | 규칙 | 예시 |
|------|------|------|
Expand All @@ -29,7 +49,7 @@

---

## 3. 상태 관리 기준
## 4. 상태 관리 기준

**Zustand** — UI 상태만 담당

Expand All @@ -49,7 +69,7 @@

---

## 4. 폴더 사용 기준
## 5. 폴더 사용 기준

```txt
src/domains/* 도메인별 비즈니스 로직, API client, hooks, constants
Expand All @@ -62,7 +82,7 @@ src/styles 전역 SCSS 변수, mixin, base

---

## 5. 금지 구조
## 6. 금지 구조

```txt
src/features
Expand All @@ -77,7 +97,7 @@ src/app/(page)/*/*.store.ts

---

## 6. 보안 주의사항
## 7. 보안 주의사항

```txt
console.log에 사건 원문 / 개인정보 출력 금지
Expand All @@ -88,7 +108,7 @@ API 응답에 불필요한 민감 정보 포함 금지

---

## 7. MUI 사용 기준
## 8. MUI 사용 기준

MUI는 아래 허용 범위 내에서만 사용한다.

Expand Down
109 changes: 109 additions & 0 deletions src/app/(page)/disputes/[id]/statement/StatementPage.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;
@use '@/styles/abstracts/mixins' as m;

.page {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: var(--bg-page);
}

.content {
flex: 1;
padding: fn.r(16) fn.r(20) fn.r(100);
display: flex;
flex-direction: column;
gap: fn.r(16);
}

.section {
display: flex;
flex-direction: column;
gap: fn.r(16);
}

.label {
@include m.text-body-s;
color: v.$color-black;
margin: 0;
Comment thread
evenif99 marked this conversation as resolved.
}

.categories {
display: flex;
flex-direction: row;
}

.categoryItem {
display: flex;
flex-direction: column;
align-items: center;
gap: fn.r(4);
width: fn.r(44);
}

.categoryIcon {
display: flex;
align-items: center;
justify-content: center;
width: fn.r(44);
height: fn.r(44);
border-radius: fn.r(8);
background-color: var(--bg-surface);
border: 1px solid var(--border-brand);
}

.categoryLabel {
@include m.text-caption;
font-weight: v.$font-weight-bold;
color: var(--text-primary);
white-space: nowrap;
}

.statementGroup {
display: flex;
flex-direction: column;
gap: fn.r(8);
}

.textarea {
min-height: fn.r(350);
}

.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: fn.r(40) fn.r(20);
background-color: var(--bg-page);
}

.modalOverlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
padding: fn.r(20);
}

.modal {
background-color: var(--bg-page);
border-radius: fn.r(16);
padding: fn.r(24) fn.r(20);
width: 100%;
max-width: fn.r(320);
display: flex;
flex-direction: column;
gap: fn.r(20);
}

.modalText {
@include m.text-body-m;
color: var(--text-primary);
margin: 0;
text-align: center;
}
110 changes: 110 additions & 0 deletions src/app/(page)/disputes/[id]/statement/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client'

import React from 'react'
import { useRouter } from 'next/navigation'
import Header from '@/components/layout/Header'
import Button from '@/components/ui/Button'
import Select from '@/components/ui/Select'
import Textarea from '@/components/ui/Textarea'
import { CATEGORY_ICON_MAP, CATEGORY_LABEL_MAP } from '@/components/ui/CategoryIcon'
import type { CategoryGroup } from '@/types/common'
import styles from './StatementPage.module.scss'

const VALID_CATEGORIES: CategoryGroup[] = ['romance', 'work', 'friend', 'family']

const MBTI_OPTIONS = [
'INTJ', 'INTP', 'ENTJ', 'ENTP',
'INFJ', 'INFP', 'ENFJ', 'ENFP',
'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ',
'ISTP', 'ISFP', 'ESTP', 'ESFP',
].map((v) => ({ value: v, label: v }))

export default function StatementPage({
params,
searchParams,
}: {
params: Promise<{ id: string }>
searchParams: Promise<{ category?: string }>
}) {
const { id } = React.use(params)
const { category: rawCategory } = React.use(searchParams)
const router = useRouter()

// TODO: 이전 페이지 카테고리 데이터 연동 후 null 처리로 교체
const category: CategoryGroup = VALID_CATEGORIES.includes(rawCategory as CategoryGroup)
? (rawCategory as CategoryGroup)
: 'romance'
Comment thread
evenif99 marked this conversation as resolved.

const [mbti, setMbti] = React.useState('')
const [content, setContent] = React.useState('')

const handleSave = () => {
// TODO: API 연결 — 진술 저장 후 사건 상세 페이지로 이동
router.push(`/disputes/${id}`)
}

if (!category) {
return (
<div className={styles.page}>
<Header title="사건작성" onBack={() => router.back()} />
<div className={styles.modalOverlay}>
<div className={styles.modal}>
<p className={styles.modalText}>카테고리를 선택해주세요</p>
<Button onClick={() => router.back()}>확인</Button>
</div>
</div>
</div>
)
}

const Icon = CATEGORY_ICON_MAP[category]

return (
<div className={styles.page}>
<Header title="사건작성" onBack={() => router.back()} />

<div className={styles.content}>
{/* 사건 카테고리 — 이전 페이지에서 선택된 카테고리만 표시 */}
<section className={styles.section}>
<p className={styles.label}>사건 카테고리</p>
<div className={styles.categories}>
<div className={styles.categoryItem}>
<div className={styles.categoryIcon}>
<Icon sx={{ fontSize: 20 }} />
</div>
<span className={styles.categoryLabel}>
{CATEGORY_LABEL_MAP[category]}
</span>
</div>
</div>
</section>

{/* 진술서 */}
<section className={styles.section}>
<p className={styles.label}>작성자님의 진술서</p>
<div className={styles.statementGroup}>
<Select
value={mbti}
onChange={(e) => setMbti(e.target.value)}
options={MBTI_OPTIONS}
placeholder="작성자님의 MBTI 선택 해주세요"
/>
<Textarea
value={content}
onChange={(e) => setContent(e.target.value)}
maxLength={1000}
placeholder={"사건내용을 작성해주세요.\n욕설의 경우 가리기 처리 될 수 있어요"}
className={styles.textarea}
/>
</div>
</section>
</div>

<div className={styles.footer}>
<Button onClick={handleSave} disabled={!content.trim()}>
진술저장
</Button>
</div>
</div>
)
}
7 changes: 3 additions & 4 deletions src/app/api/disputes/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export async function GET(request: NextRequest) {
}

// POST /api/disputes
// 사건 생성 (1:1 전환). 방이 one_to_one 상태일 때만 생성 가능. 생성자는 role_a로 확정
// 사건 생성. 활성 방(ai_chat, invite_ready, one_to_one)에서 생성 가능. 생성자는 role_a로 확정
export async function POST(request: NextRequest) {
const session = await getServerSession(authOptions)
const userId = getSessionUserId(session)
Expand Down Expand Up @@ -173,14 +173,13 @@ export async function POST(request: NextRequest) {
)
}

// AI 대화방 없이 바로 1:1 사건 생성 금지
if (room.roomMode !== 'ONE_TO_ONE') {
if (room.roomMode === 'CLOSED' || room.roomMode === 'EXPIRED') {
return NextResponse.json<ApiResponse>(
{
success: false,
error: {
code: 'ROOM_NOT_READY',
message: '상대방이 참여한 1:1 방에서만 사건을 생성할 수 있습니다.',
message: '이미 종료되거나 만료된 방입니다.',
details: `현재 방 상태: ${room.roomMode.toLowerCase()}`,
},
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/feedback/Toast.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.toast {
width: calc(100vw - fn.r(32));
Expand Down
4 changes: 2 additions & 2 deletions src/components/layout/BottomNavigation.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.nav {
position: fixed;
Expand Down
4 changes: 2 additions & 2 deletions src/components/layout/Header.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.header {
width: 100%;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/ActionPrompt.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.prompt {
border: 1px dashed var(--border-brand);
Expand Down
6 changes: 3 additions & 3 deletions src/components/ui/Button.module.scss
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: fn.r(52);
padding: 0 fn.r(16);
padding: 0 fn.r(12);
border-radius: fn.r(12);
font-family: v.$font-family-base;
font-size: v.$font-size-body-m;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/CaseCard.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.card {
background-color: var(--bg-surface);
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/CategoryFilter.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/functions' as fn;

.container {
display: flex;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/Input.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '../../styles/abstracts/variables' as v;
@use '../../styles/abstracts/functions' as fn;
@use '@/styles/abstracts/variables' as v;
@use '@/styles/abstracts/functions' as fn;

.field {
display: flex;
Expand Down
Loading