diff --git a/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.module.css b/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.module.css
new file mode 100644
index 0000000..a419c59
--- /dev/null
+++ b/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.module.css
@@ -0,0 +1,44 @@
+.item_container {
+ display: flex;
+ /* justify-content: space-between; */
+ justify-content: center;
+ margin-bottom: 8px;
+}
+.item_input_wrapper {
+ display: flex;
+}
+.input {
+ all: unset;
+ font-size: 12px;
+ padding: 8px;
+ border: 1px solid #CDCDCD;
+ border-radius: 4px;
+}
+.item_name_input {
+ width: 100px;
+ margin-right: 16px;
+}
+.score_container {
+ margin-right: 16px;
+}
+.score_container > span {
+ font-size: 14px;
+ font-weight: bold;
+}
+.item_score_input {
+ width: 40px;
+ margin-right: 4px;
+}
+.delete_btn {
+ padding: 4px 8px;
+ background-color: rgb(248, 64, 64);
+ color: #ffffff;
+ font-size: 12px;
+ border-radius: 8px;
+}
+
+.input::-webkit-outer-spin-button,
+.input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.tsx b/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.tsx
new file mode 100644
index 0000000..1bd9e2b
--- /dev/null
+++ b/app/(route)/verification/ibulsin/_components/ActiveInvestmentItem/index.tsx
@@ -0,0 +1,86 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+'use client'
+
+import React, { useState } from 'react'
+import S from './index.module.css'
+import cn from 'classnames'
+import { useRecoilState } from 'recoil'
+import investmentItemAtom, { ActiveInvestmentItemType } from '@/app/_store/atom'
+
+interface ActiveInvestmentItemProps {
+ item: ActiveInvestmentItemType
+}
+
+function ActiveInvestmentItem({ item }: ActiveInvestmentItemProps) {
+ const [investmentItem, setInvestmentItem] = useRecoilState(investmentItemAtom)
+ const [itemName, setItemName] = useState(item?.name ?? '')
+ const [itemScore, setItemScore] = useState(item?.score ?? 0)
+
+ const handleDeleteItem = () => {
+ if (investmentItem.length <= 1) {
+ return
+ }
+
+ setInvestmentItem((prev) => {
+ const next = prev.filter(
+ (investmentItem) => investmentItem.id !== item.id,
+ )
+ console.log(next)
+
+ return next
+ })
+ }
+
+ const handleChangeNameInput = (e: any) => {
+ const { value } = e.target
+ setItemName(value)
+
+ setInvestmentItem((prev) =>
+ prev.map((investmentItem) =>
+ investmentItem.id === item.id
+ ? { ...item, name: value }
+ : investmentItem,
+ ),
+ )
+ }
+ const handleChangeScoreInput = (e: any) => {
+ const { value } = e.target
+ setItemScore(value)
+
+ setInvestmentItem((prev) =>
+ prev.map((investmentItem) =>
+ investmentItem.id === item.id
+ ? { ...item, name: value }
+ : investmentItem,
+ ),
+ )
+ }
+
+ return (
+
+ )
+}
+
+export default ActiveInvestmentItem
diff --git a/app/(route)/verification/ibulsin/_components/ActiveInvestmentList/index.module.css b/app/(route)/verification/ibulsin/_components/ActiveInvestmentList/index.module.css
new file mode 100644
index 0000000..e69de29
diff --git a/app/(route)/verification/ibulsin/_components/ActiveInvestmentList/index.tsx b/app/(route)/verification/ibulsin/_components/ActiveInvestmentList/index.tsx
new file mode 100644
index 0000000..bc0f42c
--- /dev/null
+++ b/app/(route)/verification/ibulsin/_components/ActiveInvestmentList/index.tsx
@@ -0,0 +1,21 @@
+'use client'
+
+import React from 'react'
+import S from './index.module.css'
+import ActiveInvestmentItem from '../ActiveInvestmentItem'
+import { useRecoilValue } from 'recoil'
+import investmentItemAtom from '@/app/_store/atom'
+
+function ActiveInvestmentList() {
+ const investmentItem = useRecoilValue(investmentItemAtom)
+
+ return (
+
+ {investmentItem.map((item) => {
+ return
+ })}
+
+ )
+}
+
+export default ActiveInvestmentList
diff --git a/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.module.css b/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.module.css
new file mode 100644
index 0000000..21eb28b
--- /dev/null
+++ b/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.module.css
@@ -0,0 +1,8 @@
+.btn {
+ font-size: 12px;
+ background-color: var(--purple-700);
+ color: #FFFFFF;
+ border-radius: 4px;
+ padding: 4px 8px;
+ height: 20px;
+}
diff --git a/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.tsx b/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.tsx
new file mode 100644
index 0000000..861145e
--- /dev/null
+++ b/app/(route)/verification/ibulsin/_components/ItemAddBtn/index.tsx
@@ -0,0 +1,27 @@
+'use client'
+
+import React from 'react'
+import S from './index.module.css'
+import { useSetRecoilState } from 'recoil'
+import investmentItemAtom, { ActiveInvestmentItemType } from '@/app/_store/atom'
+
+function ItemAddBtn() {
+ const setInvestmentItem = useSetRecoilState(investmentItemAtom)
+
+ const handleAddItem = () => {
+ const newItem: ActiveInvestmentItemType = {
+ id: new Date().toISOString(),
+ name: '',
+ score: 0,
+ }
+ setInvestmentItem((prev) => [...prev, newItem])
+ }
+
+ return (
+
+ 추가
+
+ )
+}
+
+export default ItemAddBtn
diff --git a/app/(route)/verification/ibulsin/_components/MoreExplainToggle/index.tsx b/app/(route)/verification/ibulsin/_components/MoreExplainToggle/index.tsx
index d6347c6..d189403 100644
--- a/app/(route)/verification/ibulsin/_components/MoreExplainToggle/index.tsx
+++ b/app/(route)/verification/ibulsin/_components/MoreExplainToggle/index.tsx
@@ -1,39 +1,22 @@
import React from 'react'
import S from './index.module.css'
+import { explainData } from '@/app/_constants/ibulsin'
interface MoreExplainToggleProps {
type: string
}
function MoreExplainToggle({ type }: MoreExplainToggleProps) {
+ const filteredData = explainData.filter(
+ (explainItem) => explainItem.type === type,
+ )
return (
- {type === 'marketResponse' && (
-
- 아이디어: 넷플릭스(초기 DVD 기반 모델)
-
-
- 시장호응가설: 우편 배송 기반의 DVD 대여 서비스를 월정액 요금제, 반납
- 지연 과태료 무료 정책과 결합하여 많은 사람들이 비디오 가게를 이용하는
- 대신 우리 서비스에 가입할 것이다.
-
-
- 이와같이 시장이 우리의 제품이나 서비스를 어떻게 받아들이지에 대해
- 정확한 정의를 내리고, 해당 시장 수요가 존재한다는 증거를 찾아내는 것이
- 시장호응가설이 해야 할 일이에요.
-
- )}
- {type === 'XYZ' && (
-
- X는 표적시장의 몇 퍼센트를 차지할 수 있을지, Y는 어떤 표적시장에 대한,
- Z는 표적 시장이 제품에 대해 어떤 방식으로 어느 범위까지 호응 할 까를
- 의미해요
-
-
- 예제: 적어도 10%의 대기질 지수가 100 이상인 도시에 사는 사람들은
- 120달러 짜리 휴대용 오염 탐지기를 구매할 것이다.
+ {filteredData.map((explainItem, idx) => (
+
+ {explainItem.content}
- )}
+ ))}
)
}
diff --git a/app/(route)/verification/ibulsin/_components/Step3/index.module.css b/app/(route)/verification/ibulsin/_components/Step3/index.module.css
index e69de29..20c4973 100644
--- a/app/(route)/verification/ibulsin/_components/Step3/index.module.css
+++ b/app/(route)/verification/ibulsin/_components/Step3/index.module.css
@@ -0,0 +1,73 @@
+.main_container {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+.flex_between_wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.flex_wrapper {
+ display: flex;
+ align-items: center;
+}
+.step_info {
+ color: #FFFFFF;
+ margin: 24px;
+ margin-bottom: 32px;
+}
+.step_info > h3 {
+ padding: 12px 0;
+ font-weight: 500;
+ font-size: 24px;
+}
+.step_info > p {
+ font-size: 24px;
+}
+.survey_container {
+ background-color: #FFFFFF;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ padding: 12px;
+}
+.input_container {
+ margin-bottom: 8px;
+}
+.input_title_container {
+ display: flex;
+ align-items: center;
+}
+.input_title {
+ font-size: 16px;
+ padding:12px 0;
+ margin-right: 8px;
+}
+.input_explain {
+ font-size: 12px;
+ margin-bottom: 8px;
+}
+.direction_btns {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.direction_btns > a {
+ margin: 0 8px;
+}
+.prev_btn {
+ background-color: #FFFFFF;
+ padding: 12px 20px;
+ font-size: 14px;
+ font-weight: bold;
+ border: 1px solid #DCDCDC;
+ border-radius: 8px;
+}
+.next_btn {
+ background-color: var(--purple-700);
+ color: #FFFFFF;
+ padding: 12px 20px;
+ font-size: 14px;
+ font-weight: bold;
+ border-radius: 8px;
+}
diff --git a/app/(route)/verification/ibulsin/_components/Step3/index.tsx b/app/(route)/verification/ibulsin/_components/Step3/index.tsx
index 26a1f93..8de3ae7 100644
--- a/app/(route)/verification/ibulsin/_components/Step3/index.tsx
+++ b/app/(route)/verification/ibulsin/_components/Step3/index.tsx
@@ -2,9 +2,62 @@
import React from 'react'
import S from './index.module.css'
+import Link from 'next/link'
+import Textarea from '../Textarea'
+import MoreExplainBtn from '../MoreExplainBtn'
+import { ibulsinVariants } from '@/app/_constants/ibulsin'
function Step3() {
- return hello
+ const { xyz, pretotyping } = ibulsinVariants
+
+ return (
+
+
+
STEP 3
+
+ xyz 가설과 적극적 투자지표를
+
+ 세워보아요.
+
+
+
+
+
+
xyz 가설
+
+
+
+ XYZ 법칙으로 표현된 시장 호응 가설의 범위를 잘게 쪼개 xyz법칙으로
+ 표현해보세요.
+
+
+
+
+
+
+ {`xyz가설로 표현된 시장 호응 가설을 빠르게 적은 비용으로 테스트를
+ 시도해보는 것을 '프리토타이핑' 이라구해요. 시제품을 의미하는 프로토타입과는
+ 다르게 시제품이 없어도 다른 방법을 만들어 가설을 확인하는 테스트 방법이에요.
+ `}
+
+
+
+
+
+ 이전 단계
+
+
+ 제출 하기
+
+
+
+
+ )
}
export default Step3
diff --git a/app/(route)/verification/ibulsin/_components/Textarea/index.tsx b/app/(route)/verification/ibulsin/_components/Textarea/index.tsx
index eb6390d..ad2fa2b 100644
--- a/app/(route)/verification/ibulsin/_components/Textarea/index.tsx
+++ b/app/(route)/verification/ibulsin/_components/Textarea/index.tsx
@@ -6,11 +6,10 @@ import cn from 'classnames'
import { useForm } from 'react-hook-form'
interface TextAreaProps {
- fieldKey: 'outline' | 'why' | 'marketResponse' | 'XYZ'
+ fieldKey: 'outline' | 'why' | 'marketResponse' | 'XYZ' | 'xyz' | 'pretotyping'
}
function Textarea({ fieldKey }: TextAreaProps) {
- console.log(fieldKey)
const { register, watch } = useForm()
const outlineField = register('outline', {
@@ -29,6 +28,14 @@ function Textarea({ fieldKey }: TextAreaProps) {
required: '',
maxLength: { value: 500, message: '최대 500자까지 작성가능합니다.' },
})
+ const xyzField = register('xyz', {
+ required: '',
+ maxLength: { value: 500, message: '최대 500자까지 작성가능합니다.' },
+ })
+ const pretotypingField = register('pretotyping', {
+ required: '',
+ maxLength: { value: 500, message: '최대 500자까지 작성가능합니다.' },
+ })
const fields = {
outline: {
@@ -47,6 +54,14 @@ function Textarea({ fieldKey }: TextAreaProps) {
register: XYZField,
MaxLength: 500,
},
+ xyz: {
+ register: xyzField,
+ MaxLength: 500,
+ },
+ pretotyping: {
+ register: pretotypingField,
+ MaxLength: 500,
+ },
}
const countCharacters = () => {
diff --git a/app/_constants/ibulsin.ts b/app/_constants/ibulsin.ts
new file mode 100644
index 0000000..bdc4f65
--- /dev/null
+++ b/app/_constants/ibulsin.ts
@@ -0,0 +1,53 @@
+interface ibulsinVariantsType {
+ outline: 'outline'
+ why: 'why'
+ marketResponse: 'marketResponse'
+ XYZ: 'XYZ'
+ xyz: 'xyz'
+ pretotyping: 'pretotyping'
+}
+interface explainDataType {
+ type: string
+ content: string
+}
+
+export const explainData: explainDataType[] = [
+ {
+ type: 'marketResponse',
+ content: `아이디어: 넷플릭스(초기 DVD 기반 모델)
+
+ 시장호응가설: 우편 배송 기반의 DVD 대여 서비스를 월정액 요금제, 반납 지연 과태료 무료 정책과 결합하여 많은 사람들이 비디오 가게를 이용하는 대신 우리 서비스에 가입할 것이다.
+
+ 이와같이 시장이 우리의 제품이나 서비스를 어떻게 받아들이지에 대해 정확한 정의를 내리고, 해당 시장 수요가 존재한다는 증거를 찾아내는 것이 시장호응가설 단계에서 해야 할 일이에요.`,
+ },
+ {
+ type: 'XYZ',
+ content: `X는 표적시장의 몇 퍼센트를 차지할 수 있을지, Y는 어떤 표적시장에 대한, Z는 표적 시장이 제품에 대해 어떤 방식으로 어느 범위까지 호응 할 것인가를 의미해요
+
+ 예제: 적어도 10퍼센트의 한국의 직장인들은 관심사가 같은 사람들과 출퇴근 이야기를 나누고 싶어 할 것이다.
+ `,
+ },
+ {
+ type: 'xyz',
+ content: `XYZ 법칙 중 범위를 줄여 봐요.
+
+ 예제: 적어도 10퍼센트의 분당의 직장인들은 관심사가 같은 사람들과 출근에 이야기를 나누고 싶어 할 것
+ `,
+ },
+ {
+ type: 'pretotyping',
+ content: `설정한 xyz법칙을 간단하게 테스트해봐요.
+
+ 예제: 가볍게 전단지와 홍보패널을 만들어 출퇴근시간에 수원에서 판교까지가는 버스에서 설계한 상품을 얘기하여 사람들의 반응을 살펴본다.
+ `,
+ },
+]
+
+export const ibulsinVariants: ibulsinVariantsType = {
+ outline: 'outline',
+ why: 'why',
+ marketResponse: 'marketResponse',
+ XYZ: 'XYZ',
+ xyz: 'xyz',
+ pretotyping: 'pretotyping',
+}
diff --git a/app/_store/atom.ts b/app/_store/atom.ts
new file mode 100644
index 0000000..41cd9b5
--- /dev/null
+++ b/app/_store/atom.ts
@@ -0,0 +1,24 @@
+import { atom } from 'recoil'
+
+export interface ActiveInvestmentItemType {
+ id: string
+ name: string
+ score: number
+}
+
+// type ActiveType = Omit
+// 시온 그는 신이야...
+// 고오급 타입 스크립트,,,
+
+const investmentItemAtom = atom({
+ key: 'investmentItemAtom',
+ default: [
+ {
+ id: new Date().toISOString(),
+ name: '',
+ score: 0,
+ },
+ ],
+})
+
+export default investmentItemAtom