Skip to content

Commit

Permalink
Merge pull request #46 from innovationacademy-kr/develop
Browse files Browse the repository at this point in the history
메인 서비스 배포를 위한 PR
42inshin authored Mar 13, 2023
2 parents bdd2ed4 + ef0054c commit a59a6d8
Showing 161 changed files with 14,554 additions and 11,105 deletions.
5 changes: 0 additions & 5 deletions .eslint

This file was deleted.

15 changes: 15 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')

module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier'
],
parserOptions: {
ecmaVersion: 'latest'
}
}
8 changes: 5 additions & 3 deletions .github/workflows/s3_deploy_dev.yml
Original file line number Diff line number Diff line change
@@ -12,20 +12,22 @@ jobs:
REACT_APP_API_URL: ${{secrets.REACT_APP_API_URL_DEV}}
REACT_APP_ENV: ${{secrets.REACT_APP_ENV_DEV}}
REACT_APP_SENTRY_DSN: ${{secrets.REACT_APP_SENTRY_DSN}}
VITE_APP_API_URL: ${{secrets.VITE_APP_API_URL_DEV}}
VITE_TOKEN: ${{secrets.VITE_TOKEN}}

steps:
- uses: actions/checkout@v2

- name: Install dependencies
run: yarn
run: npm i

- name: Build dev
run: yarn build:dev
run: npm run build

- name: Deploy
uses: reggionick/s3-deploy@v3
with:
folder: build
folder: dist
bucket: ${{ secrets.S3_BUCKET_DEV }}
bucket-region: ${{secrets.DEFAULT_BUCKET_REGION}}
dist-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID_DEV }}
8 changes: 5 additions & 3 deletions .github/workflows/s3_deploy_master.yml
Original file line number Diff line number Diff line change
@@ -12,20 +12,22 @@ jobs:
REACT_APP_API_URL: ${{secrets.REACT_APP_API_URL}}
REACT_APP_ENV: ${{secrets.REACT_APP_ENV}}
REACT_APP_SENTRY_DSN: ${{secrets.REACT_APP_SENTRY_DSN}}
VITE_APP_API_URL: ${{secrets.VITE_APP_API_URL}}
VITE_TOKEN: ${{secrets.VITE_TOKEN}}

steps:
- uses: actions/checkout@v2

- name: Install dependencies
run: yarn
run: npm i

- name: Build Production
run: yarn build:prod
run: npm run build

- name: Deploy
uses: reggionick/s3-deploy@v3
with:
folder: build
folder: dist
bucket: ${{ secrets.S3_BUCKET }}
bucket-region: ${{secrets.DEFAULT_BUCKET_REGION}}
dist-id: ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
47 changes: 26 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# dependencies
/node_modules
/.pnp
.pnp.js
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

# testing
/coverage
*.env

# production
/build
/cypress/videos/
/cypress/screenshots/

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env.development*
.env.production
12 changes: 5 additions & 7 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
{
"trailingComma": "all",
"printWidth": 100,
"semi": true,
"singleQuote": false,
"bracketSameLine": false,
"arrowParens": "always",
"semi": true,
"useTabs": false,
"tabWidth": 2,
"printWidth": 80,
"bracketSpacing": true,
"jsxSingleQuote": true,
"tabWidth": 2
"arrowParens": "always"
}
204 changes: 139 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,155 @@
# 24HANE (42Checkin_v3-frontend)

# 프로젝트 실행 방법
<a href="https://24hoursarenotenough.42seoul.kr/" target="_blank">
<img width="100" alt="image" src="https://user-images.githubusercontent.com/72684256/222954950-6ab18005-81e1-4d0b-93d5-6097f11fee32.png">
</a>

# 24HANE

### 24 hours are not enough! 24시간이 부족해!

안녕하세요:) 42서울 공식 체크인 서비스 24HANE입니다💌

`"24 Hane" (24 hours are not enough)` 은 Cadet 여러분의 클러스터 출입 여부와 출입 누적 시간을 직접 확인할 수 있도록 개발된 서비스 입니다.

원활하게 서비스를 이용할 수 있도록 기본 기능을 소개합니다. 👋

<br />
<br />

## 서비스 소개

<p align="center">
<a href="https://24hoursarenotenough.42seoul.kr/" target="_blank">
<img width="100" alt="image" src="https://user-images.githubusercontent.com/72684256/223358425-9eca18d8-577b-4476-8c93-aeeb9c3ec934.png" />
</a>
</p>

<p align="center">
<b>24 HANE (24 hours are not enough)</b>
</p>

```
카뎃 여러분이 직접 클러스터 출입 누적 시간을 확인할 수 있는 24 HANE 서비스를 소개합니다.
- 클러스터 출입태깅이 잘 됐는지 궁금한가요?
- 클러스터 출입 누적 시간이 궁금한가요?
24HANE 서비스를 이용한다면?
- 클러스터 출입 누적 시간을 오늘 / 월 기준으로 한 눈에 볼 수 있습니다.
- 목표 시간을 설정하여 오늘 / 월의 학습 시간 진척도를 그래프로 볼 수 있습니다.
- 최근 6주간 / 6달간의 학습 시간 진척도를 그래프로 볼 수 있습니다.
- 클러스터에서 학습중인 실시간 인원을 확인할 수 있습니다.
- 자신의 상세한 클러스터 출입 기록을 일별로 확인할 수 있습니다.(누락된 기록까지!)
- 자정 전후로 별도 태깅 없이 학습에만 몰입 할 수 있습니다.
- 카드를 분실한 경우 카드 재발급을 앱에서 신청할 수 있습니다.
```

<br />
<br />

## 기능 소개

새로워진 24HANE 서비스의 각 페이지를 소개합니다.

<table align="center">
<tr>
<td><img width="345" alt="image" src="https://user-images.githubusercontent.com/72684256/222955031-4ce9edd6-6dda-46f3-ac17-e6bacacfbcfe.png"></td>
<td><img width="345" alt="image" src="https://user-images.githubusercontent.com/72684256/222955101-301b7088-890b-4291-899f-9e685264c78f.png"></td>
<td><img width="345" alt="image" src="https://user-images.githubusercontent.com/72684256/222955264-301460c7-cc7d-4d2c-905a-5b73cba8383c.png"></td>
<tr>
</table>

- **로그인**
- Intra 로그인을 통해 서비스 로그인이 가능합니다.

- ****
- 로그인 후 바로 보이는 페이지로 본인의 정보와 출입 누적 시간(오늘, 이번 달) 현황을 한 눈에 볼 수 있습니다.
- **`개인 정보`**
- 본인의 인트라 사진과 ID를 통해 본인의 정보를 확인합니다.
- **`출입 여부`**
- 입실 시 홈 화면의 전체 배경이 바뀝니다. 또한
인트라ID의 우측 상단에 있는 작은 원으로 출입 여부가 표시됩니다.
- **(초록색🟢)** 정상적으로 입실(check-in) 되었음을 의미합니다.
- **`이용시간`**
- 오늘의 클러스터 누적 시간을 표시합니다.
- 단, 개인별 사진 출입 카드의 ‘입실’ 과 ‘퇴실’ 태깅 짝이 일치하는 경우에만 ‘오늘 누적 시간’으로 기록됩니다.
- 따라서, 입실(check-in) 중일 경우 오늘 누적 시간은 기록되지 않으며 퇴실(check-out) 태깅이 확인 된 후 누적 시간이 기록됩니다.
- 목표시간을 설정할 수 있습니다. 기본 값으로 4시간이 적용되어 있으며, 목표시간을 1시간 단위로 24시간까지 변경하실 수 있습니다.
- **`월 누적시간`**
- 이번 달 클러스터 누적 시간을 표시합니다.
- 목표시간을 설정할 수 있습니다. 기본 값으로 80시간이 적용되어 있으며, 목표시간을 20시간 단위로 420시간까지 변경하실 수 있습니다.
- **`6주 / 6개월 체류시간 그래프`**
- 6주, 6개월 체류 시간을 표시하는 바형 그래프가 추가되었습니다.
- 각 바를 클릭하면 누적 시간과 일 평균 시간을 확인할 수 있습니다.
- **`실시간 현황`**
- 개포, 서초 클러스터의 현재 체류 인원이 표시됩니다.

- **캘린더**
- 2022. 8(24HANE 서비스 시작 월)부터 현재까지의 기록을 확인 할 수 있습니다.
- 월별로 총 누적시간을 확인할 수 있습니다.
- 일별 진척도를 색상을 통해 확인할 수 있습니다. (3시간 단위)
- Pagination과 타이틀을 클릭해서 월을 변경할 수 있습니다.
- 일별로 태깅기록을 확일 할 수 있으며, 누락된 기록도 표시됩니다.
- 캘린더 메뉴버튼 클릭 시, 현재 일의 태깅기록을 볼 수 있습니다.

- **더보기**
- 앱을 통해 카드 재발급 신청을 진행하실 수 있습니다.
- 지원금 지침 안내페이지(링크)
- 출입기록 문의(링크)
- 이용 가이드(링크)
- 앱 피드백(링크)
- 로그아웃

<br />
<br />

## 기술 스택

<p>
<img src="https://img.shields.io/badge/Vue.js-4FC08D?style=for-the-badge&logo=Vue.js&logoColor=black">
<img src="https://img.shields.io/badge/typescript-3178C6?style=for-the-badge&logo=typescript&logoColor=black">
<img src="https://img.shields.io/badge/amazons3-569A31?style=for-the-badge&logo=amazons3&logoColor=black">
</p>


<br />
<br />

## 프로젝트 실행 방법

