Skip to content
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

feature(admin): survey를 위한 viewmodel 작업 #2

Open
wants to merge 2 commits into
base: feat/admin
Choose a base branch
from
Open
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
1 change: 0 additions & 1 deletion apps/survey-admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<div id="root"></div>
Expand Down
41 changes: 41 additions & 0 deletions apps/survey-admin/src/app/app.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { vars } from '@ssoon-servey/shared-ui';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이거 직접 만드신건가요? 좋네요

import { style } from '@vanilla-extract/css';

export const container = style({
margin: 'auto',
maxWidth: '90vw',
width: '640px',
marginTop: '12px',
marginBottom: '12px',
});
export const wrapper = style({
width: '100%',
display: 'flex',
flexDirection: 'column',
gap: '12px',
position: 'relative',
});

export const cardWrapper = style({
padding: '24px',
paddingTop: '22px',
});

export const borderTop = style({
backgroundColor: vars.color.primary500,
borderTopLeftRadius: '8px',
borderTopRightRadius: '8px',
height: '10px',
position: 'absolute',
top: 0,
left: 0,
right: 0,
});

export const toolbar = style({
position: 'absolute',
top: '106px',
right: '-50px',
});

export const surveyTitleInput = style({});
126 changes: 64 additions & 62 deletions apps/survey-admin/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,70 @@
import { Survey, useCreateSurvey } from '../hooks/query/useServey';
import { borderTop, wrapper, container, cardWrapper, toolbar } from './app.css';
import { Card } from '@ssoon-servey/shared-ui';
import ToolBar from './components/ToolBar';
import SurveyItem from './components/SurveyItem';
import useSurveyViewModel from './hooks/viewmodel/useSurveyViewModel';

const payload: Survey = {
title: '설문입니다.',
sections: [
{
items: [
{
title: 'radio input 1번째',
type: 'radio',
required: true,
options: [
{ text: 'radio option1 1번째' },
{ text: 'radio option2 1번째' },
{ text: 'radio option3 1번째' },
],
},
{
title: 'checkbox input 1번째',
type: 'radio',
required: true,
options: [
{ text: 'checkbox option1 1번째' },
{ text: 'checkbox option2 1번째' },
{ text: 'checkbox option3 1번째' },
],
},
],
},
{
items: [
{
title: 'select 1번째',
type: 'select',
required: true,
options: [
{
text: 'select option1 1번째',
},
{
text: 'select option2 1번째',
},
{
text: 'select option3 1번째',
},
],
},
{ title: 'select 1번째', type: 'textarea', required: true },
],
},
],
};
export default function App() {
const {
survey,
handleSurveyInput,
surveySections,
handleActiveItem,
handleAddOption,
handleChangeOptionText,
handleChangeItemTitle,
handleAddItems,
handleAddSections,
onSubmit,
} = useSurveyViewModel();

export function App() {
const mutate = useCreateSurvey();

const onMutateSurvey = () => {
mutate(payload);
};
return (
<div>
<h2>설문 만들기</h2>
<button onClick={onMutateSurvey}>제출</button>
<div className={container}>
<div className={wrapper}>
<Card>
<div className={cardWrapper}>
<div className={borderTop} />
<div>
<input
placeholder="설문지 제목"
name="title"
value={survey.title}
onChange={handleSurveyInput}
/>
</div>
<div>
<input
placeholder="설문지 설명"
name="description"
value={survey.description}
onChange={handleSurveyInput}
/>
</div>
</div>
</Card>
{surveySections.map((section, sectionIndex) => (
<div key={sectionIndex}>
<div>{`section ${sectionIndex + 1}`}</div>
{section.items.map((item, itemIndex) => (
<SurveyItem
key={`${sectionIndex}_${itemIndex}`}
item={item}
onActiveItem={() => handleActiveItem(sectionIndex, itemIndex)}
onAddOptions={handleAddOption}
onChangeOptionText={handleChangeOptionText}
onChangeItemTitle={handleChangeItemTitle}
/>
))}
</div>
))}
<div className={toolbar}>
<ToolBar
onAddItems={handleAddItems}
onAddSections={handleAddSections}
/>
</div>
<button onClick={onSubmit}>제출하기</button>
</div>
</div>
);
}

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { style } from '@vanilla-extract/css';

export const cardWrapper = style({
padding: '24px',
paddingTop: '22px',
});
75 changes: 75 additions & 0 deletions apps/survey-admin/src/app/components/SurveyItem/SurveyItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Card, Radio } from '@ssoon-servey/shared-ui';
import { cardWrapper } from './SurveyItem.css';
import { type SurveyItem } from '../../hooks/query/useServey';

interface SurveyItemProps {
item: SurveyItem;
onActiveItem: () => void;
onAddOptions: () => void;
onChangeOptionText: (value: string, optionIndex: number) => void;
onChangeItemTitle: (value: string) => void;
}

const SurveyItem = ({
item,
onActiveItem,
onAddOptions,
onChangeOptionText,
onChangeItemTitle,
}: SurveyItemProps) => {
const handleAddOptions = () => {
onAddOptions();
};
const handleItemsTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.currentTarget;
onChangeItemTitle(value);
};
const handleOptionTextChange = (
e: React.ChangeEvent<HTMLInputElement>,
optionIndex: number
) => {
const { value } = e.currentTarget;
onChangeOptionText(value, optionIndex);
};
return (
<Card onClick={onActiveItem}>
<div className={cardWrapper}>
<div>
<input
placeholder="질문"
onChange={handleItemsTitleChange}
value={item.title}
/>
{/* <select>
<option value="textarea">장문형</option>
<option value="radio" selected>
객관식
</option>
<option value="checkbox">체크박스</option>
<option value="select">드롭다운</option>
</select> */}
</div>
<div>
{
<>
{item.options.map((option, i) => (
<label key={i}>
<Radio disabled />
<input
type="text"
placeholder={option.text}
onChange={(e) => handleOptionTextChange(e, i)}
value={option.text}
/>
</label>
))}
<button onClick={handleAddOptions}>옵션추가</button>
</>
}
</div>
</div>
</Card>
);
};

export default SurveyItem;
1 change: 1 addition & 0 deletions apps/survey-admin/src/app/components/SurveyItem/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './SurveyItem';
10 changes: 10 additions & 0 deletions apps/survey-admin/src/app/components/ToolBar/Toolbar.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { style } from '@vanilla-extract/css';

export const cardWrapper = style({
padding: '8px',
display: 'flex',
flexDirection: 'column',
gap: '4px',
justifyContent: 'center',
alignItems: 'center',
});
37 changes: 37 additions & 0 deletions apps/survey-admin/src/app/components/ToolBar/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Card } from '@ssoon-servey/shared-ui';
import { cardWrapper } from './Toolbar.css';

interface Props {
onAddItems: () => void;
onAddSections: () => void;
}

const ToolBar = ({ onAddItems, onAddSections }: Props) => {
return (
<Card>
<div className={cardWrapper}>
<button onClick={onAddItems}>
<svg width={23} height={23} viewBox="0 0 25 25">
<g xmlns="http://www.w3.org/2000/svg">
<path
fill="#5F6368"
d="M11 17h2v-4h4v-2h-4V7h-2v4H7v2h4Zm1 5q-2.075 0-3.9-.788-1.825-.787-3.175-2.137-1.35-1.35-2.137-3.175Q2 14.075 2 12t.788-3.9q.787-1.825 2.137-3.175 1.35-1.35 3.175-2.138Q9.925 2 12 2t3.9.787q1.825.788 3.175 2.138 1.35 1.35 2.137 3.175Q22 9.925 22 12t-.788 3.9q-.787 1.825-2.137 3.175-1.35 1.35-3.175 2.137Q14.075 22 12 22Zm0-2q3.35 0 5.675-2.325Q20 15.35 20 12q0-3.35-2.325-5.675Q15.35 4 12 4 8.65 4 6.325 6.325 4 8.65 4 12q0 3.35 2.325 5.675Q8.65 20 12 20Zm0-8Z"
/>
</g>
</svg>
</button>
<button onClick={onAddSections}>
<svg width={23} height={23} viewBox="0 0 25 25">
<path
xmlns="http://www.w3.org/2000/svg"
fill="#5F6368"
d="M19 5v4H4V5h15m0 10v4H4v-4h15m1-12H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1zm0 10H3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h17c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1z"
/>
</svg>
</button>
</div>
</Card>
);
};

export default ToolBar;
1 change: 1 addition & 0 deletions apps/survey-admin/src/app/components/ToolBar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Toolbar';
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { useSupabaseContext } from '@ssoon-servey/supabase';

interface Option {
export type Option = {
text: string;
}
};

interface Item {
export type SurveyItem = {
title: string;
type: 'radio' | 'select' | 'checkbox' | 'textarea';
required: boolean;
options?: Option[];
}
options: Option[];
};

interface Section {
export type SurveySection = {
title?: string;
items: Item[];
}
items: SurveyItem[];
};

export interface Survey {
export type Survey = {
title: string;
description?: string;
sections: Section[];
}
sections: SurveySection[];
};

export const useCreateSurvey = () => {
const { supabase } = useSupabaseContext();
Expand All @@ -33,7 +33,10 @@ export const useCreateSurvey = () => {
return survey;
};

const createSections = async (serveyId: number, sections: Section[]) => {
const createSections = async (
serveyId: number,
sections: SurveySection[]
) => {
const { data: survey_section } = await supabase
.from('survey_sections')
.insert(
Expand All @@ -48,7 +51,7 @@ export const useCreateSurvey = () => {

const createSurveyItems = async (
sectionIds: number[],
sections: Section[]
sections: SurveySection[]
) => {
const insertItems: {
section_id: number;
Expand Down Expand Up @@ -80,7 +83,7 @@ export const useCreateSurvey = () => {

const createItemOptions = async (
surveyItemIds: number[],
sections: Section[]
sections: SurveySection[]
) => {
const insertOptions: { item_id: number; option_text: string }[] = [];

Expand Down
Loading