├── .babelrc ├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ └── feature_request.md ├── .gitignore ├── .prettierrc ├── README.md ├── next-env.d.ts ├── next.config.js ├── package.json ├── public └── images │ ├── alarmModal │ ├── already.svg │ └── checked.svg │ ├── common │ ├── 404.svg │ ├── alarm.svg │ ├── github.svg │ ├── joinProfile.svg │ ├── mic_team.png │ ├── modalDog.svg │ └── update.svg │ ├── footerMenu │ ├── menu01.svg │ ├── menu02.svg │ └── menu03.svg │ ├── header │ ├── FilledHeart.svg │ ├── alarm.svg │ ├── back.svg │ ├── heart.svg │ ├── nonAlarm.svg │ ├── search.svg │ ├── searchBack.svg │ └── setting.svg │ ├── home │ └── dog.svg │ ├── icon │ └── dog.svg │ ├── login │ ├── character_color.svg │ ├── email.svg │ ├── kakao.svg │ └── naver.svg │ ├── post │ ├── comment.svg │ ├── comment_delet.svg │ ├── recoment.svg │ └── upload.svg │ ├── signup │ ├── character.svg │ ├── character_com.svg │ ├── check.svg │ ├── job01.svg │ ├── job02.svg │ ├── job03.svg │ ├── logo.svg │ └── profile.svg │ ├── splashScreen │ ├── dog.svg │ ├── logo01.svg │ └── logo2.svg │ └── together │ ├── btn_delete.svg │ └── btn_write.svg ├── src ├── apis │ ├── dummyData │ │ └── index.tsx │ └── index.tsx ├── components │ ├── Alarm │ │ ├── List │ │ │ └── index.tsx │ │ └── Title │ │ │ └── index.tsx │ ├── AlarmModal │ │ ├── index.tsx │ │ └── styles.tsx │ ├── AuthLogin │ │ └── index.tsx │ ├── Common │ │ ├── BigTitle │ │ │ └── index.tsx │ │ ├── CommentModal │ │ │ └── index.tsx │ │ ├── FlatBox │ │ │ └── index.tsx │ │ ├── FootButton │ │ │ └── index.tsx │ │ ├── FooterMenu │ │ │ └── index.tsx │ │ ├── HashWrap │ │ │ └── index.tsx │ │ ├── Header │ │ │ ├── Back │ │ │ │ └── index.tsx │ │ │ ├── BackOptional │ │ │ │ └── index.tsx │ │ │ ├── EditBackOptional │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── IsModal │ │ │ └── index.tsx │ │ ├── JobButton │ │ │ └── index.tsx │ │ ├── PasswordField │ │ │ └── index.tsx │ │ ├── TextField │ │ │ └── index.tsx │ │ ├── TextFieldProfile │ │ │ └── index.tsx │ │ ├── Title │ │ │ └── index.tsx │ │ ├── wellseeError │ │ │ └── index.tsx │ │ └── wellseeErrorHome │ │ │ └── index.tsx │ ├── ConfirmModal │ │ ├── index.tsx │ │ └── styles.tsx │ ├── DataForm │ │ ├── index.tsx │ │ └── style.tsx │ ├── EditForm │ │ └── index.tsx │ ├── Home │ │ ├── Header │ │ │ └── index.tsx │ │ ├── Main │ │ │ └── index.tsx │ │ └── StudySection │ │ │ └── index.tsx │ ├── LikePost │ │ └── index.tsx │ ├── Loading │ │ └── index.tsx │ ├── LogOutModal │ │ ├── index.tsx │ │ └── styles.tsx │ ├── Modal │ │ ├── index.tsx │ │ └── styles.tsx │ ├── MyPage │ │ ├── Career │ │ │ ├── index.tsx │ │ │ └── style.tsx │ │ ├── Portfolio │ │ │ ├── index.tsx │ │ │ └── style.tsx │ │ ├── Profile │ │ │ ├── index.tsx │ │ │ └── style.tsx │ │ └── School │ │ │ ├── index.tsx │ │ │ └── style.tsx │ ├── PortFolioDeleteForm │ │ └── index.tsx │ ├── Post │ │ ├── EditComment │ │ │ └── index.tsx │ │ └── PostFooter │ │ │ └── index.tsx │ ├── SignupDeleteForm │ │ └── index.tsx │ ├── SplashScreen │ │ └── index.tsx │ ├── SubmitModal │ │ ├── index.tsx │ │ └── styles.tsx │ └── Together │ │ ├── Header │ │ ├── Search │ │ │ ├── index.tsx │ │ │ └── style.tsx │ │ ├── index.tsx │ │ └── style.tsx │ │ ├── SearchBox │ │ ├── index.tsx │ │ └── style.tsx │ │ ├── StudyBox │ │ ├── index.tsx │ │ └── style.tsx │ │ ├── StudySection │ │ └── index.tsx │ │ ├── StudySectionOption │ │ └── index.tsx │ │ ├── StudySlider │ │ ├── index.tsx │ │ └── style.tsx │ │ └── WriteButton │ │ └── index.tsx ├── hooks │ ├── useHandleOverflow.ts │ └── useHeader.ts ├── lib │ └── apiClient.ts ├── pages │ ├── 404.tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── _error.tsx │ ├── alarm │ │ └── index.tsx │ ├── class_join_list │ │ └── [id].tsx │ ├── failure │ │ └── index.tsx │ ├── home │ │ └── index.tsx │ ├── index.tsx │ ├── mypage │ │ └── index.tsx │ ├── posts │ │ ├── [id].tsx │ │ └── comment │ │ │ └── [id].tsx │ ├── sign_in │ │ ├── auth_start │ │ │ └── index.tsx │ │ ├── email_start │ │ │ └── index.tsx │ │ ├── find_password │ │ │ └── index.tsx │ │ └── reset_password │ │ │ └── index.tsx │ ├── sign_up │ │ ├── completion │ │ │ └── index.tsx │ │ ├── experience │ │ │ ├── index.tsx │ │ │ ├── need_update │ │ │ │ └── index.tsx │ │ │ └── update.tsx │ │ ├── index.tsx │ │ ├── portfolio │ │ │ ├── index.tsx │ │ │ ├── need_update │ │ │ │ └── index.tsx │ │ │ └── update.tsx │ │ ├── profile_start │ │ │ └── index.tsx │ │ ├── school │ │ │ ├── index.tsx │ │ │ ├── need_update │ │ │ │ └── index.tsx │ │ │ └── update.tsx │ │ ├── self_introduction │ │ │ ├── index.tsx │ │ │ ├── need_update │ │ │ │ └── index.tsx │ │ │ └── update.tsx │ │ └── something_job │ │ │ └── index.tsx │ ├── template │ │ └── index.tsx │ ├── together │ │ ├── index.tsx │ │ ├── search │ │ │ └── index.tsx │ │ ├── search_result │ │ │ └── [id].tsx │ │ └── write │ │ │ └── index.tsx │ └── token │ │ └── index.tsx ├── reducers │ ├── comments │ │ └── index.tsx │ ├── common │ │ └── index.tsx │ ├── home │ │ └── index.tsx │ ├── index.tsx │ ├── mypage │ │ └── index.tsx │ ├── notifications │ │ └── index.tsx │ ├── posts │ │ └── index.tsx │ └── todos │ │ └── index.tsx ├── sagas │ ├── comments │ │ └── index.tsx │ ├── home │ │ └── index.tsx │ ├── index.tsx │ ├── mypage │ │ └── index.tsx │ ├── notifications │ │ └── index.tsx │ └── posts │ │ └── index.tsx ├── store │ └── index.tsx ├── styles │ ├── common.tsx │ └── global-styles.tsx └── types │ └── index.ts └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "next/babel", 5 | { 6 | "preset-react": { 7 | "runtime": "automatic", 8 | "importSource": "@emotion/react" 9 | } 10 | } 11 | ] 12 | ], 13 | "plugins": ["@emotion/babel-plugin"] 14 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "node": true, 5 | "es6": true, 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 8 9 | }, // to enable features such as async/await 10 | "ignorePatterns": ["node_modules/*", ".next/*", ".out/*", "!.prettierrc.js"], // We don"t want to lint generated files nor node_modules, but we want to lint .prettierrc.js (ignored by default by eslint) 11 | "extends": ["eslint:recommended"], 12 | "overrides": [ 13 | // This configuration will apply only to TypeScript files 14 | { 15 | "files": ["**/*.ts", "**/*.tsx"], 16 | "parser": "@typescript-eslint/parser", 17 | "settings": { 18 | "react": { 19 | "version": "detect" 20 | } 21 | }, 22 | "env": { 23 | "browser": true, 24 | "node": true, 25 | "es6": true, 26 | }, 27 | "extends": [ 28 | "eslint:recommended", 29 | "plugin:@typescript-eslint/recommended", // TypeScript rules 30 | "plugin:react/recommended", // React rules 31 | "plugin:react-hooks/recommended", // React hooks rules 32 | "plugin:jsx-a11y/recommended", // Accessibility rules 33 | "plugin:prettier/recommended", // Prettier plugin 34 | ], 35 | "rules": { 36 | // We will use TypeScript"s types for component props instead 37 | "react/prop-types": "off", 38 | 39 | // No need to import React when using Next.js 40 | "react/react-in-jsx-scope": "off", 41 | 42 | // This rule is not compatible with Next.js"s components 43 | "jsx-a11y/anchor-is-valid": "off", 44 | 45 | "jsx-a11y/accessible-emoji": "off", 46 | 47 | // Why would you want unused vars? 48 | "@typescript-eslint/no-unused-vars": ["error"], 49 | 50 | // I suggest this setting for requiring return types on functions only where useful 51 | "@typescript-eslint/explicit-function-return-type": "off", 52 | "@typescript-eslint/explicit-module-boundary-types": "off", 53 | 54 | // Includes .prettierrc.js rules 55 | "prettier/prettier": ["error", {}, { 56 | "usePrettierrc": true 57 | }], 58 | }, 59 | }, 60 | ], 61 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[WSC-XXX]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 📄 이슈 내용 11 | - 이슈 내용 요약 설명 12 | 13 | ## 📝 상세 내용 14 | - 이슈 내용 구현 관련 상세 내용 작성 15 | 16 | ## ✔ 체크리스트 17 | - [ ] TODO A 18 | - [ ] TODO B 19 | - [ ] TODO C 20 | 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/react,node 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=react,node 4 | 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env* 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | /out/ 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and not Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | # Stores VSCode versions used for testing VSCode extensions 112 | .vscode-test 113 | 114 | ### react ### 115 | .DS_* 116 | **/*.backup.* 117 | **/*.back.* 118 | 119 | node_modules 120 | 121 | *.sublime* 122 | 123 | psd 124 | thumb 125 | sketch 126 | 127 | # End of https://www.toptal.com/developers/gitignore/api/react,node -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "printWidth": 120, 6 | "tabWidth": 2, 7 | "useTabs": false 8 | } -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: (config) => { 3 | config.module.rules.push({ 4 | test: /\.svg$/, 5 | use: ['@svgr/webpack'], 6 | }) 7 | 8 | return config 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chanho-boilerplate", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "dev": "next", 6 | "build": "npm run env -- next build && npm run env -- next export", 7 | "build2": "next build", 8 | "start": "next start", 9 | "type-check": "tsc" 10 | }, 11 | "dependencies": { 12 | "@emotion/core": "^11.0.0", 13 | "@emotion/react": "^11.4.0", 14 | "@emotion/styled": "^11.3.0", 15 | "@material-ui/core": "^4.12.3", 16 | "@material-ui/icons": "^4.11.2", 17 | "@material-ui/lab": "^4.0.0-alpha.60", 18 | "@material-ui/styles": "^4.11.4", 19 | "@types/faker": "^5.5.8", 20 | "@types/react-cookies": "^0.1.0", 21 | "@types/react-icons": "^3.0.0", 22 | "axios": "^0.21.4", 23 | "emotion-reset": "^3.0.1", 24 | "faker": "^5.5.3", 25 | "immer": "^9.0.5", 26 | "next": "^11.0.1", 27 | "next-cookies": "^2.0.3", 28 | "next-redux-wrapper": "^7.0.2", 29 | "npm": "^8.1.3", 30 | "react": "^17.0.2", 31 | "react-cookies": "^0.1.1", 32 | "react-dom": "^17.0.2", 33 | "react-icons": "^4.2.0", 34 | "react-redux": "^7.2.4", 35 | "redux-saga": "^1.1.3" 36 | }, 37 | "devDependencies": { 38 | "@emotion/babel-plugin": "^11.3.0", 39 | "@svgr/webpack": "^5.5.0", 40 | "@types/axios": "^0.14.0", 41 | "@types/node": "^16.4.4", 42 | "@types/react": "^17.0.15", 43 | "@types/react-dom": "^17.0.9", 44 | "@types/react-redux": "^7.1.18", 45 | "@typescript-eslint/eslint-plugin": "^4.28.5", 46 | "@typescript-eslint/parser": "^4.28.5", 47 | "eslint": "^7.31.0", 48 | "eslint-config-prettier": "^8.3.0", 49 | "eslint-plugin-jsx-a11y": "^6.4.1", 50 | "eslint-plugin-prettier": "^3.4.0", 51 | "eslint-plugin-react": "^7.24.0", 52 | "eslint-plugin-react-hooks": "^4.2.0", 53 | "prettier": "^2.3.2", 54 | "redux-devtools-extension": "^2.13.9", 55 | "typescript": "4.3" 56 | }, 57 | "license": "MIT" 58 | } 59 | -------------------------------------------------------------------------------- /public/images/alarmModal/already.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/alarmModal/checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/common/mic_team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIC-TEAM/wellseecoding-front/f810217b3dad26b64eb31a93d16308672abac11e/public/images/common/mic_team.png -------------------------------------------------------------------------------- /public/images/common/update.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/footerMenu/menu01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/footerMenu/menu02.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/images/footerMenu/menu03.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/header/FilledHeart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/header/alarm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/images/header/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/header/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/header/nonAlarm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/header/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/header/searchBack.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/header/setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/login/character_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/images/login/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/images/login/naver.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/post/comment.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/images/post/comment_delet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/post/recoment.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /public/images/post/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /public/images/signup/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/signup/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/signup/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/images/splashScreen/logo01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/splashScreen/logo2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/images/together/btn_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/together/btn_write.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/apis/dummyData/index.tsx: -------------------------------------------------------------------------------- 1 | export const studyArr1 = [ 2 | { 3 | id: 1, 4 | title: '[서울] 모각코 할 사람!', 5 | schedule: '올해 하반기까지 끝내는 것이 목표', 6 | qualification: '주 2회 / 온라인', 7 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 8 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 9 | hashtagArr: ['UI-KIT', 'IOS', '오프라인', '모여라'], 10 | }, 11 | { 12 | id: 2, 13 | title: '[24/7] 모각코 하실분!', 14 | schedule: '올해 하반기까지 끝내는 것이 목표', 15 | qualification: '주 2회 / 온라인', 16 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 17 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 18 | hashtagArr: ['프론트엔드', '리액트', '온라인', '스터디'], 19 | }, 20 | ] 21 | 22 | export const studyArr2 = [ 23 | { 24 | id: 3, 25 | title: '[경기/안양] 스터디 구해요!', 26 | schedule: '올해 하반기까지 끝내는 것이 목표', 27 | qualification: '주 2회 / 온라인', 28 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 29 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 30 | hashtagArr: ['프론트엔드', '리액트', '온라인', '스터디'], 31 | }, 32 | { 33 | id: 4, 34 | title: '[강남] 오프라인 토이프로젝트!', 35 | schedule: '올해 하반기까지 끝내는 것이 목표', 36 | qualification: '주 2회 / 오프라인', 37 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 38 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 39 | hashtagArr: ['백엔드', '노드', '익스프레스', 'MYSQL'], 40 | }, 41 | ] 42 | 43 | export const studyArr3 = [ 44 | { 45 | id: 5, 46 | title: '[강남] 오프라인 토이프로젝트!', 47 | schedule: '올해 하반기까지 끝내는 것이 목표', 48 | qualification: '주 2회 / 오프라인', 49 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 50 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 51 | hashtagArr: ['UIKIT', 'IOS', '오프라인', '스터디'], 52 | }, 53 | { 54 | id: 6, 55 | title: '[역삼] 오프라인 토이프로젝트!', 56 | schedule: '올해 하반기까지 끝내는 것이 목표', 57 | qualification: '주 2회 / 오프라인', 58 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 59 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 60 | hashtagArr: ['토이프로젝트', '역삼역', '오프라인'], 61 | }, 62 | { 63 | id: 7, 64 | title: '[서울] 모각코 할 사람!', 65 | schedule: '올해 하반기까지 끝내는 것이 목표', 66 | qualification: '주 2회 / 온라인', 67 | summary: 'Swift UI가 아니라 UIkit 기반의 ...', 68 | peopleNum: '코로나 때문에 4명 전후로 모집할 예정입니다', 69 | hashtagArr: ['UI-KIT', 'IOS', '오프라인', '모여라'], 70 | }, 71 | ] 72 | -------------------------------------------------------------------------------- /src/apis/index.tsx: -------------------------------------------------------------------------------- 1 | export const API_URL = `https://jsonplaceholder.typicode.com/todos` 2 | 3 | /* 후에 사가로 연결할 때는 필요 없을 것 같습니다*/ 4 | export const GET_POSTS_URL = '/api/v1/posts' 5 | export const WRITE_POST_URL = '/api/v1/posts' 6 | 7 | // 이메일로 회원가입 한 것 로그인 8 | export const REGISTER_USERS_LOGIN = '/api/v1/users/token' 9 | 10 | // 회원가입 이름, 아이디, 비밀번호 11 | export const REGISTER_USERS_URL = '/api/v1/users' 12 | 13 | // 자기소개 14 | export const REGISTER_ABOUT_ME_URL = '/api/v1/users/profile/preface' 15 | 16 | // 학교정보 17 | export const REGISTER_EDUCATION_URL = '/api/v1/users/profile/education' 18 | 19 | // 경력정보 20 | export const REGISTER_WORK_URL = '/api/v1/users/profile/works' 21 | 22 | // 포트폴리오 23 | export const REGISTER_LINK_URL = '/api/v1/users/profile/links' 24 | 25 | // 현재 직업 (취준생, 학생, 직장인) 26 | export const REGISTER_STATUS_URL = '/api/v1/users/profile/status' 27 | -------------------------------------------------------------------------------- /src/components/Alarm/List/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 2 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 3 | import { css } from '@emotion/react' 4 | import { useRouter } from 'next/router' 5 | import { useCallback } from 'react' 6 | import { useDispatch } from 'react-redux' 7 | import { UPDATE_NOTI_REQUEST } from 'src/reducers/notifications' 8 | import { Common } from 'src/styles/common' 9 | import { notificationType } from 'src/types' 10 | // import Alarm from 'public/images/common/alarm.svg' 11 | 12 | type Props = { 13 | data: notificationType[] 14 | } 15 | const AlarmList = ({ data }: Props) => { 16 | const router = useRouter() 17 | const dispatch = useDispatch() 18 | 19 | const locationTo = useCallback( 20 | (id, query) => { 21 | router.push(`posts/${query}`).then(() => { 22 | dispatch({ 23 | type: UPDATE_NOTI_REQUEST, 24 | data: Number(id), 25 | }) 26 | }) 27 | }, 28 | [router, dispatch] 29 | ) 30 | 31 | return ( 32 |
33 | {data.map((v) => ( 34 |
35 | {v.read ? ( 36 |
locationTo(`${v.id}`, `${v.postId}`)}> 37 |
38 |

39 | {v.eventCategory === 'COMMENT_ADDED' && '댓글알림 '} 40 | {v.eventCategory === 'MEMBER_APPLIED' && '가입요청 '} 41 | {v.eventCategory === 'MEMBER_APPROVED' && '가입승인 '} 42 | 알림 43 |

44 | {Math.floor((Date.now() / 1000 - v.timestamp) / 24 / 60 / 60) >= 1 ? ( 45 | {Math.floor((Date.now() / 1000 - v.timestamp) / 24 / 60 / 60)} 일전 46 | ) : ( 47 | 오늘 48 | )} 49 |
50 | 51 |

52 | {v.eventCategory === 'COMMENT_ADDED' && `${v.senderUserName}님이 '${v.postTitle}' 글에 댓글을 달았어요`} 53 | {v.eventCategory === 'MEMBER_APPLIED' && 54 | `${v.senderUserName}님이 ${v.receiverUserName}님의 '${v.postTitle}' 글에 가입신청했어요`} 55 | {v.eventCategory === 'MEMBER_APPROVED' && 56 | `${v.receiverUserName}님이 요청하신 '${v.postTitle}' 글에 가입이 완료됐어요`} 57 |

58 |
59 | ) : ( 60 |
locationTo(`${v.id}`, `${v.postId}`)}> 61 |
62 |

63 | {v.eventCategory === 'COMMENT_ADDED' && '댓글알림 '} 64 | {v.eventCategory === 'MEMBER_APPLIED' && '가입요청 '} 65 | {v.eventCategory === 'MEMBER_APPROVED' && '가입승인 '} 66 | 알림 67 |

68 | {Math.floor((Date.now() / 1000 - v.timestamp) / 24 / 60 / 60) >= 1 ? ( 69 | {Math.floor((Date.now() / 1000 - v.timestamp) / 24 / 60 / 60)} 일전 70 | ) : ( 71 | 오늘 72 | )} 73 |
74 | 75 |

76 | {v.eventCategory === 'COMMENT_ADDED' && `${v.senderUserName}님이 '${v.postTitle}' 글에 댓글을 달았어요`} 77 | {v.eventCategory === 'MEMBER_APPLIED' && 78 | `${v.senderUserName}님이 ${v.receiverUserName}님의 '${v.postTitle}' 글에 가입신청했어요`} 79 | {v.eventCategory === 'MEMBER_APPROVED' && 80 | `${v.receiverUserName}님이 요청하신 '${v.postTitle}' 글에 가입이 완료됐어요`} 81 |

82 |
83 | )} 84 |
85 | ))} 86 |
87 | ) 88 | } 89 | 90 | export default AlarmList 91 | 92 | const alarmListBox = css` 93 | cursor: pointer; 94 | img { 95 | margin-right: 10px; 96 | } 97 | .on { 98 | background: #ffeee7; 99 | } 100 | & > div > div { 101 | &:nth-of-type(1) { 102 | border-top: 1px solid #efebe8; 103 | } 104 | padding: 20px; 105 | border-bottom: 1px solid #efebe8; 106 | } 107 | .header { 108 | font-size: ${Common.fontSize.fs14}; 109 | letter-spacing: -0.4px; 110 | color: #8f8c8b; 111 | display: flex; 112 | justify-content: space-between; 113 | } 114 | p { 115 | font-size: ${Common.fontSize.fs16}; 116 | line-height: 18px; 117 | letter-spacing: -0.8px; 118 | color: #262626; 119 | margin-top: 13px; 120 | margin-left: 22px; 121 | } 122 | ` 123 | -------------------------------------------------------------------------------- /src/components/Alarm/Title/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback, useEffect } from 'react' 2 | import { css } from '@emotion/react' 3 | import { Common } from 'src/styles/common' 4 | import { useDispatch } from 'react-redux' 5 | import { DELETE_ALL_NOTIS_REQUEST, READ_ALL_NOTIS_REQUEST } from 'src/reducers/notifications' 6 | import ConfirmModal from 'src/components/ConfirmModal' 7 | 8 | type Props = { 9 | num: number 10 | } 11 | const AlarmTitle = ({ num }: Props) => { 12 | /* 전체 삭제 모달 */ 13 | const [deleteModalShowing, setDeleteModalShowing] = useState(false) 14 | /* 전체 삭제 모달 결과값 - delete modal confirm */ 15 | const [dmConfirmResult, setDmConfirmResult] = useState(false) 16 | /* 전체 읽기 모달 */ 17 | const [readModalShowing, setReadModalShowing] = useState(false) 18 | /* 전체 읽기 모달 결과값 = read modal confirm */ 19 | const [rmConfirmResult, setRmConfirmResult] = useState(false) 20 | 21 | const dispatch = useDispatch() 22 | 23 | useEffect(() => { 24 | if (dmConfirmResult) { 25 | setDeleteModalShowing(false) 26 | setDmConfirmResult(false) 27 | deleteAll() 28 | } 29 | }, [dmConfirmResult]) 30 | 31 | useEffect(() => { 32 | if (rmConfirmResult) { 33 | setReadModalShowing(false) 34 | setRmConfirmResult(false) 35 | readAll() 36 | } 37 | }, [rmConfirmResult]) 38 | 39 | useEffect(() => { 40 | if (deleteModalShowing) document.body.style.overflow = 'hidden' 41 | else document.body.style.overflow = 'auto' 42 | }, [deleteModalShowing]) 43 | 44 | const readAll = useCallback(() => { 45 | dispatch({ 46 | type: READ_ALL_NOTIS_REQUEST, 47 | }) 48 | }, [dispatch]) 49 | 50 | const deleteAll = useCallback(() => { 51 | dispatch({ 52 | type: DELETE_ALL_NOTIS_REQUEST, 53 | }) 54 | }, [dispatch]) 55 | 56 | const toggleReadModal = useCallback(() => { 57 | setReadModalShowing(true) 58 | }, []) 59 | 60 | const toggleDeleteModal = useCallback(() => { 61 | setDeleteModalShowing((prevState) => !prevState) 62 | document.body.style.overflow = 'hidden' 63 | }, []) 64 | 65 | return ( 66 |
67 |
68 |

69 | {num !== 0 ? ( 70 | 71 | {num}개의 읽지 않은 알림이 있습니다. 72 | 73 | ) : ( 74 | 읽지 않은 알림이 없습니다 75 | )} 76 |

77 | 78 |
79 | 82 | 85 |
86 |
87 | 88 | {readModalShowing && ( 89 | setReadModalShowing(false)} 91 | confirmResult={() => setRmConfirmResult(true)} 92 | h3={'알림을 전체 읽음 표시 하시겠어요?'} 93 | p1={'내 서랍의 모든 알림이 읽음 표시로 처리됩니다'} 94 | p2={'읽음 표시한 알림은 이전 상태로 되돌릴 수 없습니다'} 95 | /> 96 | )} 97 | {deleteModalShowing && ( 98 | setDmConfirmResult(true)} 101 | h3={'알림을 모두 삭제 하시겠어요?'} 102 | p1={'내 서랍의 모든 알림이 삭제됩니다'} 103 | p2={'삭제된 알림은 다시 복구할 수 없습니다'} 104 | /> 105 | )} 106 |
107 | ) 108 | } 109 | 110 | export default AlarmTitle 111 | 112 | const alarmTitWrap = css` 113 | padding: 0 20px 22px; 114 | h1 { 115 | font-weight: 500; 116 | font-size: ${Common.fontSize.title}; 117 | line-height: 36px; 118 | letter-spacing: -1px; 119 | color: #222222; 120 | } 121 | .allDelete { 122 | margin-left: 1.6em; 123 | } 124 | .desc { 125 | font-weight: 500; 126 | font-size: ${Common.fontSize.fs14}; 127 | line-height: 17px; 128 | letter-spacing: -0.4px; 129 | display: flex; 130 | justify-content: space-between; 131 | margin-top: 0.4em; 132 | p { 133 | color: #262626; 134 | strong { 135 | color: #ff6e35; 136 | } 137 | } 138 | } 139 | ` 140 | -------------------------------------------------------------------------------- /src/components/AlarmModal/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | import { Modal } from './styles' 4 | 5 | interface onCloseProps { 6 | onClose: (event: React.MouseEvent) => void 7 | path: string 8 | text?: string 9 | textOpt?: string 10 | } 11 | 12 | const AlarmModal = ({ onClose, path, text, textOpt }: onCloseProps) => { 13 | return ( 14 |
15 |
16 |
17 | 체크 18 |

{text}

19 |

{textOpt}

20 |
21 |
22 |
23 | ) 24 | } 25 | 26 | export default AlarmModal 27 | -------------------------------------------------------------------------------- /src/components/AlarmModal/styles.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const Modal = css` 4 | width: 100%; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | background: rgba(0, 0, 0, 0.3); 13 | z-index: 9999; 14 | .modal { 15 | &__wrap { 16 | max-width: 600px; 17 | width: 100%; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | position: relative; 22 | } 23 | &__box { 24 | padding: 30px; 25 | box-sizing: border; 26 | overflow: hidden; 27 | width: 77%; 28 | background: #ffffff; 29 | backdrop-filter: blur(12px); 30 | border-radius: 16px; 31 | text-align: center; 32 | h3 { 33 | font-weight: 700; 34 | font-size: 2.2rem; 35 | line-height: 26px; 36 | letter-spacing: -1px; 37 | color: #262626; 38 | padding-top: 33px; 39 | 40 | @media (max-width: 420px) { 41 | font-size: 1.7rem; 42 | } 43 | } 44 | p { 45 | font-size: 1.6rem; 46 | line-height: 22px; 47 | text-align: center; 48 | letter-spacing: -0.6px; 49 | color: #696766; 50 | padding: 10px 0 20px 0; 51 | 52 | @media (max-width: 420px) { 53 | font-size: 1.3rem; 54 | } 55 | } 56 | } 57 | } 58 | ` 59 | -------------------------------------------------------------------------------- /src/components/AuthLogin/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import { css } from '@emotion/react' 3 | import { Common } from 'src/styles/common' 4 | // import KaKao from 'public/images/login/kakao.svg' 5 | // import Naver from 'public/images/login/naver.svg' 6 | // import Email from 'public/images/login/email.svg' 7 | 8 | export default function AuthLogin() { 9 | /* 배포용 */ 10 | const kakaoLogin = () => (location.href = 'https://api.wellseecoding.com/oauth2/authorization/kakao') 11 | const naverLogin = () => (location.href = 'https://api.wellseecoding.com/oauth2/authorization/naver') 12 | 13 | /* 로컬용 */ 14 | // const kakaoLogin = () => (location.href = 'http://localhost:8080/oauth2/authorization/kakao') 15 | // const naverLogin = () => (location.href = 'http://localhost:8080/oauth2/authorization/naver') 16 | 17 | return ( 18 |
19 | 23 | 27 | 31 |