해당 앱은 벡엔드 서버와, env 설정이 필요합니다.
벡엔드 설정을 아래의 링크를 따라가서 설정해 주시고 env설정은 `env.sample` 을 참고하여 작성 하여 주시기 바랍니다.

### .env 예시

루트에 .env파일을 만듭니다.

```
# 백엔드 url 주소
VITE_APP_API_URL = http://localhost:2424
# 토큰 이름
VITE_TOKEN = accessToken
```

### 앱 실행

- 해당 레포를 clone 받습니다.
- [벡엔드 레포](https://github.com/innovationacademy-kr/42checkin_v3-backend) 를 클론 받습니다.
- [벡엔드 레포](https://github.com/innovationacademy-kr/24hane-backend) 를 클론 받습니다.

```
//의존성 패키지 설정
npm install
//코드를 실행 합니다
npm run start
npm run dev
```

# 프로젝트 배포 방법
<br />
<br />

## 프로젝트 배포 방법

### GithubActions 설정

배포자동화를 통해서 아래 와 같이 연동 되어 있습니다.
배포자동화를 통해서 아래 와 같이 연동되어 있습니다.

- main 브랜치는 상용 서버와 연동
- develop 브랜치는 개발 서버와 연동
@@ -41,64 +168,11 @@ S3_BUCKET_DEV //S3 Bucket 이름 개발용
REACT_APP_ENV //개발 환경 (ex: development,local,production)
REACT_APP_API_URL_DEV //server url (ex. https://localhost:3000)
REACT_APP_SENTRY_DSN = Sentry DSN key (ex: Sentry credential 파일 확인)
VITE_APP_API_URL // 배포 환경의 server url
VITE_APP_API_URL_DEV // 개발 환경의 server url
VITE_TOKEN // 토큰 명
```

# 프로젝트 소개

**24 HANE(24 hours are not enough)**

```
카뎃 여러분이 직접 클러스터 출입 누적 시간을 확인할 수 있는 24 HANE 서비스를 소개합니다.
- 클러스터 출입태깅이 잘 됐는지 궁금한가요?
- 클러스터 출입 누적 시간이 궁금한가요?
24HANE 서비스를 이용해서 확인 할 수 있습니다.
- 클러스터 출입 누적 시간을 오늘/이번 달 기준으로 한 눈에 볼 수 있습니다.
- 상세한 클러스터 출입 기록을 확인할 수 있습니다.
- 자정 전후로 별도 태깅 없이 학습에만 몰입 할 수 있습니다.
```

<div align="center">
<a align="center" href="https://24hoursarenotenough.42seoul.kr/">
<img width="522" alt="스크린샷 2022-10-02 오후 5 04 40" src="https://user-images.githubusercontent.com/61973070/193444442-3f718559-06b5-43d5-bd31-6f91e9ee4194.png"></a></br>

위의 로고를 클릭하여, 24HANE 으로 접속하세요!
[사이트 링크](https://24hoursarenotenough.42seoul.kr/)

</div>

# 기술 스택

<p>
<img src="https://img.shields.io/badge/react-61DAFB?style=for-the-badge&logo=react&logoColor=black">
<img src="https://img.shields.io/badge/typescript-3178C6?style=for-the-badge&logo=typescript&logoColor=black">
<img src="https://img.shields.io/badge/reactquery-FF4154?style=for-the-badge&logo=reactquery&logoColor=black">
<img src="https://img.shields.io/badge/amazons3-569A31?style=for-the-badge&logo=amazons3&logoColor=black">

</p>

# 기능 소개

- **로그인**
- Intra 로그인을 통해 서비스 로그인이 가능합니다.
- **MainPage**

- 로그인 후 바로 보이는 페이지로 본인의 정보와 출입 누적 시간(오늘, 이번 달) 현황을 한 눈에 볼 수 있습니다.
- **`개인 정보`** 본인의 인트라 사진과 아이디를 통해 본인의 정보를 확인합니다.
- **`입출입 여부`** 인트라 사진의 우측 상단에 있는 작은 원으로 입 출입 여부가 표시됩니다.
- **(초록색🟢)** 정상적으로 입실(check-in) 되었음을 의미합니다.
- **(회색⚪)** 정상적으로 퇴실(check-out) 되었음을 의미합니다.
- **`오늘 누적시간`** 오늘의 클러스터 누적 시간을 표시합니다.
- 단, 개인별 사진 출입 카드의 ‘입실’ 과 ‘퇴실’ 태깅 짝이 일치하는 경우에만 ‘오늘 누적 시간’이 기록됩니다.
- 따라서, 입실(check-in) 중일 경우 오늘 누적 시간은 기록되지 않으며 퇴실(check-out) 태깅이 확인 된 후 누적 시간이 기록됩니다.
- **`이번 달 누적시간`** 이번 달 클러스터 누적 시간을 표시합니다.
- 우측 상단에 있는 버튼을 클릭🖱️하면 SUB PAGE로 이동합니다! 🚀🪂🌌
<p align="center"><img width="640" alt="메인페이지 스크린샷" src="https://user-images.githubusercontent.com/61973070/193443447-ef653232-7d9d-4b11-a598-5aaed1a243ad.png"></p>

- **SubPage**
- 이번 달 총 입, 퇴실 기록을 확인할 수 있는 페이지 입니다.
- **`입-퇴실 상세 내역`** 입-퇴실 별 상세 기록과 입-퇴실에 따른 체류 시간을 확인할 수 있습니다. 데이터가 많을 경우 스크롤을 내려 정보를 확인하시면 됩니다.
- **`문의하기`** 입-퇴실 내역에 궁금한 점이 있거나, 기타 서비스 관련 문의 사항이 있을 경우 문의하기 버튼을 눌러 설문을 작성해주세요.
- SUB PAGE 우측 상단에 있는 버튼을 클릭🖱️하면 다시 MAIN PAGE로 이동합니다! 🚀🪂🌌
## 개선 / 문의사항

<p align="center"><img width="320" alt="서브페이지 스크린샷" src="https://user-images.githubusercontent.com/61973070/193443620-5e48da91-5391-4b5e-b1a2-f603293c7ed4.png"></p>
Frontend 문의: 인트라ID `inshin`에게 DM 주세요💌
1 change: 1 addition & 0 deletions env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
41 changes: 41 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="shortcut icon" href="logo.png" />
<link rel="apple-touch-icon" href="logo.png" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#f5f5f5"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#333333"
/>
<meta property="og:type" content="website" />
<meta
property="og:url"
content="https://24hoursarenotenough.42seoul.kr"
/>
<meta property="og:title" content="24HANE" />
<meta
property="og:image"
content="https://24hoursarenotenough.42seoul.kr/logo.png"
/>
<meta property="og:description" content="42서울 출입기록 관리 시스템" />
<meta property="og:site_name" content="24HANE" />
<meta property="og:locale" content="ko_KR" />
<title>24HANE</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
28 changes: 0 additions & 28 deletions localhost-key.pem

This file was deleted.

27 changes: 0 additions & 27 deletions localhost.pem

This file was deleted.

9,784 changes: 9,784 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

81 changes: 32 additions & 49 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,58 +1,41 @@
{
"name": "24hoursarenotenough",
"name": "24hane",
"version": "1.0.0",
"private": true,
"dependencies": {
"@sentry/react": "^7.8.1",
"@sentry/tracing": "^7.8.1",
"@tanstack/react-query": "^4.2.3",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-csv": "^1.1.3",
"@types/react-dom": "^18.0.0",
"@types/react-router-dom": "^5.3.3",
"axios": "^0.27.2",
"css-loader": "^6.7.1",
"dayjs": "^1.11.4",
"env-cmd": "^10.1.0",
"eslint-config-airbnb": "^19.0.4",
"react": "^18.2.0",
"react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-ga4": "^1.4.1",
"react-router-dom": "^6.3.0",
"react-scripts": "^5.0.1",
"style-loader": "^3.3.1",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
},
"scripts": {
"start": "HTTPS=false SSL_CRT_FILE=localhost.pem SSL_KEY_FILE=localhost-key.pem react-scripts start",
"build:prod": "react-scripts build",
"build:dev": "react-scripts build",
"build:local": "env-cmd -f .env.development.local react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"dev": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
"test:unit": "vitest --environment jsdom --root src/",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
"dependencies": {
"axios": "^1.3.3",
"pinia": "^2.0.28",
"swiper": "^9.0.5",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},
"devDependencies": {
"prettier-eslint": "^15.0.1"
"@rushstack/eslint-patch": "^1.1.4",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.11.12",
"@vitejs/plugin-vue": "^4.0.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
"@vue/test-utils": "^2.2.6",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0",
"jsdom": "^20.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.7.1",
"typescript": "~4.7.4",
"vite": "^4.0.0",
"vitest": "^0.25.6",
"vue-tsc": "^1.0.12"
}
}
Binary file removed public/apple-touch-icon.png
Binary file not shown.
Binary file modified public/favicon.ico
Binary file not shown.
Binary file added public/favicon_old.ico
Binary file not shown.
Binary file added public/home-bg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 0 additions & 54 deletions public/index.html

This file was deleted.

Binary file added public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 0 additions & 20 deletions public/manifest.json

This file was deleted.

3 changes: 0 additions & 3 deletions public/robots.txt

This file was deleted.

34 changes: 0 additions & 34 deletions src/App.tsx

This file was deleted.

45 changes: 45 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { onMounted } from "vue";
import { RouterView, useRoute } from "vue-router";
import MenuBar from "@/components/common/MenuBar.vue";
import HeaderBar from "@/components/common/HeaderBar.vue";
const route = useRoute();
const checkValidRoute = (visibleRoutes: string[]) => {
return visibleRoutes.includes(route.name as string);
};
const setScreenSize = () => {
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty("--vh", `${vh}px`);
};
onMounted(() => {
setScreenSize();
});
</script>

<template>
<HeaderBar v-if="checkValidRoute(['home'])" />
<RouterView />
<MenuBar
v-if="
checkValidRoute([
'home',
'calendar',
'more',
'notification',
'apply-card',
])
"
/>
</template>

<style scoped>
main {
padding: 30px 30px 80px;
background-color: var(--color-background-soft);
min-height: 100%;
}
</style>
53 changes: 30 additions & 23 deletions src/api/baseAPI.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { STATUS_401_UNAUTHORIZED } from "utils/const/const";
import { getAccessToken } from "utils/cookie";

export const VERSION_PATH = "v1";
export const makeAPIPath = (path: string) => `${VERSION_PATH}/${path}`;
import axios from "axios";
import { getCookie, removeCookie } from "./cookie/cookies";
import { STATUS_401_UNAUTHORIZED } from "@/constants/statusCode";
import { clearStorage } from "@/utils/localStorage";

export const instance = axios.create({
baseURL: process.env.REACT_APP_API_URL,
baseURL: import.meta.env.VITE_APP_API_URL,
withCredentials: true,
});

instance.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getAccessToken();
const newConfig = { ...config };
if (newConfig.headers) {
newConfig.headers.Authorization = `Bearer ${token}`;
(config) => {
const token = getCookie();
if (config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return newConfig;
return config;
},
(error) => Promise.reject(error.response),
(error) => Promise.reject(error.response)
);

let isAlert = false;

instance.interceptors.response.use(
(response: AxiosResponse) => {
(response) => {
return response;
},
(error: AxiosError) => {
if (error.response?.status === STATUS_401_UNAUTHORIZED)
return Promise.resolve({
status: 401,
message: "로그인이 필요합니다.",
});
else return Promise.reject(error);
},
(error) => {
if (
error.response?.status === STATUS_401_UNAUTHORIZED ||
error.response?.status === undefined
) {
localStorage.removeItem("isLogin");
removeCookie();
clearStorage();
window.location.href = "/";
if (!isAlert) {
alert("로그인 정보가 유효하지 않습니다.\n다시 로그인해주세요.");
isAlert = true;
}
}
return Promise.reject(error);
}
);
14 changes: 14 additions & 0 deletions src/api/cookie/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const tokenName = import.meta.env.VITE_TOKEN;

export const getCookie = () => {
return document.cookie
.split(";")
.map((cookie) => cookie.trim())
.filter((cookie) => tokenName === cookie.split("=")[0])
.join("")
.split("=")[1];
};

export const removeCookie = (): void => {
document.cookie = `${tokenName}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
};
37 changes: 20 additions & 17 deletions src/api/logsAPI.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import { instance, makeAPIPath } from "./baseAPI";
import { instance } from "./baseAPI";

export type LogsResponse = {
login: string; //loginID 로 변경해서 사용
profileImage: string;
inOutLogs: InOutLog[];
const getLogsDayURL = "v2/tag-log/getAllTagPerDay";
export const getLogsDate = async (year: number, month: number, day: number) => {
const response = await instance.get(getLogsDayURL, {
params: {
year,
month,
day,
},
});
return response;
};

export type InOutLog = {
inTimeStamp: number;
outTimeStamp: number;
durationSecond: number;
};

export const getLogsDay = (year: number, month: number, day: number) => {
return instance.get(makeAPIPath(`tag-log/perday?year=${year}&month=${month}&day=${day}`));
};

export const getLogsmonth = (year: number, month: number) => {
return instance.get(makeAPIPath(`tag-log/permonth?year=${year}&month=${month}`));
const getLogsMonthURL = "v2/tag-log/getAllTagPerMonth";
export const getLogsmonth = async (year: number, month: number) => {
const response = await instance.get(getLogsMonthURL, {
params: {
year,
month,
},
});
return response;
};
14 changes: 14 additions & 0 deletions src/api/redirectAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 지원금 지침 안내 페이지
export const moneyHuidelinesURL = "/redirect/money_guidelines";

// 출입기록 문의 페이지
export const questionURL = "/redirect/question";

// 이용 가이드 지침 안내 페이지
export const usageURL = "/redirect/usage";

// 피드백 안내 페이지
export const feedbackURL = "/redirect/feedback";

// 카드 재발급 가이드 페이지
export const cardReissueURL = "/redirect/reissuance_guidelines";
19 changes: 19 additions & 0 deletions src/api/reissueAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { instance } from "@/api/baseAPI";

const reissueURL = "v2/reissue";
export const getReissue = async () => {
const response = await instance.get(reissueURL);
return response;
};

const setReissueRequestURL = "v2/reissue/request";
export const setReissueRequest = async () => {
const response = await instance.post(setReissueRequestURL);
return response;
};

const setReissueFinishURL = "v2/reissue/finish";
export const setReissueFinish = async () => {
const response = await instance.patch(setReissueFinishURL);
return response;
};
21 changes: 13 additions & 8 deletions src/api/userAPI.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { instance, makeAPIPath } from "api/baseAPI";
import { UserInfoResponse } from "types/User";
import { instance } from "@/api/baseAPI";

export const getIsLogin = () => {
return instance.get("user/login/isLogin");
const isLoginURL = "user/login/isLogin";
export const getIsLogin = async () => {
const response = await instance.get(isLoginURL);
return response;
};

export const getUserInfo = () => {
return instance.get<UserInfoResponse>(makeAPIPath("tag-log/maininfo"));
const mainInfoURL = "v2/tag-log/maininfo";
export const getMainInfo = async () => {
const response = await instance.get(mainInfoURL);
return response;
};

export const getAccumulationTimes = () => {
return instance.get(makeAPIPath("tag-log/accumulationTimes"));
const accTimesURL = "v2/tag-log/accumulationTimes";
export const getAccTimes = async () => {
const response = await instance.get(accTimesURL);
return response;
};
Binary file removed src/assets/42-logo-black.png
Binary file not shown.
Binary file removed src/assets/42-logo-white.png
Binary file not shown.
Binary file removed src/assets/42.png
Binary file not shown.
Binary file removed src/assets/apple-touch-icon.png
Binary file not shown.
132 changes: 132 additions & 0 deletions src/assets/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
:root {
--color-primary: #735bf2;
--color-secondary: #00babc;

--white: #ffffff;
--white-soft: #f5f5f5;

--gray: #9b9797;
--gray-dark: #5b5b5b;
--gray-dark2: #707070;
--gray-soft: #d9d9d9;

--black: #333333;
--black-soft: #444444;
--black-dark: #000000;

--divider: #eaeaea;
--red: #ea1515;

--text-black: var(--black);
--text-gray: var(--gray);
--text-white: var(--white);

--bar-gradation: linear-gradient(200.75deg, #9ac3f4 11.67%, #735bf2 86.56%);
--circle-border: 16px solid;
--circle-gradation: linear-gradient(
200.75deg,
rgba(0, 186, 188, 0.04) 11.67%,
#735bf2 86.56%
);
--circle-gradation-bg: linear-gradient(
200.75deg,
#bcf8f9 11.67%,
#735bf2 86.56%
);
--font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;

--vh: 100%;
}

/* semantic color variables for this project */
:root {
--color-background: var(--white);
--color-background-soft: var(--white-soft);
--color-background-btn: var(--black);

--color-border: var(--divider);

--color-heading: var(--text-black);
--color-heading-on: var(--text-white);
--color-text-soft: var(--text-gray);
--color-text: var(--text-black);
--color-text-more: var(--gray-dark2);
--color-log-bg: var(--divider);

--header-bg: var(--white-soft);
--menu-shadow: drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.05));
--color-vbutton: var(--gray-dark);

--cal-bg-1: rgba(154, 199, 244, 0.4);
--cal-bg-2: rgba(144, 167, 244, 0.5);
--cal-bg-3: rgba(128, 125, 244, 0.7);
--cal-bg-4: rgba(115, 91, 242, 0.9);
--cal-today: var(--color-primary);
}

@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--black);
--color-background-soft: var(--black);
--color-background-btn: var(--white);

--color-border: var(--gray-dark2);

--color-heading: var(--text-white);
--color-heading-on: var(--text-black);
--color-text-soft: var(--text-gray);
--color-text: var(--text-white);
--color-text-more: var(--divider);
--color-log-bg: #555555;

--header-bg: var(--black);
--color-vbutton: var(--gray);

--cal-today: #fff;
}
}

*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}

a {
text-decoration: none;
color: inherit;
transition: 0.4s;
-webkit-tap-highlight-color: transparent;
}

.tapHighlight {
-webkit-tap-highlight-color: transparent;
}

html {
height: 100%;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

body {
min-height: 100%;
color: var(--color-text);
background-color: var(--color-background-soft);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-size: 16px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
Binary file removed src/assets/logo.png
Binary file not shown.
40 changes: 40 additions & 0 deletions src/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@import "./base.css";

html,
body {
height: 100%;
}

#app {
padding: 0;
font-weight: normal;
height: 100%;
margin: 0 auto;
max-width: 425px;
}

/* swiper */
.swiper-pagination-bullet {
width: 6px;
height: 6px;
background-color: var(--gray-dark2);
}
.swiper-pagination-bullet-active {
background-color: var(--color-primary);
}

@media (hover: hover) {
}

@media (min-width: 1024px) {
/* body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
} */
}
Binary file removed src/assets/user-default.png
Binary file not shown.
31 changes: 0 additions & 31 deletions src/components/Card/Card.tsx

This file was deleted.

36 changes: 0 additions & 36 deletions src/components/Card/CardContents.tsx

This file was deleted.

17 changes: 0 additions & 17 deletions src/components/Card/CardItem.tsx

This file was deleted.

15 changes: 0 additions & 15 deletions src/components/Card/LoadingCard.tsx

This file was deleted.

82 changes: 0 additions & 82 deletions src/components/Card/LogCardContents.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions src/components/Card/Profile.tsx

This file was deleted.

26 changes: 0 additions & 26 deletions src/components/Card/ProfileCard.tsx

This file was deleted.

17 changes: 0 additions & 17 deletions src/components/Card/SettingButton.tsx

This file was deleted.

19 changes: 0 additions & 19 deletions src/components/Card/TimeLogCard.tsx

This file was deleted.

30 changes: 0 additions & 30 deletions src/components/ClusterStatusBoard.tsx

This file was deleted.

22 changes: 0 additions & 22 deletions src/components/DurationTime.tsx

This file was deleted.

30 changes: 0 additions & 30 deletions src/components/Footer.tsx

This file was deleted.

12 changes: 0 additions & 12 deletions src/components/Layout.tsx

This file was deleted.

47 changes: 47 additions & 0 deletions src/components/Login/LoginButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import LoadingAnimation from "@/components/common/LoadingAnimation.vue";
import { ref } from "vue";
const ORIGIN_URL = window.location.origin;
const BACKEND_URL = import.meta.env.VITE_APP_API_URL;
const isClicked = ref(false);
</script>

<template>
<button class="button">
<LoadingAnimation v-if="isClicked" />
<a
v-else
@click="isClicked = true"
:href="`${BACKEND_URL}/user/login/42?redirect=${ORIGIN_URL}/auth`"
>LOG IN</a
>
</button>
</template>

<style scoped>
.button {
cursor: pointer;
border: none;
text-align: center;
-webkit-tap-highlight-color: transparent;
background-color: var(--black);
max-width: 330px;
width: 100%;
height: 45px;
border-radius: 10px;
cursor: pointer;
font-size: 1.25rem;
color: var(--white);
font-weight: 700;
padding: 0;
}
.button a {
display: block;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
</style>
44 changes: 44 additions & 0 deletions src/components/alarm/AlarmItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script setup lang="ts">
const props = defineProps<{
message: {
date: string;
title: string;
content: string;
};
}>();
</script>

<template>
<li class="alarmItem">
<div class="date">{{ props.message.date }}</div>
<h3>{{ props.message.title }}</h3>
<p>{{ props.message.content }}</p>
</li>
</template>

<style scoped>
.alarmItem {
list-style: none;
border-bottom: 1px solid var(--color-border);
padding: 6px 0;
margin-bottom: 10px;
text-align: left;
}
.alarmItem .date {
font-size: 0.75rem;
color: var(--color-text-more);
}
.alarmItem h3 {
font-size: 0.875rem;
font-weight: 700;
color: var(--color-text);
margin: 2px 0;
}
.alarmItem p {
font-size: 0.75rem;
color: var(--color-text);
margin: 2px 0;
}
</style>
31 changes: 31 additions & 0 deletions src/components/calendar/AccMonth.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script setup lang="ts">
import { useMonthLogStore } from "@/stores/monthlog";
import { ref, watch } from "vue";
const { getMonthAccTimeText, showLogs } = useMonthLogStore();
const monthText = ref(getMonthAccTimeText());
watch(showLogs, () => {
monthText.value = getMonthAccTimeText();
});
</script>

<template>
<div class="month">총 {{ monthText.hour }}시간 {{ monthText.minute }}분</div>
</template>

<style scoped>
.month {
display: flex;
justify-content: center;
align-items: center;
height: 45px;
margin: 20px 0;
background-color: var(--black);
color: var(--white);
border-radius: 10px;
font-size: 0.875rem;
font-weight: 700;
border: 2px solid #fff;
}
</style>
96 changes: 96 additions & 0 deletions src/components/calendar/CalPagination.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script setup lang="ts">
import VIcon from "@/components/icons/IconChevron.vue";
import { useMonthLogStore } from "@/stores/monthlog";
import { ref, watch } from "vue";
const monthLog = useMonthLogStore();
const {
showDateTitle,
calcOptions,
nextMonth,
prevMonth,
selectMonth,
showIsLoading,
} = monthLog;
const isLoading = ref(showIsLoading());
watch(showIsLoading, (val) => {
isLoading.value = val;
});
const clickPrevMonth = () => {
if (!isLoading.value) prevMonth();
};
const clickNextMonth = () => {
if (!isLoading.value) nextMonth();
};
</script>

<template>
<div class="pagination tapHighlight">
<button @click="clickPrevMonth">
<VIcon :color="`var(--color-vbutton)`" />
</button>
<select
class="title select"
@change="selectMonth"
v-model="monthLog.dateTitle"
name="year-month"
>
<option
:disabled="isLoading"
v-for="(date, i) in calcOptions()"
:key="i"
:value="date"
:selected="showDateTitle() === date"
>
{{ date }}
</option>
</select>
<button @click="clickNextMonth">
<VIcon :color="`var(--color-vbutton)`" />
</button>
</div>
</template>

<style scoped>
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.pagination button {
width: 40px;
height: 40px;
background-color: transparent;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.pagination button:first-child {
transform: rotate(180deg);
}
.pagination .title {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-heading);
user-select: none;
cursor: pointer;
text-align: center;
font-family: var(--font-family);
}
.select {
cursor: pointer;
-o-appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: transparent;
border: none;
}
</style>
32 changes: 32 additions & 0 deletions src/components/calendar/CalWeek.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script setup lang="ts"></script>

<template>
<div class="week">
<div class="day">일</div>
<div class="day">월</div>
<div class="day">화</div>
<div class="day">수</div>
<div class="day">목</div>
<div class="day">금</div>
<div class="day">토</div>
</div>
</template>

<style scoped>
.week {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 10px;
width: 100%;
height: 100%;
margin-bottom: 12px;
user-select: none;
}
.week .day {
font-size: 0.75rem;
text-align: center;
color: var(--gray);
color: var(--color-text-more);
}
</style>
137 changes: 137 additions & 0 deletions src/components/calendar/LogTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useMonthLogStore } from "@/stores/monthlog";
const {
getSelectedDateAccTimeText,
getDateLogs,
showSelectedDateText,
showIsLoading,
} = useMonthLogStore();
const logs = ref(getDateLogs());
const isLoading = ref(showIsLoading());
watch(showSelectedDateText, () => {
logs.value = getDateLogs();
});
watch(showIsLoading, (val) => {
isLoading.value = val;
if (val == false) {
logs.value = getDateLogs();
}
});
const calcHeight = () => {
const days = document.getElementById("days");
const daysHeight = days?.clientHeight;
const logs = document.getElementById("logs");
if (!!logs && !!daysHeight && daysHeight > 190) {
logs?.classList.add("smaller");
} else {
logs?.classList.remove("smaller");
}
};
watch(getDateLogs, () => {
calcHeight();
});
</script>

<template>
<div class="wrap">
<div class="totalLog">
<div class="title">{{ showSelectedDateText() }}</div>
<div class="time">{{ getSelectedDateAccTimeText() }}</div>
</div>
<div class="logList">
<ul class="logTitle">
<li>입실</li>
<li>퇴실</li>
<li>체류시간</li>
</ul>
<ul class="logs" v-if="isLoading">
<li class="log logEmpty">기록이 없습니다.</li>
</ul>
<ul v-show="!isLoading" id="logs" class="logs">
<li
v-for="(log, i) in logs"
:key="i"
class="log"
:class="{ missing: log.accLogTime === '누락' }"
>
<div class="inLogTime">{{ log.inLogTime }}</div>
<div class="outLogTime">{{ log.outLogTime }}</div>
<div class="accLogTime">{{ log.accLogTime }}</div>
</li>
<li class="log logEmpty" v-if="logs.length == 0">기록이 없습니다.</li>
</ul>
</div>
</div>
</template>

<style scoped>
.totalLog {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.875rem;
color: var(--color-text-more);
border-bottom: 1px solid var(--color-border);
padding: 0 10px;
}
.logList {
padding: 10px 0;
}
.logTitle {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0;
font-size: 0.875rem;
margin-bottom: 8px;
padding: 0 10px;
}
.logTitle li {
list-style: none;
text-align: center;
font-weight: 700;
width: 65px;
}
.logs {
font-size: 0.875rem;
margin-bottom: 10px;
padding: 0;
overflow-y: scroll;
scrollbar-width: none;
-ms-overflow-style: none;
height: calc(var(--vh, 1vh) * 100 - 504px);
padding: 0 10px;
}
.logs.smaller {
height: calc(var(--vh, 1vh) * 100 - 544px);
}
.logs .log {
display: flex;
justify-content: space-between;
align-items: center;
list-style: none;
text-align: center;
margin-bottom: 6px;
}
.logs .logEmpty {
justify-content: center;
font-size: 0.875rem;
}
.log div {
width: 65px;
}
.log.missing {
background-color: var(--color-log-bg);
border-radius: 12px;
}
</style>
24 changes: 0 additions & 24 deletions src/components/common/Button.tsx

This file was deleted.

27 changes: 0 additions & 27 deletions src/components/common/Circle.tsx

This file was deleted.

48 changes: 48 additions & 0 deletions src/components/common/DefaultButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script setup lang="ts">
const props = defineProps<{
title: string;
path?: string;
background?: string;
color?: string;
marginTop?: string;
isDisable?: boolean;
}>();
</script>

<template>
<button
:disabled="isDisable"
class="button"
:style="{ background: background, color: color, marginTop: marginTop }"
>
<a v-if="path" :href="props.path" target="_blank">{{ props.title }}</a>
<span v-else>{{ props.title }}</span>
</button>
</template>

<style scoped>
.button {
cursor: pointer;
border: none;
text-align: center;
-webkit-tap-highlight-color: transparent;
background-color: var(--color-background-btn);
color: var(--color-heading-on);
width: 100%;
height: 45px;
border-radius: 10px;
cursor: pointer;
font-size: 0.875rem;
padding: 0;
}
.button a,
.button span {
display: block;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
font-weight: 700;
align-items: center;
}
</style>
63 changes: 63 additions & 0 deletions src/components/common/DefaultModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script setup lang="ts"></script>

<template>
<div class="wrap">
<div class="modal">
<div>
<h3>
<slot name="title"></slot>
</h3>
<p>
<slot name="content"></slot>
</p>
</div>
<div class="buttons">
<slot name="button"></slot>
</div>
</div>
</div>
</template>

<style scoped>
.wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 300px;
min-height: 300px;
background-color: var(--white);
border-radius: 20px;
padding: 50px 40px 30px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
}
h3 {
font-weight: 700;
font-size: 1rem;
margin-bottom: 6px;
color: var(--black);
text-align: center;
}
p {
font-size: 0.875rem;
color: var(--color-primary);
font-weight: 700;
text-align: center;
}
</style>
145 changes: 145 additions & 0 deletions src/components/common/HeaderBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import LogoIcon from "@/components/icons/IconLogo.vue";
import NotificationIconFill from "@/components/icons/IconNotificationFill.vue";
import NotificationIconLine from "@/components/icons/IconNotificationLine.vue";
import { useHomeStore } from "@/stores/home";
const { getUserInfo } = useHomeStore();
const userInfo = ref(getUserInfo());
const isNotification = ref(false);
const isClickImg = ref(false);
watch(
() => getUserInfo(),
() => {
userInfo.value = getUserInfo();
}
);
</script>

<template>
<nav class="wrap" :class="{ online: userInfo.inoutState === 'IN' }">
<div class="profile">
<div class="profileImg">
<img
v-if="!!userInfo.profileImage && !isClickImg"
@click="isClickImg = true"
:src="userInfo.profileImage"
alt="프로필 이미지"
/>
<LogoIcon v-else @click="isClickImg = false" />
</div>
<h2 :class="{ online: userInfo.inoutState === 'IN' }">
{{ userInfo.login }}
</h2>
</div>
<RouterLink
v-if="false"
to="/notification"
class="notification"
:class="{ on: isNotification }"
>
<NotificationIconFill
v-if="isNotification"
color="var(--color-background-btn)"
/>
<NotificationIconLine v-else />
</RouterLink>
</nav>
</template>

<style scoped>
.wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 55px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px 10px 30px;
z-index: 9;
background: var(--header-bg);
}
@media (min-width: 425px) {
.wrap {
max-width: 425px;
left: 50%;
transform: translateX(-50%);
}
}
.wrap.online {
background: none;
}
.profile {
display: flex;
justify-content: flex-start;
align-items: center;
height: 100%;
width: 100%;
user-select: none;
}
.profileImg {
width: 32px;
height: 32px;
border-radius: 50%;
overflow: hidden;
}
.profileImg svg,
.profileImg img {
width: 100%;
height: 100%;
object-fit: cover;
}
.profile h2 {
margin-left: 10px;
font-size: 1.25rem;
font-weight: 700;
color: var(--color-heading);
position: relative;
}
.profile h2.online {
color: var(--white);
}
.profile .online::after {
content: "";
position: absolute;
top: 5px;
right: -10px;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--color-secondary);
}
.notification {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 100%;
cursor: pointer;
}
.notification.on::before {
content: "";
position: absolute;
display: block;
top: 0px;
left: calc(50% - 2px);
width: 4px;
height: 4px;
border-radius: 50%;
background-color: red;
}
</style>
75 changes: 75 additions & 0 deletions src/components/common/HeaderBarSub.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script setup lang="ts">
import VIcon from "@/components/icons/IconChevron.vue";
import router from "@/router";
const props = defineProps<{
title: string;
backButton?: boolean;
path?: string;
}>();
const clickButton = () => {
if (props.path) router.push(props.path);
else router.go(-1);
};
</script>

