diff --git a/backend/package.json b/backend/package.json index eb7ad713..00196de4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,33 +1,34 @@ -{ - "name": "ddara-backend", - "private": true, - "workspaces": [ - "frontend", - "backend" - ], - "version": "0.0.0", - "type": "module", - "description": "따라따라의 선따라길따라 BackEnd 코드", - "main": "index.js", - "scripts": { - "dev": "node src/index.js", - "test": "vitest", - "test:watch": "vitest --watch", - "test:coverage": "vitest run --coverage", - "lint": "pnpm lint-staged" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "bcrypt": "^5.1.1", - "dotenv": "^16.4.5", - "express": "^4.21.1", - "express-validator": "^7.2.0", - "jsonwebtoken": "^9.0.2", - "pg": "^8.13.1", - "swagger-jsdoc": "^6.2.8", - "swagger-ui-express": "^5.0.1", - "ws": "^8.11.0" - } -} +{ + "name": "ddara-backend", + "private": true, + "workspaces": [ + "frontend", + "backend" + ], + "version": "0.0.0", + "type": "module", + "description": "따라따라의 선따라길따라 BackEnd 코드", + "main": "index.js", + "scripts": { + "dev": "node src/index.js", + "test": "vitest", + "test:watch": "vitest --watch", + "test:coverage": "vitest run --coverage", + "lint": "pnpm lint-staged" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "^5.1.1", + "dotenv": "^16.4.5", + "express": "^4.21.1", + "express-validator": "^7.2.0", + "jsonwebtoken": "^9.0.2", + "pg": "^8.13.1", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", + "uuid": "^11.0.3", + "ws": "^8.11.0" + } + } \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 829f880a..a52c70fa 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,64 +1,64 @@ -{ - "name": "ddara-frontend", - "private": true, - "workspaces": [ - "frontend", - "backend" - ], - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "test": "vitest", - "test:watch": "vitest --watch", - "test:coverage": "vitest run --coverage", - "lint": "pnpm lint-staged", - "preview": "vite preview", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" - }, - "dependencies": { - "@fontsource/pretendard": "^5.1.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-icons": "^5.3.0", - "react-router-dom": "^6.28.0" - }, - "devDependencies": { - "@chromatic-com/storybook": "^3.2.2", - "@eslint/js": "^9.13.0", - "@storybook/addon-essentials": "^8.4.2", - "@storybook/addon-interactions": "^8.4.2", - "@storybook/addon-onboarding": "^8.4.2", - "@storybook/blocks": "^8.4.2", - "@storybook/react": "^8.4.2", - "@storybook/react-vite": "^8.4.2", - "@storybook/test": "^8.4.2", - "@types/navermaps": "^3.7.8", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@types/react-router-dom": "^5.3.3", - "@vitejs/plugin-react-swc": "^3.5.0", - "autoprefixer": "^10.4.20", - "classnames": "^2.5.1", - "eslint": "^9.13.0", - "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", - "eslint-plugin-storybook": "^0.11.0", - "globals": "^15.11.0", - "postcss": "^8.4.47", - "storybook": "^8.4.2", - "tailwindcss": "^3.4.14", - "typescript": "~5.6.2", - "typescript-eslint": "^8.11.0", - "vite": "^5.4.10", - "vite-tsconfig-paths": "^5.1.1" - }, - "eslintConfig": { - "extends": [ - "plugin:storybook/recommended" - ] - } -} +{ + "name": "ddara-frontend", + "private": true, + "workspaces": [ + "frontend", + "backend" + ], + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "test": "vitest", + "test:watch": "vitest --watch", + "test:coverage": "vitest run --coverage", + "lint": "pnpm lint-staged", + "preview": "vite preview", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" + }, + "dependencies": { + "@fontsource/pretendard": "^5.1.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.3.0", + "react-router-dom": "^6.28.0" + }, + "devDependencies": { + "@chromatic-com/storybook": "^3.2.2", + "@eslint/js": "^9.13.0", + "@storybook/addon-essentials": "^8.4.2", + "@storybook/addon-interactions": "^8.4.2", + "@storybook/addon-onboarding": "^8.4.2", + "@storybook/blocks": "^8.4.2", + "@storybook/react": "^8.4.2", + "@storybook/react-vite": "^8.4.2", + "@storybook/test": "^8.4.2", + "@types/navermaps": "^3.7.8", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.20", + "classnames": "^2.5.1", + "eslint": "^9.13.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "eslint-plugin-storybook": "^0.11.0", + "globals": "^15.11.0", + "postcss": "^8.4.47", + "storybook": "^8.4.2", + "tailwindcss": "^3.4.14", + "typescript": "~5.6.2", + "typescript-eslint": "^8.11.0", + "vite": "^5.4.10", + "vite-tsconfig-paths": "^5.1.1" + }, + "eslintConfig": { + "extends": [ + "plugin:storybook/recommended" + ] + } + } \ No newline at end of file diff --git a/frontend/src/component/common/InputBox.tsx b/frontend/src/component/common/InputBox.tsx new file mode 100644 index 00000000..3452bec7 --- /dev/null +++ b/frontend/src/component/common/InputBox.tsx @@ -0,0 +1,28 @@ +import { ChangeEvent, FocusEvent } from 'react'; + +export interface IInputBoxProps { + type?: 'text' | 'number' | 'password' | 'email' | 'tel'; + placeholder?: string; + onChange?: (event: ChangeEvent) => void; + onFocus?: (event: FocusEvent) => void; + onBlur?: (event: FocusEvent) => void; + className?: string; +} + +export const InputBox = ({ + type = 'text', + placeholder = '', + onChange, + onFocus, + onBlur, + className = '', +}: IInputBoxProps) => ( + +); diff --git a/frontend/src/component/layout/Footer.tsx b/frontend/src/component/layout/Footer.tsx index 66199cf9..437debd0 100644 --- a/frontend/src/component/layout/Footer.tsx +++ b/frontend/src/component/layout/Footer.tsx @@ -6,12 +6,13 @@ interface IFooterProps { title?: string; onClick?: () => void; active?: boolean; + isTranperency?: boolean; } -export const Footer = (props: IFooterProps) => { - const buttonStyle = props.active ? buttonActiveType.ACTIVE : buttonActiveType.PASSIVE; +export const Footer = ({ title, onClick, active = false, isTranperency = true }: IFooterProps) => { + const buttonStyle = active ? buttonActiveType.ACTIVE : buttonActiveType.PASSIVE; - return ( + return isTranperency ? (
+ ) : ( +
); }; diff --git a/frontend/src/component/layout/Layout.tsx b/frontend/src/component/layout/Layout.tsx index 1d343139..e9aac79d 100644 --- a/frontend/src/component/layout/Layout.tsx +++ b/frontend/src/component/layout/Layout.tsx @@ -7,6 +7,7 @@ interface ILayoutProps { footerTitle?: string; footerActive?: boolean; handleFooterClick?: () => void; + footerTransperency?: boolean; } export const Layout = (props: ILayoutProps) => { @@ -25,6 +26,7 @@ export const Layout = (props: ILayoutProps) => { title={props.footerTitle} onClick={props.handleFooterClick} active={props.footerActive} + isTranperency={props.footerTransperency} /> ); diff --git a/frontend/src/component/layout/header/LayoutHeader.tsx b/frontend/src/component/layout/header/LayoutHeader.tsx index 2d53754b..aad5a1dc 100644 --- a/frontend/src/component/layout/header/LayoutHeader.tsx +++ b/frontend/src/component/layout/header/LayoutHeader.tsx @@ -1,7 +1,7 @@ import { useContext } from 'react'; import { Header } from '@/component/header/Header'; import { NoticeText } from '@/component/text/NoticeText'; -import { HeaderContext } from '@/component/layout/header/LayoutHeaderProvider.tsx'; +import { HeaderContext } from '@/component/layout/header/LayoutHeaderProvider'; export const LayoutHeader = () => { const { headerOption } = useContext(HeaderContext); diff --git a/frontend/src/index.css b/frontend/src/index.css index 010a3ce1..a051f0c5 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -37,6 +37,7 @@ time, mark, audio, video { margin: 0; padding: 0; border: 0; + border-style: solid; font-size: 100%; font: inherit; vertical-align: baseline; diff --git a/frontend/src/pages/AddChannel.tsx b/frontend/src/pages/AddChannel.tsx index cc2654aa..5295e73f 100644 --- a/frontend/src/pages/AddChannel.tsx +++ b/frontend/src/pages/AddChannel.tsx @@ -1 +1,142 @@ -export const AddChannel = () => <>Hello; +import { useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { IoClose } from 'react-icons/io5'; +import { HiMiniInformationCircle } from 'react-icons/hi2'; +import { InputBox } from '../component/common/InputBox'; + +interface IUser { + id: number; + name: string; + mockData?: number; +} + +/** + * Divider 컴포넌트: 구분선 역할을 하는 컴포넌트입니다. + * @return {JSX.Element} 수평선 `
` 요소를 반환합니다. + */ +const Divider = () =>
; + +/** + * AddChannel 컴포넌트 + * + * 사용자가 경로 이름을 입력하고 최대 5명까지 사용자 추가가 가능합니다. + * 각 사용자는 출발지와 도착지의 설정이 가능합니다. + * + * @return {JSX.Element} 채널 추가 페이지 레이아웃을 반환합니다. + * + * @remarks + * 이 컴포넌트는 사용자가 지정된 이름으로 사용자를 추가하고, + * 추가된 사용자마다 출발지, 도착지, 그리고 경로 설정이 가능합니다. + * + * @example + * ```tsx + * + * ``` + */ + +export const AddChannel = () => { + const [users, setUsers] = useState([{ id: 1, name: '사용자1', mockData: 10 }]); + + /** + * 사용자 추가 함수 + * + * 현재 사용자 목록에 새로운 사용자를 추가합니다. + * 최대 5명까지 추가할 수 있으며, 그 이상은 추가할 수 없습니다. + * + * @return {void} 반환값이 없습니다. + * + * @remarks + * 사용자가 5명 이상일 경우 추가되지 않도록 제한하고, + * 추가되는 사용자는 '사용자[n]' 형식의 이름을 가집니다. + * + * @example + * ``` + * addUser(); // 사용자 추가 + * ``` + */ + const addUser = () => { + if (users.length === 5) return; + const newUser: IUser = { + id: users.length + 1, + name: `사용자${users.length + 1}`, + mockData: Math.floor(Math.random() * 100), + }; + setUsers([...users, newUser]); + }; + + /** + * 사용자 추가 함수 + * + * 현재 사용자 목록에 사용자를 삭제합니다. + * 최대 4명까지 삭제할 수 있으며, 첫 사용자는 삭제할 수 없습니다. + * + * @return {void} 반환값이 없습니다. + * + * @remarks + * 사용자 2번 부터 삭제 버튼이 생깁니다. + * 사용자가 삭제되면 id를 다시 부여하여 빈 부분을 당겨와 채웁니다. + * + * @example + * ``` + * deleteUser(3); // 3번 id 사용자 삭제 + * ``` + */ + + const deleteUser = (id: number) => { + const updatedUsers = users + .filter(user => user.id !== id) + .map((user, index) => ({ + ...user, + id: index + 1, + name: `사용자${index + 1}`, + })); + setUsers(updatedUsers); + }; + + useEffect(() => { + console.log(users); + }, [users]); + + return ( +
+ + +
+ {users.map(user => ( +
+
+ {user.name} +
+
1 ? 'w-56' : 'w-64', + )} + > + 클릭시 출발지/도착지, 경로 설정 가능 +
+ {user.id > 1 && ( + + )} +
+ ))} +
+
+ + 사용자 별로 출발지/도착지(마커), 경로(그림)을 설정할 수 있습니다. +
+ {users.length < 5 && ( +
+ +
+ )} +
+ ); +}; diff --git a/frontend/src/pages/DrawRoute.tsx b/frontend/src/pages/DrawRoute.tsx index 7abd6fe4..85acc729 100644 --- a/frontend/src/pages/DrawRoute.tsx +++ b/frontend/src/pages/DrawRoute.tsx @@ -1,4 +1,4 @@ -import { Footer } from '@/component/Layout/Footer'; +import { Footer } from '@/component/layout/Footer'; import { Map } from '@/component/maps/Map'; import { MdArrowBack } from 'react-icons/md'; import { Linedrawer } from '@/component/linedrawer/Linedrawer'; diff --git a/frontend/src/pages/GuestView.tsx b/frontend/src/pages/GuestView.tsx index 8468e830..e36522b0 100644 --- a/frontend/src/pages/GuestView.tsx +++ b/frontend/src/pages/GuestView.tsx @@ -1,5 +1,5 @@ import { Map } from '@/component/maps/Map.tsx'; -import { HeaderContext } from '@/component/layout/header/LayoutHeaderProvider.tsx'; +import { HeaderContext } from '@/component/layout/header/LayoutHeaderProvider'; import { useContext, useEffect } from 'react'; export const GuestView = () => { diff --git a/frontend/src/pages/Main.tsx b/frontend/src/pages/Main.tsx index f131b312..42bd85bd 100644 --- a/frontend/src/pages/Main.tsx +++ b/frontend/src/pages/Main.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; import { getUserLocation } from '@/hooks/getUserLocation'; import { Map } from '@/component/maps/Map'; -import { BottomSheet } from '@/component/bottomsheet/BottomSheet'; +import { BottomSheet } from '@/component/BottomSheet/BottomSheet'; import { Content } from '@/component/content/Content'; import { MdFormatListBulleted } from 'react-icons/md'; diff --git a/frontend/src/stories/BottomSheet.stories.tsx b/frontend/src/stories/BottomSheet.stories.tsx index d197d643..59fa16e2 100644 --- a/frontend/src/stories/BottomSheet.stories.tsx +++ b/frontend/src/stories/BottomSheet.stories.tsx @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import { Meta, Story } from '@storybook/react'; -import { BottomSheet } from '@/component/bottomsheet/BottomSheet'; +import { BottomSheet } from '@/component/BottomSheet/BottomSheet'; import { Content } from '@/component/content/Content'; export default { diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index e4e3d093..f0836ae7 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -9,13 +9,15 @@ module.exports = { colors: { grayscale: { white: '#FFFFFF', - 25: '#FEFEFEF2', - 50: 'rgba(60, 60, 67, 0.36)', - 100: '#B7B7B7', + 25: '#FBFBFB', + 50: '#A0AEC0', + 75: '#E2E8F0', + 100: '#EDF2F7', + 150: '#AAB6C7', 200: '#6D6D6D', 400: '#555555', 800: '#3E3E3E', - 900: '1C1C1C', + 900: '#1C1C1C', }, blueGray: { 200: '#495664', @@ -48,6 +50,7 @@ module.exports = { floatButton: '0 5px 10px rgba(0,0,0,0.3)', floatMenuButton: '0 4px 4px rgba(0,0,0,0.25)', float: '0 4px 20px rgba(0, 0, 0, 0.13)', + userName: '5px 4px 7.1px rgba(0, 0, 0, 0.05)', basic: 'inset 0 0 3px rgba(0, 0, 0, 0.11)', dark: '0px -6px 20px 0px rgba(0, 0, 0, 0.25)', }, diff --git a/package.json b/package.json index 5b2e16a0..ff9abf31 100644 --- a/package.json +++ b/package.json @@ -1,71 +1,71 @@ -{ - "name": "web28-ddara", - "version": "0.0.0", - "private": true, - "description": "중장년층을 위한 접근성을 바탕으로 한 위치 기반 서비스", - "scripts": { - "prepare": "husky", - "front": "pnpm --filter ddara-frontend dev", - "backend": "pnpm --filter ddara-backend dev", - "build": "pnpm --filter ddara-frontend build", - "build:docusaurus": "pnpm --filter docusaurus build", - "dev": "pnpm -r dev", - "lint": "pnpm lint-staged", - "test": "vitest", - "test:watch": "vitest --watch", - "test:coverage": "vitest run --coverage", - "typedoc": "typedoc --options typedoc.json", - "jsdoc": "jsdoc -c jsdoc.json" - }, - "workspaces": [ - "frontend", - "backend", - "docs" - ], - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@eslint/eslintrc": "^3.1.0", - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@eslint/js": "^9.13.0", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", - "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^9.14.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-react-refresh": "^0.4.14", - "eslint-plugin-storybook": "^0.11.0", - "globals": "^15.11.0", - "husky": "^9.1.6", - "jsdoc": "^4.0.4", - "lint-staged": "^15.2.10", - "prettier": "^3.3.3", - "prettier-plugin-tailwindcss": "^0.6.8", - "typedoc": "^0.26.11", - "typescript": "~5.6.2", - "typescript-eslint": "^8.11.0", - "vitest": "^2.1.4" - }, - "lint-staged": { - "**/*.{js,jsx,ts,tsx}": [ - "eslint --fix", - "git add" - ], - "!docs/**/*.{js,jsx,ts,tsx}": "eslint --fix" - } -} +{ + "name": "web28-ddara", + "version": "0.0.0", + "private": true, + "description": "중장년층을 위한 접근성을 바탕으로 한 위치 기반 서비스", + "scripts": { + "prepare": "husky", + "front": "pnpm --filter ddara-frontend dev", + "backend": "pnpm --filter ddara-backend dev", + "build": "pnpm --filter ddara-frontend build", + "build:docusaurus": "pnpm --filter docusaurus build", + "dev": "pnpm -r dev", + "lint": "pnpm lint-staged", + "test": "vitest", + "test:watch": "vitest --watch", + "test:coverage": "vitest run --coverage", + "typedoc": "typedoc --options typedoc.json", + "jsdoc": "jsdoc -c jsdoc.json" + }, + "workspaces": [ + "frontend", + "backend", + "docs" + ], + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@eslint/eslintrc": "^3.1.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@typescript-eslint/eslint-plugin": "^8.13.0", + "@typescript-eslint/parser": "^8.13.0", + "@vitejs/plugin-react-swc": "^3.5.0", + "eslint": "^9.14.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "eslint-plugin-storybook": "^0.11.0", + "globals": "^15.11.0", + "husky": "^9.1.6", + "jsdoc": "^4.0.4", + "lint-staged": "^15.2.10", + "prettier": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.8", + "typedoc": "^0.26.11", + "typescript": "~5.6.2", + "typescript-eslint": "^8.11.0", + "vitest": "^2.1.4" + }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx}": [ + "eslint --fix", + "git add" + ], + "!docs/**/*.{js,jsx,ts,tsx}": "eslint --fix" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a16330d9..5414d6c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17112,4 +17112,4 @@ snapshots: optionalDependencies: commander: 9.5.0 - zwitch@2.0.4: {} + zwitch@2.0.4: {} \ No newline at end of file