32 | 웰시가 처음이신가요? 33 | 34 | 회원가입 35 | 36 |

37 |
38 | ) 39 | } 40 | 41 | const authLoginButton = css` 42 | width: 100%; 43 | display: block; 44 | margin-top: 11em; 45 | padding: 0 20px; 46 | button { 47 | margin-top: 9px; 48 | width: 100%; 49 | border-radius: 16px; 50 | padding: 16px 0; 51 | font-weight: 500; 52 | font-size: ${Common.fontSize.fs18}; 53 | position: relative; 54 | img, 55 | svg { 56 | position: absolute; 57 | left: 21px; 58 | top: 50%; 59 | transform: translateY(-50%); 60 | } 61 | } 62 | ` 63 | 64 | const kakaoStyle = css` 65 | background: #fee500; 66 | color: #262626; 67 | ` 68 | 69 | const naverStyle = css` 70 | color: #ffffff; 71 | background: #03c75a; 72 | ` 73 | 74 | const email = css` 75 | color: #ffffff; 76 | background: #ff6e35; 77 | ` 78 | 79 | const passwordFind = css` 80 | text-align: center; 81 | font-size: ${Common.fontSize.fs16}; 82 | margin-top: 26px; 83 | color: #8f8c8b; 84 | a { 85 | margin-left: 4px; 86 | color: #ff6e35; 87 | font-weight: 500; 88 | text-decoration-line: underline; 89 | } 90 | ` 91 | -------------------------------------------------------------------------------- /src/components/Common/BigTitle/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { Common } from 'src/styles/common' 3 | // import Logo from 'public/images/login/character_color.svg' 4 | 5 | type Props = { 6 | title: string 7 | } 8 | 9 | export default function BigTitle({ title }: Props) { 10 | return ( 11 | <> 12 | 웰시코딩 로고 13 |

{title}

14 | 15 | ) 16 | } 17 | 18 | const bigTitleStyle = css` 19 | font-size: ${Common.fontSize.bigTitle}; 20 | font-weight: 500; 21 | color: ${Common.colors.black}; 22 | margin-top: 27px; 23 | ` 24 | -------------------------------------------------------------------------------- /src/components/Common/CommentModal/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 2 | import React, { useCallback, useEffect } from 'react' 3 | import { css } from '@emotion/react' 4 | import { useDispatch, useSelector } from 'react-redux' 5 | import { CLOSE_EDITMODE, CLOSE_ISMODAL, OPEN_EDITMODE } from 'src/reducers/common' 6 | import { RootState } from 'src/reducers' 7 | import { DELETE_COMMENT_REQUEST } from 'src/reducers/comments' 8 | import { useRouter } from 'next/router' 9 | import usehandleOverFlow from 'src/hooks/useHandleOverflow' 10 | 11 | function CommentModal() { 12 | const dispatch = useDispatch() 13 | const router = useRouter() 14 | 15 | const { isModal } = useSelector((state: RootState) => state.common) 16 | const { deleteCommentSuccess } = useSelector((state: RootState) => state.comments) 17 | 18 | const { show } = usehandleOverFlow() 19 | 20 | useEffect(() => { 21 | if (deleteCommentSuccess) router.reload() 22 | }, [deleteCommentSuccess, router]) 23 | 24 | const setModal = useCallback( 25 | (e) => { 26 | show() 27 | e.stopPropagation() 28 | dispatch({ 29 | type: CLOSE_ISMODAL, 30 | }) 31 | dispatch({ 32 | type: CLOSE_EDITMODE, 33 | }) 34 | }, 35 | [dispatch, show] 36 | ) 37 | 38 | const updatePost = useCallback( 39 | (e) => { 40 | show() 41 | e.stopPropagation() 42 | dispatch({ 43 | type: CLOSE_ISMODAL, 44 | }) 45 | dispatch({ 46 | type: OPEN_EDITMODE, 47 | }) 48 | }, 49 | [dispatch, show] 50 | ) 51 | 52 | const removePost = useCallback( 53 | (e, id) => { 54 | e.stopPropagation() 55 | dispatch({ 56 | type: DELETE_COMMENT_REQUEST, 57 | data: { 58 | postId: Number(id), 59 | commentId: Number(isModal.uniqId), 60 | }, 61 | }) 62 | }, 63 | [dispatch, isModal] 64 | ) 65 | 66 | return ( 67 | // eslint-disable-next-line jsx-a11y/no-static-element-interactions 68 |
69 |
70 |
71 | 74 | 77 |
78 |
79 | 82 |
83 |
84 |
85 | ) 86 | } 87 | 88 | export default CommentModal 89 | 90 | const modalWrap = css` 91 | background-color: rgba(196, 196, 196, 0.6); 92 | width: 100%; 93 | height: 100vh; 94 | z-index: 10500; 95 | position: absolute; 96 | top: 0; 97 | ` 98 | 99 | const modalBtnWrap = css` 100 | padding: 20px 20px 28px 20px; 101 | display: block; 102 | position: absolute; 103 | bottom: 0px; 104 | width: 100%; 105 | 106 | button + button { 107 | margin-bottom: 10px; 108 | } 109 | 110 | div + div { 111 | background-color: #fff !important; 112 | } 113 | ` 114 | 115 | const modalInner = css` 116 | display: block; 117 | background-color: #f1f1f1; 118 | border-radius: 14px; 119 | 120 | button + button { 121 | border-top: 1px solid rgba(196, 196, 196, 0.6); 122 | color: #fb4843 !important; 123 | } 124 | 125 | button { 126 | color: #1c81fa; 127 | padding: 18px; 128 | display: block; 129 | width: 100%; 130 | font-size: 18px; 131 | } 132 | ` 133 | -------------------------------------------------------------------------------- /src/components/Common/FlatBox/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { Common } from 'src/styles/common' 3 | 4 | type Props = { 5 | name: string 6 | contents: string 7 | } 8 | 9 | function FlatBox({ name, contents }: Props) { 10 | return ( 11 |
12 |

{name}

13 |

{contents}

14 |
15 | ) 16 | } 17 | 18 | export default FlatBox 19 | 20 | const FlatWrap = css` 21 | background-color: #fff; 22 | padding: 21px; 23 | margin-bottom: 9px; 24 | 25 | h3 { 26 | font-size: ${Common.fontSize.fs18}; 27 | font-weight: 500; 28 | margin-bottom: 8px; 29 | } 30 | 31 | p { 32 | font-size: ${Common.fontSize.fs16}; 33 | font-weight: 500; 34 | line-height: 22px; 35 | letter-spacing: -0.6px; 36 | } 37 | ` 38 | -------------------------------------------------------------------------------- /src/components/Common/FootButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonHTMLAttributes } from 'react' 2 | import { css } from '@emotion/react' 3 | import { Common } from 'src/styles/common' 4 | 5 | export enum FootButtonType { 6 | DISABLE = 'disable', 7 | ACTIVATION = 'activation', 8 | SKIP = 'skip', 9 | } 10 | 11 | interface IProps extends ButtonHTMLAttributes { 12 | footButtonType: FootButtonType 13 | } 14 | 15 | function FootButton({ children, type, footButtonType, ...props }: IProps) { 16 | return ( 17 | 26 | ) 27 | } 28 | 29 | export default FootButton 30 | 31 | const container = css` 32 | width: 100%; 33 | height: 52px; 34 | display: block; 35 | font-size: ${Common.fontSize.fs18}; 36 | border-radius: 16px; 37 | padding: 16px 0; 38 | font-style: normal; 39 | font-weight: 500; 40 | line-height: 20px; 41 | color: #ffffff; 42 | background: ${Common.colors.gray04}; 43 | 44 | &:disabled { 45 | background: ${Common.colors.gray04}; 46 | cursor: default; 47 | } 48 | 49 | &.activation { 50 | background: #ff6e35; 51 | } 52 | 53 | &.skip { 54 | background: #ffffff; 55 | border: 1px solid #ff6e35; 56 | color: ${Common.colors.black}; 57 | } 58 | ` 59 | -------------------------------------------------------------------------------- /src/components/Common/HashWrap/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { Common } from 'src/styles/common' 3 | 4 | type Props = { 5 | content: string 6 | } 7 | 8 | function HashWrap({ content }: Props) { 9 | return
#{content}
10 | } 11 | 12 | export default HashWrap 13 | 14 | const hashWrap = css` 15 | background: #ffeee7; 16 | border-radius: 56px; 17 | padding: 8px 12px; 18 | color: #ff6e35; 19 | display: inline-block; 20 | font-weight: bold; 21 | font-size: ${Common.fontSize.fs14}; 22 | line-height: 20px; 23 | margin-right: 4px; 24 | margin-top: 4px; 25 | ` 26 | -------------------------------------------------------------------------------- /src/components/Common/Header/Back/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { useRouter } from 'next/router' 3 | import { Common } from 'src/styles/common' 4 | /* 웹팩 설정을 통해 pre-render 담당 */ 5 | // import BackSvg from '/public/images/header/back.svg' 6 | 7 | type Props = { 8 | text: string 9 | } 10 | 11 | function Back({ text }: Props) { 12 | const router = useRouter() 13 | 14 | return ( 15 |
16 | 19 |