<template>
<div class="wrap" :class="{ backButton: backButton }">
<button v-if="props.backButton" :to="path" @click="clickButton">
<VIcon :color="`var(--color-vbutton)`" class="vIcon" />
</button>
<h2>{{ props.title }}</h2>
</div>
</template>

<style scoped>
.wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50px;
line-height: 2rem;
padding: 15px 30px 0;
z-index: 9;
background-color: var(--color-background-soft);
user-select: none;
}
@media (min-width: 425px) {
.wrap {
max-width: 425px;
left: 50%;
transform: translateX(-50%);
}
}
.wrap.backButton h2 {
text-align: center;
}
button {
position: absolute;
bottom: 2px;
left: 16px;
display: block;
width: 35px;
height: 35px;
margin-right: 10px;
background-color: transparent;
border: none;
display: flex;
justify-content: center;
align-items: center;
z-index: 9;
}
.vIcon {
transform: rotate(180deg);
}
h2 {
font-size: 1.25rem;
font-weight: 700;
}
</style>
21 changes: 0 additions & 21 deletions src/components/common/Icon.tsx

This file was deleted.

14 changes: 0 additions & 14 deletions src/components/common/Loading.tsx

This file was deleted.

54 changes: 54 additions & 0 deletions src/components/common/LoadingAnimation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup lang="ts"></script>

