-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
메인 서비스 배포를 위한 PR
There are no files selected for viewing
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' | ||
} | ||
} |
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 |
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" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/// <reference types="vite/client" /> |
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> |
This file was deleted.
This file was deleted.
Large diffs are not rendered by default.
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" | ||
} | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
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> |
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); | ||
} | ||
); |
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;`; | ||
}; |
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; | ||
}; |
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"; |
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; | ||
}; |
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; | ||
}; |
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; | ||
} |
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; | ||
} */ | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
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> |
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> |
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> |
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> |
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> |
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> |
This file was deleted.
This file was deleted.
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> |
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> |
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> |
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> |
This file was deleted.
This file was deleted.
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> |
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> |
This file was deleted.
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |