diff --git a/apps/survey-web/src/app/app.css.ts b/apps/survey-web/src/app/app.css.ts
new file mode 100644
index 0000000..4bb23aa
--- /dev/null
+++ b/apps/survey-web/src/app/app.css.ts
@@ -0,0 +1,9 @@
+import { style } from '@vanilla-extract/css';
+
+export const container = style({
+ margin: 'auto',
+ maxWidth: '90vw',
+ width: '640px',
+ marginTop: '12px',
+ marginBottom: '12px',
+});
diff --git a/apps/survey-web/src/app/app.tsx b/apps/survey-web/src/app/app.tsx
index dee085e..d4cd639 100644
--- a/apps/survey-web/src/app/app.tsx
+++ b/apps/survey-web/src/app/app.tsx
@@ -1,29 +1,22 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-import { SharedUi } from '@ssoon-servey/shared-ui';
import { Route, Routes, Link } from 'react-router-dom';
+import { container } from './app.css';
+import SurveyPage from './survey/page';
export function App() {
return (
-
-
+
This is the generated root route.{' '}
- Click here for page 2.
-
- }
- />
-
- Click here to go back to root page.
+ Click here for page survey.
}
/>
+ } />
{/* END: routes */}
diff --git a/apps/survey-web/src/app/survey/hooks/useSurvey.ts b/apps/survey-web/src/app/survey/hooks/useSurvey.ts
new file mode 100644
index 0000000..8c95d88
--- /dev/null
+++ b/apps/survey-web/src/app/survey/hooks/useSurvey.ts
@@ -0,0 +1,111 @@
+import {
+ SupabaseContextValue,
+ useSupabaseContext,
+} from '@ssoon-servey/supabase';
+import { useEffect, useState } from 'react';
+
+type Options = {
+ id: number;
+ option_text: string;
+ item_id: number | null;
+};
+
+type SurveyItems = {
+ id: number;
+ options: Options[];
+ question_required: boolean;
+ question_title: string;
+ question_type: string;
+ section_id: number | null;
+};
+
+type SurveySections = {
+ id: number;
+ survey_title: string | null;
+ survey_id: number | null;
+ items: SurveyItems[];
+ isNext: boolean;
+ isPrevious: boolean;
+};
+
+type Survey = {
+ id: number;
+ title: string;
+ description?: string | null;
+
+ sections: SurveySections[];
+};
+
+type apiState =
+ | {
+ data: null;
+ isLoading: false;
+ isError: true;
+ }
+ | {
+ data: null;
+ isLoading: true;
+ isError: false;
+ }
+ | {
+ data: T;
+ isLoading: false;
+ isError: false;
+ };
+
+const getSurvey = async (supabase: SupabaseContextValue['supabase']) => {
+ const { data: surveys } = await supabase
+ .from('surveys')
+ .select('id,title,description');
+ if (!surveys) return null;
+
+ const _survey = surveys[0];
+
+ const { data: sections } = await supabase.from('survey_sections').select('*');
+ if (!sections) return null;
+
+ const { data: items } = await supabase.from('survey_items').select('*');
+ if (!items) return null;
+
+ const { data: options } = await supabase.from('question_options').select('*');
+ if (!options) return null;
+
+ const survey: Survey = {
+ ..._survey,
+ sections: sections.map((section, i) => ({
+ ...section,
+ isNext: i !== sections.length - 1,
+ isPrevious: i !== 0,
+ items: items
+ .filter((item) => item.section_id === section.id)
+ .map((items) => ({
+ ...items,
+ options: options.filter((option) => option.item_id === items.id),
+ })),
+ })),
+ };
+ return survey;
+};
+
+const useGetSurvey = (): apiState => {
+ const { supabase } = useSupabaseContext();
+ const [data, setData] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isError, setIsError] = useState(false);
+
+ useEffect(() => {
+ getSurvey(supabase)
+ .then((data) => {
+ setData(data);
+ setIsLoading(false);
+ })
+ .catch(() => {
+ setIsError(true);
+ setIsLoading(false);
+ });
+ }, [supabase]);
+
+ return { data, isLoading, isError } as apiState;
+};
+
+export { useGetSurvey };
diff --git a/apps/survey-web/src/app/survey/page.css.ts b/apps/survey-web/src/app/survey/page.css.ts
new file mode 100644
index 0000000..a3f0f7b
--- /dev/null
+++ b/apps/survey-web/src/app/survey/page.css.ts
@@ -0,0 +1,24 @@
+import { vars } from '@ssoon-servey/shared-ui';
+import { style } from '@vanilla-extract/css';
+
+export const cardContainer = style({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: '10px',
+});
+
+export const borderTop = style({
+ backgroundColor: vars.color.primary500,
+ borderTopLeftRadius: '8px',
+ borderTopRightRadius: '8px',
+ height: '10px',
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+});
+
+export const cardWrapper = style({
+ padding: '24px',
+ paddingTop: '22px',
+});
diff --git a/apps/survey-web/src/app/survey/page.tsx b/apps/survey-web/src/app/survey/page.tsx
new file mode 100644
index 0000000..548486b
--- /dev/null
+++ b/apps/survey-web/src/app/survey/page.tsx
@@ -0,0 +1,66 @@
+import { useGetSurvey } from './hooks/useSurvey';
+import { Block, Card } from '@ssoon-servey/shared-ui';
+import * as $ from './page.css';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useEffect } from 'react';
+
+const INITIAL_ID = 1;
+
+const SurveyPage = () => {
+ const { data, isError, isLoading } = useGetSurvey();
+ const { id } = useParams();
+ const sectionId = Number(id);
+
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ navigate(`/survey/${INITIAL_ID}`, { replace: true });
+ }, []);
+
+ if (isError) {
+ return error
;
+ }
+
+ if (isLoading) {
+ return loading...
;
+ }
+
+ const goNextSection = () => {
+ navigate(`/survey/${sectionId + 1}`);
+ };
+
+ const goBackSection = () => {
+ navigate(-1);
+ };
+
+ const sections = data.sections[sectionId - 1];
+
+ return (
+
+
+
+
+
{data?.title}
+
* 표시는 필수 질문임
+
+
+ {sections.items.map((item) => (
+
+
+
{item.question_title}
+ {item.options.map((option) => (
+
{option.option_text}
+ ))}
+
+
+ ))}
+
+
+ {sections.isPrevious && }
+ {sections.isNext && }
+
+
+ );
+};
+
+export default SurveyPage;
diff --git a/apps/survey-web/src/main.tsx b/apps/survey-web/src/main.tsx
index 563fabf..cd65457 100644
--- a/apps/survey-web/src/main.tsx
+++ b/apps/survey-web/src/main.tsx
@@ -1,8 +1,9 @@
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
-
+import { SupabaseProvider } from '@ssoon-servey/supabase';
import App from './app/app';
+import '@ssoon-servey/shared-ui/css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
@@ -10,7 +11,9 @@ const root = ReactDOM.createRoot(
root.render(
-
+
+
+
);
diff --git a/apps/survey-web/vite.config.ts b/apps/survey-web/vite.config.ts
index 3d8a345..9ea868f 100644
--- a/apps/survey-web/vite.config.ts
+++ b/apps/survey-web/vite.config.ts
@@ -2,6 +2,7 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
export default defineConfig({
root: __dirname,
@@ -17,7 +18,7 @@ export default defineConfig({
host: 'localhost',
},
- plugins: [react(), nxViteTsPaths()],
+ plugins: [react(), nxViteTsPaths(), vanillaExtractPlugin()],
// Uncomment this if you are using workers.
// worker: {
diff --git a/libs/supabase/src/types/index.ts b/libs/supabase/src/types/index.ts
index 2364c1b..e754f51 100644
--- a/libs/supabase/src/types/index.ts
+++ b/libs/supabase/src/types/index.ts
@@ -37,7 +37,6 @@ export type Database = {
};
survey_items: {
Row: {
- hasOption: boolean | null;
id: number;
question_required: boolean;
question_title: string;
@@ -45,7 +44,6 @@ export type Database = {
section_id: number | null;
};
Insert: {
- hasOption?: boolean | null;
id?: never;
question_required: boolean;
question_title: string;
@@ -53,7 +51,6 @@ export type Database = {
section_id?: number | null;
};
Update: {
- hasOption?: boolean | null;
id?: never;
question_required?: boolean;
question_title?: string;
@@ -73,19 +70,16 @@ export type Database = {
survey_sections: {
Row: {
id: number;
- section_id: number | null;
survey_id: number | null;
survey_title: string | null;
};
Insert: {
id?: never;
- section_id?: number | null;
survey_id?: number | null;
survey_title?: string | null;
};
Update: {
id?: never;
- section_id?: number | null;
survey_id?: number | null;
survey_title?: string | null;
};