<template>
<div class="wrap">
<div class="loading"></div>
</div>
</template>

<style scoped>
.wrap {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
user-select: none;
}
.loading {
position: relative;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--gray-soft);
animation: loading 1.2s infinite ease-in;
animation-delay: 0.4s;
}
.loading:before,
.loading:after {
content: "";
position: absolute;
display: block;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--gray-soft);
top: 50%;
transform: translateY(-50%);
animation: loading 1.2s infinite linear;
}
.loading:before {
left: -12px;
}
.loading:after {
left: 12px;
animation-delay: 0.8s;
}
@keyframes loading {
50% {
background: var(--color-primary);
}
}
</style>
80 changes: 80 additions & 0 deletions src/components/common/MenuBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script setup lang="ts">
import HomeIconFill from "@/components/icons/IconHomeFill.vue";
import HomeIconLine from "@/components/icons/IconHomeLine.vue";
import CalendarIconFill from "@/components/icons/IconCalendarFill.vue";
import CalendarIconLine from "@/components/icons/IconCalendarLine.vue";
import MoreIconFill from "@/components/icons/IconMoreFill.vue";
import MoreIconLine from "@/components/icons/IconMoreLine.vue";
import { useMonthLogStore } from "@/stores/monthlog";
const monthLog = useMonthLogStore();
const { resetSelectedDate } = monthLog;
</script>

<template>
<div class="wrap">
<nav>
<RouterLink to="/home">
<HomeIconFill
v-if="$route.name === 'home'"
color="var(--color-background-btn)"
/>
<HomeIconLine v-else />
</RouterLink>
<RouterLink to="/calendar">
<CalendarIconFill
v-if="$route.name === 'calendar'"
@click="resetSelectedDate"
color="var(--color-background-btn)"
/>
<CalendarIconLine v-else />
</RouterLink>
<RouterLink to="/more">
<MoreIconFill
v-if="$route.name === 'more'"
color="var(--color-background-btn)"
/>
<MoreIconLine v-else />
</RouterLink>
</nav>
</div>
</template>

<style scoped>
.wrap {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 50px;
background-color: var(--color-background);
filter: var(--menu-shadow);
z-index: 9;
padding: 0 30px;
user-select: none;
}
@media (min-width: 425px) {
.wrap {
max-width: 425px;
left: 50%;
transform: translateX(-50%);
}
}
nav {
display: flex;
justify-content: space-around;
align-items: center;
height: 100%;
}
nav a {
display: block;
height: 100%;
width: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
</style>
8 changes: 0 additions & 8 deletions src/components/common/Spinner.tsx

This file was deleted.

203 changes: 203 additions & 0 deletions src/components/home/BarChartCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<script setup lang="ts">
import { ref, computed, watch } from "vue";
import type { PeriodData } from "@/types/logs";
import { useHomeStore } from "@/stores/home";
import LoadingAnimation from "@/components/common/LoadingAnimation.vue";
const { getIsLoading } = useHomeStore();
const isLoading = ref(getIsLoading());
watch(getIsLoading, (val) => {
isLoading.value = val;
});
const props = defineProps<{
periodsData: PeriodData[];
isMonth?: boolean;
}>();
const timeArr = computed(() => {
const arr: number[] = [];
if (!props.periodsData) return arr;
props.periodsData.forEach((data) => {
arr.push(Number(data.total));
});
return arr;
});
const calcTimePercent = (time: number, times: number[]) => {
const maxTime = Math.max(...times);
const percent = Math.round((time / maxTime) * 100);
if (!percent) return "6px";
return percent + "%";
};
const getLastDate = (index: number) => {
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
return new Date(year, month - index, 0).getDate();
};
const calcAvgTime = (time: number, index: number) => {
if (props.isMonth) {
const lastDate = getLastDate(index);
return (time / lastDate).toFixed(1);
}
return (time / 7).toFixed(1);
};
const clickIndex = ref(0);
</script>

<template>
<div class="wrap">
<h3 v-if="isMonth">최근 월간 그래프<span> (6개월)</span></h3>
<h3 v-else>최근 주간 그래프<span> (6주)</span></h3>
<div v-if="isLoading" class="detailView">
<LoadingAnimation class="loadingAnimation" />
</div>
<div v-else class="detailView">
<div class="title">{{ props.periodsData[clickIndex].periods }}</div>
<div class="timeView">
<div class="time">총 {{ props.periodsData[clickIndex].total }}시간</div>
<div class="time">
평균
{{
calcAvgTime(
Number(props.periodsData[clickIndex].total),
clickIndex
)
}}시간
</div>
</div>
</div>
<ul>
<li
class="tapHighlight"
:class="{ on: clickIndex == i }"
v-for="(data, i) in props.periodsData"
:key="i"
@click="clickIndex = i"
>
<div
:style="{ height: calcTimePercent(Number(data.total), timeArr) }"
></div>
</li>
</ul>
<div class="preiodBox">
<div class="period">
<div class="text">최신순</div>
<div class="text">오래된순</div>
</div>
</div>
</div>
</template>

<style scoped>
.wrap {
width: 100%;
background-color: #fff;
border-radius: 20px;
padding: 30px 20px;
user-select: none;
}
h3 {
font-size: 0.875rem;
font-weight: 700;
color: var(--black);
}
h3 span {
font-weight: 400;
color: var(--gray);
}
.detailView {
font-size: 0.75rem;
color: #fff;
text-align: right;
margin-top: 10px;
transition: transform 0.3s ease-in-out;
background: var(--black);
height: 60px;
border-radius: 10px;
padding: 11px 20px;
display: flex;
justify-content: space-between;
}
.detailView .loadingAnimation {
background-color: transparent;
padding: 0;
}
.detailView div {
font-weight: 700;
}
ul {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin: 20px auto 0;
height: 90px;
padding: 0 20px;
max-width: 280px;
}
ul li {
display: flex;
justify-content: space-between;
align-items: flex-end;
font-size: 0.875rem;
font-weight: 400;
cursor: pointer;
transition: transform 0.3s ease-in-out;
height: 100%;
}
ul li:active {
transform: scale(1.1) translateY(-5px);
}
ul li div {
background: var(--bar-gradation);
list-style: none;
display: inline-block;
width: 26px;
border-radius: 4px 4px 0px 0px;
}
ul li.on::after {
content: "";
display: inline-block;
position: absolute;
top: -21px;
left: 4px;
width: 0;
height: 0;
border-bottom: 8px solid transparent;
border-top: 13px solid var(--black);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.preiodBox {
margin-top: 4px;
}
.period {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 280px;
padding: 0 20px;
margin: 0 auto;
}
.text {
font-size: 0.75rem;
font-weight: 700;
color: var(--gray);
}
</style>
94 changes: 94 additions & 0 deletions src/components/home/CircleProgress.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<script setup lang="ts">
import CircleProgressBg from "@/components/images/CircleProgressBg.vue";
const props = defineProps<{
isOpen: boolean;
percent: number;
}>();
const calcLine = () => {
// 350은 svg의 라인 길이 = 0
const totalLine = 350;
if (!props.isOpen) {
return totalLine;
}
const line = totalLine - (totalLine * props.percent) / 100;
if (line < 0) return 0;
return line;
};
</script>

<template>
<div class="circle">
<div class="text">{{ props.percent }}<span class="percent">%</span></div>
<CircleProgressBg class="bg" />
<svg
class="progress"
width="120"
height="120"
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient
id="paint_linear_progress"
x1="104"
y1="13.25"
x2="62.9394"
y2="121.604"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00BABC" stop-opacity="0.04" />
<stop offset="1" stop-color="#735BF2" />
</linearGradient>
</defs>
<circle
:style="{ strokeDashoffset: calcLine() }"
cx="60"
cy="60"
r="56"
stroke-linecap="round"
></circle>
</svg>
</div>
</template>

<style scoped>
.circle {
position: relative;
width: 120px;
height: 120px;
margin: 10px auto 0;
user-select: none;
}
.circle .progress {
position: absolute;
top: 0;
left: 0;
}
.progress circle {
fill: none;
stroke: url(#paint_linear_progress);
stroke-width: 8px;
stroke-dasharray: 350;
stroke-dashoffset: 350;
transition: all 1s ease;
}
.circle .text {
position: absolute;
width: 100%;
top: 50%;
font-size: 2rem;
text-align: center;
font-weight: 500;
transform: translateY(-50%);
color: var(--black);
}
.circle .text .percent {
font-size: 0.875rem;
}
</style>
242 changes: 242 additions & 0 deletions src/components/home/FoldCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import ChevronIcon from "@/components/icons/IconChevron.vue";
import CircleProgress from "@/components/home/CircleProgress.vue";
import LoadingAnimationVue from "@/components/common/LoadingAnimation.vue";
import { useMonthLogStore } from "@/stores/monthlog";
import { useHomeStore } from "@/stores/home";
const props = defineProps<{
hour: number;
min: number;
isMonth?: boolean;
}>();
const { getGoalDateHour, getGoalMonthHour, setGoalDateHour, setGoalMonthHour } =
useHomeStore();
const monthStore = useMonthLogStore();
const { showIsLoading } = monthStore;
const isLoading = ref(showIsLoading());
const isOpen = ref(false);
const goalTimeSet = () => {
if (props.isMonth) {
return Number(getGoalMonthHour());
} else {
return Number(getGoalDateHour());
}
};
const goalTime = ref(goalTimeSet());
const colorSet = ref(props.isMonth);
watch(showIsLoading, (val) => {
isLoading.value = val;
});
watch(goalTime, (val) => {
if (props.isMonth) {
setGoalMonthHour(Number(val));
} else {
setGoalDateHour(Number(val));
}
});
const culculatePercent = () => {
if (goalTime.value === 0) return 0;
return Math.round(((props.hour + props.min / 60) / goalTime.value) * 100);
};
const clickHandler = () => {
isOpen.value = !isOpen.value;
if (isOpen.value) {
colorSet.value = false;
} else {
colorSet.value = props.isMonth;
}
};
const checkColor = () => {
if (colorSet.value) {
return "#ffffff";
}
};
const dayOptions = [
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
];
const monthOptions = [
80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360, 380,
400, 420,
];
</script>

<template>
<div class="wrap" :class="{ on: isOpen, primaryColor: colorSet }">
<div class="textWrap use tapHighlight" @click="clickHandler">
<h2>
<slot name="title"></slot>
</h2>
<div v-if="isLoading" class="loading"><LoadingAnimationVue /></div>
<div v-else>
<span class="timeNumber">
{{ props.hour }}
</span>
<span class="timeUnit">시간{{ " " }}</span>
<span class="timeNumber">
{{ props.min }}
</span>
<span class="timeUnit">분</span>
<div class="vIcon" :class="{ on: isOpen }">
<ChevronIcon :color="checkColor()" />
</div>
</div>
</div>
<div class="textWrap goal" :class="{ on: isOpen }">
<h2>목표 시간</h2>
<div>
<select v-if="!isMonth" v-model="goalTime" class="timeNumber select">
<option
v-for="index in dayOptions"
:key="index"
:value="index"
:selected="index === 4"
>
{{ index }}
</option>
</select>
<select v-else v-model="goalTime" class="timeNumber select">
<option
v-for="index in monthOptions"
:key="index"
:value="index"
:selected="index === 80"
>
{{ index }}
</option>
</select>
<span class="timeUnit">시간</span>
</div>
</div>
<CircleProgress :isOpen="isOpen" :percent="culculatePercent()" />
</div>
</template>

<style scoped>
.wrap {
width: 100%;
height: 80px;
background-color: #fff;
border-radius: 20px;
display: flex;
flex-direction: column;
transition: all 0.3s ease-in-out;
overflow: hidden;
padding: 26px 20px;
user-select: none;
}
.wrap.on {
height: 260px;
}
.wrap.primaryColor {
background-color: var(--color-primary);
}
.wrap.primaryColor .textWrap h2,
.wrap.primaryColor .textWrap .timeUnit,
.wrap.primaryColor .textWrap .timeNumber {
color: #fff;
}
.textWrap {
display: flex;
justify-content: space-between;
align-items: center;
vertical-align: top;
user-select: none;
}
.textWrap.use {
cursor: pointer;
}
.textWrap.use.on {
padding: 0;
}
.textWrap.goal {
margin-top: 60px;
margin-right: 20px;
transition: all 0.3s ease-in-out;
opacity: 0;
}
.textWrap.goal.on {
margin-top: 16px;
opacity: 1;
}
.loading .wrap {
width: 100px;
height: 27px;
padding: 0;
background-color: transparent;
}
h2 {
font-size: 1rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--black);
}
.timeNumber {
font-size: 1.25rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--black);
}
.goal .timeNumber {
width: 60px;
height: 30px;
border: none;
text-align: right;
margin-right: 2px;
font-size: 1.25rem;
font-weight: 700;
font-family: Inter, sans-serif;
color: var(--black);
}
.select {
direction: rtl;
cursor: pointer;
-o-appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: transparent;
}
.timeUnit {
font-size: 1rem;
font-weight: 600;
line-height: 1.5rem;
color: var(--black);
}
.vIcon {
display: inline-block;
position: relative;
margin-left: 10px;
transition: transform 0.3s linear;
}
.vIcon.on {
transform: rotate(90deg);
}
</style>
49 changes: 49 additions & 0 deletions src/components/home/UserNumCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
const props = defineProps<{
userNum: number;
}>();
</script>

<template>
<div class="wrap">
<h2>
<slot name="title"></slot>
</h2>
<div class="userNum">
{{ props.userNum }}<span class="timeUnit">명</span>
</div>
</div>
</template>

<style scoped>
.wrap {
padding: 20px;
background-color: #fff;
border-radius: 20px;
width: 100%;
height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
}
h2 {
font-size: 1rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--black);
}
.userNum {
font-size: 1.125rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--black);
}
.timeUnit {
font-size: 0.875rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--black);
}
</style>
50 changes: 50 additions & 0 deletions src/components/home/UserNumSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import UserNumCard from "@/components/home/UserNumCard.vue";
const props = defineProps<{
numberOfPeople: {
gaepo: number;
seocho: number;
};
isOnline: boolean;
}>();
</script>