{text}

20 |
21 | ) 22 | } 23 | 24 | export default Back 25 | 26 | Back.defaultProps = { 27 | text: '', 28 | } 29 | 30 | const backHeader = css` 31 | width: 100%; 32 | text-align: left; 33 | background: #fff; 34 | top: 0; 35 | padding: 0 20px; 36 | display: flex; 37 | align-items: center; 38 | button { 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | } 43 | h1 { 44 | font-weight: 500; 45 | font-size: ${Common.fontSize.fs20}; 46 | line-height: 28px; 47 | letter-spacing: -0.4px; 48 | color: #262626; 49 | margin-left: -30px; 50 | } 51 | img { 52 | width: 100%; 53 | height: 100%; 54 | } 55 | ` 56 | -------------------------------------------------------------------------------- /src/components/Common/Header/EditBackOptional/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import usehandleOverFlow from 'src/hooks/useHandleOverflow' 3 | import { useCallback } from 'react' 4 | import { useDispatch } from 'react-redux' 5 | import { CLOSE_EDITMODE } from 'src/reducers/common' 6 | import { Common } from 'src/styles/common' 7 | 8 | type Props = { 9 | text: string 10 | } 11 | 12 | function EditBack({ text }: Props) { 13 | const dispatch = useDispatch() 14 | const { show } = usehandleOverFlow() 15 | 16 | const closeModal = useCallback(() => { 17 | show() 18 | dispatch({ 19 | type: CLOSE_EDITMODE, 20 | }) 21 | }, [show, dispatch]) 22 | 23 | return ( 24 |
25 | 28 |

{text}

29 |
30 | ) 31 | } 32 | 33 | export default EditBack 34 | 35 | EditBack.defaultProps = { 36 | text: '', 37 | } 38 | 39 | const backHeader = css` 40 | width: 100%; 41 | text-align: left; 42 | position: sticky; 43 | background: #fff; 44 | /* top: 0; */ 45 | padding: 0 20px; 46 | display: flex; 47 | align-items: center; 48 | button { 49 | display: flex; 50 | justify-content: center; 51 | align-items: center; 52 | } 53 | h1 { 54 | font-weight: 500; 55 | font-size: ${Common.fontSize.fs20}; 56 | line-height: 28px; 57 | letter-spacing: -0.4px; 58 | color: #262626; 59 | margin-left: -30px; 60 | } 61 | 62 | img { 63 | width: 100%; 64 | height: 100%; 65 | } 66 | ` 67 | -------------------------------------------------------------------------------- /src/components/Common/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import useHeader from 'src/hooks/useHeader' 2 | import { css } from '@emotion/react' 3 | 4 | function Header() { 5 | const { teamName } = useHeader() 6 | 7 | return

{teamName}

8 | } 9 | 10 | export default Header 11 | 12 | const container = css` 13 | margin: 15px 0; 14 | text-align: center; 15 | font-size: 20px; 16 | ` 17 | -------------------------------------------------------------------------------- /src/components/Common/IsModal/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 3 | import React, { useCallback, useEffect } from 'react' 4 | import { css } from '@emotion/react' 5 | import { useDispatch, useSelector } from 'react-redux' 6 | import { CLOSE_ISMODAL, OPEN_EDITMODE } from 'src/reducers/common' 7 | import { RootState } from 'src/reducers' 8 | import { DELETE_POST_REQUEST } from 'src/reducers/posts' 9 | import usehandleOverFlow from 'src/hooks/useHandleOverflow' 10 | 11 | const IsModal = () => { 12 | const { hidden, show } = usehandleOverFlow() 13 | const dispatch = useDispatch() 14 | 15 | const { isModal } = useSelector((state: RootState) => state.common) 16 | const { deletePostSuccess } = useSelector((state: RootState) => state.posts) 17 | 18 | // 내 게시글 삭제시 오류 때문에 일단 멈춰둠 19 | 20 | useEffect(() => { 21 | if (deletePostSuccess) { 22 | location.replace('/home') 23 | } 24 | }, [deletePostSuccess]) 25 | 26 | useEffect(() => { 27 | isModal && hidden() 28 | }, [isModal, hidden]) 29 | 30 | const setModal = useCallback( 31 | (e) => { 32 | e.stopPropagation() 33 | dispatch({ 34 | type: CLOSE_ISMODAL, 35 | }) 36 | show() 37 | }, 38 | [dispatch, show] 39 | ) 40 | 41 | const updatePost = useCallback( 42 | (e) => { 43 | e.stopPropagation() 44 | dispatch({ 45 | type: CLOSE_ISMODAL, 46 | }) 47 | dispatch({ 48 | type: OPEN_EDITMODE, 49 | }) 50 | }, 51 | [dispatch] 52 | ) 53 | 54 | const removePost = useCallback( 55 | (e, id) => { 56 | e.stopPropagation() 57 | dispatch({ 58 | type: DELETE_POST_REQUEST, 59 | data: id, 60 | }) 61 | }, 62 | [dispatch] 63 | ) 64 | 65 | return ( 66 |
67 |
68 |
69 | 72 | 75 |
76 |
77 | 80 |
81 |
82 |
83 | ) 84 | } 85 | 86 | export default IsModal 87 | 88 | const modalWrap = css` 89 | background-color: rgba(196, 196, 196, 0.6); 90 | width: 100%; 91 | height: 100vh; 92 | z-index: 10500; 93 | position: absolute; 94 | top: 0; 95 | ` 96 | 97 | const modalBtnWrap = css` 98 | padding: 20px 20px 28px 20px; 99 | display: block; 100 | position: absolute; 101 | bottom: 0px; 102 | width: 100%; 103 | 104 | button + button { 105 | margin-bottom: 10px; 106 | } 107 | 108 | div + div { 109 | background-color: #fff !important; 110 | } 111 | ` 112 | 113 | const modalInner = css` 114 | display: block; 115 | background-color: #f1f1f1; 116 | border-radius: 14px; 117 | 118 | button + button { 119 | border-top: 1px solid rgba(196, 196, 196, 0.6); 120 | color: #fb4843 !important; 121 | } 122 | 123 | button { 124 | color: #1c81fa; 125 | padding: 18px; 126 | display: block; 127 | width: 100%; 128 | font-size: 18px; 129 | } 130 | ` 131 | -------------------------------------------------------------------------------- /src/components/Common/JobButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | type Props = { 4 | job_text: string 5 | onClick: (e: React.MouseEvent) => void 6 | className: string 7 | } 8 | 9 | function JobButton({ job_text, onClick, className }: Props) { 10 | return ( 11 | 14 | ) 15 | } 16 | 17 | export default JobButton 18 | 19 | const hashTag = css` 20 | background: #ffffff; 21 | border: 1px solid #d3cfcc; 22 | color: #d3cfcc; 23 | border-radius: 60px; 24 | display: inline-block; 25 | padding: 10px 14px; 26 | margin-top: 14px; 27 | margin-right: 8px; 28 | &:focus { 29 | border: 1px solid #ff6e35; 30 | color: #ff6e35; 31 | } 32 | p { 33 | font-size: 1.6rem; 34 | } 35 | ` 36 | -------------------------------------------------------------------------------- /src/components/Common/PasswordField/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import Input from '@material-ui/core/Input' 3 | import InputLabel from '@material-ui/core/InputLabel' 4 | import FormControl from '@material-ui/core/FormControl' 5 | 6 | interface State { 7 | amount: string 8 | password: string 9 | weight: string 10 | weightRange: string 11 | showPassword: boolean 12 | } 13 | 14 | type Props = { 15 | title: string 16 | passwordText: string 17 | typeTitle: string 18 | onChange: (e: React.ChangeEvent) => void 19 | } 20 | 21 | export default function PasswordField({ title, passwordText, typeTitle, onChange }: Props) { 22 | const [values, setValues] = useState({ 23 | amount: '', 24 | password: '', 25 | weight: '', 26 | weightRange: '', 27 | showPassword: false, 28 | }) 29 | 30 | const handleChange = (key: keyof State) => (event: React.ChangeEvent) => { 31 | setValues({ ...values, [key]: event.target.value }) 32 | onChange(event) 33 | } 34 | 35 | return ( 36 | 37 | {title} 38 | 46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Common/TextField/index.tsx: -------------------------------------------------------------------------------- 1 | import TextField from '@material-ui/core/TextField' 2 | 3 | type Props = { 4 | text: string 5 | type: string 6 | typeName: string 7 | onChange: (e: React.ChangeEvent) => void 8 | } 9 | 10 | export default function TextFields({ text, typeName, type, onChange }: Props) { 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Common/TextFieldProfile/index.tsx: -------------------------------------------------------------------------------- 1 | import TextField from '@material-ui/core/TextField' 2 | 3 | type Props = { 4 | text: string 5 | type: string 6 | onChange: (e: React.ChangeEvent) => void 7 | onKeyUp?: (e: React.ChangeEvent) => void 8 | placeholder?: string 9 | value?: string | number 10 | name?: string 11 | } 12 | 13 | export default function TextFields({ text, type, onChange, placeholder, value }: Props) { 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Common/Title/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { Common } from 'src/styles/common' 3 | 4 | type Props = { 5 | title: string 6 | className?: string 7 | } 8 | 9 | export default function Title({ title, className }: Props) { 10 | return ( 11 |

12 | {title} 13 |

14 | ) 15 | } 16 | 17 | const titleStyle = css` 18 | font-size: ${Common.fontSize.title}; 19 | font-weight: 500; 20 | color: ${Common.colors.black}; 21 | padding-left: 20px; 22 | padding-top: 3.7em; 23 | ` 24 | -------------------------------------------------------------------------------- /src/components/Common/wellseeError/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import React from 'react' 3 | import { Common } from 'src/styles/common' 4 | 5 | export type Props = { 6 | text: string 7 | textOpt?: string 8 | buttonOpt?: string 9 | } 10 | 11 | const WellseeError = ({ text, textOpt, buttonOpt }: Props) => { 12 | return ( 13 |
14 | 뒤로가기 15 |
16 |

17 | {text} 18 |

19 |
20 | {textOpt && ( 21 |

22 | {textOpt} 23 |

24 | )} 25 | 26 | {buttonOpt && ( 27 |
28 | 31 |
32 | )} 33 |
34 |
35 | ) 36 | } 37 | 38 | export default WellseeError 39 | 40 | const errorWrap = css` 41 | display: flex; 42 | width: 100%; 43 | justify-content: center; 44 | height: 95vh; 45 | align-items: center; 46 | flex-direction: column; 47 | background: #ffeee7; 48 | ` 49 | 50 | const footButtonWrapper = css` 51 | font-size: ${Common.fontSize.fs16}; 52 | margin-top: 30px; 53 | padding: 16px; 54 | border-radius: 16px; 55 | background-color: #ff6e35; 56 | color: #ffffff; 57 | ` 58 | -------------------------------------------------------------------------------- /src/components/Common/wellseeErrorHome/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import React from 'react' 3 | import { Common } from 'src/styles/common' 4 | 5 | export type Props = { 6 | text: string 7 | textOpt?: string 8 | buttonOpt?: string 9 | } 10 | 11 | const WellseeErrorHome = ({ text, textOpt, buttonOpt }: Props) => { 12 | return ( 13 |
14 | 뒤로가기 15 |
16 |

17 | {text} 18 |

19 |
20 | {textOpt && ( 21 |

22 | {textOpt} 23 |

24 | )} 25 | 26 | {buttonOpt && ( 27 |
28 | 31 |
32 | )} 33 |
34 |
35 | ) 36 | } 37 | 38 | export default WellseeErrorHome 39 | 40 | const ErrorOpt = css` 41 | background: #ffeee7; 42 | margin-top: 3em; 43 | padding-top: 10em; 44 | display: flex; 45 | width: 100%; 46 | justify-content: center; 47 | height: 100%; 48 | align-items: center; 49 | flex-direction: column; 50 | background: #ffeee7; 51 | 52 | @media (max-width: 420px) { 53 | padding-top: 3em; 54 | } 55 | ` 56 | 57 | const footButtonWrapper = css` 58 | font-size: ${Common.fontSize.fs16}; 59 | margin-top: 30px; 60 | padding: 16px; 61 | border-radius: 16px; 62 | background-color: #ff6e35; 63 | color: #ffffff; 64 | ` 65 | -------------------------------------------------------------------------------- /src/components/ConfirmModal/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/click-events-have-key-events */ 2 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 3 | import { Modal } from './styles' 4 | 5 | interface Props { 6 | onClose: (event: React.MouseEvent) => void 7 | confirmResult: () => void 8 | h3: string 9 | p1: string 10 | p2?: string 11 | } 12 | 13 | const ConfirmModal = ({ onClose, confirmResult, h3, p1, p2 }: Props) => { 14 | return ( 15 |
16 |
17 | 안내견 18 |
19 |

{h3}

20 |

21 | {p1} 22 |
23 | {p2} 24 |

25 |
26 | 29 | 32 |
33 |
34 |
35 |
36 | ) 37 | } 38 | 39 | export default ConfirmModal 40 | -------------------------------------------------------------------------------- /src/components/ConfirmModal/styles.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const Modal = css` 4 | width: 100%; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | background: rgba(0, 0, 0, 0.3); 13 | z-index: 9999; 14 | .modal { 15 | &__wrap { 16 | max-width: 600px; 17 | width: 100%; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | position: relative; 22 | img { 23 | position: absolute; 24 | top: -78px; 25 | z-index: 100; 26 | } 27 | } 28 | &__box { 29 | box-sizing: border; 30 | overflow: hidden; 31 | width: 77%; 32 | background: #ffffff; 33 | backdrop-filter: blur(12px); 34 | border-radius: 16px; 35 | text-align: center; 36 | h3 { 37 | font-weight: 700; 38 | font-size: 2.2rem; 39 | line-height: 26px; 40 | letter-spacing: -1px; 41 | color: #262626; 42 | padding-top: 33px; 43 | 44 | @media (max-width: 420px) { 45 | font-size: 1.6rem; 46 | } 47 | } 48 | p { 49 | font-size: 1.6rem; 50 | line-height: 22px; 51 | text-align: center; 52 | letter-spacing: -0.6px; 53 | color: #696766; 54 | padding: 10px 0 20px 0; 55 | 56 | @media (max-width: 420px) { 57 | font-size: 1.2rem; 58 | } 59 | } 60 | } 61 | &__btn { 62 | display: flex; 63 | justify-content: center; 64 | align-content: center; 65 | width: 100%; 66 | border-top: 1px solid #efebe8; 67 | button { 68 | padding: 18px 0; 69 | font-size: 2rem; 70 | width: 50%; 71 | box-sizing: border-box; 72 | 73 | @media (max-width: 420px) { 74 | font-size: 1.5rem; 75 | } 76 | &.delete { 77 | color: #ff6e35; 78 | border-left: 1px solid #efebe8; 79 | } 80 | } 81 | } 82 | } 83 | ` 84 | -------------------------------------------------------------------------------- /src/components/DataForm/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TodoType } from 'src/types' 3 | import { MainContent } from './style' 4 | 5 | type Props = { 6 | datas: TodoType[] | undefined 7 | } 8 | 9 | function DataForm(props: Props) { 10 | const { datas } = props 11 | 12 | return ( 13 |
14 | {datas && 15 | datas.map((data) => ( 16 | 17 | {data.completed === false ?

non-completed

:

completed

} 18 |

userId: {data.userId}

19 |

title: {data.title}

20 |
21 | ))} 22 |
23 | ) 24 | } 25 | 26 | export default DataForm 27 | -------------------------------------------------------------------------------- /src/components/DataForm/style.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled' 2 | 3 | interface StyledDivProps { 4 | complete: boolean 5 | } 6 | 7 | export const MainContent = styled.div` 8 | border: 1px solid black; 9 | margin: 20px 0; 10 | padding: 20px; 11 | 12 | background-color: ${(props) => props.complete === false && 'salmon'}; 13 | 14 | & p { 15 | font-weight: bolder; 16 | } 17 | ` 18 | -------------------------------------------------------------------------------- /src/components/Home/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import { css } from '@emotion/react' 3 | 4 | type Props = { 5 | notis: boolean 6 | } 7 | 8 | function HomeHeader({ notis }: Props) { 9 | return ( 10 |
11 | 12 | {notis ? ( 13 | 14 | 알림페이지 15 | 16 | ) : ( 17 | 18 | 알림페이지 19 | 20 | )} 21 | 22 |
23 | ) 24 | } 25 | 26 | export default HomeHeader 27 | 28 | export const HomeHeaderWrap = css` 29 | display: flex; 30 | justify-content: flex-end; 31 | padding: 0 20px; 32 | ` 33 | -------------------------------------------------------------------------------- /src/components/Home/Main/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { Common } from 'src/styles/common' 3 | 4 | interface Props { 5 | user: string | null 6 | num?: number 7 | } 8 | function HomeMain({ user, num }: Props) { 9 | return ( 10 |
11 | 웰시코딩 캐릭터 12 | 13 |
14 |

15 | 안녕하세요 {user}님 👋 16 |

17 |

18 | {num ? ( 19 | 20 | 가입하신 스터디는 21 |
총 {num}개에요~ 22 |
23 | ) : ( 24 | 25 | 아직 가입하신 26 |
스터디가 없어요.. 🥲 27 |
28 | )} 29 |

30 |
31 |
32 | ) 33 | } 34 | 35 | export default HomeMain 36 | 37 | export const homeMainWrap = css` 38 | position: relative; 39 | background-color: white; 40 | display: flex; 41 | justify-content: center; 42 | 43 | img { 44 | z-index: 10; 45 | position: absolute; 46 | left: 5%; 47 | clip: rect(0px, 220px, 100px, 0px); 48 | } 49 | div { 50 | margin-top: 1em; 51 | margin-left: 1em; 52 | @media (max-width: 420px) { 53 | margin-left: 12em; 54 | } 55 | h1 { 56 | font-weight: 500; 57 | font-size: ${Common.fontSize.fs18}; 58 | line-height: 26px; 59 | color: #262626; 60 | letter-spacing: -0.6px; 61 | @media (max-width: 420px) { 62 | font-size: ${Common.fontSize.fs16}; 63 | } 64 | } 65 | p { 66 | font-weight: 500; 67 | font-size: ${Common.fontSize.title}; 68 | line-height: 32px; 69 | letter-spacing: -1px; 70 | color: #262626; 71 | @media (max-width: 420px) { 72 | font-size: ${Common.fontSize.fs16}; 73 | } 74 | } 75 | } 76 | ` 77 | -------------------------------------------------------------------------------- /src/components/Home/StudySection/index.tsx: -------------------------------------------------------------------------------- 1 | import StudySlider from 'src/components/Together/StudySlider' 2 | import { css } from '@emotion/react' 3 | import { PostType } from 'src/types' 4 | 5 | // data는 dataProps { } 객체 형식으로 이루어진 배열이다 6 | 7 | type Props = { 8 | title: string 9 | data: PostType[] 10 | } 11 | 12 | function StudyTitle({ title, data }: Props) { 13 | return ( 14 |
15 |

16 | {title} 17 |

18 | 19 | 20 |
21 | ) 22 | } 23 | 24 | export default StudyTitle 25 | 26 | const titleStyle = css` 27 | font-size: 2.2rem; 28 | color: #262626; 29 | font-weight: 500; 30 | margin-top: 22px; 31 | margin-left: 20px; 32 | margin-bottom: 16px; 33 | ` 34 | -------------------------------------------------------------------------------- /src/components/LikePost/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | interface Props { 3 | title: string 4 | name: string 5 | date: number 6 | } 7 | 8 | function LikePostList({ title, name, date }: Props) { 9 | return ( 10 |
  • 11 |

    {title}

    12 |
    13 |
    {name}
    | {date} 14 |
    15 |
  • 16 | ) 17 | } 18 | 19 | export default LikePostList 20 | 21 | const list = css` 22 | border-bottom: 1px solid #d3cfcc; 23 | padding: 20px 0; 24 | h3 { 25 | font-size: 1.6rem; 26 | line-height: 22px; 27 | letter-spacing: -0.6px; 28 | color: #262626; 29 | } 30 | div { 31 | margin-top: 4px; 32 | font-size: 1.6rem; 33 | line-height: 22px; 34 | letter-spacing: -0.6px; 35 | color: rgba(131, 131, 131, 0.87); 36 | display: flex; 37 | } 38 | h5 { 39 | margin-right: 0.5rem; 40 | } 41 | span { 42 | margin-left: 0.5rem; 43 | } 44 | ` 45 | -------------------------------------------------------------------------------- /src/components/Loading/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import React from 'react' 3 | import { Common } from 'src/styles/common' 4 | 5 | const Loading = () => { 6 | return ( 7 | <> 8 |
    loading...
    9 | 10 | ) 11 | } 12 | 13 | const loading = css` 14 | font-size: ${Common.fontSize.title}; 15 | width: 100%; 16 | height: 100vh; 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | ` 21 | 22 | export default Loading 23 | -------------------------------------------------------------------------------- /src/components/LogOutModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | import { Modal } from './styles' 3 | 4 | interface onCloseProps { 5 | onClose: (event: React.MouseEvent) => void 6 | } 7 | 8 | const MoreModal = (props: onCloseProps) => { 9 | const logOut = useCallback(() => { 10 | if (typeof window !== 'undefined') { 11 | localStorage.clear() 12 | eraseCookie('access_token') 13 | props.onClose 14 | location.replace('/sign_in/auth_start') 15 | } 16 | }, []) 17 | 18 | function eraseCookie(name: string) { 19 | document.cookie = name + '=; Max-Age=0' 20 | } 21 | return ( 22 |
    23 |
    24 | 안내견 25 |
    26 |

    로그아웃

    27 |

    28 | 아직 다양한 모임들이 참여를 기다리고 있어요! 29 |
    30 | 그래도 로그아웃 하시겠어요? 31 |

    32 |
    33 | 36 | 39 |
    40 |
    41 |
    42 |
    43 | ) 44 | } 45 | 46 | export default MoreModal 47 | -------------------------------------------------------------------------------- /src/components/LogOutModal/styles.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const Modal = css` 4 | width: 100%; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | background: rgba(0, 0, 0, 0.3); 13 | z-index: 100; 14 | .modal { 15 | &__wrap { 16 | max-width: 600px; 17 | width: 100%; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | position: relative; 22 | img { 23 | position: absolute; 24 | top: -78px; 25 | z-index: 100; 26 | } 27 | } 28 | &__box { 29 | box-sizing: border; 30 | overflow: hidden; 31 | width: 77%; 32 | background: #ffffff; 33 | backdrop-filter: blur(12px); 34 | border-radius: 16px; 35 | text-align: center; 36 | h3 { 37 | font-weight: 700; 38 | font-size: 2.2rem; 39 | line-height: 26px; 40 | letter-spacing: -1px; 41 | color: #262626; 42 | padding-top: 33px; 43 | } 44 | p { 45 | font-size: 1.6rem; 46 | line-height: 22px; 47 | text-align: center; 48 | letter-spacing: -0.6px; 49 | color: #696766; 50 | padding: 10px 0 20px 0; 51 | } 52 | } 53 | &__btn { 54 | display: flex; 55 | justify-content: center; 56 | align-content: center; 57 | width: 100%; 58 | border-top: 1px solid #efebe8; 59 | button { 60 | padding: 18px 0; 61 | font-size: 2rem; 62 | width: 50%; 63 | box-sizing: border-box; 64 | &.delete { 65 | color: #ff6e35; 66 | border-left: 1px solid #efebe8; 67 | } 68 | } 69 | } 70 | } 71 | ` 72 | -------------------------------------------------------------------------------- /src/components/Modal/index.tsx: -------------------------------------------------------------------------------- 1 | import { Modal } from './styles' 2 | 3 | interface onCloseProps { 4 | onClose: (event: React.MouseEvent) => void 5 | } 6 | 7 | const MoreModal = (props: onCloseProps) => { 8 | return ( 9 |
    10 |
    11 | 안내견 12 |
    13 |

    알림을 모두 삭제 하시겠어요?

    14 |

    15 | 내 서랍의 모든 알림이 삭제됩니다.
    16 | 삭제된 알림은 다시 복구할 수 없습니다. 17 |

    18 |
    19 | 22 | 23 |
    24 |
    25 |
    26 |
    27 | ) 28 | } 29 | 30 | export default MoreModal 31 | -------------------------------------------------------------------------------- /src/components/Modal/styles.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const Modal = css` 4 | width: 100%; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | background: rgba(0, 0, 0, 0.3); 13 | z-index: 100; 14 | .modal { 15 | &__wrap { 16 | max-width: 600px; 17 | width: 100%; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | position: relative; 22 | img { 23 | position: absolute; 24 | top: -78px; 25 | z-index: 100; 26 | } 27 | } 28 | &__box { 29 | box-sizing: border; 30 | overflow: hidden; 31 | width: 77%; 32 | background: #ffffff; 33 | backdrop-filter: blur(12px); 34 | border-radius: 16px; 35 | text-align: center; 36 | h3 { 37 | font-weight: 700; 38 | font-size: 2.2rem; 39 | line-height: 26px; 40 | letter-spacing: -1px; 41 | color: #262626; 42 | padding-top: 33px; 43 | } 44 | p { 45 | font-size: 1.6rem; 46 | line-height: 22px; 47 | text-align: center; 48 | letter-spacing: -0.6px; 49 | color: #696766; 50 | padding: 10px 0 20px 0; 51 | } 52 | } 53 | &__btn { 54 | display: flex; 55 | justify-content: center; 56 | align-content: center; 57 | width: 100%; 58 | border-top: 1px solid #efebe8; 59 | button { 60 | padding: 18px 0; 61 | font-size: 2rem; 62 | width: 50%; 63 | box-sizing: border-box; 64 | &.delete { 65 | color: #ff6e35; 66 | border-left: 1px solid #efebe8; 67 | } 68 | } 69 | } 70 | } 71 | ` 72 | -------------------------------------------------------------------------------- /src/components/MyPage/Career/index.tsx: -------------------------------------------------------------------------------- 1 | import { box } from './style' 2 | import React, { useCallback, useState } from 'react' 3 | import AlarmModal from 'src/components/AlarmModal' 4 | 5 | interface CareerProps { 6 | company: string 7 | job: string 8 | year: number 9 | } 10 | 11 | const Career = (props: CareerProps) => { 12 | const myInfo = JSON.stringify(localStorage.getItem('access_token')) 13 | const [confirmModal, setConfirmModal] = useState(false) 14 | 15 | // 이미 가입된 알림 모달 끄기 16 | const closeModal = useCallback(() => { 17 | setConfirmModal(false) 18 | }, []) 19 | return ( 20 |
    21 |

    22 | 경력 총 {props.year}년차 23 |

    24 | 25 | {/* 회사이름 */} 26 |

    {props.company}

    27 | {/* 직업군 | 기술스택 년도 */} 28 |

    29 | 기술스택 | {props.job} | 경력 | {props.year}년차 30 |

    31 | 32 | {myInfo ? ( 33 | 36 | ) : ( 37 |
    38 | )} 39 | {confirmModal && ( 40 | 45 | )} 46 |
    47 | ) 48 | } 49 | 50 | export default Career 51 | -------------------------------------------------------------------------------- /src/components/MyPage/Career/style.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const box = css` 4 | background: #ffffff; 5 | border: 1px solid #ffeee7; 6 | box-sizing: border-box; 7 | box-shadow: 0px 7px 24px rgb(0 0 0 / 10%); 8 | border-radius: 10px; 9 | margin-bottom: 18px; 10 | padding: 26px; 11 | position: relative; 12 | button { 13 | position: absolute; 14 | right: 26px; 15 | top: 26px; 16 | } 17 | h2 { 18 | font-weight: bold; 19 | font-size: 2rem; 20 | line-height: 22px; 21 | letter-spacing: 0.15px; 22 | color: rgba(0, 0, 0, 0.87); 23 | margin-bottom: 17px; 24 | strong { 25 | color: #ff6e35; 26 | margin-left: 4px; 27 | font-weight: 500; 28 | } 29 | } 30 | p { 31 | font-weight: 500; 32 | font-size: 1.8rem; 33 | line-height: 26px; 34 | display: flex; 35 | letter-spacing: -0.6px; 36 | color: #444241; 37 | } 38 | .company { 39 | font-weight: bold; 40 | font-size: 1.8rem; 41 | line-height: 26px; 42 | letter-spacing: -0.6px; 43 | color: #444241; 44 | } 45 | .desc { 46 | font-size: 1.6rem; 47 | line-height: 22px; 48 | letter-spacing: -0.6px; 49 | color: rgba(131, 131, 131, 0.87); 50 | margin-top: 3px; 51 | } 52 | ` 53 | -------------------------------------------------------------------------------- /src/components/MyPage/Portfolio/index.tsx: -------------------------------------------------------------------------------- 1 | import { box } from './style' 2 | import React, { useCallback, useState } from 'react' 3 | import AlarmModal from 'src/components/AlarmModal' 4 | 5 | interface PortfolioProps { 6 | link: string 7 | name: string 8 | description: string 9 | } 10 | 11 | const Portfolio = (props: PortfolioProps) => { 12 | const myInfo = JSON.stringify(localStorage.getItem('access_token')) 13 | const [confirmModal, setConfirmModal] = useState(false) 14 | 15 | // 이미 가입된 알림 모달 끄기 16 | const closeModal = useCallback(() => { 17 | setConfirmModal(false) 18 | }, []) 19 | 20 | return ( 21 |
    22 |

    포트폴리오

    23 |

    24 | 25 | {props.name} 26 |

    27 | 28 | 29 | {props.link} 30 | 31 | 32 |

    {props.description}

    33 | 34 | {myInfo ? ( 35 | 38 | ) : ( 39 |
    40 | )} 41 | 42 | {confirmModal && ( 43 | 48 | )} 49 |
    50 | ) 51 | } 52 | 53 | export default Portfolio 54 | -------------------------------------------------------------------------------- /src/components/MyPage/Portfolio/style.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const box = css` 4 | background: #ffffff; 5 | border: 1px solid #ffeee7; 6 | box-sizing: border-box; 7 | box-shadow: 0px 7px 24px rgb(0 0 0 / 10%); 8 | border-radius: 10px; 9 | margin-bottom: 18px; 10 | padding: 26px; 11 | position: relative; 12 | button { 13 | position: absolute; 14 | right: 26px; 15 | top: 26px; 16 | } 17 | h2 { 18 | font-weight: bold; 19 | font-size: 2rem; 20 | line-height: 22px; 21 | letter-spacing: 0.15px; 22 | color: rgba(0, 0, 0, 0.87); 23 | margin-bottom: 18px; 24 | } 25 | 26 | p { 27 | font-weight: 500; 28 | font-size: 1.8rem; 29 | line-height: 26px; 30 | display: flex; 31 | letter-spacing: -0.6px; 32 | color: #444241; 33 | display: flex; 34 | align-items: center; 35 | margin-bottom: 8px; 36 | img { 37 | margin-right: 6px; 38 | } 39 | } 40 | .desc { 41 | font-size: 1.6rem; 42 | line-height: 22px; 43 | letter-spacing: -0.6px; 44 | color: rgba(131, 131, 131, 0.87); 45 | margin-top: 14px; 46 | } 47 | a { 48 | font-size: 1.4rem; 49 | line-height: 18px; 50 | letter-spacing: -0.4px; 51 | text-decoration-line: underline; 52 | color: #ff6e35; 53 | } 54 | ` 55 | -------------------------------------------------------------------------------- /src/components/MyPage/Profile/index.tsx: -------------------------------------------------------------------------------- 1 | import { box } from './style' 2 | import React, { useCallback } from 'react' 3 | import { useRouter } from 'next/router' 4 | 5 | interface ProfileProps { 6 | id: string | null 7 | name: string | null 8 | job: string 9 | nowJob: string 10 | skill: string[] 11 | aboutme: string 12 | } 13 | 14 | const Profile = (props: ProfileProps) => { 15 | const myInfo = JSON.stringify(localStorage.getItem('access_token')) 16 | const router = useRouter() 17 | 18 | const UpdatePage = useCallback(() => { 19 | router.push('/sign_up/self_introduction/update') 20 | }, [router]) 21 | 22 | return ( 23 |
    24 |
    25 |

    26 | gravatar 27 |

    28 | 29 |
    30 |

    {props.name}

    31 | {props.job} 32 |
    33 | 34 | {myInfo ? ( 35 | 38 | ) : ( 39 |
    40 | )} 41 |
    42 | 43 |
    44 |

    {props.name}님은 현재?

    45 |

    {props.nowJob}이에요!

    46 |
    47 | 48 |
    49 |

    {props.name}님의 기술스택은?

    50 |
      51 | {props.skill.map((v, i) => ( 52 |
    • #{v}
    • 53 | ))} 54 |
    55 |
    56 | 57 |
    58 |

    {props.name}님의 자기소개

    59 |
      60 |
    • {props.aboutme}
    • 61 |
    62 |
    63 |
    64 | ) 65 | } 66 | 67 | export default Profile 68 | -------------------------------------------------------------------------------- /src/components/MyPage/Profile/style.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const box = css` 4 | background: #ffffff; 5 | border: 1px solid #ffeee7; 6 | box-sizing: border-box; 7 | box-shadow: 0px 7px 24px rgb(0 0 0 / 10%); 8 | border-radius: 10px; 9 | margin-bottom: 18px; 10 | padding: 26px; 11 | .skill { 12 | margin-bottom: 2rem; 13 | ul { 14 | display: flex; 15 | 16 | li { 17 | margin-right: 5px; 18 | } 19 | } 20 | } 21 | .profile { 22 | display: grid; 23 | grid-template-columns: 1fr 6fr auto; 24 | align-items: center; 25 | margin-bottom: 15px; 26 | .me { 27 | margin-left: 1.2em; 28 | h2 { 29 | margin-bottom: 6px; 30 | } 31 | } 32 | 33 | p { 34 | width: 56px; 35 | height: 56px; 36 | border-radius: 50%; 37 | box-sizing: border-box; 38 | object-fit: cover; 39 | background-color: #d3cfcc; 40 | img { 41 | width: 56px; 42 | height: 56px; 43 | border-radius: 50%; 44 | box-sizing: border-box; 45 | object-fit: cover; 46 | } 47 | } 48 | strong { 49 | font-size: 1.6rem; 50 | line-height: 22px; 51 | letter-spacing: -0.6px; 52 | color: #ff6e35; 53 | } 54 | button { 55 | justify-content: end; 56 | } 57 | } 58 | h2 { 59 | font-weight: bold; 60 | font-size: 2rem; 61 | line-height: 22px; 62 | letter-spacing: 0.15px; 63 | color: rgba(0, 0, 0, 0.87); 64 | } 65 | 66 | .moreme { 67 | &.career { 68 | margin-bottom: 2rem; 69 | } 70 | h3 { 71 | font-size: 1.6rem; 72 | line-height: 20px; 73 | letter-spacing: -0.4px; 74 | color: #8f8c8b; 75 | margin-bottom: 8px; 76 | } 77 | p, 78 | li { 79 | font-weight: 500; 80 | font-size: 1.6rem; 81 | line-height: 24px; 82 | letter-spacing: -1px; 83 | color: #444241; 84 | } 85 | } 86 | ` 87 | -------------------------------------------------------------------------------- /src/components/MyPage/School/index.tsx: -------------------------------------------------------------------------------- 1 | import { box } from './style' 2 | import React, { useCallback } from 'react' 3 | import { useRouter } from 'next/router' 4 | 5 | interface SchoolProps { 6 | degree: string 7 | graduated: boolean 8 | major: string 9 | } 10 | 11 | const School = (props: SchoolProps) => { 12 | const myInfo = JSON.stringify(localStorage.getItem('access_token')) 13 | const router = useRouter() 14 | 15 | const UpdatePage = useCallback(() => { 16 | router.push('/sign_up/school/update') 17 | }, [router]) 18 | 19 | return ( 20 |
    21 |

    학교정보

    22 |

    23 | {/* 학위 / 전공 */} 24 | {props.degree} / {props.major} 25 |

    26 | {/* 재학여부 */} 27 | {props.graduated === true ?
    졸업
    :
    재학중
    }
    28 | 29 | {myInfo ? ( 30 | 33 | ) : ( 34 |
    35 | )} 36 |
    37 | ) 38 | } 39 | 40 | export default School 41 | -------------------------------------------------------------------------------- /src/components/MyPage/School/style.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const box = css` 4 | background: #ffffff; 5 | border: 1px solid #ffeee7; 6 | box-sizing: border-box; 7 | box-shadow: 0px 7px 24px rgb(0 0 0 / 10%); 8 | border-radius: 10px; 9 | margin-bottom: 18px; 10 | padding: 26px; 11 | position: relative; 12 | button { 13 | position: absolute; 14 | right: 26px; 15 | top: 26px; 16 | } 17 | h2 { 18 | font-weight: bold; 19 | font-size: 2rem; 20 | line-height: 22px; 21 | letter-spacing: 0.15px; 22 | color: rgba(0, 0, 0, 0.87); 23 | margin-bottom: 18px; 24 | } 25 | 26 | p { 27 | font-weight: 500; 28 | font-size: 1.8rem; 29 | line-height: 26px; 30 | display: flex; 31 | letter-spacing: -0.6px; 32 | color: #444241; 33 | display: flex; 34 | align-items: center; 35 | margin-bottom: 8px; 36 | } 37 | .desc { 38 | font-size: 1.6rem; 39 | line-height: 22px; 40 | letter-spacing: -0.6px; 41 | color: rgba(131, 131, 131, 0.87); 42 | margin-top: 3px; 43 | } 44 | ` 45 | -------------------------------------------------------------------------------- /src/components/PortFolioDeleteForm/index.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | interface IList { 4 | idx: number 5 | name: string 6 | link: string 7 | description?: string 8 | isDelete?: boolean 9 | onDelete: (idx: number) => void 10 | } 11 | 12 | function PortFolioDeleteForm(props: IList) { 13 | const handleDelete = () => { 14 | props.onDelete(props.idx) 15 | } 16 | 17 | return ( 18 |
    19 | {props.idx !== 0 && !props.isDelete && ( 20 |
    21 | 29 | 30 |

    31 | 프로젝트 이름 {props.name} 32 |

    33 |

    34 | 링크 {props.link} 35 |

    36 |

    37 | 설명 {props.description} 38 |

    39 |
    40 | )} 41 |
    42 | ) 43 | } 44 | 45 | export default PortFolioDeleteForm 46 | 47 | const info = css` 48 | background: #ffffff; 49 | border: 1px solid #ffeee7; 50 | box-sizing: border-box; 51 | box-shadow: 0px 7px 24px rgba(0, 0, 0, 0.1); 52 | border-radius: 10px; 53 | margin-bottom: 18px; 54 | padding: 26px; 55 | p { 56 | font-size: 2rem; 57 | margin-bottom: 22px; 58 | color: #444; 59 | &:nth-of-type(1) { 60 | margin-top: 44px; 61 | } 62 | b { 63 | font-weight: 600; 64 | } 65 | } 66 | ` 67 | 68 | const infoWrap = css` 69 | padding: 0.5rem 0; 70 | .formBox { 71 | margin-bottom: 250px; 72 | } 73 | .delete { 74 | font-size: 30px; 75 | float: right; 76 | color: #444; 77 | } 78 | ` 79 | -------------------------------------------------------------------------------- /src/components/Post/EditComment/index.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router' 2 | import React, { useCallback, useEffect, useState } from 'react' 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import { RootState } from 'src/reducers' 5 | import { UPDATE_COMMENT_REQUEST } from 'src/reducers/comments' 6 | import { CLOSE_EDITMODE, CLOSE_ISMODAL } from 'src/reducers/common' 7 | 8 | export type Props = { 9 | value: string 10 | postId: string | string[] | undefined 11 | commentId: number 12 | } 13 | 14 | function EditComment({ value, commentId, postId }: Props) { 15 | const [text, setText] = useState(value) 16 | const dispatch = useDispatch() 17 | const { updateCommentSuccess } = useSelector((state: RootState) => state.comments) 18 | const router = useRouter() 19 | 20 | useEffect(() => { 21 | if (updateCommentSuccess) router.reload() 22 | }, [updateCommentSuccess, router]) 23 | 24 | const onChangeText = useCallback((e) => { 25 | setText(e.target.value) 26 | }, []) 27 | 28 | const editComment = useCallback( 29 | (e) => { 30 | e.preventDefault() 31 | try { 32 | dispatch({ 33 | type: UPDATE_COMMENT_REQUEST, 34 | data: { 35 | postId: Number(postId), 36 | commentId: commentId, 37 | text: text, 38 | }, 39 | }) 40 | } catch (err) { 41 | console.error(err) 42 | } 43 | }, 44 | [dispatch, commentId, postId, text] 45 | ) 46 | 47 | return ( 48 |
    49 |