├── .DS_Store ├── .github └── workflows │ └── nodejs.yml ├── .travis.yml ├── .vscode └── settings.json ├── README.md ├── auction-server ├── .env.example ├── .gitignore ├── Dockerfile ├── index.js ├── mailService.js ├── package-lock.json └── package.json ├── chat-server ├── .gitignore ├── Dockerfile ├── README.md ├── index.js ├── package-lock.json └── package.json ├── client ├── .env.example ├── .firebaserc ├── .gitignore ├── .storybook │ ├── addons.js │ └── config.js ├── README.md ├── database.rules.json ├── firebase.json ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── App.test.js │ ├── Router.jsx │ ├── assets │ │ ├── cloth.svg │ │ ├── default-profile.svg │ │ ├── fail.svg │ │ ├── fbShareIcon.svg │ │ ├── font │ │ │ ├── NanumGothic-Regular.ttf │ │ │ └── NanumGothic-Regular.woff │ │ ├── found.png │ │ ├── geek.svg │ │ ├── kakaoShareIcon.svg │ │ ├── letmessengerbutton.svg │ │ ├── loading.gif │ │ ├── logo.svg │ │ ├── messenger.svg │ │ ├── notFound.png │ │ ├── notify.svg │ │ ├── report.svg │ │ ├── settings.svg │ │ ├── success.svg │ │ ├── television.svg │ │ ├── tickets.svg │ │ ├── twitterShareIcon.svg │ │ └── urlShareIcon.svg │ ├── components │ │ ├── Atoms │ │ │ ├── BoxButton │ │ │ │ └── index.jsx │ │ │ ├── BoxHeader │ │ │ │ └── index.jsx │ │ │ ├── BtnListItem │ │ │ │ └── index.jsx │ │ │ ├── Card │ │ │ │ └── index.jsx │ │ │ ├── DayButton │ │ │ │ └── index.jsx │ │ │ ├── Footer │ │ │ │ └── index.jsx │ │ │ ├── Header │ │ │ │ └── index.jsx │ │ │ ├── InputWithLimit │ │ │ │ └── index.jsx │ │ │ ├── LoadingBar │ │ │ │ └── index.jsx │ │ │ ├── NotiNumber │ │ │ │ └── index.jsx │ │ │ ├── RatingButton │ │ │ │ └── index.jsx │ │ │ ├── ReportButton │ │ │ │ └── index.jsx │ │ │ ├── SelectOptionButton │ │ │ │ └── index.jsx │ │ │ ├── SmallCard │ │ │ │ └── index.jsx │ │ │ ├── Spinner │ │ │ │ └── index.jsx │ │ │ ├── TextTimer │ │ │ │ └── index.jsx │ │ │ ├── TextareaWithLength │ │ │ │ └── index.jsx │ │ │ └── ToggleButton │ │ │ │ └── index.jsx │ │ ├── Messenger │ │ │ ├── Container │ │ │ │ ├── ChatContainer │ │ │ │ │ ├── ChatMessage.jsx │ │ │ │ │ └── index.jsx │ │ │ │ ├── RoomElement │ │ │ │ │ └── index.jsx │ │ │ │ └── index.jsx │ │ │ ├── CreateButton │ │ │ │ └── index.jsx │ │ │ └── index.jsx │ │ ├── Molecules │ │ │ ├── AlertDialog │ │ │ │ └── index.jsx │ │ │ ├── CardContainer │ │ │ │ └── index.jsx │ │ │ ├── Carousel │ │ │ │ ├── AddButton │ │ │ │ │ └── index.jsx │ │ │ │ ├── BeforeButton │ │ │ │ │ └── index.jsx │ │ │ │ ├── CarouselImage │ │ │ │ │ └── index.jsx │ │ │ │ ├── NextButton │ │ │ │ │ └── index.jsx │ │ │ │ ├── RemoveButton │ │ │ │ │ └── index.jsx │ │ │ │ ├── constant.jsx │ │ │ │ └── index.jsx │ │ │ ├── CustomModal │ │ │ │ ├── FailModal.jsx │ │ │ │ ├── LoginModal.jsx │ │ │ │ ├── Modal.jsx │ │ │ │ ├── ReportModal.jsx │ │ │ │ ├── SignUpModal.jsx │ │ │ │ ├── SuccessModal.jsx │ │ │ │ ├── UserModalCommonStyles.jsx │ │ │ │ └── constant.jsx │ │ │ ├── InfiniteScroll │ │ │ │ ├── index.jsx │ │ │ │ └── useIntersect.jsx │ │ │ ├── MoneyBox │ │ │ │ └── index.jsx │ │ │ ├── NotifyList │ │ │ │ ├── NotifyItem.jsx │ │ │ │ └── index.jsx │ │ │ ├── RatingDialog │ │ │ │ └── index.jsx │ │ │ ├── SelectBox │ │ │ │ ├── List │ │ │ │ │ └── index.jsx │ │ │ │ ├── ListItem │ │ │ │ │ └── index.jsx │ │ │ │ └── index.jsx │ │ │ ├── ShareBox │ │ │ │ └── index.jsx │ │ │ ├── SmallCardContainer │ │ │ │ └── index.jsx │ │ │ ├── TitleListBox │ │ │ │ └── index.jsx │ │ │ ├── TradeBase │ │ │ │ └── index.jsx │ │ │ └── UserInfoBox │ │ │ │ └── index.jsx │ │ └── Organism │ │ │ ├── AuctionGraph │ │ │ └── index.jsx │ │ │ ├── CategoryBar │ │ │ ├── CategoryIcon │ │ │ │ ├── ImageIcon.jsx │ │ │ │ ├── TextIcon.jsx │ │ │ │ └── index.jsx │ │ │ ├── ExpandList │ │ │ │ └── index.jsx │ │ │ ├── LoginButton │ │ │ │ └── index.jsx │ │ │ ├── Logo │ │ │ │ └── index.jsx │ │ │ ├── Profile │ │ │ │ └── index.jsx │ │ │ └── index.jsx │ │ │ ├── Chat │ │ │ ├── Chat.jsx │ │ │ ├── ChatBox.jsx │ │ │ └── ChatSend.jsx │ │ │ ├── ItemCategorySelector │ │ │ └── index.jsx │ │ │ ├── ProductInfo │ │ │ └── index.jsx │ │ │ ├── RegisterProgress │ │ │ ├── Button │ │ │ │ └── index.jsx │ │ │ └── index.jsx │ │ │ ├── RegisterTermSelector │ │ │ └── index.jsx │ │ │ ├── TradeBox │ │ │ ├── constants.jsx │ │ │ └── index.jsx │ │ │ └── TradeListBox │ │ │ └── index.jsx │ ├── config │ │ ├── api.js │ │ ├── firebase.js │ │ └── path.js │ ├── constants │ │ ├── strings.js │ │ └── values.js │ ├── context │ │ ├── MessengerContext.js │ │ ├── ModalContext.js │ │ ├── NotificationContext.js │ │ ├── ProductPageContext.js │ │ ├── SocketContext.js │ │ └── UserContext.js │ ├── data │ │ └── detail-category-list.js │ ├── hooks │ │ ├── useFetch.js │ │ ├── usePrevious.js │ │ └── useSocket.js │ ├── index.js │ ├── mock │ │ ├── deadline-items │ │ │ └── deadline-items.js │ │ ├── index.jsx │ │ ├── myitems │ │ │ └── myitems.js │ │ └── popular-items │ │ │ └── popular-items.js │ ├── pages │ │ ├── CategoryItems │ │ │ └── index.jsx │ │ ├── ErrorPage │ │ │ └── index.jsx │ │ ├── Main │ │ │ └── index.jsx │ │ ├── MyItems │ │ │ ├── contants.jsx │ │ │ └── index.jsx │ │ ├── Product │ │ │ └── index.jsx │ │ ├── ProductUpdate │ │ │ ├── constants.jsx │ │ │ └── index.jsx │ │ ├── Register │ │ │ ├── constants.jsx │ │ │ ├── context.jsx │ │ │ ├── index.jsx │ │ │ └── template │ │ │ │ ├── Complete │ │ │ │ └── index.jsx │ │ │ │ ├── InsertInfo │ │ │ │ └── index.jsx │ │ │ │ └── SelectCategory │ │ │ │ └── index.jsx │ │ └── TradeList │ │ │ └── index.jsx │ ├── serviceWorker.js │ ├── services │ │ ├── fetchService.js │ │ └── imageService.js │ ├── shared │ │ └── firebase.js │ ├── stories │ │ ├── 3-Progress.stories.js │ │ ├── 5-Carousel.stories.js │ │ ├── 6-SelectBox.stories.js │ │ ├── Atoms │ │ │ ├── BoxButton.stories.js │ │ │ ├── Card.stories.js │ │ │ ├── Color.stories.js │ │ │ ├── Footer.stories.js │ │ │ ├── Header.stories.js │ │ │ ├── InputWithLimits.stories.js │ │ │ ├── LoadingBar.stories.js │ │ │ ├── Padding.stories.js │ │ │ ├── Text.stories.js │ │ │ ├── TextareaWithLength.stories.js │ │ │ └── ToggleButton.stories.js │ │ ├── Modal │ │ │ ├── AlretDialog.stories.js │ │ │ └── ReportDialog.stories.js │ │ ├── Molecules │ │ │ ├── InfiniteScroll.stories.js │ │ │ ├── ShareButtonBox.stories.js │ │ │ ├── TermSelector.stories.js │ │ │ └── TradeResultBox.stories.js │ │ ├── Organisms │ │ │ ├── CategoryBar.stories.js │ │ │ ├── Chats.stories.js │ │ │ ├── Messenger.stories.js │ │ │ └── Products.stories.js │ │ └── TradeList.stories.js │ ├── style │ │ ├── App.css │ │ └── index.css │ └── utils │ │ ├── converter.js │ │ ├── dateUtil.js │ │ ├── fetchUtil.js │ │ └── validator.js └── yarn.lock ├── docker-compose.yml └── server ├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── ormconfig.js ├── package-lock.json ├── package.json ├── src ├── app.ts ├── config │ ├── firestore.ts │ ├── key.ts │ └── objectStorage.ts ├── constants │ ├── category.json │ └── oauthAPIs.ts ├── controllers │ └── api │ │ ├── BidContorller.ts │ │ ├── ItemController.ts │ │ ├── LogController.ts │ │ ├── LoginController.ts │ │ ├── ProductController.ts │ │ ├── StaticController.ts │ │ ├── StorageController.ts │ │ └── UserController.ts ├── custom │ └── CustomNamingStrategy.ts ├── database │ ├── factories │ │ ├── BidFactory.ts │ │ ├── ImageFactory.ts │ │ ├── ProductFactory.ts │ │ └── UserFactory.ts │ └── seeds │ │ ├── BidSeed.ts │ │ └── UserSeed.ts ├── dto │ ├── BidResponseDTO.ts │ ├── ImageDTO.ts │ ├── ImageResponseDTO.ts │ ├── ProductCardResponseDTO.ts │ ├── ProductDTO.ts │ ├── ProductResponseDTO.ts │ ├── UserDTO.ts │ └── UserResponseDTO.ts ├── middlewares │ └── SystemLogger.ts ├── models │ ├── Bids.ts │ ├── Images.ts │ ├── Products.ts │ └── Users.ts ├── repositories │ ├── BidRepository.ts │ ├── ImageRepository.ts │ ├── LogRepository.ts │ ├── ProductRepository.ts │ └── UserRepository.ts ├── server.ts ├── services │ ├── BidService.ts │ ├── FireStoreService.ts │ ├── ItemService.ts │ ├── LogService.ts │ ├── ProductService.ts │ ├── S3Service.ts │ └── UserService.ts └── util │ ├── DateUtils.ts │ ├── StringUtils.ts │ ├── authUtils.ts │ └── fetchUtil.ts └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | name: yarn script test (install, build, test) 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - uses: borales/actions-yarn@v1.19.1 10 | with: 11 | cmd: install # will run `yarn install` command 12 | - uses: borales/actions-yarn@v1.19.1 13 | with: 14 | cmd: build # will run `yarn build` command 15 | - uses: borales/actions-yarn@v1.19.1 16 | with: 17 | cmd: test # will run `yarn test` command -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 12.13 5 | 6 | branches: 7 | only: 8 | - master 9 | - develop 10 | 11 | jobs: 12 | include: 13 | - state: "Build Client" 14 | name: "Build Client" 15 | before_script: 16 | - cd client 17 | - yarn 18 | script: 19 | - yarn build 20 | - state: "Build Server" 21 | name: "Build Server" 22 | before_script: 23 | - cd server 24 | - npm install 25 | script: 26 | - npm build 27 | 28 | jobs: 29 | include: 30 | - state: "Build Chat" 31 | name: "Build Chat" 32 | before_script: 33 | - cd chat-server 34 | script: 35 | - npm start 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /auction-server/.env.example: -------------------------------------------------------------------------------- 1 | DB_TYPE= 2 | DB_HOST= 3 | DB_DOCKER_COMPOSE_SERVICE_HOST= 4 | DB_PORT= 5 | DB_USER= 6 | DB_PASSWORD= 7 | DB_NAME= 8 | 9 | CHAT_SERVER= 10 | CHAT_DEV_SERVER= 11 | 12 | 13 | ### Mail 14 | MAIL_ID= 15 | MAIL_PASSWORD= 16 | MAIL_BASE= -------------------------------------------------------------------------------- /auction-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_STORE 3 | .env 4 | dist -------------------------------------------------------------------------------- /auction-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | WORKDIR /app 3 | 4 | RUN npm install -g pm2 5 | RUN npm install -g cross-env 6 | 7 | COPY package*.json ./ 8 | RUN npm install 9 | 10 | COPY . . 11 | EXPOSE 5000 12 | CMD [ "npm", "start"] -------------------------------------------------------------------------------- /auction-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auction-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "cross-env NODE_ENV=production pm2-runtime index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "axios": "^0.19.0", 15 | "dot-env": "0.0.1", 16 | "dotenv": "^8.2.0", 17 | "moment": "^2.24.0", 18 | "moment-timezone": "^0.5.27", 19 | "mysql2": "^2.0.2", 20 | "node-cron": "^2.0.3", 21 | "socket.io-client": "^2.3.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chat-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_STORE 3 | .env 4 | dist -------------------------------------------------------------------------------- /chat-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | WORKDIR /app 3 | 4 | RUN npm install -g pm2 5 | RUN npm install -g cross-env 6 | 7 | COPY package*.json ./ 8 | RUN npm install 9 | 10 | COPY . . 11 | EXPOSE 4000 12 | CMD [ "npm", "start"] -------------------------------------------------------------------------------- /chat-server/README.md: -------------------------------------------------------------------------------- 1 | ## chat-serer 개발 서버 실행 절차 2 | 3 | ```bash 4 | docker-compose up -d chat-server 5 | ``` 6 | -------------------------------------------------------------------------------- /chat-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "pm2-runtime index.js", 8 | "prod": "pm2 start index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.17.1", 15 | "socket.io": "^2.3.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/.env.example: -------------------------------------------------------------------------------- 1 | # Develop 2 | ## Api 3 | REACT_APP_DEV_CLIENT=http://localhost 4 | REACT_APP_DEV_API=http://localhost:3000 5 | REACT_APP_DEV_CHAT=http://localhost:4000 6 | REACT_APP_DEV_KAKAO_KEY= 7 | 8 | ## Firebase 9 | REACT_APP_DEV_API_KEY= YOUR-API-KEY 10 | REACT_APP_DEV_AUTH_DOMAIN= YOUR-AUTH-KEY 11 | REACT_APP_DEV_DATABASE_URL= YOUR-AUTH-DATABASE-URL 12 | REACT_APP_DEV_PROJECT_ID= YOUR-PROJECT-ID 13 | REACT_APP_DEV_STORAGE_BUCKET= YOUR-STORAGE-BUCK 14 | REACT_APP_DEV_MESSAGING_SENDERID= YOUR-MESSAGING-SENDERID 15 | REACT_APP_DEV_APP_ID= YOUR-APP-ID 16 | REACT_APP_DEV_MEASUREMENT_ID= YOUR-MEASUREMENT-ID 17 | 18 | ## OAuth 19 | REACT_APP_DEV_OAUTH_KAKAO_KEY= 20 | REACT_APP_DEV_OAUTH_GOOGLE_KEY= 21 | 22 | # Production 23 | ## Api 24 | REACT_APP_CLIENT= 25 | REACT_APP_API= 26 | REACT_APP_CHAT= 27 | REACT_APP_KAKAO_KEY= 28 | 29 | ## Firebase 30 | REACT_APP_API_KEY= 31 | REACT_APP_AUTH_DOMAIN= 32 | REACT_APP_DATABASE_URL= 33 | REACT_APP_PROJECT_ID= 34 | REACT_APP_STORAGE_BUCKET= 35 | REACT_APP_MESSAGING_SENDERID= 36 | REACT_APP_APP_ID= 37 | REACT_APP_MEASUREMENT_ID= 38 | 39 | ### OAuth 40 | REACT_APP_OAUTH_KAKAO_KEY= 41 | REACT_APP_OAUTH_GOOGLE_KEY= -------------------------------------------------------------------------------- /client/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "palda-df880" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .env 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | /.vscode -------------------------------------------------------------------------------- /client/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | -------------------------------------------------------------------------------- /client/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | // automatically import all files ending in *.stories.js 4 | configure(require.context('../src/stories', true, /\.stories\.js$/), module); 5 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Here is Palda Client-Server 2 | 3 | ## How to Start 4 | ```bash 5 | cd client 6 | yarn install 7 | yarn start 8 | ``` 9 | 10 | ## Storybook 11 | if you want to show components 12 | ```bash 13 | yarn storybook 14 | ``` 15 | 16 | ## Notice 17 | - 서비스를 정상적으로 사용하기 위해서는 server의 readme를 먼저 참고하시기 바랍니다. 18 | - 가급적 Server 설치 이후 사용하셔야 합니다. 19 | - Local이 아닌 타 사용자와 테스팅을 위해서는 데모 페이지를 이용하시기 바랍니다. 20 | - Auction 기능을 사용하기 위해서는 Auction-server가 가동되고 있어야 합니다. 21 | 22 | --- 23 | Readme last updated : 2019 11 21 24 | -------------------------------------------------------------------------------- /client/database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": true, 4 | ".write": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /client/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.6.0", 7 | "@material-ui/icons": "^4.5.1", 8 | "axios": "^0.19.0", 9 | "firebase": "^7.5.0", 10 | "moment": "^2.24.0", 11 | "moment-timezone": "^0.5.27", 12 | "react": "^16.11.0", 13 | "react-copy-to-clipboard": "^5.0.2", 14 | "react-dom": "^16.11.0", 15 | "react-google-login": "^5.0.7", 16 | "react-image-file-resizer": "^0.2.1", 17 | "react-kakao-login": "^1.2.0", 18 | "react-router-dom": "^5.1.2", 19 | "react-scripts": "3.2.0", 20 | "react-spinners": "^0.6.1", 21 | "react-tooltip": "^3.11.1", 22 | "recharts": "^1.8.5", 23 | "socket.io-client": "^2.3.0", 24 | "styled-components": "^4.4.1" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "build": "react-scripts build", 29 | "build:dev": "react-scripts --max_old_space_size=768 build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject", 32 | "storybook": "start-storybook -p 8888 -s public", 33 | "build-storybook": "build-storybook -s public" 34 | }, 35 | "eslintConfig": { 36 | "extends": "react-app" 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | }, 50 | "devDependencies": { 51 | "@storybook/addon-actions": "^5.2.6", 52 | "@storybook/addon-links": "^5.2.6", 53 | "@storybook/addons": "^5.2.6", 54 | "@storybook/react": "^5.2.6" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 23 | 24 | 25 | No.1 중고 경매사이트 팔다 입니다. 26 | 27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { BrowserRouter } from "react-router-dom"; 3 | import Router from "./Router"; 4 | import UserContext from "./context/UserContext"; 5 | import "./style/App.css"; 6 | import ModalContext from "./context/ModalContext"; 7 | import { Modal } from "../src/components/Molecules/CustomModal/Modal"; 8 | import NotificationContext from "./context/NotificationContext"; 9 | import MessengerContext from "./context/MessengerContext"; 10 | import SocketContext from "./context/SocketContext"; 11 | import { useSocket } from "./hooks/useSocket"; 12 | 13 | function App() { 14 | const [user, setUser] = useState({}); 15 | const [modal, setModal] = useState({ 16 | isOpen: false, 17 | component: null, 18 | props: {} 19 | }); 20 | const [notifications, setNotifications] = useState([]); 21 | const [messengerOpen, setMessengerOpen] = useState(false); 22 | const { socket } = useSocket(user, setNotifications); 23 | 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 33 | {modal.isOpen ? : null} 34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default App; 47 | -------------------------------------------------------------------------------- /client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /client/src/Router.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Switch, Route } from "react-router-dom"; 3 | 4 | import Main from "./pages/Main"; 5 | import TradeList from "./pages/TradeList"; 6 | import CategoryBar from "./components/Organism/CategoryBar"; 7 | import Register from "./pages/Register"; 8 | import ProductPage from "./pages/Product/index"; 9 | import MyItem from "./pages/MyItems"; 10 | import CategoryItems from "./pages/CategoryItems"; 11 | import ProductUpdate from "./pages/ProductUpdate"; 12 | 13 | import ErrorPage from "./pages/ErrorPage"; 14 | 15 | const Router = () => { 16 | return ( 17 | <> 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | }; 33 | 34 | export default Router; 35 | -------------------------------------------------------------------------------- /client/src/assets/cloth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | -------------------------------------------------------------------------------- /client/src/assets/default-profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/src/assets/fail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fail 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/src/assets/fbShareIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 30 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /client/src/assets/font/NanumGothic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/src/assets/font/NanumGothic-Regular.ttf -------------------------------------------------------------------------------- /client/src/assets/font/NanumGothic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/src/assets/font/NanumGothic-Regular.woff -------------------------------------------------------------------------------- /client/src/assets/found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/src/assets/found.png -------------------------------------------------------------------------------- /client/src/assets/kakaoShareIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 31 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /client/src/assets/letmessengerbutton.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 15 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /client/src/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/src/assets/loading.gif -------------------------------------------------------------------------------- /client/src/assets/notFound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/connect-foundation/2019-11/099efa2433ec6c46de86290ef5d747f2492194a1/client/src/assets/notFound.png -------------------------------------------------------------------------------- /client/src/assets/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /client/src/assets/success.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | success 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/assets/tickets.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /client/src/assets/twitterShareIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 32 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /client/src/components/Atoms/BoxButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Button = styled.button` 5 | width: 10rem; 6 | height: 4rem; 7 | padding: 0.5rem 0.25rem; 8 | border: #bfbfbf solid 1px; 9 | background: None; 10 | font-size: 1.5rem; 11 | box-sizing: border-box; 12 | outline: none; 13 | 14 | transition: border 0.2s ease-in-out; 15 | 16 | &:hover, 17 | &:focus { 18 | border: var(--color-primary) solid 2px; 19 | border-radius: 10px; 20 | } 21 | ` 22 | 23 | const Component = ({ onClick, text }) => { 24 | return 25 | } 26 | 27 | export default Component 28 | -------------------------------------------------------------------------------- /client/src/components/Atoms/BoxHeader/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Container = styled.div` 5 | width: 100%; 6 | height: 2.5rem; 7 | box-sizing: border-box; 8 | padding: 0.15rem 0.5rem; 9 | background-color: var(--color-primary); 10 | color: white; 11 | display: flex; 12 | justify-items: center; 13 | ` 14 | 15 | const Title = styled.span` 16 | width: 100%; 17 | height: fit-content; 18 | font-size: 1.3rem; 19 | font-weight: 400; 20 | margin: auto; 21 | letter-spacing: 0.5rem; 22 | ` 23 | 24 | const Components = props => { 25 | return ( 26 | 27 | {props.text} 28 | 29 | ) 30 | } 31 | 32 | export default Components 33 | -------------------------------------------------------------------------------- /client/src/components/Atoms/BtnListItem/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Button = styled.button` 5 | font-family: D2Coding, "D2 coding", monosapce; 6 | width: 100%; 7 | font-size: 1rem; 8 | background: ${props => (props.selected ? "var(--color-gray-lighter-plus)" : "white")}; 9 | padding: 0.5em; 10 | text-align: left; 11 | border-bottom: var(--color-gray) solid 1px; 12 | outline: none; 13 | 14 | &:hover { 15 | background: ${props => (props.selected ? "var(--color-gray-darker)" : "whitesmoke")}; 16 | } 17 | ` 18 | 19 | const Component = props => { 20 | const { selected, onClick } = props 21 | 22 | const handle = e => onClick() 23 | 24 | return ( 25 | 28 | ) 29 | } 30 | 31 | export default Component 32 | -------------------------------------------------------------------------------- /client/src/components/Atoms/DayButton/index.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import React, { useState } from "react" 3 | const Ul = styled.ul` 4 | list-style: none; 5 | padding: 0 0 0 0; 6 | margin: 0 0 0 0; 7 | height: 2rem; 8 | ` 9 | const Li = styled.li` 10 | display: list-item; 11 | list-style: none; 12 | margin: 0 0 0 0; 13 | padding: 0 0 0 0; 14 | border: 0; 15 | float: right; 16 | ` 17 | 18 | const ButtonDay = styled.button` 19 | all: unset; 20 | background-color: ${props => 21 | props.state ? "var(--color-secondary)" : "var(--color-secondary-minus1)"}; 22 | text-align: center; 23 | width: 3rem; 24 | height: 2rem; 25 | margin: 0 0.1rem 0 0.1rem; 26 | &:hover { 27 | cursor: pointer; 28 | } 29 | &:active { 30 | background-color: var(--color-secondary); 31 | } 32 | ` 33 | 34 | function ButtonDays(props) { 35 | const [day, setDay] = useState(true) 36 | const [week, setWeek] = useState(false) 37 | const [year, setYear] = useState(false) 38 | const [year3, setYear3] = useState(false) 39 | 40 | function checkday() { 41 | props.select_1d() 42 | setDay(true) 43 | setWeek(false) 44 | setYear(false) 45 | setYear3(false) 46 | } 47 | 48 | function checkweek() { 49 | props.select_1w() 50 | setDay(false) 51 | setWeek(true) 52 | setYear(false) 53 | setYear3(false) 54 | } 55 | 56 | function checkyear() { 57 | props.select_1y() 58 | setDay(false) 59 | setWeek(false) 60 | setYear(true) 61 | setYear3(false) 62 | } 63 | 64 | function checkyear3() { 65 | props.select_3y() 66 | setDay(false) 67 | setWeek(false) 68 | setYear(false) 69 | setYear3(true) 70 | } 71 | 72 | return ( 73 | 95 | ) 96 | } 97 | 98 | export default ButtonDays 99 | -------------------------------------------------------------------------------- /client/src/components/Atoms/Footer/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Container = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | width: 100%; 8 | height: 5rem; 9 | background: var(--color-quaternary); 10 | text-align: center; 11 | justify-content: center; 12 | 13 | font-family: "BMDOHYEON"; 14 | color: white; 15 | ` 16 | 17 | const Copyright = styled.div` 18 | font-size: 0.8rem; 19 | font-weight: 700; 20 | ` 21 | 22 | const Component = () => { 23 | return ( 24 | 25 | Copyright 2019. Team Palda, All right reserved 26 | 27 | ) 28 | } 29 | 30 | export default Component 31 | -------------------------------------------------------------------------------- /client/src/components/Atoms/Header/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Container = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | width: 100%; 9 | height: 5rem; 10 | text-align: left; 11 | box-sizing: border-box; 12 | align-items: flex-start; 13 | padding: 0.5rem; 14 | ` 15 | 16 | const Text = styled.span` 17 | font-size: var(--font-size-xxl); 18 | font-weight: 700; 19 | color: var(--color-secondary); 20 | ` 21 | 22 | const Header = ({ text }) => { 23 | return ( 24 | 25 | {text} 26 | 27 | ) 28 | } 29 | 30 | export default Header 31 | -------------------------------------------------------------------------------- /client/src/components/Atoms/InputWithLimit/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import styled from "styled-components"; 3 | 4 | const Container = styled.div` 5 | width: 100%; 6 | padding: var(--padding-sm) var(--padding-md); 7 | box-sizing: border-box; 8 | display: flex; 9 | border-radius: 10px; 10 | outline: none; 11 | border: ${props => (props.exceed ? "var(--color-danger)" : "var(--color-gray)")} solid 1.5px; 12 | justify-content: space-between; 13 | 14 | transition: border 0.15s ease-in-out; 15 | &:focus-within { 16 | border-color: ${props => 17 | props.exceed ? "var(--color-danger)" : "var(--color-primary-minus0)"}; 18 | } 19 | `; 20 | 21 | const NonBorder = styled.input` 22 | font-family: "BMJUA"; 23 | width: 90%; 24 | height: fit-content; 25 | font-size: var(--font-size-xl); 26 | outline: none; 27 | `; 28 | 29 | const Counter = styled.div` 30 | width: fit-content; 31 | font-size: var(--font-size-xs); 32 | color: ${props => (props.exceed ? "var(--color-danger)" : "var(--color-gray)")}; 33 | `; 34 | 35 | const Component = ({ limit, hint, onChange, value, isBlockMode }) => { 36 | const [focus, setFocus] = useState(false); 37 | const [length, setLength] = useState(value.length); 38 | 39 | const handleOnChange = e => { 40 | const content = e.target.value; 41 | const validContent = 42 | isBlockMode && content.length > limit ? content.substring(0, limit) : content; 43 | onChange(validContent); 44 | setLength(validContent.length); 45 | }; 46 | 47 | const handleBlock = e => { 48 | const keyCode = e.keyCode; 49 | const value = e.target.value; 50 | if (keyCode !== 8 && keyCode !== 46 && value.length >= limit) e.preventDefault(); 51 | }; 52 | 53 | return ( 54 | limit}> 55 | setFocus(true)} 58 | onBlur={e => setFocus(false)} 59 | onChange={handleOnChange} 60 | onKeyDown={isBlockMode ? handleBlock : undefined} 61 | value={value} 62 | /> 63 | limit}>{`${length} / ${limit}`} 64 | 65 | ); 66 | }; 67 | 68 | export default Component; 69 | -------------------------------------------------------------------------------- /client/src/components/Atoms/LoadingBar/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const Container = styled.div` 5 | display: flex; 6 | width: fit-content; 7 | height: 30px; 8 | `; 9 | 10 | const Content = styled.div` 11 | font-family: "BMJUA"; 12 | display: flex; 13 | width: 60%; 14 | min-width: 400px; 15 | color: white; 16 | border-radius: 30px; 17 | background-color: var(--color-tertiary-plus0); 18 | align-items: center; 19 | justify-content: center; 20 | `; 21 | 22 | const Component = () => { 23 | return ( 24 | 25 | 26 | Loading... 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default Component; 33 | -------------------------------------------------------------------------------- /client/src/components/Atoms/NotiNumber/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from "react"; 2 | import styled from "styled-components"; 3 | import NotificationContext from "../../../context/NotificationContext"; 4 | import { usePrevious } from "../../../hooks/usePrevious"; 5 | 6 | const NotiNumberStyle = styled.div` 7 | position: absolute; 8 | top: 4px; 9 | right: 4px; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | width: 16px; 14 | height: 17px; 15 | font-size: 0.8rem; 16 | border-radius: 50%; 17 | background-color: red; 18 | border: 1px solid red; 19 | color: white; 20 | z-index: 1; 21 | font-weight: bold; 22 | `; 23 | 24 | const NotiNumber = props => { 25 | const { active } = props; 26 | const [notifications] = useContext(NotificationContext); 27 | const prevCount = usePrevious(notifications.length); 28 | 29 | if (active) { 30 | return <>; 31 | } 32 | 33 | if (prevCount < notifications.length) { 34 | return N; 35 | } 36 | 37 | return <>; 38 | }; 39 | 40 | export default NotiNumber; 41 | -------------------------------------------------------------------------------- /client/src/components/Atoms/RatingButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import styled from "styled-components"; 3 | import RatingDialog from "../../Molecules/RatingDialog"; 4 | 5 | const Button = styled.button` 6 | margin: 0 var(--margin-xs); 7 | padding: var(--padding-xs); 8 | font-size: 0.5rem; 9 | font-weight: bold; 10 | color: var(--color-primary); 11 | border-radius: 16px; 12 | display: inline-block; 13 | border: 1px solid var(--color-primary); 14 | background-color: white; 15 | 16 | &:hover { 17 | color: white; 18 | background-color: var(--color-primary); 19 | cursor: pointer; 20 | } 21 | `; 22 | const ButtonWrap = styled.div` 23 | display: inline-block; 24 | height: 2rem; 25 | margin: 0; 26 | `; 27 | /** 28 | * 해당 유저에대해 평가 진행 29 | * 30 | * userid : number 31 | * targetId : id 32 | */ 33 | const Component = props => { 34 | const [show, setShow] = useState(false); 35 | function RatingWrite() { 36 | setShow(!show); 37 | } 38 | return ( 39 | 40 | 41 | {show ? ( 42 | 49 | ) : ( 50 | undefined 51 | )} 52 | 53 | ); 54 | }; 55 | 56 | export default Component; 57 | -------------------------------------------------------------------------------- /client/src/components/Atoms/ReportButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styled from "styled-components"; 3 | import ModalContext from "../../../context/ModalContext"; 4 | import ReportModal from "../../Molecules/CustomModal/ReportModal"; 5 | 6 | const Button = styled.button` 7 | margin: 0 var(--margin-xs); 8 | padding: var(--padding-xs); 9 | font-size: 0.5rem; 10 | font-weight: bold; 11 | color: var(--color-primary); 12 | border-radius: 16px; 13 | display: inline-block; 14 | border: 1px solid var(--color-primary); 15 | background-color: white; 16 | 17 | &:hover { 18 | color: white; 19 | background-color: var(--color-primary); 20 | cursor: pointer; 21 | } 22 | `; 23 | const ButtonWrap = styled.div` 24 | display: inline-block; 25 | height: 2rem; 26 | margin: 0; 27 | `; 28 | /** 29 | * 유저여부와, 해당 id를 입력하여 신고하기 버튼 제작 30 | * 31 | * isUser : boolean 32 | * targetId : id 33 | */ 34 | const Component = props => { 35 | const [modal, setModal] = useContext(ModalContext); 36 | function ReportWrite() { 37 | return setModal({ 38 | isOpen: true, 39 | component: ReportModal, 40 | props: { userId: props.userId, productId: props.productId } 41 | }); 42 | } 43 | return ( 44 | 45 | 46 | 47 | ); 48 | }; 49 | 50 | export default Component; 51 | -------------------------------------------------------------------------------- /client/src/components/Atoms/SelectOptionButton/index.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components" 2 | import React, { useState } from "react" 3 | 4 | const ButtonSB = styled.button` 5 | all: unset; 6 | background-color: ${props => 7 | props.state ? "var(--color-secondary)" : "var(--color-secondary-minus1)"}; 8 | text-align: center; 9 | width: 3rem; 10 | height: 2rem; 11 | margin: 0 0.1rem 0 0.1rem; 12 | border-radius: 0.5rem; 13 | &:hover { 14 | cursor: pointer; 15 | } 16 | ` 17 | 18 | function ButtonSelect(props) { 19 | const [select, setSelect] = useState(true) 20 | function clickbutton() { 21 | props.select() 22 | if (select) setSelect(false) 23 | else setSelect(true) 24 | } 25 | return ( 26 | 27 | {props.name} 28 | 29 | ) 30 | } 31 | 32 | export default ButtonSelect 33 | -------------------------------------------------------------------------------- /client/src/components/Atoms/SmallCard/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const smallCardSize = "9em"; 5 | 6 | const SmallCardStyle = styled.div` 7 | display: flex; 8 | height: ${smallCardSize}; 9 | width: ${smallCardSize}; 10 | margin: 1rem; 11 | border-radius: 1rem; 12 | overflow: hidden; 13 | cursor: pointer; 14 | box-shadow: 0 0.1rem 0.4rem 0 rgba(0, 0, 0, 0.2), 15 | 0 0.3rem 0.2rem 0 rgba(0, 0, 0, 0.19); 16 | transition: all 0.15s ease-in-out; 17 | img { 18 | width: 100%; 19 | height: 100%; 20 | object-fit: cover; 21 | } 22 | 23 | &:hover { 24 | transform: scale(1.05); 25 | } 26 | `; 27 | const Link = styled.a` 28 | height: ${smallCardSize}; 29 | `; 30 | 31 | const SmallCard = ({ item }) => { 32 | const { id, thumbnailUrl } = item; 33 | const link = `/products/${id}`; 34 | return ( 35 | 36 | 37 | {"thumbnail"} 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default SmallCard; 44 | -------------------------------------------------------------------------------- /client/src/components/Atoms/Spinner/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import { ClipLoader } from "react-spinners"; 4 | 5 | const SpinnerStyle = styled.div` 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | width: 100%; 10 | height: 100%; 11 | flex-direction: column; 12 | `; 13 | const SpinnerText = styled.div` 14 | color: var(--color-primary); 15 | font-size: var(--font-size-xl); 16 | font-weight: bold; 17 | text-align: center; 18 | margin-bottom: var(--margin-md); 19 | `; 20 | 21 | const Spinner = ({ text }) => { 22 | return ( 23 | 24 | {text || "Loading.."} 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default Spinner; 31 | -------------------------------------------------------------------------------- /client/src/components/Atoms/TextTimer/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import styled from "styled-components"; 3 | import { getDiffDateTime } from "../../../utils/dateUtil"; 4 | 5 | const TextTimerStyle = styled.span``; 6 | 7 | const TextTimer = ({ datetime }) => { 8 | const [deadLine, setDeadLine] = useState(`시간 계산중`); 9 | 10 | useEffect(() => { 11 | if (!datetime) return; 12 | const timer = setInterval(() => { 13 | const { diff, d, h, m, s } = getDiffDateTime(datetime); 14 | 15 | if (diff > 0) { 16 | setDeadLine(`D-${d} ${h}:${m}:${s}`); 17 | } else { 18 | clearInterval(timer); 19 | setDeadLine(`종료`); 20 | } 21 | }, 1000); 22 | return () => { 23 | clearInterval(timer); 24 | }; 25 | }, [datetime, setDeadLine]); 26 | 27 | return {deadLine}; 28 | }; 29 | 30 | export default TextTimer; 31 | -------------------------------------------------------------------------------- /client/src/components/Atoms/TextareaWithLength/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import styled from "styled-components"; 3 | 4 | const Container = styled.div` 5 | width: 100%; 6 | border: none; 7 | outline: none; 8 | `; 9 | 10 | const Header = styled.div` 11 | width: 100%; 12 | display: flex; 13 | justify-content: space-between; 14 | margin-bottom: 5px; 15 | `; 16 | 17 | const Title = styled.span` 18 | font-weight: bold; 19 | `; 20 | 21 | const Counter = styled.span` 22 | color: ${props => (props.isOver ? "var(--color-danger)" : "var(--color-gary)")}; 23 | font-weight: ${props => (props.isOver ? "700" : "400")}; 24 | font-size: 0.9rem; 25 | `; 26 | 27 | const Content = styled.textarea` 28 | font-family: D2Coding, "D2 coding", monosapce; 29 | width: 100%; 30 | height: 20rem; 31 | border: var(--color-primary) solid 1px; 32 | resize: none; 33 | box-sizing: border-box; 34 | border-radius: 10px; 35 | outline: none; 36 | font-size: 1.2rem; 37 | padding: 1rem; 38 | `; 39 | 40 | const Component = ({ title, limit, content, handler, isBlockMode }) => { 41 | const [len, setLen] = useState(content ? content.length : 0); 42 | 43 | const handleContent = event => { 44 | const content = event.target.value; 45 | const validContent = 46 | isBlockMode && content.length > limit ? content.substring(0, limit) : content; 47 | handler(validContent); 48 | setLen(validContent.length); 49 | }; 50 | 51 | const handleBlock = e => { 52 | const keyCode = e.keyCode; 53 | const value = e.target.value; 54 | if (keyCode !== 8 && keyCode !== 46 && value.length >= limit) e.preventDefault(); 55 | }; 56 | 57 | return ( 58 | 59 |
60 | {title} 61 | limit}>{`(${len} / ${limit})`} 62 |
63 | 69 |
70 | ); 71 | }; 72 | 73 | export default Component; 74 | -------------------------------------------------------------------------------- /client/src/components/Atoms/ToggleButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | 4 | const Container = styled.div` 5 | position: relative; 6 | width: 60px; 7 | height: 20px; 8 | border: none; 9 | outline: none; 10 | display: flex; 11 | `; 12 | 13 | const Thumb = styled.span` 14 | position: absolute; 15 | width: 20px; 16 | height: 20px; 17 | box-sizing: border-box; 18 | z-index: 1; 19 | right: 0; 20 | border-radius: 50%; 21 | background: ${props => (props.checked ? "var(--color-primary)" : "var(--color-gray)")}; 22 | -webkit-box-shadow: 0px 0px 7px 0px var(--color-darkgray-lighter); 23 | -moz-box-shadow: 0px 0px 7px 0px var(--color-darkgray-lighter); 24 | box-shadow: 0px 0px 7px 0px var(--color-darkgray-lighter); 25 | 26 | transition: transform 0.15s ease-in-out; 27 | ${props => (props.checked ? `transform: translateX(-40px)` : "")}; 28 | `; 29 | 30 | const Bar = styled.div` 31 | width: 100%; 32 | height: 14px; 33 | background: #dfdfdf; 34 | border-radius: 7px; 35 | margin: auto 5px; 36 | `; 37 | 38 | const Component = ({ checked, onClick }) => { 39 | return ( 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default Component; 48 | -------------------------------------------------------------------------------- /client/src/components/Messenger/Container/ChatContainer/ChatMessage.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import React from "react"; 3 | import { dateDiff2Str, sec2date } from "../../../../utils/converter"; 4 | const Wrap = styled.div` 5 | width: 19rem; 6 | 7 | display: flex; 8 | flex-direction: ${props => (props.isSend ? "row" : "row-reverse")}; 9 | justify-content: ${props => (props.isSend ? "flex-end" : "flex-end")}; 10 | padding: 0.25rem 0.25rem; 11 | margin: 0 0.5rem 0 0; 12 | `; 13 | 14 | const MessageText = styled.span` 15 | display: inline-block; 16 | text-align: left; 17 | word-break: break-word; 18 | 19 | font-size: var(--font-size-xs); 20 | 21 | width: 10rem; 22 | padding: 0.3rem 1rem; 23 | border: solid 0.1rem; 24 | border-color: ${props => (props.isSend ? "var(--color-primary-minus0)" : "var(--color-primary)")}; 25 | border-radius: 1rem; 26 | `; 27 | const TimeText = styled.div` 28 | font-size: var(--font-size-xxs); 29 | `; 30 | 31 | function ChatMessage(props) { 32 | return ( 33 | 34 | {dateDiff2Str(sec2date(props.Time))} 35 | {props.Text} 36 | 37 | ); 38 | } 39 | 40 | export default ChatMessage; 41 | -------------------------------------------------------------------------------- /client/src/components/Messenger/Container/RoomElement/index.jsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import React, { useState, useEffect } from "react"; 3 | import DefaultProfileIcon from "../../../../assets/default-profile.svg"; 4 | 5 | import apiConfig from "../../../../config/api"; 6 | import pathConfig from "../../../../config/path"; 7 | 8 | const { apiUrl } = apiConfig; 9 | const { userid } = pathConfig; 10 | 11 | const Wrap = styled.div` 12 | width: 19.5rem; 13 | height: 3rem; 14 | 15 | display: flex; 16 | flex-direction: row; 17 | padding: 0.25rem 0.25rem; 18 | margin-bottom: 0.5rem; 19 | `; 20 | 21 | const Img = styled.div` 22 | width: 3rem; 23 | height: 3rem; 24 | border-radius: 3rem; 25 | background-color: white; 26 | overflow: hidden; 27 | text-align: center; 28 | margin: 0 0.5rem 0 0; 29 | img { 30 | width: 100%; 31 | height: 100%; 32 | object-fit: contain; 33 | } 34 | `; 35 | const RoomContent = styled.div` 36 | display: flex; 37 | flex-direction: column; 38 | padding: 0.5rem 0; 39 | `; 40 | const HostName = styled.div` 41 | color: var(--color-darkgray-lighter); 42 | font-size: 0.8rem; 43 | text-align: left; 44 | margin-bottom: 0.1rem; 45 | `; 46 | const HostRecentMsg = styled.span` 47 | display: inline-block; 48 | overflow: hidden; 49 | text-overflow: ellipsis; 50 | white-space: nowrap; 51 | width: 15rem; 52 | text-align: left; 53 | `; 54 | 55 | function RoomElement(props) { 56 | const [name, setName] = useState("Anonymous"); 57 | const [point, setPoint] = useState(0); 58 | const [profile, setProfile] = useState(null); 59 | 60 | useEffect(() => { 61 | fetch(`${apiUrl}${userid}`, { 62 | method: "POST", 63 | headers: { 64 | "Content-Type": "application/json" 65 | }, 66 | body: JSON.stringify({ 67 | id: props.roomUserId 68 | }) 69 | }) 70 | .then(result => { 71 | return result.json(); 72 | }) 73 | .then(result => { 74 | if (result.name !== "NotFoundError") { 75 | setName(result.name); 76 | setPoint(result.mannerPoint); 77 | setProfile(result.profileUrl); 78 | } 79 | }); 80 | }, []); 81 | return ( 82 | props.clickroom()}> 83 | 84 | 85 | 86 | 87 | 88 | {name} point:{point} 89 | 90 |
91 | {props.RecentMsg} 92 |
93 |
94 |
95 | ); 96 | } 97 | 98 | export default RoomElement; 99 | -------------------------------------------------------------------------------- /client/src/components/Messenger/CreateButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styled from "styled-components"; 3 | import firebase from "../../../shared/firebase"; 4 | import MessengerContext from "../../../context/MessengerContext"; 5 | 6 | const Button = styled.button` 7 | margin: 0 var(--margin-xs); 8 | padding: var(--padding-xs); 9 | font-size: 0.5rem; 10 | font-weight: bold; 11 | color: var(--color-secondary); 12 | border-radius: 16px; 13 | display: inline-block; 14 | border: 1px solid var(--color-secondary); 15 | background-color: white; 16 | 17 | &:hover { 18 | color: white; 19 | background-color: var(--color-secondary); 20 | cursor: pointer; 21 | } 22 | `; 23 | 24 | const CreateButton = props => { 25 | const [, setMessengerOpen] = useContext(MessengerContext); 26 | 27 | function makeRoom() { 28 | setMessengerOpen(true); 29 | firebase.makeRoom(props.userId, props.sellerId); 30 | } 31 | return ; 32 | }; 33 | 34 | export default CreateButton; 35 | -------------------------------------------------------------------------------- /client/src/components/Messenger/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect, useContext } from "react"; 2 | import Container from "./Container"; 3 | import CategoryIcon from "../Organism/CategoryBar/CategoryIcon"; 4 | import MessengerContext from "../../context/MessengerContext"; 5 | 6 | function Messenger(props) { 7 | const [messengerOpen, setMessengerOpen] = useContext(MessengerContext); 8 | const [show, setShow] = useState(false); 9 | const node = useRef(); 10 | useEffect(() => { 11 | if (messengerOpen) { 12 | OpenMessenger(); 13 | setMessengerOpen(false); 14 | } 15 | }, [messengerOpen]); 16 | useEffect(() => { 17 | document.addEventListener("mousedown", handleOnBlur); 18 | }); 19 | const handleOnBlur = e => { 20 | if (node.current !== (undefined || null)) { 21 | if (!node.current.contains(e.target)) { 22 | setShow(false); 23 | } 24 | } 25 | }; 26 | function ChangeState() { 27 | props.onClick(); 28 | setShow(!show); 29 | } 30 | 31 | function OpenMessenger() { 32 | setShow(true); 33 | } 34 | 35 | return ( 36 |
37 | 38 | ChangeState()} 43 | > 44 |
45 | ); 46 | } 47 | export default Messenger; 48 | -------------------------------------------------------------------------------- /client/src/components/Molecules/AlertDialog/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | const Container = styled.div` 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | width: 100vw; 9 | height: 100vh; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | z-index: 500; 14 | 15 | background: rgba(64, 64, 64, 0.6); 16 | ` 17 | 18 | const Content = styled.div` 19 | width: 30rem; 20 | min-height: 3rem; 21 | background: white; 22 | border-radius: 10px; 23 | overflow: hidden; 24 | padding: 1rem; 25 | ` 26 | 27 | const TitleContainer = styled.div` 28 | width: 100%; 29 | height: fit-content; 30 | text-align: left; 31 | ` 32 | 33 | const BodyContainer = styled.div` 34 | width: 100%; 35 | min-height: 1rem; 36 | height: fit-content; 37 | margin: 0.3rem 0; 38 | ` 39 | 40 | const ButtonContainer = styled.div` 41 | display: flex; 42 | flex-direction: row-reverse; 43 | margin: var(--margin-lg) 0 var(--margin-xs) 0; 44 | ` 45 | 46 | const Title = styled.div` 47 | padding: 0.5rem 0; 48 | font-size: 1.3rem; 49 | ` 50 | 51 | const Body = styled.div` 52 | margin: 0.3rem 0; 53 | white-space: pre-wrap; 54 | ` 55 | 56 | const Button = styled.button` 57 | width: fit-content; 58 | padding: 0.3rem 1rem; 59 | font-size: 1.1rem; 60 | border-radius: 10px; 61 | margin-left: 3px; 62 | color: white; 63 | ` 64 | 65 | export const Components = ({ title, content, cancelAble, onCancel, onAccept, onDismiss }) => { 66 | const handleSuccess = async e => { 67 | if (onAccept !== undefined) await onAccept() 68 | if (onDismiss === undefined) return 69 | onDismiss() 70 | } 71 | 72 | const handleCancel = async e => { 73 | if (onCancel !== undefined) await onCancel() 74 | if (onDismiss === undefined) return 75 | onDismiss() 76 | } 77 | 78 | return ( 79 | 80 | 81 | 82 | {title} 83 | 84 | 85 | {content} 86 | 87 | 88 | 91 | {cancelAble ? ( 92 | 95 | ) : ( 96 | undefined 97 | )} 98 | 99 | 100 | 101 | ) 102 | } 103 | 104 | export default Components 105 | -------------------------------------------------------------------------------- /client/src/components/Molecules/CardContainer/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import Card from "../../Atoms/Card"; 4 | import NotFoundImg from "../../../assets/notFound.png"; 5 | 6 | const Container = styled.div` 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | padding-left: 7rem; 11 | `; 12 | 13 | const Title = styled.label` 14 | display: flex; 15 | font-size: xx-large; 16 | justify-content: flex-start; 17 | `; 18 | 19 | const CardContainerStyle = styled.div` 20 | display: flex; 21 | justify-content: flex-start; 22 | width: 100%; 23 | height: ${props => (props.isWrap ? "35rem" : "17rem")}; 24 | margin-bottom: 2rem; 25 | flex-wrap: ${props => (props.isWrap ? "wrap" : "")}; 26 | overflow: ${props => (props.isWrap ? "auto" : "")}; 27 | `; 28 | 29 | const NotItemInfo = styled.div` 30 | margin: auto; 31 | text-align: center; 32 | `; 33 | 34 | const CardContainer = ({ title, items, isWrap }) => { 35 | return ( 36 | 37 | {title} 38 | 39 | {items.length === 0 ? ( 40 | 41 | 42 |
등록된 물품이 없습니다.
43 |
44 | ) : ( 45 | items.map(item => ) 46 | )} 47 |
48 |
49 | ); 50 | }; 51 | 52 | export default CardContainer; 53 | -------------------------------------------------------------------------------- /client/src/components/Molecules/Carousel/AddButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import styled from "styled-components" 3 | 4 | import FoundImage from "../../../../assets/found.png" 5 | import NotFoundImage from "../../../../assets/notFound.png" 6 | import { size } from "../constant" 7 | 8 | const Container = styled.div` 9 | width: ${size}rem; 10 | height: ${size}rem; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | text-align: center; 15 | flex-direction: column; 16 | background-color: white; 17 | ` 18 | 19 | const Button = styled.input` 20 | display: inline-block; 21 | width: 15rem; 22 | height: 15rem; 23 | overflow: hidden; 24 | box-sizing: border-box; 25 | border-radius: 20px; 26 | padding: 15rem 0 0 0; 27 | background: url(${NotFoundImage}) no-repeat center center; 28 | background-size:cover; 29 | border:none; 30 | outline: none; 31 | 32 | transition: background .5s ease-in-out; 33 | 34 | &:hover{ 35 | background-image: url('${FoundImage}'); 36 | } 37 | ` 38 | 39 | const Span = styled.span` 40 | font-family: "BMJUA"; 41 | width: 100%; 42 | word-break: keep-all; 43 | ` 44 | 45 | const Component = props => { 46 | const { trigger } = props 47 | const handleFile = ev => { 48 | const files = ev.target.files 49 | trigger(files) 50 | } 51 | 52 | return ( 53 | 54 | 61 | 69 | 70 | 71 | ) 72 | } 73 | 74 | export default Components 75 | -------------------------------------------------------------------------------- /client/src/config/api.js: -------------------------------------------------------------------------------- 1 | export const devConfig = { 2 | url: process.env.REACT_APP_DEV_CLIENT, 3 | apiUrl: process.env.REACT_APP_DEV_API, 4 | chatUrl: process.env.REACT_APP_DEV_CHAT, 5 | kakaoKey: process.env.REACT_APP_DEV_KAKAO_KEY, 6 | kakaoOAuthKey: process.env.REACT_APP_DEV_OAUTH_KAKAO_KEY, 7 | googleOAuthKey: process.env.REACT_APP_DEV_OAUTH_GOOGLE_KEY 8 | }; 9 | 10 | export const prodConfig = { 11 | url: process.env.REACT_APP_CLIENT, 12 | apiUrl: process.env.REACT_APP_API, 13 | chatUrl: process.env.REACT_APP_CHAT, 14 | kakaoKey: process.env.REACT_APP_KAKAO_KEY, 15 | kakaoOAuthKey: process.env.REACT_APP_OAUTH_KAKAO_KEY, 16 | googleOAuthKey: process.env.REACT_APP_OAUTH_GOOGLE_KEY 17 | }; 18 | 19 | export default process.env.NODE_ENV === "development" ? devConfig : prodConfig; 20 | -------------------------------------------------------------------------------- /client/src/config/firebase.js: -------------------------------------------------------------------------------- 1 | export const devConfig = { 2 | apiKey: process.env.REACT_APP_DEV_API_KEY, 3 | authDomain: process.env.REACT_APP_DEV_AUTH_DOMAIN, 4 | databaseURL: process.env.REACT_APP_DEV_DATABASE_URL, 5 | projectId: process.env.REACT_APP_DEV_PROJECT_ID, 6 | storageBucket: process.env.REACT_APP_DEV_STORAGE_BUCKET, 7 | messagingSenderId: process.env.REACT_DEV_APP_MESSAGING_SENDERID, 8 | appId: process.env.REACT_APP_DEV_APP_ID, 9 | measurementId: process.env.REACT_APP_DEV_MEASUREMENT_ID 10 | } 11 | 12 | export const prodConfig = { 13 | apiKey: process.env.REACT_APP_API_KEY, 14 | authDomain: process.env.REACT_APP_AUTH_DOMAIN, 15 | databaseURL: process.env.REACT_APP_DATABASE_URL, 16 | projectId: process.env.REACT_APP_PROJECT_ID, 17 | storageBucket: process.env.REACT_APP_STORAGE_BUCKET, 18 | messagingSenderId: process.env.REACT_APP_MESSAGING_SENDERID, 19 | appId: process.env.REACT_APP_APP_ID, 20 | measurementId: process.env.REACT_APP_MEASUREMENT_ID 21 | } 22 | 23 | export default process.env.NODE_ENV === "development" ? devConfig : prodConfig; -------------------------------------------------------------------------------- /client/src/config/path.js: -------------------------------------------------------------------------------- 1 | export default { 2 | storage: { 3 | image: "/api/storage/image", 4 | profile: "/api/storage/profile" 5 | }, 6 | bids: "/api/bids", 7 | logfilter: "/api/log/filter", 8 | sign: { 9 | in: "/api/sign/login", 10 | out: "/api/sign/logout", 11 | kakao: "/api/sign/kakao", 12 | google: "/api/sign/google" 13 | }, 14 | products: "/api/products", 15 | productsWithBids: "/api/products/withBids", 16 | productsRating: "/api/products/rating", 17 | users: "/api/users", 18 | userid: "/api/users/idx", 19 | items: { 20 | category: "/api/items/category", 21 | hot: "/api/items/hot", 22 | deadline: "/api/items/deadline", 23 | related: "/api/items/related" 24 | }, 25 | statics: { 26 | categories: "/api/statics/categories" 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/constants/strings.js: -------------------------------------------------------------------------------- 1 | export const message = { 2 | serverBusy: "Sorry, Server Is too Busy" 3 | } 4 | 5 | export const notice = { 6 | successRegister: "정상적으로 등록되었습니다." 7 | } -------------------------------------------------------------------------------- /client/src/constants/values.js: -------------------------------------------------------------------------------- 1 | export const limits = { 2 | productTitle: 50, 3 | productContent: 1000 4 | } -------------------------------------------------------------------------------- /client/src/context/MessengerContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const MessengerContext = React.createContext(); 4 | 5 | export default MessengerContext; 6 | -------------------------------------------------------------------------------- /client/src/context/ModalContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ModalContext = React.createContext(); 4 | 5 | export default ModalContext; 6 | -------------------------------------------------------------------------------- /client/src/context/NotificationContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const NotificationContext = React.createContext(); 4 | 5 | export default NotificationContext; 6 | -------------------------------------------------------------------------------- /client/src/context/ProductPageContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ProductPageContext = React.createContext(); 4 | 5 | export default ProductPageContext; 6 | -------------------------------------------------------------------------------- /client/src/context/SocketContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const SocketContext = React.createContext(); 4 | 5 | export default SocketContext; 6 | -------------------------------------------------------------------------------- /client/src/context/UserContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const UserContext = React.createContext(); 4 | 5 | export default UserContext; 6 | -------------------------------------------------------------------------------- /client/src/data/detail-category-list.js: -------------------------------------------------------------------------------- 1 | const detailCategoryList = [ 2 | { 3 | idx: 1, 4 | details: [ 5 | { title: "남성의류", code: 1001 }, 6 | { title: "여성의류", code: 1002 }, 7 | { title: "아동의류", code: 1003 } 8 | ] 9 | }, 10 | { 11 | idx: 2, 12 | details: [ 13 | { title: "컴퓨터", code: 2001 }, 14 | { title: "휴대폰", code: 2002 }, 15 | { title: "카메라", code: 2003 } 16 | ] 17 | }, 18 | { 19 | idx: 3, 20 | details: [ 21 | { title: "도서", code: 3001 }, 22 | { title: "문구", code: 3002 } 23 | ] 24 | } 25 | ] 26 | 27 | export default detailCategoryList 28 | -------------------------------------------------------------------------------- /client/src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import apiConfig from "../config/api"; 3 | import axios from "axios"; 4 | 5 | const { apiUrl } = apiConfig; 6 | 7 | 8 | export const useFetch = (path, handleFetchSuccess, handleFetchError) => { 9 | React.useEffect(() => { 10 | let isUnmounted = false; 11 | 12 | const fetchData = async () => { 13 | try { 14 | const response = await axios.get(`${apiUrl}${path}`); 15 | if (!isUnmounted) { 16 | handleFetchSuccess(response); 17 | } 18 | } catch (error) { 19 | if (!isUnmounted) { 20 | handleFetchError(error); 21 | } 22 | } 23 | }; 24 | fetchData(); 25 | 26 | return () => (isUnmounted = true); 27 | }, []); 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/hooks/usePrevious.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | 3 | export const usePrevious = value => { 4 | const ref = useRef(); 5 | 6 | useEffect(() => { 7 | ref.current = value; 8 | }, [value]); 9 | 10 | return ref.current; 11 | }; 12 | -------------------------------------------------------------------------------- /client/src/hooks/useSocket.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import io from "socket.io-client"; 3 | import apiConfig from "../config/api"; 4 | 5 | const { chatUrl } = apiConfig; 6 | 7 | export const useSocket = (user, setNotifications) => { 8 | const [socket, setSocket] = useState(null); 9 | 10 | useEffect(() => { 11 | if (Object.keys(user).length === 0) return; 12 | 13 | const socket = io(chatUrl); 14 | 15 | socket.on("connect", () => { 16 | setSocket(socket); 17 | }); 18 | 19 | socket.on("auctionResult", ({ type, product }) => { 20 | setNotifications(notis => [...notis, { type, product }]); 21 | }); 22 | 23 | return () => { 24 | socket.close(); 25 | }; 26 | }, [user]); 27 | 28 | return { socket }; 29 | }; 30 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './style/index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /client/src/mock/deadline-items/deadline-items.js: -------------------------------------------------------------------------------- 1 | const deadlines = [ 2 | { "id": 1, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827762-7ac1fe00-06e6-11ea-9c20-76e7d77e6d76.jpg", "title": "고양이 발자국", "isAuction": true, "date": "2019-11-23", "bids": 11, "buyNowPrice": 35000, "topBid": 14000 }, 3 | { "id": 2, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827774-86152980-06e6-11ea-8f16-4e026ca02b73.jpg", "title": "젤리", "isAuction": true, "date": "2019-11-23", "bids": 3, "buyNowPrice": null, "topBid": 7000 }, 4 | { "id": 3, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827782-9200eb80-06e6-11ea-888b-ff1d46448147.jpg", "title": "곤란한 고양이", "isAuction": true, "date": "2019-11-24", "bids": 5, "buyNowPrice": null, "topBid": 12000 }, 5 | { "id": 4, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827808-a644e880-06e6-11ea-9729-bb2188344e78.png", "title": "뭘보냐는 고양이", "isAuction": true, "date": "2019-11-24", "bids": 2, "buyNowPrice": 13000, "topBid": 3500 }, 6 | { "id": 5, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827823-b066e700-06e6-11ea-97e5-74b09cf54625.jpg", "title": "아련한 고양이", "isAuction": true, "date": "2019-11-25", "bids": 0, "buyNowPrice": 8000, "topBid": 0 } 7 | ] 8 | 9 | export default deadlines; -------------------------------------------------------------------------------- /client/src/mock/index.jsx: -------------------------------------------------------------------------------- 1 | import deadlines from './deadline-items/deadline-items'; 2 | import populars from './popular-items/popular-items'; 3 | 4 | export { deadlines, populars } -------------------------------------------------------------------------------- /client/src/mock/myitems/myitems.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | title: "임시데이터 방금 전", 4 | thumbnail: 5 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 6 | status: "경매중", 7 | price: 3000, 8 | time: new Date(`2019-11-28 18:18:00`) 9 | }, 10 | { 11 | title: "임시데이터 몇분 전", 12 | thumbnail: 13 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 14 | status: "경매중", 15 | price: 3000, 16 | time: new Date(`2019-11-28 18:00:00`) 17 | }, 18 | { 19 | title: "임시데이터 몇 시간 전", 20 | thumbnail: 21 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 22 | status: "경매중", 23 | price: 3000, 24 | time: new Date(`2019-11-28 15:00:01`) 25 | }, 26 | { 27 | title: "임시데이터 몇 일전", 28 | thumbnail: 29 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 30 | status: "경매중", 31 | price: 3000, 32 | time: new Date(`2019-11-23 00:00:00`) 33 | }, 34 | { 35 | title: "임시데이터 몇 개월 전", 36 | thumbnail: 37 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 38 | status: "경매중", 39 | price: 3000, 40 | time: new Date(`2019-08-23 00:00:01`) 41 | }, 42 | { 43 | title: "임시데이터 몇 년전", 44 | thumbnail: 45 | "https://post-phinf.pstatic.net/MjAxODA0MDNfMjgy/MDAxNTIyNjgxNjQzMTc2.9zObByVQ-Az9SuNbnhDA34JAkBHBgBL0zh2xjibG8cIg.s9M1q3XTHMUBXLY1RuDZ7h40YZGu8RpXAEcTk4lKCxog.JPEG/bjsn-20171130-195451-000-resize.jpg?type=w1200", 46 | status: "경매중", 47 | price: 3000, 48 | time: new Date(`2013-11-23 00:00:01`) 49 | } 50 | ] 51 | 52 | -------------------------------------------------------------------------------- /client/src/mock/popular-items/popular-items.js: -------------------------------------------------------------------------------- 1 | const populars = [ 2 | { "id": 6, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827678-30d91800-06e6-11ea-83fa-2afd1ada89aa.jpg", "title": "화가난 고양이", "isAuction": true, "date": "2019-11-26", "bids": 13, "buyNowPrice": 95000, "topBid": 54000 }, 3 | { "id": 7, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827706-477f6f00-06e6-11ea-946f-ad123880bc54.jpg", "title": "비행하는 고양이", "isAuction": true, "date": "2019-11-27", "bids": 9, "buyNowPrice": null, "topBid": 34000 }, 4 | { "id": 8, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827717-5108d700-06e6-11ea-86c4-81465e6efa85.jpg", "title": "토끼귀 고양이", "isAuction": true, "date": "2019-11-25", "bids": 8, "buyNowPrice": null, "topBid": 27000 }, 5 | { "id": 9, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827731-5e25c600-06e6-11ea-9d7e-ee914a85dbdb.png", "title": "웃는 고양이", "isAuction": true, "date": "2019-11-26", "bids": 6, "buyNowPrice": 15000, "topBid": 12000 }, 6 | { "id": 10, "thumbnail": "https://user-images.githubusercontent.com/37038262/68827745-67af2e00-06e6-11ea-9787-ff303548ac1d.png", "title": "집사를 깨우는 고양이", "isAuction": true, "date": "2019-11-25", "bids": 0, "buyNowPrice": 3000, "topBid": 0 } 7 | ] 8 | 9 | export default populars; -------------------------------------------------------------------------------- /client/src/pages/CategoryItems/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import styled from "styled-components"; 3 | import CardContainer from "../../components/Molecules/CardContainer"; 4 | 5 | import apiConfig from "../../config/api"; 6 | import pathConfig from "../../config/path"; 7 | import ErrorPage from "../ErrorPage"; 8 | import { getFetch } from "../../services/fetchService"; 9 | const { apiUrl } = apiConfig; 10 | const { items, statics } = pathConfig; 11 | 12 | const MainStyle = styled.div` 13 | display: flex; 14 | font-family: "BMJUA"; 15 | width: 100%; 16 | flex-direction: column; 17 | justify-content: center; 18 | .category { 19 | display: flex; 20 | font-size: xx-large; 21 | justify-content: flex-start; 22 | padding-left: 10rem; 23 | } 24 | `; 25 | 26 | const CategoryItems = ({ match }) => { 27 | const categoryCode = Number(match.params.code); 28 | const [itemlist, setItemslist] = useState([]); 29 | const [title, setTitle] = useState(""); 30 | 31 | const getCategoryList = async () => { 32 | const result = await getFetch( 33 | `${apiUrl}${statics.categories}/${categoryCode}`, 34 | {}, 35 | { code: categoryCode } 36 | ); 37 | setTitle(result.title); 38 | }; 39 | 40 | const fetcher = async () => { 41 | setItemslist([]); 42 | const url = `${apiUrl}${items.category}/${categoryCode}`; 43 | let result = await fetch(url); 44 | const list = await result.json(); 45 | 46 | setItemslist(list[0]); 47 | }; 48 | useEffect(() => { 49 | getCategoryList(); 50 | fetcher(); 51 | }, [categoryCode]); 52 | 53 | return ( 54 | <> 55 | {title && title.length ? ( 56 | 57 | 58 | 59 | ) : ( 60 | 61 | )} 62 | 63 | ); 64 | }; 65 | 66 | export default CategoryItems; 67 | -------------------------------------------------------------------------------- /client/src/pages/ErrorPage/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components"; 3 | import NotFoundImg from "../../assets/notFound.png"; 4 | 5 | const Wrap = styled.div` 6 | width: 100%; 7 | margin: auto 0; 8 | display: flex; 9 | justify-content: center; 10 | font-family: "BMJUA"; 11 | `; 12 | const Contents = styled.div` 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | text-align: right; 17 | `; 18 | const Image = styled.div``; 19 | 20 | const Error = () => { 21 | return ( 22 | 23 | 24 |

요청한 페이지를 찾을 수 없습니다.

25 | 존재하지 않는 주소를 입력하셨거나, 26 | 요청하신 페이지의 주소가 변경, 삭제되어 찾을 수 없습니다. 27 | 입력하신 주소가 정확한지 다시 한 번 확인해 주시길 바랍니다. 28 |
29 | 30 | 31 | 32 |
33 | ); 34 | }; 35 | 36 | export default Error; 37 | -------------------------------------------------------------------------------- /client/src/pages/Main/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import styled from "styled-components"; 3 | import CardContainer from "../../components/Molecules/CardContainer"; 4 | import apiConfig from "../../config/api"; 5 | import pathConfig from "../../config/path"; 6 | const { apiUrl } = apiConfig; 7 | const { items } = pathConfig; 8 | 9 | const MainStyle = styled.div` 10 | display: flex; 11 | font-family: "BMJUA"; 12 | width: 100%; 13 | flex-direction: column; 14 | justify-content: center; 15 | .category { 16 | display: flex; 17 | font-size: xx-large; 18 | justify-content: flex-start; 19 | padding-left: 10rem; 20 | } 21 | `; 22 | 23 | const Main = () => { 24 | const [popular, setPopular] = useState([]); 25 | const [deadline, setDeadline] = useState([]); 26 | 27 | const getPopularList = () => { 28 | const url = `${apiUrl}${items.hot}`; 29 | fetch(url) 30 | .then(result => result.json()) 31 | .then(result => { 32 | setPopular(result); 33 | }); 34 | }; 35 | 36 | const getDeadLineList = () => { 37 | const url = `${apiUrl}${items.deadline}`; 38 | fetch(url) 39 | .then(result => result.json()) 40 | .then(result => setDeadline(result)); 41 | }; 42 | 43 | useEffect(() => { 44 | getPopularList(); 45 | getDeadLineList(); 46 | }, []); 47 | 48 | return ( 49 | 50 | 51 | 52 | 53 | ); 54 | }; 55 | 56 | export default Main; 57 | -------------------------------------------------------------------------------- /client/src/pages/MyItems/contants.jsx: -------------------------------------------------------------------------------- 1 | export const limits = 10 2 | 3 | export default { 4 | limits 5 | } 6 | -------------------------------------------------------------------------------- /client/src/pages/ProductUpdate/constants.jsx: -------------------------------------------------------------------------------- 1 | export const validDialog = { 2 | title: "확인", 3 | content: "해당 정보로 수정하시겠습니까?", 4 | cancelAble: true 5 | } 6 | 7 | export const invalidDialog = { 8 | title: "경고", 9 | content: "입력 값 중 빈 값이 있습니다.", 10 | cancelAble: false 11 | } 12 | -------------------------------------------------------------------------------- /client/src/pages/Register/constants.jsx: -------------------------------------------------------------------------------- 1 | export const phaseList = ["Step 1. 카테고리", "Step 2. 상품등록", "Step 3. 완료"] 2 | 3 | export const termList = [ 4 | { title: "1일", term: 1 }, 5 | { title: "2일", term: 2 }, 6 | { title: "3일", term: 3 }, 7 | { title: "4일", term: 4 }, 8 | { title: "5일", term: 5 }, 9 | { title: "6일", term: 6 }, 10 | { title: "1주", term: 7 } 11 | ] 12 | 13 | export const categoryList = { 14 | leftTitle: "카테고리", 15 | rightTitle: "상세 카테고리", 16 | leftList: ["의류", "가전", "생활"], 17 | rightList: [ 18 | ["남성의류", "여성의류", "아동의류"], 19 | ["컴퓨터", "휴대폰", "카메라"], 20 | ["도서", "문구"] 21 | ] 22 | } 23 | 24 | export const itemDescription = [ 25 | "경매시 즉시 구매가는 변동 될 수 있습니다.", 26 | "경매가 시작되는 가격입니다.", 27 | "낙찰 예상가를 적어주세요. 낙찰가와 차이를 알려드립니다." 28 | ] 29 | 30 | export const dialogOption = { 31 | title: "알림", 32 | content: "해당 정보로 등록하시겠습니까?" 33 | } 34 | 35 | export const defaultData = { 36 | title: "", 37 | contents: "", 38 | thumbnail: "", 39 | images: [], 40 | nowPrice: undefined, 41 | hopePrice: undefined, 42 | minPrice: undefined, 43 | endDate: "", 44 | categoryCode: undefined 45 | } 46 | 47 | export default { phaseList, termList, dialogOption } 48 | -------------------------------------------------------------------------------- /client/src/pages/Register/context.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export const context = React.createContext({ 4 | data: {}, 5 | callback: undefined 6 | }) 7 | 8 | export default context 9 | -------------------------------------------------------------------------------- /client/src/pages/Register/template/Complete/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import styled from "styled-components"; 3 | 4 | import ShareBox from "../../../../components/Molecules/ShareBox"; 5 | import { notice } from "../../../../constants/strings"; 6 | import apiConfig from "../../../../config/api"; 7 | 8 | import productContext from "../../context"; 9 | 10 | const PageBase = styled.div` 11 | width: 80%; 12 | 13 | box-sizing: border-box; 14 | `; 15 | 16 | const ContentDiv = styled.div` 17 | width: 80%; 18 | margin: 0 auto; 19 | `; 20 | 21 | const ButtonDiv = styled.div` 22 | width: 15rem; 23 | height: 14rem; 24 | display: flex; 25 | flex-direction: column; 26 | justify-content: space-between; 27 | margin: 0 auto; 28 | text-align: center; 29 | `; 30 | 31 | const LinkBox = styled.a` 32 | width: 100%; 33 | height: 3rem; 34 | border: none; 35 | background: var(--color-secondary-plus1); 36 | font-size: var(--font-size-xl); 37 | border-radius: 1.5rem; 38 | text-decoration: none; 39 | color: white; 40 | line-height: 3rem; 41 | transition: background 0.15s ease-in-out; 42 | 43 | &:hover, 44 | &:focus { 45 | background: var(--color-secondary-plus1-lighter); 46 | font-weight: 700; 47 | } 48 | `; 49 | 50 | const ShareDiv = styled.div` 51 | width: fit-content; 52 | height: fit-content; 53 | margin: 20px auto 30px auto; 54 | `; 55 | 56 | const NoticeDiv = styled.div` 57 | width: 400px; 58 | height: 200px; 59 | font-family: "BMDOHYEON"; 60 | font-size: var(--font-size-xl); 61 | display: flex; 62 | justify-content: center; 63 | align-items: center; 64 | text-align: center; 65 | border: var(--color-secondary-plus1) dashed 1px; 66 | margin: 20px auto 50px auto; 67 | `; 68 | 69 | const NoticeText = styled.div` 70 | width: 200px; 71 | word-break: keep-all; 72 | `; 73 | 74 | const Component = () => { 75 | const obj = useContext(productContext).data; 76 | 77 | return ( 78 | 79 | 80 | 81 | {notice.successRegister} 82 | 83 | 84 | 85 | 86 | 87 | 상품 확인 88 | 계속 등록 89 | 마이페이지로 90 | 확인 91 | 92 | 93 | 94 | ); 95 | }; 96 | 97 | export default Component; 98 | -------------------------------------------------------------------------------- /client/src/pages/Register/template/SelectCategory/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from "react"; 2 | import styled from "styled-components"; 3 | 4 | import Button from "../../../../components/Atoms/BoxButton"; 5 | import CategorySelector from "../../../../components/Organism/ItemCategorySelector"; 6 | 7 | import productContext from "../../context"; 8 | 9 | import { categoryList } from "../../constants.jsx"; 10 | import { idxNotSelected } from "../../../../utils/validator.js"; 11 | 12 | const PageBase = styled.div` 13 | width: 80%; 14 | 15 | box-sizing: border-box; 16 | `; 17 | 18 | const ContentDiv = styled.div` 19 | width: 60%; 20 | margin: 0 auto; 21 | `; 22 | 23 | const ButtonContainer = styled.div` 24 | width: 100%; 25 | height: 3em; 26 | display: flex; 27 | justify-content: flex-end; 28 | margin: 1rem 0; 29 | `; 30 | 31 | const validation = (result, successCallback, failCallback) => { 32 | const isInvalid = result.some(value => value); 33 | isInvalid ? failCallback() : successCallback(); 34 | }; 35 | 36 | const Component = ({ next, leftList, rightList }) => { 37 | const obj = useContext(productContext).data; 38 | 39 | const [leftIdx, setLeftIdx] = useState(-1); 40 | const [rightIdx, setRightIdx] = useState(-1); 41 | 42 | const valiResult = [idxNotSelected(leftIdx), idxNotSelected(rightIdx)]; 43 | 44 | const successCallback = () => { 45 | obj.mainCategory = leftList[leftIdx]; 46 | obj.subCategory = rightList[leftIdx][rightIdx]; 47 | next(); 48 | }; 49 | 50 | const failCallback = () => { 51 | alert("선택되지 않은 값이 있습니다."); 52 | }; 53 | 54 | return ( 55 | 56 | 57 | 67 | 68 |