<template>
<section class="userNumSection">
<h2 :class="{ online: props.isOnline }">실시간 현황</h2>
<div class="userNumCards">
<UserNumCard class="m-8" :userNum="props.numberOfPeople.gaepo ?? 0">
<template #title>개포</template>
</UserNumCard>
<UserNumCard class="m-8" :userNum="props.numberOfPeople.seocho ?? 0">
<template #title>서초</template>
</UserNumCard>
</div>
</section>
</template>

<style scoped>
.userNumSection {
margin-top: 16px;
user-select: none;
}
.userNumSection h2 {
font-size: 1.125rem;
font-weight: 700;
line-height: 1.5rem;
color: var(--color-heading);
margin-bottom: 8px;
}
.userNumSection h2.online {
color: var(--white);
}
.userNumCards {
display: flex;
justify-content: center;
grid-gap: 20px;
}
</style>
20 changes: 20 additions & 0 deletions src/components/icons/IconAttentionTriangle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<svg
width="37"
height="36"
viewBox="0 0 37 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20 27C20 27.8284 19.3284 28.5 18.5 28.5C17.6716 28.5 17 27.8284 17 27C17 26.1716 17.6716 25.5 18.5 25.5C19.3284 25.5 20 26.1716 20 27Z"
fill="black"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M18.5 3C19.0448 3 19.5467 3.29534 19.8112 3.77154L34.8112 30.7715C35.0693 31.2361 35.0623 31.8026 34.7928 32.2607C34.5233 32.7188 34.0315 33 33.5 33H3.5C2.96852 33 2.47672 32.7188 2.20719 32.2607C1.93767 31.8026 1.93065 31.2361 2.18876 30.7715L17.1888 3.77154C17.4533 3.29534 17.9552 3 18.5 3ZM6.04927 30H30.9507L18.5 7.58869L6.04927 30ZM18.5 13.5C19.3284 13.5 20 14.1716 20 15V21C20 21.8284 19.3284 22.5 18.5 22.5C17.6716 22.5 17 21.8284 17 21V15C17 14.1716 17.6716 13.5 18.5 13.5Z"
fill="black"
/>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconBook.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4 5C4 3.34315 5.34315 2 7 2H17C18.6569 2 20 3.34315 20 5V19C20 20.6569 18.6569 22 17 22H7C5.34315 22 4 20.6569 4 19V5ZM6 19C6 19.5523 6.44772 20 7 20H17C17.5523 20 18 19.5523 18 19V17.8293C17.6872 17.9398 17.3506 18 17 18H7C6.44772 18 6 18.4477 6 19ZM18 15C18 15.5523 17.5523 16 17 16H7C6.64936 16 6.31278 16.0602 6 16.1707V5C6 4.44772 6.44772 4 7 4H17C17.5523 4 18 4.44772 18 5V15ZM8 7C8 6.44772 8.44772 6 9 6H15C15.5523 6 16 6.44772 16 7C16 7.55228 15.5523 8 15 8H9C8.44772 8 8 7.55228 8 7ZM9 10C9 9.44772 9.44772 9 10 9H14C14.5523 9 15 9.44772 15 10C15 10.5523 14.5523 11 14 11H10C9.44772 11 9 10.5523 9 10Z"
fill="#9B9797"
/>
</svg>
</template>
22 changes: 22 additions & 0 deletions src/components/icons/IconCalendarFill.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
const props = defineProps<{
color?: string;
}>();
</script>

<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15 1C14.4477 1 14 1.44772 14 2H10C10 1.44772 9.55228 1 9 1H7C6.44772 1 6 1.44772 6 2H5C3.34315 2 2 3.34315 2 5V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V5C22 3.34315 20.6569 2 19 2H18C18 1.44772 17.5523 1 17 1H15ZM5 4C4.44772 4 4 4.44772 4 5V6H20V5C20 4.44772 19.5523 4 19 4H5ZM6.5 10C5.94772 10 5.5 10.4477 5.5 11V12C5.5 12.5523 5.94772 13 6.5 13H7.5C8.05228 13 8.5 12.5523 8.5 12V11C8.5 10.4477 8.05228 10 7.5 10H6.5ZM10.5 11C10.5 10.4477 10.9477 10 11.5 10H12.5C13.0523 10 13.5 10.4477 13.5 11V12C13.5 12.5523 13.0523 13 12.5 13H11.5C10.9477 13 10.5 12.5523 10.5 12V11ZM16.5 10C15.9477 10 15.5 10.4477 15.5 11V12C15.5 12.5523 15.9477 13 16.5 13H17.5C18.0523 13 18.5 12.5523 18.5 12V11C18.5 10.4477 18.0523 10 17.5 10H16.5ZM15.5 16C15.5 15.4477 15.9477 15 16.5 15H17.5C18.0523 15 18.5 15.4477 18.5 16V17C18.5 17.5523 18.0523 18 17.5 18H16.5C15.9477 18 15.5 17.5523 15.5 17V16ZM11.5 15C10.9477 15 10.5 15.4477 10.5 16V17C10.5 17.5523 10.9477 18 11.5 18H12.5C13.0523 18 13.5 17.5523 13.5 17V16C13.5 15.4477 13.0523 15 12.5 15H11.5ZM5.5 16C5.5 15.4477 5.94772 15 6.5 15H7.5C8.05228 15 8.5 15.4477 8.5 16V17C8.5 17.5523 8.05228 18 7.5 18H6.5C5.94772 18 5.5 17.5523 5.5 17V16Z"
v-bind:fill="props.color ? props.color : '#5B5B5B'"
/>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconCalendarLine.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15 1C14.4477 1 14 1.44772 14 2H10C10 1.44772 9.55228 1 9 1H7C6.44772 1 6 1.44772 6 2H5C3.34315 2 2 3.34315 2 5V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V5C22 3.34315 20.6569 2 19 2H18C18 1.44772 17.5523 1 17 1H15ZM5 4C4.44772 4 4 4.44772 4 5V6H20V5C20 4.44772 19.5523 4 19 4H5ZM4 19V8H20V19C20 19.5523 19.5523 20 19 20H5C4.44772 20 4 19.5523 4 19ZM5.5 11C5.5 10.4477 5.94772 10 6.5 10H7.5C8.05228 10 8.5 10.4477 8.5 11V12C8.5 12.5523 8.05228 13 7.5 13H6.5C5.94772 13 5.5 12.5523 5.5 12V11ZM5.5 16C5.5 15.4477 5.94772 15 6.5 15H7.5C8.05228 15 8.5 15.4477 8.5 16V17C8.5 17.5523 8.05228 18 7.5 18H6.5C5.94772 18 5.5 17.5523 5.5 17V16ZM10.5 11C10.5 10.4477 10.9477 10 11.5 10H12.5C13.0523 10 13.5 10.4477 13.5 11V12C13.5 12.5523 13.0523 13 12.5 13H11.5C10.9477 13 10.5 12.5523 10.5 12V11ZM10.5 16C10.5 15.4477 10.9477 15 11.5 15H12.5C13.0523 15 13.5 15.4477 13.5 16V17C13.5 17.5523 13.0523 18 12.5 18H11.5C10.9477 18 10.5 17.5523 10.5 17V16ZM15.5 11C15.5 10.4477 15.9477 10 16.5 10H17.5C18.0523 10 18.5 10.4477 18.5 11V12C18.5 12.5523 18.0523 13 17.5 13H16.5C15.9477 13 15.5 12.5523 15.5 12V11ZM15.5 16C15.5 15.4477 15.9477 15 16.5 15H17.5C18.0523 15 18.5 15.4477 18.5 16V17C18.5 17.5523 18.0523 18 17.5 18H16.5C15.9477 18 15.5 17.5523 15.5 17V16Z"
fill="#9B9797"
/>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M20 6L4 6C3.44772 6 3 6.44772 3 7L3 9.5H21L21 7C21 6.44772 20.5523 6 20 6ZM21 11.5H3V17C3 17.5523 3.44772 18 4 18L20 18C20.5523 18 21 17.5523 21 17V11.5ZM4 4L20 4C21.6569 4 23 5.34315 23 7L23 17C23 18.6569 21.6569 20 20 20L4 20C2.34315 20 1 18.6569 1 17L1 7C1 5.34315 2.34315 4 4 4Z"
fill="#9B9797"
/>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconChatConversation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.25 7C3.25 4.23858 5.48858 2 8.25 2H12C14.7614 2 17 4.23858 17 7V8.11774C19.1013 8.61728 20.75 10.6441 20.75 13V16.3761C20.75 16.961 20.921 17.5331 21.2419 18.0221L22.8361 20.4513C23.0377 20.7586 23.0545 21.1517 22.88 21.475C22.7054 21.7984 22.3675 22 22 22H12C9.23858 22 7 19.7614 7 17V16H2C1.63251 16 1.29462 15.7984 1.12004 15.475C0.945463 15.1517 0.962325 14.7586 1.16395 14.4513L2.75815 12.0221C3.07904 11.5331 3.25 10.961 3.25 10.3761V7ZM9 16V17C9 18.6569 10.3431 20 12 20H20.1476L19.5698 19.1194C19.0349 18.3044 18.75 17.3509 18.75 16.3761V13C18.75 11.723 17.9684 10.6709 17 10.227V11C17 13.7614 14.7614 16 12 16H9ZM3.85236 14L4.43024 13.1194C4.96506 12.3044 5.25 11.3509 5.25 10.3761V7C5.25 5.34315 6.59315 4 8.25 4H12C13.6569 4 15 5.34315 15 7V11C15 12.6569 13.6569 14 12 14H3.85236Z"
fill="#9B9797"
/>
</svg>
</template>
20 changes: 20 additions & 0 deletions src/components/icons/IconChevron.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
const props = defineProps<{
color?: string;
}>();
</script>

<template>
<svg
width="8"
height="12"
viewBox="0 0 8 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.792894 11.7071C0.402369 11.3166 0.402369 10.6834 0.792894 10.2929L5.08579 6L0.792893 1.70711C0.402368 1.31658 0.402368 0.683417 0.792893 0.292893C1.18342 -0.0976315 1.81658 -0.0976315 2.20711 0.292893L7.20711 5.29289C7.59763 5.68342 7.59763 6.31658 7.20711 6.70711L2.20711 11.7071C1.81658 12.0976 1.18342 12.0976 0.792894 11.7071Z"
v-bind:fill="props.color ? props.color : '#9B9797'"
/>
</svg>
</template>
22 changes: 22 additions & 0 deletions src/components/icons/IconHomeFill.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
const props = defineProps<{
color?: string;
}>();
</script>

<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.2929 2.29289C11.6834 1.90237 12.3166 1.90237 12.7071 2.29289L21.7071 11.2929C22.0976 11.6834 22.0976 12.3166 21.7071 12.7071C21.3166 13.0976 20.6834 13.0976 20.2929 12.7071L20 12.4142V21C20 21.5523 19.5523 22 19 22H14C13.4477 22 13 21.5523 13 21V17H11V21C11 21.5523 10.5523 22 10 22H5C4.44772 22 4 21.5523 4 21V12.4142L3.70711 12.7071C3.31658 13.0976 2.68342 13.0976 2.29289 12.7071C1.90237 12.3166 1.90237 11.6834 2.29289 11.2929L11.2929 2.29289Z"
v-bind:fill="props.color ? props.color : '#5B5B5B'"
/>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconHomeLine.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.2929 2.29289C11.6834 1.90237 12.3166 1.90237 12.7071 2.29289L19.7068 9.29254C19.7069 9.29266 19.707 9.29278 19.7071 9.29289L21.7071 11.2929C22.0976 11.6834 22.0976 12.3166 21.7071 12.7071C21.3166 13.0976 20.6834 13.0976 20.2929 12.7071L20 12.4142V21C20 21.5523 19.5523 22 19 22H14C13.4477 22 13 21.5523 13 21V17H11V21C11 21.5523 10.5523 22 10 22H5C4.44772 22 4 21.5523 4 21V12.4142L3.70711 12.7071C3.31658 13.0976 2.68342 13.0976 2.29289 12.7071C1.90237 12.3166 1.90237 11.6834 2.29289 11.2929L4.29289 9.29289C4.29296 9.29282 4.29303 9.29276 4.2931 9.29269L11.2929 2.29289ZM6 10.4142L12 4.41421L18 10.4142V20H15V16C15 15.4477 14.5523 15 14 15H10C9.44772 15 9 15.4477 9 16V20H6V10.4142Z"
fill="#9B9797"
/>
</svg>
</template>
20 changes: 20 additions & 0 deletions src/components/icons/IconInfomationCircle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM12 11C12.5523 11 13 11.4477 13 12V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V12C11 11.4477 11.4477 11 12 11Z"
fill="#9B9797"
/>
<path
d="M13 8C13 8.55228 12.5523 9 12 9C11.4477 9 11 8.55228 11 8C11 7.44772 11.4477 7 12 7C12.5523 7 13 7.44772 13 8Z"
fill="#9B9797"
/>
</svg>
</template>
32 changes: 32 additions & 0 deletions src/components/icons/IconLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<svg
width="412"
height="412"
viewBox="0 0 412 412"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="412" height="412" fill="url(#paint0_linear_16_545)" />
<path
d="M43.2967 364.857V289.948H59.1344V320.855H91.2853V289.948H107.086V364.857H91.2853V333.913H59.1344V364.857H43.2967ZM125.699 364.857H108.727L134.587 289.948H154.997L180.82 364.857H163.848L145.084 307.066H144.499L125.699 364.857ZM124.638 335.413H164.726V347.776H124.638V335.413ZM245.135 289.948V364.857H231.455L198.865 317.71H198.317V364.857H182.479V289.948H196.378L228.712 337.059H229.37V289.948H245.135ZM250.863 364.857V289.948H301.339V303.006H266.701V320.855H298.742V333.913H266.701V351.799H301.485V364.857H250.863Z"
fill="white"
/>
<path
d="M44.5403 261.857V255.273L69.2662 228.206C72.1679 225.036 74.5576 222.281 76.4352 219.94C78.3128 217.575 79.7027 215.356 80.6049 213.283C81.5315 211.186 81.9948 208.991 81.9948 206.699C81.9948 204.066 81.3609 201.786 80.0929 199.859C78.8493 197.933 77.1423 196.446 74.9721 195.397C72.8019 194.349 70.3635 193.824 67.6568 193.824C64.7794 193.824 62.2678 194.422 60.122 195.617C58.0005 196.787 56.3546 198.433 55.1841 200.554C54.0381 202.676 53.465 205.163 53.465 208.016H44.8329C44.8329 203.627 45.8449 199.774 47.8688 196.458C49.8927 193.141 52.6481 190.557 56.1351 188.704C59.6465 186.85 63.5846 185.924 67.9494 185.924C72.3386 185.924 76.2279 186.85 79.6174 188.704C83.0068 190.557 85.6647 193.056 87.5911 196.202C89.5175 199.347 90.4806 202.847 90.4806 206.699C90.4806 209.455 89.9808 212.149 88.981 214.783C88.0056 217.392 86.2987 220.306 83.8603 223.525C81.4462 226.719 78.0933 230.62 73.8017 235.229L56.9764 253.225V253.81H91.7974V261.857H44.5403ZM96.1814 246.495V239.033L129.1 186.948H134.514V198.506H130.856L105.984 237.863V238.448H150.315V246.495H96.1814ZM131.441 261.857V244.227V240.752V186.948H140.073V261.857H131.441Z"
fill="white"
/>
<defs>
<linearGradient
id="paint0_linear_16_545"
x1="357.067"
y1="45.4916"
x2="216.092"
y2="417.507"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#0EA7A9" stop-opacity="0.04" />
<stop offset="1" stop-color="#735BF2" />
</linearGradient>
</defs>
</svg>
</template>
16 changes: 16 additions & 0 deletions src/components/icons/IconLogout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4 5C4 3.34315 5.34315 2 7 2H17C18.6569 2 20 3.34315 20 5V19C20 20.6569 18.6569 22 17 22H7C5.34315 22 4 20.6569 4 19C4 18.4477 4.44772 18 5 18C5.55228 18 6 18.4477 6 19C6 19.5523 6.44772 20 7 20H17C17.5523 20 18 19.5523 18 19V5C18 4.44772 17.5523 4 17 4H7C6.44772 4 6 4.44772 6 5C6 5.55228 5.55228 6 5 6C4.44772 6 4 5.55228 4 5ZM8.70711 7.29289C9.09763 7.68342 9.09763 8.31658 8.70711 8.70711L6.41421 11L15 11C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13L6.41421 13L8.70711 15.2929C9.09763 15.6834 9.09763 16.3166 8.70711 16.7071C8.31658 17.0976 7.68342 17.0976 7.29289 16.7071L3.29289 12.7071C2.90237 12.3166 2.90237 11.6834 3.29289 11.2929L7.29289 7.29289C7.68342 6.90237 8.31658 6.90237 8.70711 7.29289Z"
fill="#9B9797"
/>
</svg>
</template>
22 changes: 22 additions & 0 deletions src/components/icons/IconMoreFill.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
const props = defineProps<{
color?: string;
}>();
</script>

<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5 2C3.34315 2 2 3.34315 2 5V19C2 20.6569 3.34315 22 5 22H19C20.6569 22 22 20.6569 22 19V5C22 3.34315 20.6569 2 19 2H5ZM7 17H17C17.5523 17 18 16.5523 18 16C18 15.4477 17.5523 15 17 15H7C6.44772 15 6 15.4477 6 16C6 16.5523 6.44772 17 7 17ZM17 13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13ZM7 9H17C17.5523 9 18 8.55228 18 8C18 7.44772 17.5523 7 17 7H7C6.44772 7 6 7.44772 6 8C6 8.55229 6.44772 9 7 9Z"
v-bind:fill="props.color ? props.color : '#5B5B5B'"
/>
</svg>
</template>
22 changes: 22 additions & 0 deletions src/components/icons/IconMoreLine.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 7H19C19.5523 7 20 6.55228 20 6C20 5.44772 19.5523 5 19 5H5C4.44772 5 4 5.44772 4 6C4 6.55228 4.44772 7 5 7Z"
fill="#9B9797"
/>
<path
d="M19 13H5C4.44772 13 4 12.5523 4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13Z"
fill="#9B9797"
/>
<path
d="M19 19H5C4.44772 19 4 18.5523 4 18C4 17.4477 4.44772 17 5 17H19C19.5523 17 20 17.4477 20 18C20 18.5523 19.5523 19 19 19Z"
fill="#9B9797"
/>
</svg>
</template>
Loading

0 comments on commit a59a6d8

Please sign in to comment.